Skip to content

Commit 1c76e22

Browse files
authored
Merge pull request #59310 from slavapestov/rqm-nested-concrete-contraction-fix
RequirementMachine: Concrete contraction needs to substitute the parent type of a subject type sometimes
2 parents c6a95c6 + 9695b19 commit 1c76e22

File tree

4 files changed

+67
-23
lines changed

4 files changed

+67
-23
lines changed

lib/AST/RequirementMachine/ConcreteContraction.cpp

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -227,8 +227,8 @@ class ConcreteContraction {
227227
Optional<Type> ConcreteContraction::substTypeParameterRec(
228228
Type type, Position position) const {
229229

230-
// If the requirement is of the form 'T == C' or 'T : C', don't
231-
// substitute T, since then we end up with 'C == C' or 'C : C',
230+
// If we have a superclass (T : C) or same-type requirement (T == C),
231+
// don't substitute T, since then we end up with 'C == C' or 'C : C',
232232
// losing the requirement.
233233
if (position == Position::BaseType ||
234234
position == Position::ConformanceRequirement) {
@@ -399,9 +399,10 @@ ConcreteContraction::substRequirement(const Requirement &req) const {
399399
!module->lookupConformance(substFirstType, proto,
400400
allowMissing, allowUnavailable)) {
401401
// Handle the case of <T where T : P, T : C> where C is a class and
402-
// C does not conform to P by leaving the conformance requirement
403-
// unsubstituted.
404-
return req;
402+
// C does not conform to P and only substitute the parent type of T
403+
// by pretending we have a same-type requirement here.
404+
substFirstType = substTypeParameter(
405+
firstType, Position::SameTypeRequirement);
405406
}
406407

407408
// Otherwise, replace the generic parameter in the conformance
@@ -418,9 +419,11 @@ ConcreteContraction::substRequirement(const Requirement &req) const {
418419
if (!substFirstType->isTypeParameter() &&
419420
!substFirstType->satisfiesClassConstraint() &&
420421
req.getLayoutConstraint()->isClass()) {
421-
// If the concrete type doesn't satisfy the layout constraint,
422-
// leave it unsubstituted so that we produce a better diagnostic.
423-
return req;
422+
// If the concrete type doesn't satisfy the layout constraint, produce
423+
// a better diagnostic and only substitute the parent type by pretending
424+
// we have a same-type requirement here.
425+
substFirstType = substTypeParameter(
426+
firstType, Position::SameTypeRequirement);
424427
}
425428

426429
return Requirement(req.getKind(),

lib/AST/RequirementMachine/MinimalConformances.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,7 @@ MinimalConformances::decomposeTermIntoConformanceRuleLeftHandSides(
263263
bool simplified = System.simplify(term, &steps);
264264
if (!simplified) {
265265
llvm::errs() << "Term does not conform to protocol: " << term << "\n";
266+
System.dump(llvm::errs());
266267
abort();
267268
}
268269

lib/Sema/TypeCheckType.cpp

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -242,21 +242,8 @@ bool TypeResolution::areSameType(Type type1, Type type2) const {
242242
}
243243
}
244244

245-
// Otherwise, perform a structural check.
246245
assert(stage == TypeResolutionStage::Structural);
247-
248-
// FIXME: We should be performing a deeper equality check here.
249-
// If both refer to associated types with the same name, they'll implicitly
250-
// be considered equivalent.
251-
auto depMem1 = type1->getAs<DependentMemberType>();
252-
if (!depMem1) return false;
253-
254-
auto depMem2 = type2->getAs<DependentMemberType>();
255-
if (!depMem2) return false;
256-
257-
if (depMem1->getName() != depMem2->getName()) return false;
258-
259-
return areSameType(depMem1->getBase(), depMem2->getBase());
246+
return false;
260247
}
261248

262249
Type TypeChecker::getOptionalType(SourceLoc loc, Type elementType) {
@@ -430,7 +417,7 @@ Type TypeResolution::resolveTypeInContext(TypeDecl *typeDecl,
430417
selfType = foundDC->getSelfInterfaceType();
431418

432419
if (selfType->is<GenericTypeParamType>()) {
433-
if (typeDecl->getDeclContext()->getSelfProtocolDecl()) {
420+
if (isa<ProtocolDecl>(typeDecl->getDeclContext())) {
434421
if (isa<AssociatedTypeDecl>(typeDecl) ||
435422
(isa<TypeAliasDecl>(typeDecl) &&
436423
!cast<TypeAliasDecl>(typeDecl)->isGeneric() &&
@@ -1434,6 +1421,23 @@ static Type resolveTopLevelIdentTypeComponent(TypeResolution resolution,
14341421
auto globals = TypeChecker::lookupUnqualifiedType(DC, id, comp->getLoc(),
14351422
lookupOptions);
14361423

1424+
// If we're doing structural resolution and one of the results is an
1425+
// associated type, ignore any other results found from the same
1426+
// DeclContext; they are going to be protocol typealiases, possibly
1427+
// from constrained extensions, and trying to compute their type in
1428+
// resolveTypeInContext() might hit request cycles since structural
1429+
// resolution is performed while computing the requirement signature
1430+
// of the protocol.
1431+
DeclContext *assocTypeDC = nullptr;
1432+
if (resolution.getStage() == TypeResolutionStage::Structural) {
1433+
for (const auto &entry : globals) {
1434+
if (isa<AssociatedTypeDecl>(entry.getValueDecl())) {
1435+
assocTypeDC = entry.getDeclContext();
1436+
break;
1437+
}
1438+
}
1439+
}
1440+
14371441
// Process the names we found.
14381442
Type current;
14391443
TypeDecl *currentDecl = nullptr;
@@ -1443,6 +1447,13 @@ static Type resolveTopLevelIdentTypeComponent(TypeResolution resolution,
14431447
auto *foundDC = entry.getDeclContext();
14441448
auto *typeDecl = cast<TypeDecl>(entry.getValueDecl());
14451449

1450+
// See the comment above.
1451+
if (assocTypeDC != nullptr &&
1452+
foundDC == assocTypeDC && !isa<AssociatedTypeDecl>(typeDecl))
1453+
continue;
1454+
1455+
// Compute the type of the found declaration when referenced from this
1456+
// location.
14461457
Type type = resolveTypeDecl(typeDecl, foundDC, resolution, silParams, comp);
14471458
if (type->is<ErrorType>())
14481459
return type;

test/Generics/rdar94150249.swift

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// RUN: %target-swift-frontend -typecheck %s -debug-generic-signatures 2>&1 | %FileCheck %s
2+
3+
protocol P1 {
4+
associatedtype Value
5+
}
6+
7+
protocol P2 {
8+
associatedtype Value
9+
}
10+
11+
class G<Value> : P2 {}
12+
13+
protocol P3 {}
14+
15+
class C {}
16+
17+
extension P1 where Value: P2 {
18+
typealias Element = Value.Value
19+
20+
// Make sure we can resolve 'Element' to 'V' on the left hand side of 'Element: P3'.
21+
22+
// CHECK-LABEL: .P1 extension.set()@
23+
// CHECK-NEXT: Generic signature: <Self, V where Self : P1, V : C, V : P3, Self.[P1]Value == G<V>>
24+
func set<V>() where Element: P3 & C, Value == G<V> {
25+
takeP3(V.self)
26+
}
27+
}
28+
29+
func takeP3<T : P3>(_: T.Type) {}

0 commit comments

Comments
 (0)