Skip to content

AssociatedTypeInference: Allow Self as a fixed type witness #32799

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 6, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion include/swift/AST/GenericSignatureBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -757,7 +757,7 @@ class GenericSignatureBuilder {
/// \param resolutionKind How to perform the resolution.
///
/// \param wantExactPotentialArchetype Whether to return the precise
/// potential archetype described by the type (vs. just the equivalance
/// potential archetype described by the type (vs. just the equivalence
/// class and resolved type).
ResolvedType maybeResolveEquivalenceClass(
Type type,
Expand Down
2 changes: 1 addition & 1 deletion lib/AST/GenericSignatureBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6265,7 +6265,7 @@ static bool removalDisconnectsEquivalenceClass(
// derived edges).
if (fromComponentIndex == toComponentIndex) return false;

/// Describes the parents in the equivalance classes we're forming.
/// Describes the parents in the equivalence classes we're forming.
SmallVector<unsigned, 4> parents;
for (unsigned i : range(equivClass->derivedSameTypeComponents.size())) {
parents.push_back(i);
Expand Down
19 changes: 12 additions & 7 deletions lib/Sema/TypeCheckProtocolInference.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -805,25 +805,30 @@ Type AssociatedTypeInference::computeFixedTypeWitness(
AssociatedTypeDecl *assocType) {
// Look at all of the inherited protocols to determine whether they
// require a fixed type for this associated type.
Type dependentType = assocType->getDeclaredInterfaceType();
Type resultType;
for (auto conformedProto : adoptee->getAnyNominal()->getAllProtocols()) {
if (!conformedProto->inheritsFrom(assocType->getProtocol()))
continue;

auto genericSig = conformedProto->getGenericSignature();
const auto genericSig = conformedProto->getGenericSignature();
if (!genericSig) return Type();

Type concreteType = genericSig->getConcreteType(dependentType);
if (!concreteType) continue;
const auto nestedType = genericSig->getCanonicalTypeInContext(
DependentMemberType::get(conformedProto->getSelfInterfaceType(),
assocType->getName()));
if (nestedType->isEqual(conformedProto->getSelfInterfaceType())) {
// Self is a valid fixed type witness.
} else if (nestedType->isTypeParameter()) {
continue;
}

if (!resultType) {
resultType = concreteType;
resultType = nestedType;
continue;
}

// FIXME: Bailing out on ambiguity.
if (!resultType->isEqual(concreteType))
if (!resultType->isEqual(nestedType))
return Type();
}

Expand Down Expand Up @@ -887,7 +892,7 @@ AssociatedTypeInference::computeAbstractTypeWitness(
return AbstractTypeWitness::forFixed(assocType, concreteType);

// If we can form a default type, do so.
if (auto typeWitness = computeDefaultTypeWitness(assocType))
if (const auto &typeWitness = computeDefaultTypeWitness(assocType))
return typeWitness;

// If there is a generic parameter of the named type, use that.
Expand Down
21 changes: 6 additions & 15 deletions test/decl/protocol/req/associated_type_inference.swift
Original file line number Diff line number Diff line change
Expand Up @@ -676,21 +676,12 @@ struct S40<E: Equatable>: P40c {
typealias B = Self
}

// Self is not treated as a fixed type witness.
// Fails to find the fixed type witness B == FIXME_S1<A>.
protocol FIXME_P1a {
associatedtype A // expected-note {{protocol requires nested type 'A'}}
}
protocol FIXME_P1b: FIXME_P1a where A == Self {}
// expected-error@+2 {{type 'FIXME_S1' does not conform to protocol 'FIXME_P1a'}}
// expected-error@+1 {{type 'FIXME_S1' does not conform to protocol 'FIXME_P1b'}}
struct FIXME_S1: FIXME_P1b {}

// Fails to find the fixed type witness B == FIXME_S2<A>.
protocol FIXME_P2a {
associatedtype A: Equatable = Never // expected-note {{protocol requires nested type 'A'}}
associatedtype B: FIXME_P2a // expected-note {{protocol requires nested type 'B'}}
associatedtype B: FIXME_P1a // expected-note {{protocol requires nested type 'B'}}
}
protocol FIXME_P2b: FIXME_P2a where B == FIXME_S2<A> {}
// expected-error@+2 {{type 'FIXME_S2<T>' does not conform to protocol 'FIXME_P2a'}}
// expected-error@+1 {{type 'FIXME_S2<T>' does not conform to protocol 'FIXME_P2b'}}
struct FIXME_S2<T: Equatable>: FIXME_P2b {}
protocol FIXME_P1b: FIXME_P1a where B == FIXME_S1<A> {}
// expected-error@+2 {{type 'FIXME_S1<T>' does not conform to protocol 'FIXME_P1a'}}
// expected-error@+1 {{type 'FIXME_S1<T>' does not conform to protocol 'FIXME_P1b'}}
struct FIXME_S1<T: Equatable>: FIXME_P1b {}
105 changes: 105 additions & 0 deletions test/decl/protocol/req/associated_type_inference_fixed_type.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
// RUN: %target-typecheck-verify-swift

protocol P1 where A == Never {
associatedtype A // expected-note {{protocol requires nested type 'A'}}
}
// FIXME: Should this infer A := Never?
struct S1: P1 {} // expected-error {{type 'S1' does not conform to protocol 'P1'}}

protocol P2a {
associatedtype A
}
protocol P2b: P2a where A == Never {}
protocol P2c: P2b {}
struct S2a: P2b {} // OK, A := Never
struct S2b: P2c {} // OK, A := Never

// Fixed type witnesses can reference dependent members.
protocol P3a {
associatedtype A
associatedtype B
}
protocol P3b: P3a where A == [B] {}
struct S3: P3b { // OK, A := [Never], B := Never
typealias B = Never
}

protocol P4 {}
extension P4 {
typealias B = Self
}
struct S4: P4, P3b {} // OK, A := [S4], B := S4

// Self is a valid fixed type witness.
protocol P5a {
associatedtype A
}
protocol P5b: P5a where A == Self {}
struct S5<X>: P5b {} // OK, A := S5<X>


protocol P6 where A == Never { // expected-error {{same-type constraint type 'Never' does not conform to required protocol 'P6'}}
// expected-error@+2 {{same-type constraint type 'Never' does not conform to required protocol 'P6'}}
// expected-note@+1 {{protocol requires nested type 'A}}
associatedtype A: P6
}
struct S6: P6 {} // expected-error {{type 'S6' does not conform to protocol 'P6'}}

protocol P7a where A == Never {
associatedtype A
}
// expected-error@+2 {{'Self.A' cannot be equal to both 'Bool' and 'Never'}}
// expected-note@+1 {{same-type constraint 'Self.A' == 'Never' implied here}}
protocol P7b: P7a where A == Bool {}
struct S7: P7b {}

protocol P8 where A == Bool {
associatedtype A // expected-note {{protocol requires nested type 'A'}}
}
// expected-error@+2 {{type 'S8' does not conform to protocol 'P7a'}}
// expected-error@+1 {{type 'S8' does not conform to protocol 'P8'}}
struct S8: P8, P7a {}

protocol P9a where A == Never {
associatedtype A
}
protocol P9b: P9a {
associatedtype A // expected-note {{protocol requires nested type 'A'}}
}
// FIXME: Associated type restatement sabotages the conformance.
// expected-error@+2 {{type 'S9a' does not conform to protocol 'P9a'}}
// expected-error@+1 {{type 'S9a' does not conform to protocol 'P9b'}}
struct S9a: P9b {}
// expected-error@+2 {{'P9a' requires the types 'S9b.A' (aka 'Bool') and 'Never' be equivalent}}
// expected-note@+1 {{requirement specified as 'Self.A' == 'Never' [with Self = S9b]}}
struct S9b: P9b {
typealias A = Bool
}
struct S9c: P9b { // OK, S9c.A does not contradict Self.A == Never.
typealias Sugar = Never
typealias A = Sugar
}

protocol P10 {}
extension P10 {
typealias A = Bool
}
// FIXME: 'P10 extension.A' should not be considered a viable type witness;
// instead, the compiler should infer A := Never and synthesize S10.A.
// expected-error@+2 {{'P9a' requires the types 'S10.A' (aka 'Bool') and 'Never' be equivalent}}
// expected-note@+1 {{requirement specified as 'Self.A' == 'Never' [with Self = S10]}}
struct S10: P10, P9a {}

protocol P11a {
associatedtype A
}
protocol P11b: P11a where A == Never {}
protocol Q11 {
associatedtype A // expected-note {{protocol requires nested type 'A'}}
}
// FIXME: Unrelated protocols with a matching associated type should
// also be considered when computing a fixed type witness.
// expected-error@+3 {{type 'S11' does not conform to protocol 'Q11'}}
// expected-error@+2 {{type 'S11' does not conform to protocol 'P11a'}}
// expected-error@+1 {{type 'S11' does not conform to protocol 'P11b'}}
struct S11: Q11, P11b {}