Skip to content

Commit 03c7919

Browse files
committed
SIL: Add fields to SILFunctionType for substituted function types.
https://forums.swift.org/t/improving-the-representation-of-polymorphic-interfaces-in-sil-with-substituted-function-types/29711 This prepares SIL to be able to more accurately preserve the calling convention of polymorphic generic interfaces by letting the type system represent "substituted function types". We add a couple of fields to SILFunctionType to support this: - A substitution map, accessed by `getSubstitutions()`, which maps the generic signature of the function to its concrete implementation. This will allow, for instance, a protocol witness for a requirement of type `<Self: P> (Self, ...) -> ...` for a concrete conforming type `Foo` to express its type as `<Self: P> (Self, ...) -> ... for <Foo>`, preserving the relation to the protocol interface without relying on the pile of hacks that is the `witness_method` protocol. - A bool for whether the generic signature of the function is "implied" by the substitutions. If true, the generic signature isn't really part of the calling convention of the function. This will allow closure types to distinguish a closure being passed to a generic function, like `<T, U> in (*T, *U) -> T for <Int, String>`, from the concrete type `(*Int, *String) -> Int`, which will make it easier for us to differentiate the representation of those as types, for instance by giving them different pointer authentication discriminators to harden arm64e code. This patch is currently NFC, it just introduces the new APIs and takes a first pass at updating code to use them. Much more work will need to be done once we start exercising these new fields. This does bifurcate some existing APIs: - SILFunctionType now has two accessors to get its generic signature. `getSubstGenericSignature` gets the generic signature that is used to apply its substitution map, if any. `getInvocationGenericSignature` gets the generic signature used to invoke the function at apply sites. These differ if the generic signature is implied. - SILParameterInfo and SILResultInfo values carry the unsubstituted types of the parameters and results of the function. They now have two APIs to get that type. `getInterfaceType` returns the unsubstituted type of the generic interface, and `getArgumentType`/`getReturnValueType` produce the substituted type that is used at apply sites.
1 parent a2b119c commit 03c7919

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

72 files changed

+1173
-645
lines changed

include/swift/AST/Types.h

+92-28
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ class NominalTypeDecl;
7171
class GenericTypeDecl;
7272
class EnumDecl;
7373
class EnumElementDecl;
74+
class SILFunctionType;
7475
class StructDecl;
7576
class ProtocolDecl;
7677
class TypeVariableType;
@@ -81,6 +82,8 @@ class ProtocolConformance;
8182
enum PointerTypeKind : unsigned;
8283
struct ValueOwnershipKind;
8384

85+
typedef CanTypeWrapper<SILFunctionType> CanSILFunctionType;
86+
8487
enum class TypeKind : uint8_t {
8588
#define TYPE(id, parent) id,
8689
#define LAST_TYPE(id) Last_Type = id,
@@ -3409,9 +3412,19 @@ class SILParameterInfo {
34093412
assert(type->isLegalSILType() && "SILParameterInfo has illegal SIL type");
34103413
}
34113414

3412-
CanType getType() const {
3415+
/// Return the unsubstituted parameter type that describes the abstract
3416+
/// calling convention of the parameter.
3417+
///
3418+
/// For most purposes, you probably want \c getArgumentType .
3419+
CanType getInterfaceType() const {
34133420
return TypeAndConvention.getPointer();
34143421
}
3422+
3423+
/// Return the type of a call argument matching this parameter.
3424+
///
3425+
/// \c t must refer back to the function type this is a parameter for.
3426+
CanType getArgumentType(SILModule &M,
3427+
const SILFunctionType *t) const;
34153428
ParameterConvention getConvention() const {
34163429
return TypeAndConvention.getInt();
34173430
}
@@ -3456,10 +3469,12 @@ class SILParameterInfo {
34563469
/// storage. Therefore they will be passed using an indirect formal
34573470
/// convention, and this method will return an address type. However, in
34583471
/// canonical SIL the opaque arguments might not have an address type.
3459-
SILType getSILStorageType() const; // in SILFunctionConventions.h
3472+
SILType getSILStorageType(SILModule &M,
3473+
const SILFunctionType *t) const; // in SILFunctionConventions.h
3474+
SILType getSILStorageInterfaceType() const;
34603475

34613476
/// Return a version of this parameter info with the type replaced.
3462-
SILParameterInfo getWithType(CanType type) const {
3477+
SILParameterInfo getWithInterfaceType(CanType type) const {
34633478
return SILParameterInfo(type, getConvention());
34643479
}
34653480

@@ -3470,11 +3485,11 @@ class SILParameterInfo {
34703485
/// Type::transform does.
34713486
template<typename F>
34723487
SILParameterInfo map(const F &fn) const {
3473-
return getWithType(fn(getType()));
3488+
return getWithInterfaceType(fn(getInterfaceType()));
34743489
}
34753490

34763491
void profile(llvm::FoldingSetNodeID &id) {
3477-
id.AddPointer(getType().getPointer());
3492+
id.AddPointer(getInterfaceType().getPointer());
34783493
id.AddInteger((unsigned)getConvention());
34793494
}
34803495

@@ -3489,7 +3504,8 @@ class SILParameterInfo {
34893504
}
34903505

34913506
bool operator==(SILParameterInfo rhs) const {
3492-
return getType() == rhs.getType() && getConvention() == rhs.getConvention();
3507+
return getInterfaceType() == rhs.getInterfaceType()
3508+
&& getConvention() == rhs.getConvention();
34933509
}
34943510
bool operator!=(SILParameterInfo rhs) const {
34953511
return !(*this == rhs);
@@ -3541,9 +3557,20 @@ class SILResultInfo {
35413557
assert(type->isLegalSILType() && "SILResultInfo has illegal SIL type");
35423558
}
35433559

3544-
CanType getType() const {
3560+
/// Return the unsubstituted parameter type that describes the abstract
3561+
/// calling convention of the parameter.
3562+
///
3563+
/// For most purposes, you probably want \c getReturnValueType .
3564+
CanType getInterfaceType() const {
35453565
return TypeAndConvention.getPointer();
35463566
}
3567+
3568+
/// The type of a return value corresponding to this result.
3569+
///
3570+
/// \c t must refer back to the function type this is a parameter for.
3571+
CanType getReturnValueType(SILModule &M,
3572+
const SILFunctionType *t) const;
3573+
35473574
ResultConvention getConvention() const {
35483575
return TypeAndConvention.getInt();
35493576
}
@@ -3553,10 +3580,11 @@ class SILResultInfo {
35533580
/// storage. Therefore they will be returned using an indirect formal
35543581
/// convention, and this method will return an address type. However, in
35553582
/// canonical SIL the opaque results might not have an address type.
3556-
SILType getSILStorageType() const; // in SILFunctionConventions.h
3557-
3583+
SILType getSILStorageType(SILModule &M,
3584+
const SILFunctionType *t) const; // in SILFunctionConventions.h
3585+
SILType getSILStorageInterfaceType() const;
35583586
/// Return a version of this result info with the type replaced.
3559-
SILResultInfo getWithType(CanType type) const {
3587+
SILResultInfo getWithInterfaceType(CanType type) const {
35603588
return SILResultInfo(type, getConvention());
35613589
}
35623590

@@ -3577,7 +3605,7 @@ class SILResultInfo {
35773605
/// Type::transform does.
35783606
template <typename F>
35793607
SILResultInfo map(F &&fn) const {
3580-
return getWithType(fn(getType()));
3608+
return getWithInterfaceType(fn(getInterfaceType()));
35813609
}
35823610

35833611
void profile(llvm::FoldingSetNodeID &id) {
@@ -3616,13 +3644,13 @@ class SILYieldInfo : public SILParameterInfo {
36163644
: SILParameterInfo(type, conv) {
36173645
}
36183646

3619-
SILYieldInfo getWithType(CanType type) const {
3647+
SILYieldInfo getWithInterfaceType(CanType type) const {
36203648
return SILYieldInfo(type, getConvention());
36213649
}
36223650

36233651
template<typename F>
36243652
SILYieldInfo map(const F &fn) const {
3625-
return getWithType(fn(getType()));
3653+
return getWithInterfaceType(fn(getInterfaceType()));
36263654
}
36273655
};
36283656

@@ -3641,8 +3669,6 @@ enum class SILCoroutineKind : uint8_t {
36413669
YieldMany,
36423670
};
36433671

3644-
class SILFunctionType;
3645-
typedef CanTypeWrapper<SILFunctionType> CanSILFunctionType;
36463672
class SILFunctionConventions;
36473673

36483674
/// SILFunctionType - The lowered type of a function value, suitable
@@ -3799,8 +3825,9 @@ class SILFunctionType final : public TypeBase, public llvm::FoldingSetNode,
37993825
// CanType? // if !isCoro && NumAnyResults > 1, formal result cache
38003826
// CanType? // if !isCoro && NumAnyResults > 1, all result cache
38013827

3802-
CanGenericSignature GenericSig;
3828+
llvm::PointerIntPair<CanGenericSignature, 1, bool> GenericSigAndIsImplied;
38033829
Optional<ProtocolConformanceRef> WitnessMethodConformance;
3830+
SubstitutionMap Substitutions;
38043831

38053832
MutableArrayRef<SILParameterInfo> getMutableParameters() {
38063833
return {getTrailingObjects<SILParameterInfo>(), NumParameters};
@@ -3861,6 +3888,8 @@ class SILFunctionType final : public TypeBase, public llvm::FoldingSetNode,
38613888
ArrayRef<SILYieldInfo> yieldResults,
38623889
ArrayRef<SILResultInfo> normalResults,
38633890
Optional<SILResultInfo> errorResult,
3891+
SubstitutionMap substitutions,
3892+
bool genericSigIsImplied,
38643893
const ASTContext &ctx,
38653894
RecursiveTypeProperties properties,
38663895
Optional<ProtocolConformanceRef> witnessMethodConformance);
@@ -3874,6 +3903,8 @@ class SILFunctionType final : public TypeBase, public llvm::FoldingSetNode,
38743903
ArrayRef<SILYieldInfo> interfaceYields,
38753904
ArrayRef<SILResultInfo> interfaceResults,
38763905
Optional<SILResultInfo> interfaceErrorResult,
3906+
SubstitutionMap substitutions,
3907+
bool genericSigIsImplied,
38773908
const ASTContext &ctx,
38783909
Optional<ProtocolConformanceRef> witnessMethodConformance = None);
38793910

@@ -3894,7 +3925,7 @@ class SILFunctionType final : public TypeBase, public llvm::FoldingSetNode,
38943925
/// - a single indirect result and no direct results.
38953926
///
38963927
/// If the result is formally indirect, return the empty tuple.
3897-
SILType getFormalCSemanticResult();
3928+
SILType getFormalCSemanticResult(SILModule &M);
38983929

38993930
/// Return the convention under which the callee is passed, if this
39003931
/// is a thick non-block callee.
@@ -4003,14 +4034,15 @@ class SILFunctionType final : public TypeBase, public llvm::FoldingSetNode,
40034034
/// this function depends on the current SIL stage and is known by
40044035
/// SILFunctionConventions. It may be a wider tuple that includes formally
40054036
/// indirect results.
4006-
SILType getDirectFormalResultsType();
4037+
SILType getDirectFormalResultsType(SILModule &M);
40074038

40084039
/// Get a single non-address SILType for all SIL results regardless of whether
40094040
/// they are formally indirect. The actual SIL result type of an apply
40104041
/// instruction that calls this function depends on the current SIL stage and
40114042
/// is known by SILFunctionConventions. It may be a narrower tuple that omits
40124043
/// formally indirect results.
4013-
SILType getAllResultsType();
4044+
SILType getAllResultsSubstType(SILModule &M);
4045+
SILType getAllResultsInterfaceType();
40144046

40154047
/// Does this function have a blessed Swift-native error result?
40164048
bool hasErrorResult() const {
@@ -4041,15 +4073,36 @@ class SILFunctionType final : public TypeBase, public llvm::FoldingSetNode,
40414073
return getParameters().back();
40424074
}
40434075

4044-
bool isPolymorphic() const { return !GenericSig.isNull(); }
4045-
CanGenericSignature getGenericSignature() const { return GenericSig; }
4076+
/// Get the generic signature used to apply the substitutions of a substituted function type
4077+
CanGenericSignature getSubstGenericSignature() const {
4078+
return GenericSigAndIsImplied.getPointer();
4079+
}
4080+
/// Get the generic signature used by callers to invoke the function.
4081+
CanGenericSignature getInvocationGenericSignature() const {
4082+
if (isGenericSignatureImplied()) {
4083+
return CanGenericSignature();
4084+
} else {
4085+
return getSubstGenericSignature();
4086+
}
4087+
}
4088+
4089+
bool isGenericSignatureImplied() const {
4090+
return GenericSigAndIsImplied.getInt();
4091+
}
4092+
SubstitutionMap getSubstitutions() const {
4093+
return Substitutions;
4094+
}
4095+
4096+
bool isPolymorphic() const {
4097+
return !getInvocationGenericSignature().isNull();
4098+
}
40464099

4047-
CanType getSelfInstanceType() const;
4100+
CanType getSelfInstanceType(SILModule &M) const;
40484101

40494102
/// If this is a @convention(witness_method) function with a class
40504103
/// constrained self parameter, return the class constraint for the
40514104
/// Self type.
4052-
ClassDecl *getWitnessMethodClass() const;
4105+
ClassDecl *getWitnessMethodClass(SILModule &M) const;
40534106

40544107
/// If this is a @convention(witness_method) function, return the conformance
40554108
/// for which the method is a witness.
@@ -4095,7 +4148,11 @@ class SILFunctionType final : public TypeBase, public llvm::FoldingSetNode,
40954148
getRepresentation() == SILFunctionTypeRepresentation::Thick;
40964149
}
40974150

4098-
bool isNoReturnFunction() const; // Defined in SILType.cpp
4151+
bool isNoReturnFunction(SILModule &M) const; // Defined in SILType.cpp
4152+
4153+
/// Create a SILFunctionType with the same parameters, results, and attributes as this one, but with
4154+
/// a different set of substitutions.
4155+
CanSILFunctionType withSubstitutions(SubstitutionMap subs) const;
40994156

41004157
class ABICompatibilityCheckResult {
41014158
friend class SILFunctionType;
@@ -4139,19 +4196,24 @@ class SILFunctionType final : public TypeBase, public llvm::FoldingSetNode,
41394196
/// assertions are disabled, this just returns true.
41404197
ABICompatibilityCheckResult
41414198
isABICompatibleWith(CanSILFunctionType other,
4142-
SILFunction *context = nullptr) const;
4199+
SILFunction &context) const;
41434200

41444201
CanSILFunctionType substGenericArgs(SILModule &silModule,
41454202
SubstitutionMap subs);
41464203
CanSILFunctionType substGenericArgs(SILModule &silModule,
41474204
TypeSubstitutionFn subs,
41484205
LookupConformanceFn conformances);
41494206

4207+
SILType substInterfaceType(SILModule &M,
4208+
SILType interfaceType) const;
4209+
41504210
void Profile(llvm::FoldingSetNodeID &ID) {
4151-
Profile(ID, getGenericSignature(), getExtInfo(), getCoroutineKind(),
4211+
Profile(ID, getSubstGenericSignature(), getExtInfo(), getCoroutineKind(),
41524212
getCalleeConvention(), getParameters(), getYields(),
41534213
getResults(), getOptionalErrorResult(),
4154-
getWitnessMethodConformanceOrNone());
4214+
getWitnessMethodConformanceOrNone(),
4215+
isGenericSignatureImplied(),
4216+
getSubstitutions());
41554217
}
41564218
static void Profile(llvm::FoldingSetNodeID &ID,
41574219
GenericSignature genericSig,
@@ -4162,7 +4224,9 @@ class SILFunctionType final : public TypeBase, public llvm::FoldingSetNode,
41624224
ArrayRef<SILYieldInfo> yields,
41634225
ArrayRef<SILResultInfo> results,
41644226
Optional<SILResultInfo> errorResult,
4165-
Optional<ProtocolConformanceRef> conformance);
4227+
Optional<ProtocolConformanceRef> conformance,
4228+
bool isGenericSigImplied,
4229+
SubstitutionMap substitutions);
41664230

41674231
// Implement isa/cast/dyncast/etc.
41684232
static bool classof(const TypeBase *T) {

0 commit comments

Comments
 (0)