Skip to content

Commit 40641f5

Browse files
committed
Sema: Fix dodgy logic in findMissingGenericRequirementForSolutionFix()
'type' and 'missingType' are contextual types in the generic environment of the witness thunk. We cannot simply map them into the environment of the conformance, because if the conforming type is a class, the witness thunk has an extra generic parameter at depth=0, index=0 and all other generic parameters are shifted down by one depth.
1 parent 354a194 commit 40641f5

File tree

2 files changed

+61
-33
lines changed

2 files changed

+61
-33
lines changed

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 45 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1000,50 +1000,64 @@ findMissingGenericRequirementForSolutionFix(
10001000
type = solution.simplifyType(type);
10011001
missingType = solution.simplifyType(missingType);
10021002

1003-
if (auto *env = conformance->getGenericEnvironment()) {
1004-
// We use subst() with LookUpConformanceInModule here, because
1005-
// associated type inference failures mean that we can end up
1006-
// here with a DependentMemberType with an ArchetypeType base.
1007-
missingType = missingType.subst(
1008-
[&](SubstitutableType *type) -> Type {
1009-
return env->mapTypeIntoContext(type->mapTypeOutOfContext());
1010-
},
1011-
LookUpConformanceInModule(),
1012-
SubstFlags::SubstitutePrimaryArchetypes);
1013-
}
1014-
10151003
auto missingRequirementMatch = [&](Type type) -> RequirementMatch {
10161004
Requirement requirement(requirementKind, type, missingType);
10171005
return RequirementMatch(witness, MatchKind::MissingRequirement,
10181006
requirement);
10191007
};
10201008

1021-
if (type->is<DependentMemberType>())
1022-
return missingRequirementMatch(type);
1009+
auto selfTy = conformance->getProtocol()->getSelfInterfaceType()
1010+
.subst(reqEnvironment.getRequirementToWitnessThunkSubs());
10231011

1024-
type = type->mapTypeOutOfContext();
1025-
if (type->hasTypeParameter())
1026-
if (auto env = conformance->getGenericEnvironment())
1027-
if (auto assocType = env->mapTypeIntoContext(type))
1028-
return missingRequirementMatch(assocType);
1012+
auto sig = conformance->getGenericSignature();
1013+
auto *env = conformance->getGenericEnvironment();
10291014

1030-
auto reqSubMap = reqEnvironment.getRequirementToWitnessThunkSubs();
1031-
auto proto = conformance->getProtocol();
1032-
Type selfTy = proto->getSelfInterfaceType().subst(reqSubMap);
1033-
if (type->isEqual(selfTy)) {
1034-
type = conformance->getType();
1015+
auto &ctx = conformance->getDeclContext()->getASTContext();
10351016

1017+
if (type->is<ArchetypeType>() &&
1018+
type->mapTypeOutOfContext()->isEqual(selfTy) &&
1019+
missingType->isEqual(conformance->getType())) {
10361020
// e.g. `extension P where Self == C { func foo() { ... } }`
10371021
// and `C` doesn't actually conform to `P`.
1038-
if (type->isEqual(missingType)) {
1039-
requirementKind = RequirementKind::Conformance;
1040-
missingType = proto->getDeclaredInterfaceType();
1041-
}
1022+
requirementKind = RequirementKind::Conformance;
1023+
missingType = conformance->getProtocol()->getDeclaredInterfaceType();
1024+
}
1025+
1026+
// Map the interface types of the witness thunk signature back to
1027+
// sugared generic parameter types of the conformance, for printing.
1028+
auto getTypeInConformanceContext = [&](Type type) -> Type {
1029+
return type.subst([&](SubstitutableType *t) -> Type {
1030+
auto *gp = cast<GenericTypeParamType>(t);
1031+
if (selfTy->is<GenericTypeParamType>()) {
1032+
if (gp->isEqual(selfTy))
1033+
return conformance->getType();
1034+
1035+
ASSERT(gp->getDepth() > 0);
1036+
gp = GenericTypeParamType::get(gp->getParamKind(),
1037+
gp->getDepth() - 1,
1038+
gp->getIndex(),
1039+
gp->getValueType(),
1040+
ctx);
1041+
}
1042+
1043+
if (!sig)
1044+
return ErrorType::get(ctx);
10421045

1043-
if (auto agt = type->getAs<AnyGenericType>())
1044-
type = agt->getDecl()->getDeclaredInterfaceType();
1046+
auto params = sig.getGenericParams();
1047+
unsigned ordinal = sig->getGenericParamOrdinal(gp);
1048+
if (ordinal == params.size())
1049+
return ErrorType::get(ctx);
1050+
1051+
return env->mapTypeIntoContext(gp);
1052+
},
1053+
LookUpConformanceInModule());
1054+
};
1055+
1056+
type = getTypeInConformanceContext(type);
1057+
missingType = getTypeInConformanceContext(missingType);
1058+
1059+
if (!type->hasError() && !missingType->hasError())
10451060
return missingRequirementMatch(type);
1046-
}
10471061

10481062
return std::optional<RequirementMatch>();
10491063
}

test/decl/protocol/req/unsatisfiable.swift

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ protocol P {
1010

1111
extension P {
1212
func f<T: P>(_: T) where T.A == Self.A, T.A == Self.B { }
13-
// expected-note@-1 {{candidate would match if 'X' was the same type as 'X.B' (aka 'Int')}}
13+
// expected-note@-1 {{candidate would match if 'X.A' (aka 'X') was the same type as 'X.B' (aka 'Int')}}
1414
}
1515

1616
struct X : P {
@@ -77,9 +77,23 @@ struct S2 : P6 {
7777
typealias U = String
7878

7979
func foo() {}
80-
// expected-note@-1 {{candidate has non-matching type '() -> ()'}}
80+
// expected-note@-1 {{candidate would match if 'S2.T' (aka 'Int') was the same type as 'S2.U' (aka 'String')}}
8181

8282
// FIXME: This error is bogus and should be omitted on account of the protocol requirement itself
8383
// being invalid.
8484
}
8585

86+
// This used to emit a diagnostic with a canonical type in it.
87+
protocol P7 {
88+
associatedtype A
89+
func f() // expected-note {{protocol requires function 'f()' with type '() -> ()'}}
90+
}
91+
92+
extension P7 where A: Equatable {
93+
func f() {} // expected-note {{candidate would match if 'C7<T>.A' (aka 'T') conformed to 'Equatable'}}
94+
}
95+
96+
class C7<T>: P7 { // expected-error {{type 'C7<T>' does not conform to protocol 'P7'}}
97+
// expected-note@-1 {{add stubs for conformance}}
98+
typealias A = T
99+
}

0 commit comments

Comments
 (0)