Skip to content

Commit c61a4fe

Browse files
authored
Merge pull request #81949 from slavapestov/missing-part-of-se-0346
Sema: Implement missing part of SE-0346
2 parents 84c2c38 + 185a91d commit c61a4fe

File tree

8 files changed

+172
-85
lines changed

8 files changed

+172
-85
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3657,8 +3657,6 @@ ERROR(inheritance_from_non_protocol,none,
36573657
"inheritance from non-protocol type %0", (Type))
36583658
ERROR(inheritance_from_anyobject,none,
36593659
"only protocols can inherit from 'AnyObject'", ())
3660-
ERROR(inheritance_from_parameterized_protocol,none,
3661-
"cannot inherit from protocol type with generic argument %0", (Type))
36623660
ERROR(superclass_not_first,none,
36633661
"superclass %0 must appear first in the inheritance clause", (Type))
36643662
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,
@@ -456,6 +478,23 @@ static ResolveWitnessResult resolveTypeWitnessViaLookup(
456478
});
457479
}
458480

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

@@ -562,27 +601,40 @@ static ResolveWitnessResult resolveTypeWitnessViaLookup(
562601
}
563602
}
564603

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

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

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

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

lib/Sema/TypeCheckDeclPrimary.cpp

Lines changed: 30 additions & 45 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

@@ -1800,6 +1777,33 @@ static TypeRepr *unwrapAttributedRepr(TypeRepr *repr) {
18001777
return repr;
18011778
}
18021779

1780+
static void collectProtocolsFromInheritedEntry(
1781+
const InheritedEntry &entry,
1782+
Type inheritedTy,
1783+
llvm::SmallPtrSetImpl<ProtocolDecl *> &protocolsWithRetroactiveAttr,
1784+
SmallVectorImpl<ProtocolDecl *> &protos) {
1785+
1786+
if (auto *protoTy = inheritedTy->getAs<ProtocolType>()) {
1787+
auto *proto = protoTy->getDecl();
1788+
1789+
// As a fallback, to support previous language versions, also allow
1790+
// this through if the protocol has been explicitly module-qualified.
1791+
TypeRepr *repr = unwrapAttributedRepr(entry.getTypeRepr());
1792+
if (isModuleQualified(repr, proto->getParentModule())) {
1793+
protocolsWithRetroactiveAttr.insert(proto);
1794+
}
1795+
1796+
protos.push_back(proto);
1797+
} else if (auto *pct = inheritedTy->getAs<ProtocolCompositionType>()) {
1798+
for (auto member : pct->getMembers()) {
1799+
collectProtocolsFromInheritedEntry(entry, member,
1800+
protocolsWithRetroactiveAttr, protos);
1801+
}
1802+
} else if (auto *ppt = inheritedTy->getAs<ParameterizedProtocolType>()) {
1803+
protos.push_back(ppt->getProtocol());
1804+
}
1805+
}
1806+
18031807
/// Determines if this extension declares a conformance of a type declared
18041808
/// outside this module to a protocol declared outside this module (but only
18051809
/// in library evolution mode)
@@ -1836,7 +1840,7 @@ static void diagnoseRetroactiveConformances(
18361840
// At this point, we know we're extending a type declared outside this module.
18371841
// We better only be conforming it to protocols declared within this module.
18381842
llvm::SmallMapVector<ProtocolDecl *, bool, 8> protocols;
1839-
llvm::SmallSet<ProtocolDecl *, 8> protocolsWithRetroactiveAttr;
1843+
llvm::SmallPtrSet<ProtocolDecl *, 2> protocolsWithRetroactiveAttr;
18401844

18411845
for (auto *conformance : ext->getLocalConformances()) {
18421846
auto *proto = conformance->getProtocol();
@@ -1864,27 +1868,8 @@ static void diagnoseRetroactiveConformances(
18641868
}
18651869

18661870
SmallVector<ProtocolDecl *, 2> protos;
1867-
if (auto *protoTy = inheritedTy->getAs<ProtocolType>()) {
1868-
auto *proto = protoTy->getDecl();
1869-
1870-
// As a fallback, to support previous language versions, also allow
1871-
// this through if the protocol has been explicitly module-qualified.
1872-
TypeRepr *repr = unwrapAttributedRepr(entry.getTypeRepr());
1873-
if (isModuleQualified(repr, proto->getParentModule())) {
1874-
protocolsWithRetroactiveAttr.insert(proto);
1875-
continue;
1876-
}
1877-
1878-
protos.push_back(proto);
1879-
} else if (auto *compositionTy = inheritedTy->getAs<ProtocolCompositionType>()) {
1880-
for (auto memberTy : compositionTy->getMembers()) {
1881-
if (auto *protoTy = memberTy->getAs<ProtocolType>()) {
1882-
protos.push_back(protoTy->getDecl());
1883-
}
1884-
}
1885-
} else {
1886-
continue;
1887-
}
1871+
collectProtocolsFromInheritedEntry(entry, inheritedTy,
1872+
protocolsWithRetroactiveAttr, protos);
18881873

18891874
for (auto *proto : protos) {
18901875
proto->walkInheritedProtocols([&](ProtocolDecl *decl) {
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/Sema/extension_retroactive_conformances.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ public protocol SampleProtocol2 {}
99
public protocol SampleProtocol1a: SampleProtocol1 {}
1010
public protocol SampleProtocol1b: SampleProtocol1 {}
1111

12+
public protocol SampleProtocol3<A> {
13+
associatedtype A
14+
}
15+
1216
public struct Sample1 {}
1317
public struct Sample2 {}
1418
public struct Sample2a {}
@@ -27,6 +31,9 @@ public struct SampleAlreadyConforms: SampleProtocol1 {}
2731

2832
public struct GenericSample1<T> {}
2933

34+
public struct Sample9 {}
35+
public struct Sample10 {}
36+
3037
#else
3138

3239
import Library
@@ -118,4 +125,10 @@ extension Sample7: SampleProtocol1 & SampleProtocol2 {}
118125

119126
extension Sample8: @retroactive SampleProtocol1 & SampleProtocol2 {} // ok
120127

128+
extension Sample9: SampleProtocol3<Int> {}
129+
// expected-warning@-1 {{extension declares a conformance of imported type 'Sample9' to imported protocol 'SampleProtocol3'; this will not behave correctly if the owners of 'Library' introduce this conformance in the future}}
130+
// expected-note@-2 {{add '@retroactive' to silence this warning}}
131+
132+
extension Sample10: @retroactive SampleProtocol3<Int> {}
133+
121134
#endif

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)