Skip to content

Sema: Restore old behavior of generic parameters with associated type inference #71548

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
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
44 changes: 21 additions & 23 deletions lib/Sema/AssociatedTypeInference.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2440,29 +2440,6 @@ AssociatedTypeInference::computeAbstractTypeWitness(
void AssociatedTypeInference::collectAbstractTypeWitnesses(
TypeWitnessSystem &system,
ArrayRef<AssociatedTypeDecl *> unresolvedAssocTypes) const {
// Look for suitably-named generic parameters first, before we go digging
// through same-type requirements of protocols.
if (auto genericSig = dc->getGenericSignatureOfContext()) {
for (auto *const assocType : unresolvedAssocTypes) {
// Ignore the generic parameters for AsyncIteratorProtocol.Failure and
// AsyncSequence.Failure.
if (isAsyncIteratorProtocolFailure(assocType))
continue;

for (auto *gp : genericSig.getInnermostGenericParams()) {
// Packs cannot witness associated type requirements.
if (gp->isParameterPack())
continue;

if (gp->getName() == assocType->getName()) {
system.addTypeWitness(assocType->getName(),
dc->mapTypeIntoContext(gp),
/*preferred=*/true);
}
}
}
}

auto considerProtocolRequirements = [&](ProtocolDecl *conformedProto) {
// FIXME: The RequirementMachine will assert on re-entrant construction.
// We should find a more principled way of breaking this cycle.
Expand Down Expand Up @@ -2515,7 +2492,28 @@ void AssociatedTypeInference::collectAbstractTypeWitnesses(
system.addDefaultTypeWitness(typeWitness->getType(),
typeWitness->getDefaultedAssocType(),
preferred);
} else {
// As a last resort, look for a generic parameter that matches the name
// of the associated type.
if (auto genericSig = dc->getGenericSignatureOfContext()) {
// Ignore the generic parameters for AsyncIteratorProtocol.Failure and
// AsyncSequence.Failure.
if (!isAsyncIteratorProtocolFailure(assocType)) {
for (auto *gp : genericSig.getInnermostGenericParams()) {
// Packs cannot witness associated type requirements.
if (gp->isParameterPack())
continue;

if (gp->getName() == assocType->getName()) {
system.addTypeWitness(assocType->getName(),
dc->mapTypeIntoContext(gp),
/*preferred=*/true);
}
}
}
}
}

}
}

Expand Down
5 changes: 3 additions & 2 deletions test/decl/protocol/req/associated_type_generic_param.swift
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
// RUN: %target-typecheck-verify-swift -enable-experimental-associated-type-inference
// RUN: not %target-typecheck-verify-swift -disable-experimental-associated-type-inference
// RUN: %target-typecheck-verify-swift -disable-experimental-associated-type-inference

protocol P {
associatedtype A = Int
}

struct S<A>: P {}

let x: String.Type = S<String>.A.self
// This is unfortunate but it is the behavior of Swift 5.10.
let x: Int.Type = S<String>.A.self
Comment on lines +2 to +11
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would looking for generic parameters before defaults but after same-type requirements not fix this at least?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe, but I would rather fix this in one shot by building a disjunction containing the default associated type and generic parameter, if any, in inferTypeWitnessesViaAssociatedType(), and having the solver walk through the solution space. This first requires better ranking rules and a smarter solver that won't go exponential as easily.

Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ do {
// CHECK-NEXT: }
struct Conformer1: P17a {} // expected-error {{type 'Conformer1' does not conform to protocol 'P17a'}}
// CHECK-LABEL: Abstract type witness system for conformance of Conformer2<A> to P17b: {
// CHECK-NEXT: A => A (preferred), [[EQUIV_CLASS:0x[0-9a-f]+]]
// CHECK-NEXT: A => (unresolved), [[EQUIV_CLASS:0x[0-9a-f]+]]
// CHECK-NEXT: B => (unresolved)
// CHECK-NEXT: }
struct Conformer2<A>: P17b {} // expected-error {{type 'Conformer2<A>' does not conform to protocol 'P17b'}}
Expand All @@ -223,7 +223,7 @@ do {
// CHECK-NEXT: }
struct Conformer3: P17c {}
// CHECK-LABEL: Abstract type witness system for conformance of Conformer4<A> to P17d: {
// CHECK-NEXT: A => A (preferred), [[EQUIV_CLASS:0x[0-9a-f]+]]
// CHECK-NEXT: A => Int (preferred), [[EQUIV_CLASS:0x[0-9a-f]+]]
// CHECK-NEXT: B => Int (preferred), [[EQUIV_CLASS:0x[0-9a-f]+]]
// CHECK-NEXT: }
struct Conformer4<A>: P17d {}
Expand Down Expand Up @@ -645,7 +645,7 @@ protocol P37b {
}
do {
// CHECK-LABEL: Abstract type witness system for conformance of Conformer1<C> to P37b: {
// CHECK-NEXT: C => C (preferred),
// CHECK-NEXT: C => Self.B.A (preferred),
// CHECK-NEXT: }
struct Conformer1<C>: P37b {
struct Inner: P37a { typealias A = C }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// RUN: %target-typecheck-verify-swift -enable-experimental-associated-type-inference
// RUN: not %target-typecheck-verify-swift -enable-experimental-associated-type-inference
// RUN: not %target-typecheck-verify-swift -disable-experimental-associated-type-inference

// FIXME: Get this passing with -enable-experimental-associated-type-inference again.

struct FooIterator<T: Sequence>: IteratorProtocol {
typealias Element = T.Element

Expand Down
14 changes: 14 additions & 0 deletions test/decl/protocol/req/rdar122587432.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// RUN: %target-typecheck-verify-swift -enable-experimental-associated-type-inference
// RUN: %target-typecheck-verify-swift -disable-experimental-associated-type-inference

public struct S<Element: Hashable> {
public typealias A = [Element: Int]
}

extension S: Sequence {
public func makeIterator() -> A.Iterator {
fatalError()
}
}

let x: (key: String, value: Int).Type = S<String>.Element.self