Skip to content

Commit 653fa07

Browse files
committed
GSB: Fix maybeResolveEquivalenceClass() with member type of superclass-constrained type
Name lookup might find an associated type whose protocol is not in our conforms-to list, if we have a superclass constraint and the superclass conforms to the associated type's protocol. We used to return an unresolved type in this case, which would result in the constraint getting delayed forever and dropped. While playing wack-a-mole with regressing crashers, I had to do some refactoring to get all the tests to pass. Unfortuanately these refactorings don't lend themselves well to being peeled off into their own commits: - maybeAddSameTypeRequirementForNestedType() was almost identical to concretizeNestedTypeFromConcreteParent(), except for superclasses instead of concrete same-type constraints. I merged them together. - We used to drop same-type constraints where the subject type was an ErrorType, because maybeResolveEquivalenceClass() would return an unresolved type in this case. This violated some invariants around nested types of ArchetypeTypes, because now it was possible for a nested type of a concrete type to be non-concrete, if the type witness in the conformance was missing due to an error. Fix this by removing the ErrorType hack, and adjusting a couple of other places to handle ErrorTypes in order to avoid regressing with invalid code. Fixes <rdar://problem/45216921>, <https://bugs.swift.org/browse/SR-8945>, <https://bugs.swift.org/browse/SR-12744>.
1 parent dbb5949 commit 653fa07

File tree

9 files changed

+113
-129
lines changed

9 files changed

+113
-129
lines changed

lib/AST/GenericSignatureBuilder.cpp

Lines changed: 85 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -2390,43 +2390,6 @@ Type ResolvedType::getDependentType(GenericSignatureBuilder &builder) const {
23902390
return result->isTypeParameter() ? result : Type();
23912391
}
23922392

2393-
/// If there is a same-type requirement to be added for the given nested type
2394-
/// due to a superclass constraint on the parent type, add it now.
2395-
static void maybeAddSameTypeRequirementForNestedType(
2396-
ResolvedType nested,
2397-
const RequirementSource *superSource,
2398-
GenericSignatureBuilder &builder) {
2399-
// If there's no super conformance, we're done.
2400-
if (!superSource) return;
2401-
2402-
// If the nested type is already concrete, we're done.
2403-
if (nested.getAsConcreteType()) return;
2404-
2405-
// Dig out the associated type.
2406-
AssociatedTypeDecl *assocType = nullptr;
2407-
if (auto depMemTy =
2408-
nested.getDependentType(builder)->getAs<DependentMemberType>())
2409-
assocType = depMemTy->getAssocType();
2410-
else
2411-
return;
2412-
2413-
// Dig out the type witness.
2414-
auto superConformance = superSource->getProtocolConformance().getConcrete();
2415-
auto concreteType = superConformance->getTypeWitness(assocType);
2416-
if (!concreteType) return;
2417-
2418-
// We should only have interface types here.
2419-
assert(!superConformance->getType()->hasArchetype());
2420-
assert(!concreteType->hasArchetype());
2421-
2422-
// Add the same-type constraint.
2423-
auto nestedSource = superSource->viaParent(builder, assocType);
2424-
2425-
builder.addSameTypeRequirement(
2426-
nested.getUnresolvedType(), concreteType, nestedSource,
2427-
GenericSignatureBuilder::UnresolvedHandlingKind::GenerateConstraints);
2428-
}
2429-
24302393
auto PotentialArchetype::getOrCreateEquivalenceClass(
24312394
GenericSignatureBuilder &builder) const
24322395
-> EquivalenceClass * {
@@ -2562,7 +2525,9 @@ static void concretizeNestedTypeFromConcreteParent(
25622525
// If we don't already have a conformance of the parent to this protocol,
25632526
// add it now; it was elided earlier.
25642527
if (parentEquiv->conformsTo.count(proto) == 0) {
2565-
auto source = parentEquiv->concreteTypeConstraints.front().source;
2528+
auto source = (!isSuperclassConstrained
2529+
? parentEquiv->concreteTypeConstraints.front().source
2530+
: parentEquiv->superclassConstraints.front().source);
25662531
parentEquiv->recordConformanceConstraint(builder, parent, proto, source);
25672532
}
25682533

@@ -2592,7 +2557,7 @@ static void concretizeNestedTypeFromConcreteParent(
25922557
if (conformance.isConcrete()) {
25932558
witnessType =
25942559
conformance.getConcrete()->getTypeWitness(assocType);
2595-
if (!witnessType || witnessType->hasError())
2560+
if (!witnessType)
25962561
return; // FIXME: should we delay here?
25972562
} else if (auto archetype = concreteParent->getAs<ArchetypeType>()) {
25982563
witnessType = archetype->getNestedType(assocType->getName());
@@ -2657,20 +2622,11 @@ PotentialArchetype *PotentialArchetype::updateNestedTypeForConformance(
26572622

26582623
// If we have a potential archetype that requires more processing, do so now.
26592624
if (shouldUpdatePA) {
2660-
// If there's a superclass constraint that conforms to the protocol,
2661-
// add the appropriate same-type relationship.
2662-
const auto proto = assocType->getProtocol();
2663-
if (proto) {
2664-
if (auto superSource = builder.resolveSuperConformance(this, proto)) {
2665-
maybeAddSameTypeRequirementForNestedType(resultPA, superSource,
2666-
builder);
2667-
}
2668-
}
2669-
26702625
// We know something concrete about the parent PA, so we need to propagate
26712626
// that information to this new archetype.
2672-
if (isConcreteType()) {
2673-
concretizeNestedTypeFromConcreteParent(this, resultPA, builder);
2627+
if (auto equivClass = getEquivalenceClassIfPresent()) {
2628+
if (equivClass->concreteType || equivClass->superclass)
2629+
concretizeNestedTypeFromConcreteParent(this, resultPA, builder);
26742630
}
26752631
}
26762632

@@ -3618,50 +3574,29 @@ static Type getStructuralType(TypeDecl *typeDecl, bool keepSugar) {
36183574
return typeDecl->getDeclaredInterfaceType();
36193575
}
36203576

3621-
static Type substituteConcreteType(GenericSignatureBuilder &builder,
3622-
PotentialArchetype *basePA,
3577+
static Type substituteConcreteType(Type parentType,
36233578
TypeDecl *concreteDecl) {
3579+
if (parentType->is<ErrorType>())
3580+
return parentType;
3581+
36243582
assert(concreteDecl);
36253583

36263584
auto *dc = concreteDecl->getDeclContext();
3627-
auto *proto = dc->getSelfProtocolDecl();
36283585

36293586
// Form an unsubstituted type referring to the given type declaration,
36303587
// for use in an inferred same-type requirement.
36313588
auto type = getStructuralType(concreteDecl, /*keepSugar=*/true);
36323589

3633-
SubstitutionMap subMap;
3634-
if (proto) {
3635-
// Substitute in the type of the current PotentialArchetype in
3636-
// place of 'Self' here.
3637-
auto parentType = basePA->getDependentType(builder.getGenericParams());
3638-
3639-
subMap = SubstitutionMap::getProtocolSubstitutions(
3640-
proto, parentType, ProtocolConformanceRef(proto));
3641-
} else {
3642-
// Substitute in the superclass type.
3643-
auto parentPA = basePA->getEquivalenceClassIfPresent();
3644-
auto parentType =
3645-
parentPA->concreteType ? parentPA->concreteType : parentPA->superclass;
3646-
auto parentDecl = parentType->getAnyNominal();
3647-
3648-
subMap = parentType->getContextSubstitutionMap(
3649-
parentDecl->getParentModule(), dc);
3650-
}
3590+
auto subMap = parentType->getContextSubstitutionMap(
3591+
dc->getParentModule(), dc);
36513592

36523593
return type.subst(subMap);
3653-
};
3594+
}
36543595

36553596
ResolvedType GenericSignatureBuilder::maybeResolveEquivalenceClass(
36563597
Type type,
36573598
ArchetypeResolutionKind resolutionKind,
36583599
bool wantExactPotentialArchetype) {
3659-
// An error type is best modeled as an unresolved potential archetype, since
3660-
// there's no way to be sure what it is actually meant to be.
3661-
if (type->is<ErrorType>()) {
3662-
return ResolvedType::forUnresolved(nullptr);
3663-
}
3664-
36653600
// The equivalence class of a generic type is known directly.
36663601
if (auto genericParam = type->getAs<GenericTypeParamType>()) {
36673602
unsigned index = GenericParamKey(genericParam).findIndexIn(
@@ -3683,8 +3618,11 @@ ResolvedType GenericSignatureBuilder::maybeResolveEquivalenceClass(
36833618
wantExactPotentialArchetype);
36843619
if (!resolvedBase) return resolvedBase;
36853620
// If the base is concrete, so is this member.
3686-
if (resolvedBase.getAsConcreteType())
3687-
return ResolvedType::forConcrete(type);
3621+
if (auto parentType = resolvedBase.getAsConcreteType()) {
3622+
auto concreteType = substituteConcreteType(parentType,
3623+
depMemTy->getAssocType());
3624+
return ResolvedType::forConcrete(concreteType);
3625+
}
36883626

36893627
// Find the nested type declaration for this.
36903628
auto baseEquivClass = resolvedBase.getEquivalenceClass(*this);
@@ -3701,59 +3639,84 @@ ResolvedType GenericSignatureBuilder::maybeResolveEquivalenceClass(
37013639
basePA = baseEquivClass->members.front();
37023640
}
37033641

3704-
AssociatedTypeDecl *nestedTypeDecl = nullptr;
37053642
if (auto assocType = depMemTy->getAssocType()) {
37063643
// Check whether this associated type references a protocol to which
3707-
// the base conforms. If not, it's unresolved.
3708-
if (baseEquivClass->conformsTo.find(assocType->getProtocol())
3644+
// the base conforms. If not, it's either concrete or unresolved.
3645+
auto *proto = assocType->getProtocol();
3646+
if (baseEquivClass->conformsTo.find(proto)
37093647
== baseEquivClass->conformsTo.end()) {
3710-
if (!baseEquivClass->concreteType ||
3711-
!lookupConformance(type->getCanonicalType(),
3712-
baseEquivClass->concreteType,
3713-
assocType->getProtocol())) {
3648+
if (baseEquivClass->concreteType &&
3649+
lookupConformance(type->getCanonicalType(),
3650+
baseEquivClass->concreteType,
3651+
proto)) {
3652+
// Fall through
3653+
} else if (baseEquivClass->superclass &&
3654+
lookupConformance(type->getCanonicalType(),
3655+
baseEquivClass->superclass,
3656+
proto)) {
3657+
// Fall through
3658+
} else {
37143659
return ResolvedType::forUnresolved(baseEquivClass);
37153660
}
3661+
3662+
// FIXME: Instead of falling through, we ought to return a concrete
3663+
// type here, but then we fail to update a nested PotentialArchetype
3664+
// if one happens to already exist. It would be cleaner if concrete
3665+
// types never had nested PotentialArchetypes.
37163666
}
37173667

3718-
nestedTypeDecl = assocType;
3668+
auto nestedPA =
3669+
basePA->updateNestedTypeForConformance(*this, assocType,
3670+
resolutionKind);
3671+
if (!nestedPA)
3672+
return ResolvedType::forUnresolved(baseEquivClass);
3673+
3674+
// If base resolved to the anchor, then the nested potential archetype
3675+
// we found is the resolved potential archetype. Return it directly,
3676+
// so it doesn't need to be resolved again.
3677+
if (basePA == resolvedBase.getPotentialArchetypeIfKnown())
3678+
return ResolvedType(nestedPA);
3679+
3680+
// Compute the resolved dependent type to return.
3681+
Type resolvedBaseType = resolvedBase.getDependentType(*this);
3682+
Type resolvedMemberType =
3683+
DependentMemberType::get(resolvedBaseType, assocType);
3684+
3685+
return ResolvedType(resolvedMemberType,
3686+
nestedPA->getOrCreateEquivalenceClass(*this));
37193687
} else {
3720-
auto *typeAlias =
3688+
auto *concreteDecl =
37213689
baseEquivClass->lookupNestedType(*this, depMemTy->getName());
37223690

3723-
if (!typeAlias)
3691+
if (!concreteDecl)
37243692
return ResolvedType::forUnresolved(baseEquivClass);
37253693

3726-
auto type = substituteConcreteType(*this, basePA, typeAlias);
3727-
return maybeResolveEquivalenceClass(type, resolutionKind,
3728-
wantExactPotentialArchetype);
3729-
}
3694+
Type parentType;
3695+
auto *proto = concreteDecl->getDeclContext()->getSelfProtocolDecl();
3696+
if (!proto) {
3697+
parentType = (baseEquivClass->concreteType
3698+
? baseEquivClass->concreteType
3699+
: baseEquivClass->superclass);
3700+
} else {
3701+
if (baseEquivClass->concreteType &&
3702+
lookupConformance(type->getCanonicalType(),
3703+
baseEquivClass->concreteType,
3704+
proto)) {
3705+
parentType = baseEquivClass->concreteType;
3706+
} else if (baseEquivClass->superclass &&
3707+
lookupConformance(type->getCanonicalType(),
3708+
baseEquivClass->superclass,
3709+
proto)) {
3710+
parentType = baseEquivClass->superclass;
3711+
} else {
3712+
parentType = basePA->getDependentType(getGenericParams());
3713+
}
3714+
}
37303715

3731-
auto nestedPA =
3732-
basePA->updateNestedTypeForConformance(*this, nestedTypeDecl,
3733-
resolutionKind);
3734-
if (!nestedPA)
3735-
return ResolvedType::forUnresolved(baseEquivClass);
3736-
3737-
// If base resolved to the anchor, then the nested potential archetype
3738-
// we found is the resolved potential archetype. Return it directly,
3739-
// so it doesn't need to be resolved again.
3740-
if (basePA == resolvedBase.getPotentialArchetypeIfKnown())
3741-
return ResolvedType(nestedPA);
3742-
3743-
// Compute the resolved dependent type to return.
3744-
Type resolvedBaseType = resolvedBase.getDependentType(*this);
3745-
Type resolvedMemberType;
3746-
if (auto assocType = dyn_cast<AssociatedTypeDecl>(nestedTypeDecl)) {
3747-
resolvedMemberType =
3748-
DependentMemberType::get(resolvedBaseType, assocType);
3749-
} else {
3750-
// Note: strange case that might not even really be dependent.
3751-
resolvedMemberType =
3752-
DependentMemberType::get(resolvedBaseType, depMemTy->getName());
3716+
auto concreteType = substituteConcreteType(parentType, concreteDecl);
3717+
return maybeResolveEquivalenceClass(concreteType, resolutionKind,
3718+
wantExactPotentialArchetype);
37533719
}
3754-
3755-
return ResolvedType(resolvedMemberType,
3756-
nestedPA->getOrCreateEquivalenceClass(*this));
37573720
}
37583721

37593722
// If it's not a type parameter, it won't directly resolve to one.
@@ -5556,7 +5519,8 @@ GenericSignatureBuilder::finalize(SourceLoc loc,
55565519
// Don't allow a generic parameter to be equivalent to a concrete type,
55575520
// because then we don't actually have a parameter.
55585521
auto equivClass = rep->getOrCreateEquivalenceClass(*this);
5559-
if (equivClass->concreteType) {
5522+
if (equivClass->concreteType &&
5523+
!equivClass->concreteType->is<ErrorType>()) {
55605524
if (auto constraint = equivClass->findAnyConcreteConstraintAsWritten()){
55615525
Impl->HadAnyError = true;
55625526

test/AutoDiff/compiler_crashers/sr12744-unhandled-pullback-indirect-result.swift renamed to test/AutoDiff/compiler_crashers_fixed/sr12744-unhandled-pullback-indirect-result.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
// RUN: not --crash %target-swift-frontend -emit-sil -verify %s
2-
// REQUIRES: asserts
1+
// RUN: %target-swift-frontend -emit-sil -verify %s
32

43
// SR-12744: Pullback generation crash for unhandled indirect result.
54
// May be due to inconsistent derivative function type calculation logic in

test/Constraints/same_types.swift

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -89,15 +89,14 @@ func test6<T: Barrable>(_ t: T) -> (Y, X) where T.Bar == Y {
8989
}
9090

9191
func test7<T: Barrable>(_ t: T) -> (Y, X) where T.Bar == Y, T.Bar.Foo == X {
92-
// expected-warning@-1{{redundant same-type constraint 'T.Bar.Foo' == 'X'}}
93-
// expected-note@-2{{same-type constraint 'T.Bar.Foo' == 'Y.Foo' (aka 'X') implied here}}
92+
// expected-warning@-1{{neither type in same-type constraint ('Y.Foo' (aka 'X') or 'X') refers to a generic parameter or associated type}}
9493
return (t.bar, t.bar.foo)
9594
}
9695

9796
func fail4<T: Barrable>(_ t: T) -> (Y, Z)
9897
where
99-
T.Bar == Y, // expected-note{{same-type constraint 'T.Bar.Foo' == 'Y.Foo' (aka 'X') implied here}}
100-
T.Bar.Foo == Z { // expected-error{{'T.Bar.Foo' cannot be equal to both 'Z' and 'Y.Foo' (aka 'X')}}
98+
T.Bar == Y,
99+
T.Bar.Foo == Z { // expected-error{{generic signature requires types 'Y.Foo' (aka 'X') and 'Z' to be the same}}
101100
return (t.bar, t.bar.foo) // expected-error{{cannot convert return expression of type '(Y, X)' to return type '(Y, Z)'}}
102101
}
103102

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
public protocol P {
2+
associatedtype T
3+
}

test/Generics/sr8945.swift

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-frontend -emit-module %S/Inputs/sr8945-other.swift -emit-module-path %t/other.swiftmodule -module-name other
3+
// RUN: %target-swift-frontend -emit-silgen %s -I%t
4+
5+
import other
6+
7+
public class C : P {
8+
public typealias T = Int
9+
}
10+
11+
public func takesInt(_: Int) {}
12+
13+
public func foo<T : C, S : Sequence>(_: T, _ xs: S) where S.Element == T.T {
14+
for x in xs {
15+
takesInt(x)
16+
}
17+
}

test/IDE/print_ast_tc_decls_errors.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ protocol AssociatedType1 {
192192
// TYREPR: {{^}} associatedtype AssociatedTypeDecl4 : FooNonExistentProtocol, BarNonExistentProtocol{{$}}
193193

194194
associatedtype AssociatedTypeDecl5 : FooClass
195-
// CHECK: {{^}} associatedtype AssociatedTypeDecl5 : FooClass{{$}}
195+
// CHECK: {{^}} associatedtype AssociatedTypeDecl5{{$}}
196196
}
197197

198198
//===---

test/decl/protocol/req/recursion.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ public struct S<A: P> where A.T == S<A> { // expected-error {{circular reference
4949
// expected-note@-3 {{while resolving type 'S<A>'}}
5050
func f(a: A.T) {
5151
g(a: id(t: a))
52-
// expected-error@-1 {{cannot convert value of type 'A.T' to expected argument type 'S<A>'}}
52+
// expected-error@-1 {{type of expression is ambiguous without more context}}
5353
_ = A.T.self
5454
}
5555

validation-test/compiler_crashers_2_fixed/0159-rdar40009245.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
protocol P {
44
associatedtype A : P where A.X == Self
5+
// expected-error@-1{{'X' is not a member type of 'Self.A}}
56
associatedtype X : P where P.A == Self
67
// expected-error@-1{{associated type 'A' can only be used with a concrete type or generic parameter base}}
78
}

validation-test/compiler_crashers_2_fixed/0163-sr8033.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ protocol P1 {
77
}
88
extension Foo: P1 where A : P1 {} // expected-error {{unsupported recursion for reference to associated type 'A' of type 'Foo<T>'}}
99
// expected-error@-1 {{type 'Foo<T>' does not conform to protocol 'P1'}}
10+
// expected-error@-2 {{type 'Foo<T>' in conformance requirement does not refer to a generic parameter or associated type}}

0 commit comments

Comments
 (0)