Skip to content

Commit 0fc8377

Browse files
authored
Merge pull request swiftlang#28065 from apple/subst-function-type-value-lowering
SIL: Type lowering for function values with substituted SIL function types.
2 parents 0ac76ac + 27d7117 commit 0fc8377

File tree

8 files changed

+361
-42
lines changed

8 files changed

+361
-42
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,

include/swift/Basic/LangOptions.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,9 @@ namespace swift {
211211
/// Enable experimental operator designated types feature.
212212
bool EnableOperatorDesignatedTypes = false;
213213

214+
/// Enable SIL type lowering
215+
bool EnableSubstSILFunctionTypesForFunctionValues = false;
216+
214217
/// Enable constraint solver support for experimental
215218
/// operator protocol designator feature.
216219
bool SolverEnableOperatorDesignatedTypes = false;

include/swift/Option/FrontendOptions.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,10 @@ def enable_experimental_static_assert :
344344
Flag<["-"], "enable-experimental-static-assert">,
345345
HelpText<"Enable experimental #assert">;
346346

347+
def enable_subst_sil_function_types_for_function_values :
348+
Flag<["-"], "enable-subst-sil-function-types-for-function-values">,
349+
HelpText<"Use substituted function types for SIL type lowering of function values">;
350+
347351
def enable_deserialization_recovery :
348352
Flag<["-"], "enable-deserialization-recovery">,
349353
HelpText<"Attempt to recover from missing xrefs (etc) in swiftmodules">;

lib/AST/ASTPrinter.cpp

Lines changed: 63 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -4006,54 +4006,79 @@ class TypePrinter : public TypeVisitor<TypePrinter> {
40064006
printFunctionExtInfo(T->getExtInfo(),
40074007
T->getWitnessMethodConformanceOrInvalid());
40084008
printCalleeConvention(T->getCalleeConvention());
4009-
if (auto sig = T->getSubstGenericSignature()) {
4010-
printGenericSignature(sig,
4011-
PrintAST::PrintParams |
4012-
PrintAST::PrintRequirements);
4013-
Printer << " ";
4014-
if (T->isGenericSignatureImplied()) {
4015-
Printer << "in ";
4016-
}
4009+
4010+
// If this is a substituted function type, then its generic signature is
4011+
// independent of the enclosing context, and defines the parameters active
4012+
// in the interface params and results. Unsubstituted types use the existing
4013+
// environment, which may be a sil decl's generic environment.
4014+
//
4015+
// Yeah, this is fiddly. In the end, we probably want all decls to have
4016+
// substituted types in terms of a generic signature declared on the decl,
4017+
// which would make this logic more uniform.
4018+
TypePrinter *sub = this;
4019+
Optional<TypePrinter> subBuffer;
4020+
PrintOptions subOptions = Options;
4021+
if (T->getSubstitutions()) {
4022+
subOptions.GenericEnv = nullptr;
4023+
subBuffer.emplace(Printer, subOptions);
4024+
sub = &*subBuffer;
40174025
}
4026+
4027+
// Capture list used here to ensure we don't print anything using `this`
4028+
// printer, but only the sub-Printer.
4029+
[T, sub, &subOptions]{
4030+
if (auto sig = T->getSubstGenericSignature()) {
4031+
sub->printGenericSignature(sig,
4032+
PrintAST::PrintParams |
4033+
PrintAST::PrintRequirements);
4034+
sub->Printer << " ";
4035+
if (T->isGenericSignatureImplied()) {
4036+
sub->Printer << "in ";
4037+
}
4038+
}
40184039

4019-
Printer << "(";
4020-
bool first = true;
4021-
for (auto param : T->getParameters()) {
4022-
Printer.printSeparator(first, ", ");
4023-
param.print(Printer, Options);
4024-
}
4025-
Printer << ") -> ";
4040+
sub->Printer << "(";
4041+
bool first = true;
4042+
for (auto param : T->getParameters()) {
4043+
sub->Printer.printSeparator(first, ", ");
4044+
param.print(sub->Printer, subOptions);
4045+
}
4046+
sub->Printer << ") -> ";
40264047

4027-
unsigned totalResults =
4028-
T->getNumYields() + T->getNumResults() + unsigned(T->hasErrorResult());
4048+
unsigned totalResults =
4049+
T->getNumYields() + T->getNumResults() + unsigned(T->hasErrorResult());
40294050

4030-
if (totalResults != 1) Printer << "(";
4051+
if (totalResults != 1)
4052+
sub->Printer << "(";
40314053

4032-
first = true;
4054+
first = true;
40334055

4034-
for (auto yield : T->getYields()) {
4035-
Printer.printSeparator(first, ", ");
4036-
Printer << "@yields ";
4037-
yield.print(Printer, Options);
4038-
}
4056+
for (auto yield : T->getYields()) {
4057+
sub->Printer.printSeparator(first, ", ");
4058+
sub->Printer << "@yields ";
4059+
yield.print(sub->Printer, subOptions);
4060+
}
40394061

4040-
for (auto result : T->getResults()) {
4041-
Printer.printSeparator(first, ", ");
4042-
result.print(Printer, Options);
4043-
}
4062+
for (auto result : T->getResults()) {
4063+
sub->Printer.printSeparator(first, ", ");
4064+
result.print(sub->Printer, subOptions);
4065+
}
40444066

4045-
if (T->hasErrorResult()) {
4046-
// The error result is implicitly @owned; don't print that.
4047-
assert(T->getErrorResult().getConvention() == ResultConvention::Owned);
4048-
Printer.printSeparator(first, ", ");
4049-
Printer << "@error ";
4050-
T->getErrorResult().getInterfaceType().print(Printer, Options);
4051-
}
4067+
if (T->hasErrorResult()) {
4068+
// The error result is implicitly @owned; don't print that.
4069+
assert(T->getErrorResult().getConvention() == ResultConvention::Owned);
4070+
sub->Printer.printSeparator(first, ", ");
4071+
sub->Printer << "@error ";
4072+
T->getErrorResult().getInterfaceType().print(sub->Printer, subOptions);
4073+
}
40524074

4053-
if (totalResults != 1) Printer << ")";
4075+
if (totalResults != 1)
4076+
sub->Printer << ")";
4077+
}();
40544078

4079+
// The substitution types are always in terms of the outer environment.
40554080
if (auto substitutions = T->getSubstitutions()) {
4056-
Printer << " for ";
4081+
Printer << " for";
40574082
printSubstitutions(substitutions);
40584083
}
40594084
}
@@ -4076,7 +4101,7 @@ class TypePrinter : public TypeVisitor<TypePrinter> {
40764101
[&sub, T]{
40774102
if (auto sig = T->getLayout()->getGenericSignature()) {
40784103
sub.printGenericSignature(sig,
4079-
PrintAST::PrintParams | PrintAST::PrintRequirements);
4104+
PrintAST::PrintParams | PrintAST::PrintRequirements);
40804105
sub.Printer << " ";
40814106
}
40824107
sub.Printer << "{";

lib/Frontend/CompilerInvocation.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,9 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args,
274274
Opts.EnableExperimentalStaticAssert |=
275275
Args.hasArg(OPT_enable_experimental_static_assert);
276276

277+
Opts.EnableSubstSILFunctionTypesForFunctionValues |=
278+
Args.hasArg(OPT_enable_subst_sil_function_types_for_function_values);
279+
277280
Opts.EnableOperatorDesignatedTypes |=
278281
Args.hasArg(OPT_enable_operator_designated_types);
279282

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.

0 commit comments

Comments
 (0)