Skip to content

Commit 13cdf10

Browse files
committed
Sema: Implement missing part of SE-0346
The proposal states that this should work, but this was never implemented: protocol P<A> { associatedtype A } struct S: P<Int> {} - Fixes #62906. - Fixes rdar://91842338.
1 parent 91c9292 commit 13cdf10

File tree

7 files changed

+129
-63
lines changed

7 files changed

+129
-63
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3616,8 +3616,6 @@ ERROR(inheritance_from_non_protocol,none,
36163616
"inheritance from non-protocol type %0", (Type))
36173617
ERROR(inheritance_from_anyobject,none,
36183618
"only protocols can inherit from 'AnyObject'", ())
3619-
ERROR(inheritance_from_parameterized_protocol,none,
3620-
"cannot inherit from protocol type with generic argument %0", (Type))
36213619
ERROR(superclass_not_first,none,
36223620
"superclass %0 must appear first in the inheritance clause", (Type))
36233621
ERROR(superclass_not_open,none,

lib/Sema/AssociatedTypeInference.cpp

Lines changed: 71 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,28 @@ static bool isAsyncSequenceFailure(AssociatedTypeDecl *assocType) {
439439
return assocType->getName() == assocType->getASTContext().Id_Failure;
440440
}
441441

442+
static Type resolveTypeWitnessViaParameterizedProtocol(
443+
Type t, AssociatedTypeDecl *assocType) {
444+
if (auto *pct = t->getAs<ProtocolCompositionType>()) {
445+
for (auto member : pct->getMembers()) {
446+
if (auto result = resolveTypeWitnessViaParameterizedProtocol(
447+
member, assocType)) {
448+
return result;
449+
}
450+
}
451+
} else if (auto *ppt = t->getAs<ParameterizedProtocolType>()) {
452+
auto *proto = ppt->getProtocol();
453+
unsigned i = 0;
454+
for (auto *otherAssocType : proto->getPrimaryAssociatedTypes()) {
455+
if (otherAssocType->getName() == assocType->getName())
456+
return ppt->getArgs()[i];
457+
++i;
458+
}
459+
}
460+
461+
return Type();
462+
}
463+
442464
/// Attempt to resolve a type witness via member name lookup.
443465
static ResolveWitnessResult resolveTypeWitnessViaLookup(
444466
NormalProtocolConformance *conformance,
@@ -455,6 +477,23 @@ static ResolveWitnessResult resolveTypeWitnessViaLookup(
455477
abort();
456478
}
457479

480+
// Look for a parameterized protocol type in the conformance context's
481+
// inheritance clause.
482+
bool deducedFromParameterizedProtocolType = false;
483+
auto inherited = (isa<NominalTypeDecl>(dc)
484+
? cast<NominalTypeDecl>(dc)->getInherited()
485+
: cast<ExtensionDecl>(dc)->getInherited());
486+
for (auto index : inherited.getIndices()) {
487+
if (auto inheritedTy = inherited.getResolvedType(index)) {
488+
if (auto typeWitness = resolveTypeWitnessViaParameterizedProtocol(
489+
inheritedTy, assocType)) {
490+
recordTypeWitness(conformance, assocType, typeWitness, nullptr);
491+
deducedFromParameterizedProtocolType = true;
492+
}
493+
}
494+
}
495+
496+
// Next, look for a member type declaration with this name.
458497
NLOptions subOptions = (NL_QualifiedDefault | NL_OnlyTypes |
459498
NL_ProtocolMembers | NL_IncludeAttributeImplements);
460499

@@ -561,27 +600,40 @@ static ResolveWitnessResult resolveTypeWitnessViaLookup(
561600
}
562601
}
563602

564-
// If there are no viable witnesses, and all nonviable candidates came from
565-
// protocol extensions, treat this as "missing".
566-
if (viable.empty() &&
567-
std::find_if(nonViable.begin(), nonViable.end(),
568-
[](const std::pair<TypeDecl *, CheckTypeWitnessResult> &x) {
569-
return x.first->getDeclContext()
570-
->getSelfProtocolDecl() == nullptr;
571-
}) == nonViable.end())
572-
return ResolveWitnessResult::Missing;
603+
if (!deducedFromParameterizedProtocolType) {
604+
// If there are no viable witnesses, and all nonviable candidates came from
605+
// protocol extensions, treat this as "missing".
606+
if (viable.empty() &&
607+
std::find_if(nonViable.begin(), nonViable.end(),
608+
[](const std::pair<TypeDecl *, CheckTypeWitnessResult> &x) {
609+
return x.first->getDeclContext()
610+
->getSelfProtocolDecl() == nullptr;
611+
}) == nonViable.end())
612+
return ResolveWitnessResult::Missing;
613+
614+
// If there is a single viable candidate, form a substitution for it.
615+
if (viable.size() == 1) {
616+
auto interfaceType = viable.front().MemberType;
617+
recordTypeWitness(conformance, assocType, interfaceType,
618+
viable.front().Member);
619+
return ResolveWitnessResult::Success;
620+
}
573621

574-
// If there is a single viable candidate, form a substitution for it.
575-
if (viable.size() == 1) {
576-
auto interfaceType = viable.front().MemberType;
577-
recordTypeWitness(conformance, assocType, interfaceType,
578-
viable.front().Member);
579-
return ResolveWitnessResult::Success;
580-
}
622+
// Record an error.
623+
recordTypeWitness(conformance, assocType,
624+
ErrorType::get(ctx), nullptr);
625+
} else {
626+
// We deduced the type witness from a parameterized protocol type, so just
627+
// make sure there was nothing else.
628+
if (viable.size() == 1 &&
629+
isa<TypeAliasDecl>(viable[0].Member) &&
630+
viable[0].Member->isSynthesized()) {
631+
// We found the type alias synthesized above.
632+
return ResolveWitnessResult::Success;
633+
}
581634

582-
// Record an error.
583-
recordTypeWitness(conformance, assocType,
584-
ErrorType::get(ctx), nullptr);
635+
// Otherwise fall through.
636+
}
585637

586638
// If we had multiple viable types, diagnose the ambiguity.
587639
if (!viable.empty()) {

lib/Sema/TypeCheckDeclPrimary.cpp

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -78,21 +78,6 @@ using namespace swift;
7878

7979
#define DEBUG_TYPE "TypeCheckDeclPrimary"
8080

81-
static Type containsParameterizedProtocolType(Type inheritedTy) {
82-
if (inheritedTy->is<ParameterizedProtocolType>()) {
83-
return inheritedTy;
84-
}
85-
86-
if (auto *compositionTy = inheritedTy->getAs<ProtocolCompositionType>()) {
87-
for (auto memberTy : compositionTy->getMembers()) {
88-
if (auto paramTy = containsParameterizedProtocolType(memberTy))
89-
return paramTy;
90-
}
91-
}
92-
93-
return Type();
94-
}
95-
9681
class CheckRepressions {
9782
llvm::PointerUnion<const TypeDecl *, const ExtensionDecl *> declUnion;
9883
ASTContext &ctx;
@@ -303,14 +288,6 @@ static void checkInheritanceClause(
303288
inheritedAnyObject = { i, inherited.getSourceRange() };
304289
}
305290

306-
if (auto paramTy = containsParameterizedProtocolType(inheritedTy)) {
307-
if (!isa<ProtocolDecl>(decl)) {
308-
decl->diagnose(diag::inheritance_from_parameterized_protocol,
309-
paramTy);
310-
}
311-
continue;
312-
}
313-
314291
if (inheritedTy->isConstraintType()) {
315292
auto layout = inheritedTy->getExistentialLayout();
316293

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// RUN: %target-typecheck-verify-swift
2+
3+
protocol P<A, B> {
4+
associatedtype A // expected-note {{multiple matching types named 'A'}}
5+
associatedtype B
6+
7+
func a(_: A)
8+
}
9+
10+
extension P {
11+
func a(_: A) {}
12+
}
13+
14+
struct S: P<Int, String> {}
15+
16+
func f(_: Int.Type) {}
17+
func g(_: String.Type) {}
18+
19+
f(S.A.self)
20+
g(S.B.self)
21+
22+
struct G<T, U>: P<T, U> {}
23+
24+
f(G<Int, String>.A.self)
25+
g(G<Int, String>.B.self)
26+
27+
protocol Q<B> {
28+
associatedtype B // expected-note {{protocol requires nested type 'B'}}
29+
}
30+
31+
struct S2: P, Q<String> {
32+
func a(_: Int) {}
33+
}
34+
35+
struct S3: P & Q<String> {
36+
func a(_: Int) {}
37+
}
38+
39+
protocol R {}
40+
41+
struct S4: (P & Q<String>) & R {
42+
func a(_: Int) {}
43+
}
44+
45+
struct Bad: P<Int, Float> { // expected-error {{type 'Bad' does not conform to protocol 'P'}}
46+
typealias A = String // expected-note {{possibly intended match}}
47+
}
48+
49+
let x = Bad.A.self
50+
g(x)
51+
52+
struct Circle: Q<Circle.A> {}
53+
// expected-note@-1 {{'Circle' declared here}}
54+
// expected-error@-2 {{type 'Circle' does not conform to protocol 'Q'}}
55+
// expected-note@-3 {{add stubs for conformance}}
56+
// expected-error@-4 {{'A' is not a member type of struct 'type_witness_from_parameterized_protocol.Circle'}}

test/Generics/rdar94848868.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ protocol MyCollectionProtocol: Collection where Iterator == MyCollectionIterator
88
struct MyCollectionIterator<MyCollection: MyCollectionProtocol>: IteratorProtocol {
99
// expected-note@-1 3{{through reference here}}
1010
mutating func next() -> MyCollection.Element? {
11-
// expected-note@-1 2{{through reference here}}
1211
return nil
1312
}
1413
}

test/type/parameterized_protocol.swift

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ protocol Invalid5<Element, Element> {
3131

3232
protocol Sequence<Element> {
3333
associatedtype Element
34-
// expected-note@-1 2{{protocol requires nested type 'Element'}}
3534
}
3635

3736
extension Sequence {
@@ -56,14 +55,6 @@ struct ConcreteEquatableSequence<Element : Equatable> : EquatableSequence {}
5655
protocol IntSequence : Sequence<Int> {}
5756

5857

59-
/// Concrete types cannot inherit from a parameterized protocol
60-
61-
struct SillyStruct : Sequence<Int> {}
62-
// expected-error@-1 {{cannot inherit from protocol type with generic argument 'Sequence<Int>'}}
63-
// expected-error@-2 {{type 'SillyStruct' does not conform to protocol 'Sequence'}}
64-
// expected-note@-3 {{add stubs for conformance}}
65-
66-
6758
/// Parameterized protocol in generic parameter inheritance clause
6859

6960
// CHECK-LABEL: parameterized_protocol.(file).IntSequenceWrapper@
@@ -213,12 +204,6 @@ protocol TestCompositionProtocol1 {
213204
associatedtype S : Sequence<Int> & Sendable
214205
}
215206

216-
struct TestStructComposition : Sequence<Int> & Sendable {}
217-
// expected-error@-1 {{cannot inherit from protocol type with generic argument 'Sequence<Int>'}}
218-
// expected-error@-2 {{type 'TestStructComposition' does not conform to protocol 'Sequence'}}
219-
// expected-note@-3 {{add stubs for conformance}}
220-
221-
222207
/// Conflicts
223208

224209
protocol Pair<X, Y> where Self.X == Self.Y {

validation-test/compiler_crashers_2_fixed/0161-issue-49119.swift

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,9 @@ protocol P {
99
struct Type<Param> {}
1010
extension Type: P where Param: P, Param.A == Type<Param> {
1111
// expected-error@-1 {{extension of generic struct 'Type' has self-referential generic requirements}}
12-
// expected-note@-2 {{through reference here}}
12+
// expected-note@-2 2 {{through reference here}}
1313
// expected-error@-3 {{type 'Type<Param>' does not conform to protocol 'P'}}
1414
// expected-note@-4 {{add stubs for conformance}}
1515
typealias A = Param
16-
// expected-note@-1 3{{through reference here}}
17-
// expected-note@-2 {{possibly intended match 'Type<Param>.A' (aka 'Param') does not conform to 'P'}}
16+
// expected-note@-1 {{possibly intended match 'Type<Param>.A' (aka 'Param') does not conform to 'P'}}
1817
}

0 commit comments

Comments
 (0)