Skip to content

Commit bbefeb2

Browse files
committed
Sema: Better support for nested generic functions
There was a weird corner case with nested generic functions that would fail in the SIL verifier with some nonsense about archetypes out of context. Fix this the "right" way, by re-working Sema function declaration validation to assign generic signatures in a more principled way. Previously, nested functions did not get an interface type unless they themselves had generic parameters. This was inconsistent with methods nested inside generic types, which did get an interface type even if they themselves did not have a generic parameter list. There's some spill-over in SILGen from this change. Mostly it makes things more consistent and fixes some corner cases.
1 parent fef5257 commit bbefeb2

34 files changed

+149
-155
lines changed

include/swift/AST/DeclContext.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -367,10 +367,6 @@ class alignas(1 << DeclContextAlignInBits) DeclContext {
367367

368368
/// Determine whether the innermost context is generic.
369369
bool isInnermostContextGeneric() const;
370-
371-
/// Determine whether the innermost context is either a generic type context,
372-
/// or a concrete type nested inside a generic type context.
373-
bool isGenericTypeContext() const;
374370

375371
/// Determine the maximum depth of the current generic type context's generic
376372
/// parameters. If the current context is not a generic type context, returns

lib/AST/Decl.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4472,7 +4472,7 @@ bool EnumElementDecl::computeType() {
44724472
if (getArgumentType())
44734473
resultTy = FunctionType::get(getArgumentType(), resultTy);
44744474

4475-
if (ED->isGenericTypeContext())
4475+
if (ED->isGenericContext())
44764476
resultTy = PolymorphicFunctionType::get(argTy, resultTy,
44774477
ED->getGenericParamsOfContext());
44784478
else

lib/AST/DeclContext.cpp

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -431,17 +431,6 @@ bool DeclContext::isGenericContext() const {
431431
llvm_unreachable("illegal declcontext hierarchy");
432432
}
433433

434-
/// Determine whether the given context nested inside a generic type context
435-
/// with no local contexts in between.
436-
bool DeclContext::isGenericTypeContext() const {
437-
for (const auto *dc = this; dc->isTypeContext(); dc = dc->getParent()) {
438-
if (dc->isInnermostContextGeneric())
439-
return true;
440-
}
441-
442-
return false;
443-
}
444-
445434
/// Determine the maximum depth of the current generic type context's generic
446435
/// parameters. If the current context is not a generic type context, returns
447436
/// the maximum depth of any generic parameter in this context.

lib/AST/Module.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -726,6 +726,13 @@ TypeBase::gatherAllSubstitutions(Module *module,
726726
llvm_unreachable("Not a nominal or bound generic type");
727727
}
728728

729+
// Add forwarding substitutions from the outer context if we have
730+
// a type nested inside a generic function.
731+
if (auto *outerGenericParams = parentDC->getGenericParamsOfContext()) {
732+
for (auto archetype : outerGenericParams->getAllNestedArchetypes())
733+
substitutions[archetype] = archetype;
734+
}
735+
729736
// Collect all of the archetypes.
730737
SmallVector<ArchetypeType *, 2> allArchetypesList;
731738
ArrayRef<ArchetypeType *> allArchetypes = genericParams->getAllArchetypes();

lib/SIL/TypeLowering.cpp

Lines changed: 7 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1732,13 +1732,12 @@ static CanAnyFunctionType getIVarInitDestroyerInterfaceType(ClassDecl *cd,
17321732
GenericParamList *
17331733
TypeConverter::getEffectiveGenericParams(AnyFunctionRef fn,
17341734
CaptureInfo captureInfo) {
1735-
auto dc = fn.getAsDeclContext()->getParent();
1735+
auto dc = fn.getAsDeclContext();
17361736

1737-
if (dc->isLocalContext() &&
1738-
!captureInfo.hasGenericParamCaptures()) {
1737+
if (dc->getParent()->isLocalContext() &&
1738+
!captureInfo.hasGenericParamCaptures())
17391739
return nullptr;
1740-
}
1741-
1740+
17421741
return dc->getGenericParamsOfContext();
17431742
}
17441743

@@ -1747,14 +1746,9 @@ TypeConverter::getEffectiveGenericSignature(AnyFunctionRef fn,
17471746
CaptureInfo captureInfo) {
17481747
auto dc = fn.getAsDeclContext();
17491748

1750-
// If this is a non-generic local function that does not capture any
1751-
// generic type parameters from the outer context, don't need a
1752-
// signature at all.
1753-
if (!dc->isInnermostContextGeneric() &&
1754-
dc->getParent()->isLocalContext() &&
1755-
!captureInfo.hasGenericParamCaptures()) {
1749+
if (dc->getParent()->isLocalContext() &&
1750+
!captureInfo.hasGenericParamCaptures())
17561751
return nullptr;
1757-
}
17581752

17591753
if (auto sig = dc->getGenericSignatureOfContext())
17601754
return sig->getCanonicalSignature();
@@ -1832,8 +1826,7 @@ CanAnyFunctionType TypeConverter::makeConstantInterfaceType(SILDeclRef c) {
18321826
switch (c.kind) {
18331827
case SILDeclRef::Kind::Func: {
18341828
if (auto *ACE = c.loc.dyn_cast<AbstractClosureExpr *>()) {
1835-
// TODO: Substitute out archetypes from the enclosing context with generic
1836-
// parameters.
1829+
// FIXME: Closures could have an interface type computed by Sema.
18371830
auto funcTy = cast<AnyFunctionType>(ACE->getType()->getCanonicalType());
18381831
funcTy = cast<AnyFunctionType>(
18391832
ArchetypeBuilder::mapTypeOutOfContext(ACE->getParent(), funcTy)
@@ -1844,10 +1837,6 @@ CanAnyFunctionType TypeConverter::makeConstantInterfaceType(SILDeclRef c) {
18441837
FuncDecl *func = cast<FuncDecl>(vd);
18451838
auto funcTy = cast<AnyFunctionType>(
18461839
func->getInterfaceType()->getCanonicalType());
1847-
if (func->getParent() && func->getParent()->isLocalContext())
1848-
funcTy = cast<AnyFunctionType>(
1849-
ArchetypeBuilder::mapTypeOutOfContext(func->getParent(), funcTy)
1850-
->getCanonicalType());
18511840
funcTy = cast<AnyFunctionType>(replaceDynamicSelfWithSelf(funcTy));
18521841
return getFunctionInterfaceTypeWithCaptures(funcTy, func);
18531842
}
@@ -1909,22 +1898,6 @@ TypeConverter::getConstantContextGenericParams(SILDeclRef c) {
19091898
FuncDecl *func = cast<FuncDecl>(vd);
19101899
auto captureInfo = getLoweredLocalCaptures(func);
19111900

1912-
// FIXME: This is really weird:
1913-
// 1) For generic functions, generic methods and generic
1914-
// local functions, we return the function's generic
1915-
// parameter list twice.
1916-
// 2) For non-generic methods inside generic types, we
1917-
// return the generic type's parameters and nullptr.
1918-
// 3) For non-generic local functions, we return the
1919-
// outer function's parameters and nullptr.
1920-
//
1921-
// Local generic functions could probably be modeled better
1922-
// at the SIL level.
1923-
if (func->isInnermostContextGeneric() ||
1924-
func->getDeclContext()->isGenericTypeContext()) {
1925-
if (auto GP = func->getGenericParamsOfContext())
1926-
return {GP, func->getGenericParams()};
1927-
}
19281901
return {getEffectiveGenericParams(func, captureInfo),
19291902
func->getGenericParams()};
19301903
}

lib/SILGen/SILGenApply.cpp

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1169,9 +1169,8 @@ class SILGenApply : public Lowering::ExprVisitor<SILGenApply> {
11691169
}
11701170

11711171
ArrayRef<Substitution> subs;
1172-
if (e->getDeclRef().isSpecialized()) {
1172+
if (e->getDeclRef().isSpecialized())
11731173
subs = e->getDeclRef().getSubstitutions();
1174-
}
11751174

11761175
// Enum case constructor references are open-coded.
11771176
if (isa<EnumElementDecl>(e->getDecl()))
@@ -1191,14 +1190,13 @@ class SILGenApply : public Lowering::ExprVisitor<SILGenApply> {
11911190
captures);
11921191
ApplyCallee->setCaptures(std::move(captures));
11931192
}
1194-
1195-
if (subs.empty() && afd->getCaptureInfo().hasGenericParamCaptures()) {
1196-
subs = SGF.getForwardingSubstitutions();
1197-
}
11981193
}
11991194

12001195
// If there are substitutions, add them, always at depth 0.
1201-
if (!subs.empty())
1196+
if (!subs.empty() &&
1197+
(!afd ||
1198+
!afd->getDeclContext()->isLocalContext() ||
1199+
afd->getCaptureInfo().hasGenericParamCaptures()))
12021200
ApplyCallee->setSubstitutions(SGF, e, subs, 0);
12031201
}
12041202

lib/SILGen/SILGenFunction.cpp

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,7 @@ SILGenFunction::emitClosureValue(SILLocation loc, SILDeclRef constant,
337337
ArrayRef<Substitution> subs) {
338338
auto closure = *constant.getAnyFunctionRef();
339339
auto captureInfo = closure.getCaptureInfo();
340+
auto loweredCaptureInfo = SGM.Types.getLoweredLocalCaptures(closure);
340341

341342
assert(((constant.uncurryLevel == 1 &&
342343
captureInfo.hasLocalCaptures()) ||
@@ -351,17 +352,21 @@ SILGenFunction::emitClosureValue(SILLocation loc, SILDeclRef constant,
351352
// Apply substitutions.
352353
auto pft = constantInfo.SILFnType;
353354

355+
auto *dc = closure.getAsDeclContext()->getParent();
356+
if (dc->isLocalContext() && !loweredCaptureInfo.hasGenericParamCaptures()) {
357+
// If the lowered function type is not polymorphic but we were given
358+
// substitutions, we have a closure in a generic context which does not
359+
// capture generic parameters. Just drop the substitutions.
360+
subs = { };
361+
} else if (closure.getAbstractClosureExpr()) {
362+
// If we have a closure expression in generic context, Sema won't give
363+
// us substitutions, so we just use the forwarding substitutions from
364+
// context.
365+
subs = getForwardingSubstitutions();
366+
}
367+
354368
bool wasSpecialized = false;
355-
if (pft->isPolymorphic()) {
356-
// If the lowered function type is generic but Sema did not hand us any
357-
// substitutions, the function is a local function that appears in a
358-
// generic context but does not have a generic parameter list of its own;
359-
// just use our forwarding substitutions.
360-
if (subs.empty()) {
361-
assert(closure.getAsDeclContext()->isLocalContext() &&
362-
"cannot reference generic global function without substitutions");
363-
subs = getForwardingSubstitutions();
364-
}
369+
if (!subs.empty()) {
365370
auto specialized = pft->substGenericArgs(F.getModule(),
366371
F.getModule().getSwiftModule(),
367372
subs);

lib/Sema/CSApply.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -754,7 +754,8 @@ namespace {
754754
ConcreteDeclRef memberRef;
755755
Type refTy;
756756
Type dynamicSelfFnType;
757-
if (openedFullType->hasTypeVariable()) {
757+
if (member->getInterfaceType()->is<GenericFunctionType>() ||
758+
openedFullType->hasTypeVariable()) {
758759
// We require substitutions. Figure out what they are.
759760

760761
// Figure out the declaration context where we'll get the generic

lib/Sema/CSGen.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3462,7 +3462,7 @@ void swift::collectDefaultImplementationForProtocolMembers(ProtocolDecl *PD,
34623462
VD->getFullName());
34633463
if (Result.OtherViables.empty())
34643464
continue;
3465-
if (!Result.Favored->getDeclContext()->isGenericTypeContext())
3465+
if (!Result.Favored->getDeclContext()->isGenericContext())
34663466
continue;
34673467
for (ValueDecl *Default : Result.OtherViables) {
34683468
if (Default->getDeclContext()->isExtensionContext()) {

lib/Sema/CodeSynthesis.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1274,7 +1274,7 @@ void TypeChecker::completePropertyBehaviorStorage(VarDecl *VD,
12741274

12751275
// Add the witnesses to the conformance.
12761276
ArrayRef<Substitution> MemberSubs;
1277-
if (DC->isGenericTypeContext()) {
1277+
if (DC->isGenericContext()) {
12781278
MemberSubs = DC->getGenericParamsOfContext()
12791279
->getForwardingSubstitutions(Context);
12801280
}
@@ -1448,7 +1448,7 @@ void TypeChecker::completePropertyBehaviorParameter(VarDecl *VD,
14481448

14491449
// Add the witnesses to the conformance.
14501450
ArrayRef<Substitution> MemberSubs;
1451-
if (DC->isGenericTypeContext()) {
1451+
if (DC->isGenericContext()) {
14521452
MemberSubs = DC->getGenericParamsOfContext()
14531453
->getForwardingSubstitutions(Context);
14541454
}
@@ -2079,7 +2079,7 @@ swift::createDesignatedInitOverride(TypeChecker &tc,
20792079
//
20802080
// We might have to apply substitutions, if for example we have a declaration
20812081
// like 'class A : B<Int>'.
2082-
if (superclassDecl->isGenericTypeContext()) {
2082+
if (superclassDecl->isGenericContext()) {
20832083
if (auto *superclassSig = superclassDecl->getGenericSignatureOfContext()) {
20842084
auto *moduleDecl = classDecl->getParentModule();
20852085
auto subs = superclassTy->gatherAllSubstitutions(
@@ -2127,7 +2127,7 @@ swift::createDesignatedInitOverride(TypeChecker &tc,
21272127
auto selfType = configureImplicitSelf(tc, ctor);
21282128

21292129
// Set the interface type of the initializer.
2130-
if (classDecl->isGenericTypeContext()) {
2130+
if (classDecl->isGenericContext()) {
21312131
ctor->setGenericSignature(classDecl->getGenericSignatureOfContext());
21322132
tc.configureInterfaceType(ctor);
21332133
}

0 commit comments

Comments
 (0)