Skip to content

Commit 27d7117

Browse files
committed
SIL: Type lowering for function values with substituted SIL function types.
After converting a function type to its corresponding SIL type by our usual rules, extract the substituted generic signature and common interface type for all concrete function types that the function could be called as. We don't currently have any use cases for distinguishing types at finer granularity than generic class-constrained/generic unconstrained/not-generic, so to reduce the amount of conversion noise in SIL, generate the most general generic signature possible, by extracting every position with a dependent generic parameter or associated type thereof into a separate generic parameter in the substituted signature, discarding all constraints except for `AnyObject` layout constraints. This way, if `foo<T>(x: (T, T) -> ())` calls `bar<T, U>(x: (T, U) -> ())` and forwards `x` along, the two closures that only vary in genericity don't need a conversion even with substituted function types, because they'll both have the same abstract lowering `<A, B> (A, B) -> ()` with different substitutions.
1 parent 0367f8a commit 27d7117

File tree

4 files changed

+288
-4
lines changed

4 files changed

+288
-4
lines changed

include/swift/AST/LayoutConstraint.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,10 @@ class ASTPrinter;
3636
enum class LayoutConstraintKind : uint8_t {
3737
// It is not a known layout constraint.
3838
UnknownLayout,
39-
// It is a layout constraint representing a trivial type of an unknown size.
39+
// It is a layout constraint representing a trivial type of a known size.
4040
TrivialOfExactSize,
41-
// It is a layout constraint representing a trivial type of an unknown size.
41+
// It is a layout constraint representing a trivial type of a size known to
42+
// be no larger than a given size.
4243
TrivialOfAtMostSize,
4344
// It is a layout constraint representing a trivial type of an unknown size.
4445
Trivial,

lib/SIL/SILFunctionType.cpp

Lines changed: 154 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1059,11 +1059,163 @@ static CanSILFunctionType getSILFunctionType(
10591059
.withIsPseudogeneric(pseudogeneric)
10601060
.withNoEscape(extInfo.isNoEscape());
10611061

1062+
// Extract the abstract generic calling convention of the function into a
1063+
// substituted generic signature for the function type.
1064+
//
1065+
// This signature only needs to consider the general calling convention,
1066+
// so it can reduce away protocol and base class constraints aside from
1067+
// `AnyObject`. We wanto similar-shaped generic function types to remain
1068+
// canonically equivalent, like `(T, U) -> ()`, `(T, T) -> ()`,
1069+
// `(U, T) -> ()` or `(T, T.A) -> ()` when given substitutions that produce
1070+
// the same function types, so we also introduce a new generic argument for
1071+
// each position where we see a dependent type, and canonicalize the order in
1072+
// which we see independent generic arguments.
1073+
//
1074+
bool impliedSignature = false;
1075+
SubstitutionMap substitutions;
1076+
if (TC.Context.LangOpts.EnableSubstSILFunctionTypesForFunctionValues
1077+
// We don't currently use substituted function types for generic function
1078+
// type lowering, though we should for generic methods on classes and
1079+
// protocols.
1080+
&& !genericSig) {
1081+
class SubstFunctionTypeCollector {
1082+
public:
1083+
TypeConverter &TC;
1084+
const bool mapReplacementsOutOfContext;
1085+
1086+
SmallVector<GenericTypeParamType *, 4> substGenericParams;
1087+
SmallVector<Requirement, 4> substRequirements;
1088+
SmallVector<Type, 4> substReplacements;
1089+
1090+
SubstFunctionTypeCollector(TypeConverter &TC,
1091+
bool mapReplacementsOutOfContext)
1092+
: TC(TC), mapReplacementsOutOfContext(mapReplacementsOutOfContext) {}
1093+
SubstFunctionTypeCollector(const SubstFunctionTypeCollector &) = delete;
1094+
1095+
// TypeSubstitutionFn
1096+
Type operator()(SubstitutableType *t) {
1097+
ArchetypeType *archetype = dyn_cast<ArchetypeType>(t);
1098+
1099+
// We should only substitute contextual archetypes here.
1100+
// Opened existential or opaque archetypes are not freely substitutable
1101+
// so ought to be left as is.
1102+
assert(isa<PrimaryArchetypeType>(archetype->getRoot()));
1103+
1104+
if (!archetype)
1105+
archetype = TC.getCurGenericContext()->getGenericEnvironment()
1106+
->mapTypeIntoContext(t)
1107+
->castTo<ArchetypeType>();
1108+
1109+
// Replace every dependent type we see with a fresh type variable in
1110+
// the substituted signature.
1111+
auto paramIndex = substGenericParams.size();
1112+
auto param = CanGenericTypeParamType::get(0, paramIndex, TC.Context);
1113+
1114+
substGenericParams.push_back(param);
1115+
Type replacementTy = t;
1116+
if (mapReplacementsOutOfContext) {
1117+
replacementTy = t->mapTypeOutOfContext();
1118+
}
1119+
substReplacements.push_back(replacementTy);
1120+
1121+
// Preserve the layout constraint, if any, on the archetype in the
1122+
// generic signature, generalizing away some constraints that
1123+
// shouldn't affect ABI substitutability.
1124+
if (auto layout = archetype->getLayoutConstraint()) {
1125+
switch (layout->getKind()) {
1126+
// Keep these layout constraints as is.
1127+
case LayoutConstraintKind::RefCountedObject:
1128+
case LayoutConstraintKind::TrivialOfAtMostSize:
1129+
break;
1130+
1131+
case LayoutConstraintKind::UnknownLayout:
1132+
case LayoutConstraintKind::Trivial:
1133+
// These constraints don't really constrain the ABI, so we can
1134+
// eliminate them.
1135+
layout = LayoutConstraint();
1136+
break;
1137+
1138+
// Replace these specific constraints with one of the more general
1139+
// constraints above.
1140+
case LayoutConstraintKind::NativeClass:
1141+
case LayoutConstraintKind::Class:
1142+
case LayoutConstraintKind::NativeRefCountedObject:
1143+
// These can all be generalized to RefCountedObject.
1144+
layout = LayoutConstraint::getLayoutConstraint(
1145+
LayoutConstraintKind::RefCountedObject);
1146+
break;
1147+
1148+
case LayoutConstraintKind::TrivialOfExactSize:
1149+
// Generalize to TrivialOfAtMostSize.
1150+
layout = LayoutConstraint::getLayoutConstraint(
1151+
LayoutConstraintKind::TrivialOfAtMostSize,
1152+
layout->getTrivialSizeInBits(),
1153+
layout->getAlignmentInBits(),
1154+
TC.Context);
1155+
break;
1156+
}
1157+
1158+
if (layout)
1159+
substRequirements.push_back(
1160+
Requirement(RequirementKind::Layout, param, layout));
1161+
}
1162+
1163+
return param;
1164+
}
1165+
1166+
// LookupConformanceFn
1167+
ProtocolConformanceRef operator()(CanType dependentType,
1168+
Type conformingReplacementType,
1169+
ProtocolDecl *conformedProtocol) {
1170+
// We should only substitute with other dependent types, so that an
1171+
// abstract conformance ref is sufficient.
1172+
assert(conformingReplacementType->isTypeParameter());
1173+
return ProtocolConformanceRef(conformedProtocol);
1174+
}
1175+
};
1176+
1177+
SubstFunctionTypeCollector collector(TC,
1178+
substFnInterfaceType->hasTypeParameter());
1179+
auto contextSig = TC.getCurGenericContext();
1180+
1181+
auto collectSubstitutions = [&](CanType t) -> CanType {
1182+
if (t->hasTypeParameter()) {
1183+
t = contextSig->getGenericEnvironment()
1184+
->mapTypeIntoContext(t)
1185+
->getCanonicalType(contextSig);
1186+
}
1187+
1188+
return CanType(t.subst(collector, collector));
1189+
};
1190+
1191+
for (auto &input : inputs) {
1192+
input = input.getWithInterfaceType(
1193+
collectSubstitutions(input.getInterfaceType()));
1194+
}
1195+
for (auto &yield : yields) {
1196+
yield = yield.getWithInterfaceType(
1197+
collectSubstitutions(yield.getInterfaceType()));
1198+
}
1199+
for (auto &result : results) {
1200+
result = result.getWithInterfaceType(
1201+
collectSubstitutions(result.getInterfaceType()));
1202+
}
1203+
1204+
if (!collector.substGenericParams.empty()) {
1205+
genericSig = GenericSignature::get(collector.substGenericParams,
1206+
collector.substRequirements)
1207+
->getCanonicalSignature();
1208+
substitutions = SubstitutionMap::get(genericSig,
1209+
collector.substReplacements,
1210+
ArrayRef<ProtocolConformanceRef>());
1211+
impliedSignature = true;
1212+
}
1213+
}
10621214

10631215
return SILFunctionType::get(genericSig, silExtInfo, coroutineKind,
10641216
calleeConvention, inputs, yields,
10651217
results, errorResult,
1066-
SubstitutionMap(), false,
1218+
substitutions, impliedSignature,
10671219
TC.Context, witnessMethodConformance);
10681220
}
10691221

@@ -3049,7 +3201,7 @@ SILFunctionType::withSubstitutions(SubstitutionMap subs) const {
30493201
getExtInfo(), getCoroutineKind(),
30503202
getCalleeConvention(),
30513203
getParameters(), getYields(), getResults(),
3052-
getErrorResult(),
3204+
getOptionalErrorResult(),
30533205
subs, isGenericSignatureImplied(),
30543206
const_cast<SILFunctionType*>(this)->getASTContext());
30553207
}

lib/SIL/TypeLowering.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1547,6 +1547,8 @@ CanType TypeConverter::computeLoweredRValueType(AbstractionPattern origType,
15471547
// - types are turned into their unbridged equivalents, depending
15481548
// on the abstract CC
15491549
// - ownership conventions are deduced
1550+
// - a minimal substituted generic signature is extracted to represent
1551+
// possible ABI-compatible substitutions
15501552
if (auto substFnType = dyn_cast<AnyFunctionType>(substType)) {
15511553
// If the formal type uses a C convention, it is not formally
15521554
// abstractable, and it may be subject to implicit bridging.
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
// RUN: %target-swift-frontend -emit-silgen -disable-availability-checking -module-name main -enable-subst-sil-function-types-for-function-values %s | %FileCheck %s
2+
3+
4+
// Similarly-abstract generic signatures should share an unsubstituted type
5+
// even in different generic contexts
6+
7+
// CHECK-LABEL: sil {{.*}}1a{{.*}} : $@convention(thin) <T, U> (@noescape @callee_guaranteed <τ_0_0, τ_0_1> in (@in_guaranteed τ_0_0) -> @out τ_0_1 for <T, U>) -> ()
8+
func a<T, U>(_ x: (T) -> U) {}
9+
10+
// CHECK-LABEL: sil {{.*}}1b{{.*}} : $@convention(thin) <T, U> (@noescape @callee_guaranteed <τ_0_0, τ_0_1> in (@in_guaranteed τ_0_0) -> @out τ_0_1 for <U, T>) -> ()
11+
func b<T, U>(_ x: (U) -> T) {}
12+
13+
// CHECK-LABEL: sil {{.*}}1c{{.*}} : $@convention(thin) <T, U, V> (@noescape @callee_guaranteed <τ_0_0, τ_0_1> in (@in_guaranteed τ_0_0) -> @out τ_0_1 for <V, T>, @in_guaranteed U) -> ()
14+
func c<T, U, V>(_ x: (V) -> T, _: U) {}
15+
16+
// CHECK-LABEL: sil {{.*}}003Hca{{.*}} : $@convention(thin) <T> (@noescape @callee_guaranteed <τ_0_0, τ_0_1> in (@in_guaranteed τ_0_0) -> @out τ_0_1 for <T, T>) -> ()
17+
func ç<T>(_ x: (T) -> T) {}
18+
19+
20+
// ...including unconstrained associated types
21+
22+
protocol P {
23+
associatedtype A
24+
}
25+
26+
// CHECK-LABEL: sil {{.*}}1d{{.*}} : $@convention(thin) <T where T : P> (@noescape @callee_guaranteed <τ_0_0, τ_0_1> in (@in_guaranteed τ_0_0) -> @out τ_0_1 for <T, T.A>) -> ()
27+
func d<T: P>(_ x: (T) -> T.A) {}
28+
29+
// CHECK-LABEL: sil {{.*}}1e{{.*}} : $@convention(thin) <T where T : P> (@noescape @callee_guaranteed <τ_0_0, τ_0_1> in (@in_guaranteed τ_0_0) -> @out τ_0_1 for <T.A, T>) -> ()
30+
func e<T: P>(_ x: (T.A) -> T) {}
31+
32+
33+
// Preserve class constraints, because they're less abstract for layout and
34+
// calling convention purposes than unconstrained types
35+
36+
// CHECK-LABEL: sil {{.*}}1f{{.*}} : $@convention(thin) <T, U where T : AnyObject> (@noescape @callee_guaranteed <τ_0_0, τ_0_1 where τ_0_0 : _RefCountedObject> in (@guaranteed τ_0_0) -> @out τ_0_1 for <T, U>) -> ()
37+
func f<T: AnyObject, U>(_ x: (T) -> U) {}
38+
39+
// CHECK-LABEL: sil {{.*}}1g{{.*}} : $@convention(thin) <T, U where T : AnyObject> (@noescape @callee_guaranteed <τ_0_0, τ_0_1 where τ_0_1 : _RefCountedObject> in (@in_guaranteed τ_0_0) -> @owned τ_0_1 for <U, T>) -> ()
40+
func g<T: AnyObject, U>(_ x: (U) -> T) {}
41+
42+
// CHECK-LABEL: sil {{.*}}1h{{.*}} : $@convention(thin) <T, U where T : AnyObject, U : AnyObject> (@noescape @callee_guaranteed <τ_0_0, τ_0_1 where τ_0_0 : _RefCountedObject, τ_0_1 : _RefCountedObject> in (@guaranteed τ_0_0) -> @owned τ_0_1 for <T, U>) -> ()
43+
func h<T: AnyObject, U: AnyObject>(_ x: (T) -> U) {}
44+
45+
// CHECK-LABEL: sil {{.*}}1i{{.*}} : $@convention(thin) <T, U where T : AnyObject, U : AnyObject> (@noescape @callee_guaranteed <τ_0_0, τ_0_1 where τ_0_0 : _RefCountedObject, τ_0_1 : _RefCountedObject> in (@guaranteed τ_0_0) -> @owned τ_0_1 for <U, T>) -> ()
46+
func i<T: AnyObject, U: AnyObject>(_ x: (U) -> T) {}
47+
48+
49+
// Indirect class constraints
50+
51+
protocol PC: AnyObject { }
52+
53+
// CHECK-LABEL: sil {{.*}}1j{{.*}} : $@convention(thin) <T, U where T : PC> (@noescape @callee_guaranteed <τ_0_0, τ_0_1 where τ_0_0 : _RefCountedObject> in (@guaranteed τ_0_0) -> @out τ_0_1 for <T, U>) -> ()
54+
func j<T: PC, U>(_ x: (T) -> U) {}
55+
56+
// CHECK-LABEL: sil {{.*}}1k{{.*}} : $@convention(thin) <T, U where T : PC> (@noescape @callee_guaranteed <τ_0_0, τ_0_1 where τ_0_1 : _RefCountedObject> in (@in_guaranteed τ_0_0) -> @owned τ_0_1 for <U, T>) -> ()
57+
func k<T: PC, U>(_ x: (U) -> T) {}
58+
59+
// CHECK-LABEL: sil {{.*}}1l{{.*}} : $@convention(thin) <T, U where T : PC, U : PC> (@noescape @callee_guaranteed <τ_0_0, τ_0_1 where τ_0_0 : _RefCountedObject, τ_0_1 : _RefCountedObject> in (@guaranteed τ_0_0) -> @owned τ_0_1 for <T, U>) -> ()
60+
func l<T: PC, U: PC>(_ x: (T) -> U) {}
61+
62+
// CHECK-LABEL: sil {{.*}}1m{{.*}} : $@convention(thin) <T, U where T : PC, U : PC> (@noescape @callee_guaranteed <τ_0_0, τ_0_1 where τ_0_0 : _RefCountedObject, τ_0_1 : _RefCountedObject> in (@guaranteed τ_0_0) -> @owned τ_0_1 for <U, T>) -> ()
63+
func m<T: PC, U: PC>(_ x: (U) -> T) {}
64+
65+
// CHECK-LABEL: sil {{.*}}1n{{.*}} : $@convention(thin) <T where T : P, T : PC> (@noescape @callee_guaranteed <τ_0_0, τ_0_1 where τ_0_0 : _RefCountedObject> in (@guaranteed τ_0_0) -> @out τ_0_1 for <T, T.A>) -> ()
66+
func n<T: P & PC>(_ x: (T) -> T.A) {}
67+
68+
69+
// Superclass constraints
70+
71+
class Base {}
72+
73+
// CHECK-LABEL: sil {{.*}}1o{{.*}} : $@convention(thin) <T, U where T : Base> (@noescape @callee_guaranteed <τ_0_0, τ_0_1 where τ_0_0 : _RefCountedObject> in (@guaranteed τ_0_0) -> @out τ_0_1 for <T, U>) -> ()
74+
func o<T: Base, U> (_ x: (T) -> U) {}
75+
76+
77+
// Indirect constraints by associated type or protocol
78+
79+
protocol PCAO: AnyObject {
80+
associatedtype A
81+
}
82+
83+
protocol POAC {
84+
associatedtype A: AnyObject
85+
}
86+
87+
protocol PCAC: AnyObject {
88+
associatedtype A: AnyObject
89+
}
90+
91+
// CHECK-LABEL: sil {{.*}}1p{{.*}} : $@convention(thin) <T where T : PCAO> (@noescape @callee_guaranteed <τ_0_0, τ_0_1 where τ_0_0 : _RefCountedObject> in (@guaranteed τ_0_0) -> @out τ_0_1 for <T, T.A>) -> ()
92+
func p<T: PCAO> (_ x: (T) -> T.A) {}
93+
// CHECK-LABEL: sil {{.*}}1q{{.*}} : $@convention(thin) <T where T : POAC> (@noescape @callee_guaranteed <τ_0_0, τ_0_1 where τ_0_1 : _RefCountedObject> in (@in_guaranteed τ_0_0) -> @owned τ_0_1 for <T, T.A>) -> ()
94+
func q<T: POAC> (_ x: (T) -> T.A) {}
95+
// CHECK-LABEL: sil {{.*}}1r{{.*}} : $@convention(thin) <T where T : PCAC> (@noescape @callee_guaranteed <τ_0_0, τ_0_1 where τ_0_0 : _RefCountedObject, τ_0_1 : _RefCountedObject> in (@guaranteed τ_0_0) -> @owned τ_0_1 for <T, T.A>) -> ()
96+
func r<T: PCAC> (_ x: (T) -> T.A) {}
97+
98+
99+
// Structural positions
100+
101+
struct S<T, U> {}
102+
103+
// CHECK-LABEL: sil {{.*}}1t{{.*}} : $@convention(thin) <T, U where U : AnyObject> (@noescape @callee_guaranteed <τ_0_0, τ_0_1, τ_0_2, τ_0_3 where τ_0_1 : _RefCountedObject, τ_0_3 : _RefCountedObject> in (S<τ_0_0, τ_0_1>) -> (@out τ_0_2, @owned τ_0_3) for <T, U, T, U>) -> ()
104+
func t<T, U: AnyObject>(_: (S<T, U>) -> (T, U)) {}
105+
106+
// CHECK-LABEL: sil {{.*}}1u{{.*}} : $@convention(thin) <T> (@noescape @callee_guaranteed <τ_0_0, τ_0_1, τ_0_2> in (S<τ_0_0, τ_0_1>) -> @out τ_0_2 for <T, T, T>) -> ()
107+
func u<T>(_: (S<T, T>) -> T) {}
108+
109+
110+
class C<T, U> {}
111+
112+
// CHECK-LABEL: sil {{.*}}1v{{.*}} : $@convention(thin) <T, U where U : AnyObject> (@noescape @callee_guaranteed <τ_0_0, τ_0_1, τ_0_2, τ_0_3 where τ_0_1 : _RefCountedObject, τ_0_3 : _RefCountedObject> in (@guaranteed C<τ_0_0, τ_0_1>) -> (@out τ_0_2, @owned τ_0_3) for <T, U, T, U>) -> ()
113+
func v<T, U: AnyObject>(_: (C<T, U>) -> (T, U)) {}
114+
115+
// CHECK-LABEL: sil {{.*}}1w{{.*}} : $@convention(thin) <T> (@noescape @callee_guaranteed <τ_0_0, τ_0_1, τ_0_2> in (@guaranteed C<τ_0_0, τ_0_1>) -> @out τ_0_2 for <T, T, T>) -> ()
116+
func w<T>(_: (C<T, T>) -> T) {}
117+
118+
// CHECK-LABEL: sil {{.*}}1x{{.*}} : $@convention(thin) <T, U, V where V : C<T, U>> (@noescape @callee_guaranteed <τ_0_0 where τ_0_0 : _RefCountedObject> in (@guaranteed τ_0_0) -> () for <V>) -> ()
119+
func x<T, U, V: C<T, U>>(_: (V) -> Void) {}
120+
121+
122+
// Opaque types should not be extracted as substituted arguments because they
123+
// aren't freely substitutable.
124+
125+
dynamic func opaqueAny() -> some Any { return C<Int, String>() }
126+
dynamic func opaqueObject() -> some AnyObject { return C<Int, String>() }
127+
128+
// CHECK-LABEL: sil {{.*}}1y{{.*}} : $@convention(thin) (@noescape @callee_guaranteed (@in_guaranteed @_opaqueReturnTypeOf("$s4main9opaqueAnyQryF", 0) {{.*}}) -> @owned @_opaqueReturnTypeOf("$s4main12opaqueObjectQryF", 0) {{.*}}) -> ()
129+
func y(_: (@_opaqueReturnTypeOf("$s4main9opaqueAnyQryF", 0) X) -> (@_opaqueReturnTypeOf("$s4main12opaqueObjectQryF", 0) X)) {}

0 commit comments

Comments
 (0)