Skip to content

Commit 64ff24c

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 (cherry picked from commit 8e42049) (cherry picked from commit 00907d1)
1 parent 1b0d7e8 commit 64ff24c

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
@@ -489,47 +489,64 @@ static void checkNestedTypeConstraints(ConstraintSystem &cs, Type type,
489489
// info than that, unlike a typealias
490490
}
491491

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

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

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

535552
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)