Skip to content

Commit 8e42049

Browse files
committed
[ConstraintSystem] Delay adding contextual requirements until parent type is opened
`openUnboundGenericType` eagerly tries to add conditional requirements associated with chain of parents of the given type if type has been declared inside of constrained extension. But one of the parent types might be unbound e.g. `A.B` which means it has to be opened, which by itself, would add such requirements. Resolves: rdar://problem/49371608
1 parent e622ea6 commit 8e42049

File tree

2 files changed

+61
-33
lines changed

2 files changed

+61
-33
lines changed

lib/Sema/ConstraintSystem.cpp

Lines changed: 50 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -490,47 +490,64 @@ static void checkNestedTypeConstraints(ConstraintSystem &cs, Type type,
490490
// info than that, unlike a typealias
491491
}
492492

493+
if (!parentTy)
494+
return;
495+
493496
// If this decl is generic, the constraints are handled when the generic
494497
// parameters are applied, so we don't have to handle them here (which makes
495498
// getting the right substitution maps easier).
496-
if (decl && !decl->isGeneric()) {
497-
auto extension = dyn_cast<ExtensionDecl>(decl->getDeclContext());
498-
if (parentTy && extension && extension->isConstrainedExtension()) {
499-
auto contextSubMap = parentTy->getContextSubstitutionMap(
500-
extension->getParentModule(),
501-
extension->getSelfNominalTypeDecl());
502-
if (!subMap) {
503-
// The substitution map wasn't set above, meaning we should grab the map
504-
// for the extension itself.
505-
subMap = parentTy->getContextSubstitutionMap(
506-
extension->getParentModule(), extension);
507-
}
499+
if (!decl || decl->isGeneric())
500+
return;
508501

509-
if (auto *signature = decl->getGenericSignature()) {
510-
cs.openGenericRequirements(
511-
extension, signature, /*skipProtocolSelfConstraint*/ true, locator,
512-
[&](Type type) {
513-
// Why do we look in two substitution maps? We have to use the
514-
// context substitution map to find types, because we need to
515-
// avoid thinking about them when handling the constraints, or all
516-
// the requirements in the signature become tautologies (if the
517-
// extension has 'T == Int', subMap will map T -> Int, so the
518-
// requirement becomes Int == Int no matter what the actual types
519-
// are here). However, we need the conformances for the extension
520-
// because the requirements might look like `T: P, T.U: Q`, where
521-
// U is an associated type of protocol P.
522-
return type.subst(QuerySubstitutionMap{contextSubMap},
523-
LookUpConformanceInSubstitutionMap(subMap),
524-
SubstFlags::UseErrorType);
525-
});
526-
}
502+
// struct A<T> {
503+
// let foo: [T]
504+
// }
505+
//
506+
// extension A : Codable where T: Codable {
507+
// enum CodingKeys: String, CodingKey {
508+
// case foo = "foo"
509+
// }
510+
// }
511+
//
512+
// Reference to `A.CodingKeys.foo` would point to `A` as an
513+
// unbound generic type. Conditional requirements would be
514+
// added when `A` is "opened". Les delay this check until then.
515+
if (parentTy->hasUnboundGenericType())
516+
return;
517+
518+
auto extension = dyn_cast<ExtensionDecl>(decl->getDeclContext());
519+
if (extension && extension->isConstrainedExtension()) {
520+
auto contextSubMap = parentTy->getContextSubstitutionMap(
521+
extension->getParentModule(), extension->getSelfNominalTypeDecl());
522+
if (!subMap) {
523+
// The substitution map wasn't set above, meaning we should grab the map
524+
// for the extension itself.
525+
subMap = parentTy->getContextSubstitutionMap(extension->getParentModule(),
526+
extension);
527527
}
528528

529-
// And now make sure sure the parent is okay, for things like X<T>.Y.Z.
530-
if (parentTy) {
531-
checkNestedTypeConstraints(cs, parentTy, locator);
529+
if (auto *signature = decl->getGenericSignature()) {
530+
cs.openGenericRequirements(
531+
extension, signature, /*skipProtocolSelfConstraint*/ true, locator,
532+
[&](Type type) {
533+
// Why do we look in two substitution maps? We have to use the
534+
// context substitution map to find types, because we need to
535+
// avoid thinking about them when handling the constraints, or all
536+
// the requirements in the signature become tautologies (if the
537+
// extension has 'T == Int', subMap will map T -> Int, so the
538+
// requirement becomes Int == Int no matter what the actual types
539+
// are here). However, we need the conformances for the extension
540+
// because the requirements might look like `T: P, T.U: Q`, where
541+
// U is an associated type of protocol P.
542+
return type.subst(QuerySubstitutionMap{contextSubMap},
543+
LookUpConformanceInSubstitutionMap(subMap),
544+
SubstFlags::UseErrorType);
545+
});
532546
}
533547
}
548+
549+
// And now make sure sure the parent is okay, for things like X<T>.Y.Z.
550+
checkNestedTypeConstraints(cs, parentTy, locator);
534551
}
535552

536553
Type ConstraintSystem::openUnboundGenericType(
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// RUN: %target-typecheck-verify-swift
2+
3+
struct A<T> {
4+
let foo: [T]
5+
}
6+
7+
extension A : Codable where T: Codable {
8+
enum CodingKeys: String, CodingKey {
9+
case foo = "foo"
10+
}
11+
}

0 commit comments

Comments
 (0)