Skip to content

Commit d514e01

Browse files
committed
[Generic signature builder] diagnose typealiases with the same name and different types.
At the moment, these are only done on demand, when the typealias is actually used somewhere, because there's currently a recursive validation loop that stops doing it more eagerly from working in many cases.
1 parent f64ef03 commit d514e01

File tree

3 files changed

+123
-14
lines changed

3 files changed

+123
-14
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1581,6 +1581,9 @@ ERROR(requires_generic_param_same_type_does_not_conform,none,
15811581
(Type, Identifier))
15821582
ERROR(requires_same_concrete_type,none,
15831583
"generic signature requires types %0 and %1 to be the same", (Type, Type))
1584+
ERROR(protocol_typealias_conflict, none,
1585+
"typealias %0 requires types %1 and %2 to be the same",
1586+
(Identifier, Type, Type))
15841587

15851588
ERROR(mutiple_layout_constraints,none,
15861589
"multiple layout constraints cannot be used at the same time: %0 and %1",

lib/AST/GenericSignatureBuilder.cpp

Lines changed: 44 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -864,14 +864,44 @@ auto GenericSignatureBuilder::PotentialArchetype::getNestedType(
864864

865865
for (auto member : proto->lookupDirect(nestedName)) {
866866
PotentialArchetype *pa;
867-
867+
std::function<void(Type, Type)> diagnoseMismatch;
868+
868869
if (auto assocType = dyn_cast<AssociatedTypeDecl>(member)) {
869870
// Resolve this nested type to this associated type.
870871
pa = new PotentialArchetype(this, assocType);
872+
873+
diagnoseMismatch = [&](Type first, Type second) {
874+
llvm_unreachable(
875+
"associated type shouldn't result in new mismatches");
876+
};
871877
} else if (auto alias = dyn_cast<TypeAliasDecl>(member)) {
872878
// Resolve this nested type to this type alias.
873879
pa = new PotentialArchetype(this, alias);
874880

881+
diagnoseMismatch = [&](Type first, Type second) {
882+
if (auto NAT = dyn_cast<NameAliasType>(first.getPointer())) {
883+
if (NAT->getDecl() == member) {
884+
// If we have typealias T = Foo and Foo is completely concrete
885+
// (e.g. Array<Int?>), then the subst will leave the NameAliasType
886+
// intact. However, this means, if there's a
887+
// concrete-type-mismatch at the top level, the default error
888+
// message will be "ProtocolName.T (aka Foo)", but the "T" bit is
889+
// already in the error message so it's better to print only
890+
// "Foo".
891+
first = NAT->getSinglyDesugaredType();
892+
}
893+
}
894+
builder.Diags.diagnose(member->getLoc(),
895+
diag::protocol_typealias_conflict,
896+
member->getName(), first, second);
897+
};
898+
899+
// FIXME (recursive decl validation): if the alias doesn't have an
900+
// interface type when getNestedType is called while building a
901+
// protocol's generic signature (i.e. during validation), then it'll
902+
// fail completely, because building that alias's interface type
903+
// requires the protocol to be validated. This seems to occur when the
904+
// alias's RHS involves archetypes from the protocol.
875905
if (!alias->hasInterfaceType())
876906
builder.getLazyResolver()->resolveDeclSignature(alias);
877907
if (!alias->hasInterfaceType())
@@ -891,7 +921,7 @@ auto GenericSignatureBuilder::PotentialArchetype::getNestedType(
891921

892922
builder.addSameTypeRequirement(ResolvedType::forNewTypeAlias(pa),
893923
builder.resolve(type),
894-
sameNestedTypeSource);
924+
sameNestedTypeSource, diagnoseMismatch);
895925
} else
896926
continue;
897927

@@ -904,12 +934,14 @@ auto GenericSignatureBuilder::PotentialArchetype::getNestedType(
904934

905935
// Produce a same-type constraint between the two same-named
906936
// potential archetypes.
907-
builder.addSameTypeRequirement(pa, nested.front(), sameNestedTypeSource);
937+
builder.addSameTypeRequirement(pa, nested.front(), sameNestedTypeSource,
938+
diagnoseMismatch);
908939
} else {
909940
nested.push_back(pa);
910941

911942
if (repNested) {
912-
builder.addSameTypeRequirement(pa, repNested, sameNestedTypeSource);
943+
builder.addSameTypeRequirement(pa, repNested, sameNestedTypeSource,
944+
diagnoseMismatch);
913945
}
914946
}
915947

@@ -1478,8 +1510,14 @@ bool GenericSignatureBuilder::addConformanceRequirement(PotentialArchetype *PAT,
14781510
if (addInheritedRequirements(AssocType, AssocPA, Source, Visited))
14791511
return true;
14801512
}
1481-
1482-
continue;
1513+
} else if (auto TypeAlias = dyn_cast<TypeAliasDecl>(Member)) {
1514+
// FIXME: this should check that the typealias is makes sense (e.g. has
1515+
// the same/compatible type as typealiases in parent protocols) and
1516+
// set-up any same type requirements required. Forcing the PA to be
1517+
// created with getNestedType is currently worse than useless due to the
1518+
// 'recursive decl validation' FIXME in that function: it creates an
1519+
// unresolved PA that prints an error later.
1520+
(void)TypeAlias;
14831521
}
14841522

14851523
// FIXME: Requirement declarations.

test/Generics/protocol_type_aliases.swift

Lines changed: 76 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
// RUN: %target-typecheck-verify-swift -typecheck -debug-generic-signatures %s > %t.dump 2>&1
33
// RUN: %FileCheck %s < %t.dump
44

5+
6+
func sameType<T>(_: T.Type, _: T.Type) {}
7+
58
protocol P {
69
associatedtype A
710
typealias X = A
@@ -12,9 +15,9 @@ protocol Q {
1215

1316
// CHECK-LABEL: .requirementOnNestedTypeAlias@
1417
// CHECK-NEXT: Requirements:
15-
// CHECK-NEXT: τ_0_0 : Q [Explicit @ 19:51]
16-
// CHECK-NEXT: τ_0_0[.Q].B : P [Explicit @ 19:51 -> Protocol requirement (Q)]
17-
// CHECK-NEXT: τ_0_0[.Q].B[.P].A == Int [Explicit @ 19:62]
18+
// CHECK-NEXT: τ_0_0 : Q [Explicit @ 22:51]
19+
// CHECK-NEXT: τ_0_0[.Q].B : P [Explicit @ 22:51 -> Protocol requirement (Q)]
20+
// CHECK-NEXT: τ_0_0[.Q].B[.P].A == Int [Explicit @ 22:62]
1821
// CHECK: Canonical generic signature: <τ_0_0 where τ_0_0 : Q, τ_0_0.B.A == Int>
1922
func requirementOnNestedTypeAlias<T>(_: T) where T: Q, T.B.X == Int {}
2023

@@ -31,18 +34,83 @@ protocol Q2 {
3134

3235
// CHECK-LABEL: .requirementOnConcreteNestedTypeAlias@
3336
// CHECK-NEXT: Requirements:
34-
// CHECK-NEXT: τ_0_0 : Q2 [Explicit @ 39:59]
35-
// CHECK-NEXT: τ_0_0[.Q2].B : P2 [Explicit @ 39:59 -> Protocol requirement (Q2)]
36-
// CHECK-NEXT: τ_0_0[.Q2].C == S<T.B.A> [Explicit @ 39:69]
37+
// CHECK-NEXT: τ_0_0 : Q2 [Explicit @ 42:59]
38+
// CHECK-NEXT: τ_0_0[.Q2].B : P2 [Explicit @ 42:59 -> Protocol requirement (Q2)]
39+
// CHECK-NEXT: τ_0_0[.Q2].C == S<T.B.A> [Explicit @ 42:69]
3740
// CHECK-NEXT: τ_0_0[.Q2].B[.P2].X == S<T.B.A> [Nested type match]
3841
// CHECK: Canonical generic signature: <τ_0_0 where τ_0_0 : Q2, τ_0_0.C == S<τ_0_0.B.A>>
3942
func requirementOnConcreteNestedTypeAlias<T>(_: T) where T: Q2, T.C == T.B.X {}
4043

4144
// CHECK-LABEL: .concreteRequirementOnConcreteNestedTypeAlias@
4245
// CHECK-NEXT: Requirements:
43-
// CHECK-NEXT: τ_0_0 : Q2 [Explicit @ 48:67]
44-
// CHECK-NEXT: τ_0_0[.Q2].B : P2 [Explicit @ 48:67 -> Protocol requirement (Q2)]
46+
// CHECK-NEXT: τ_0_0 : Q2 [Explicit @ 51:67]
47+
// CHECK-NEXT: τ_0_0[.Q2].B : P2 [Explicit @ 51:67 -> Protocol requirement (Q2)]
4548
// CHECK-NEXT: τ_0_0[.Q2].C == τ_0_0[.Q2].B[.P2].A [Explicit]
4649
// CHECK-NEXT: τ_0_0[.Q2].B[.P2].X == S<T.B.A> [Nested type match]
4750
// CHECK: Canonical generic signature: <τ_0_0 where τ_0_0 : Q2, τ_0_0.C == τ_0_0.B.A>
4851
func concreteRequirementOnConcreteNestedTypeAlias<T>(_: T) where T: Q2, S<T.C> == T.B.X {}
52+
53+
54+
// Incompatable concrete typealias types are flagged as such
55+
protocol P3 {
56+
typealias T = Int // expected-error{{typealias 'T' requires types 'Int' and 'Float' to be the same}}
57+
}
58+
protocol Q3: P3 {
59+
typealias T = Float
60+
}
61+
62+
protocol P3_1 {
63+
typealias T = Float // expected-error{{typealias 'T' requires types 'Float' and 'Int' to be the same}}
64+
}
65+
protocol Q3_1: P3, P3_1 {}
66+
67+
// FIXME: these shouldn't be necessary to trigger the errors above, but are, due to
68+
// the 'recusive decl validation' FIXME in GenericSignatureBuilder.cpp.
69+
func useTypealias<T: Q3>(_: T, _: T.T) {}
70+
func useTypealias1<T: Q3_1>(_: T, _: T.T) {}
71+
72+
// Subprotocols can force associated types in their parents to be concrete, and
73+
// this should be understood for types constrained by the subprotocols.
74+
protocol Q4: P {
75+
typealias A = Int
76+
}
77+
protocol Q5: P {
78+
typealias X = Int
79+
}
80+
81+
// fully generic functions that manipulate the archetypes in a P
82+
func getP_A<T: P>(_: T.Type) -> T.A.Type { return T.A.self }
83+
func getP_X<T: P>(_: T.Type) -> T.X.Type { return T.X.self }
84+
85+
// ... which we use to check if the compiler is following through the concrete
86+
// same-type constraints implied by the subprotocols.
87+
func checkQ4_A<T: Q4>(x: T.Type) { sameType(getP_A(x), Int.self) }
88+
func checkQ4_X<T: Q4>(x: T.Type) { sameType(getP_X(x), Int.self) }
89+
90+
// FIXME: these do not work, seemingly mainly due to the 'recursive decl validation'
91+
// FIXME in GenericSignatureBuilder.cpp.
92+
/*
93+
func checkQ5_A<T: Q5>(x: T.Type) { sameType(getP_A(x), Int.self) }
94+
func checkQ5_X<T: Q5>(x: T.Type) { sameType(getP_X(x), Int.self) }
95+
*/
96+
97+
98+
// Typealiases happen to allow imposing same type requirements between parent
99+
// protocols
100+
protocol P6_1 {
101+
associatedtype A
102+
}
103+
protocol P6_2 {
104+
associatedtype B
105+
}
106+
protocol Q6: P6_1, P6_2 {
107+
typealias A = B
108+
}
109+
110+
func getP6_1_A<T: P6_1>(_: T.Type) -> T.A.Type { return T.A.self }
111+
func getP6_2_B<T: P6_2>(_: T.Type) -> T.B.Type { return T.B.self }
112+
113+
func checkQ6<T: Q6>(x: T.Type) {
114+
sameType(getP6_1_A(x), getP6_2_B(x))
115+
}
116+

0 commit comments

Comments
 (0)