Skip to content

Commit 41a0f6d

Browse files
authored
Merge pull request #7364 from DougGregor/structural-same-type
2 parents 517ae38 + b412961 commit 41a0f6d

File tree

8 files changed

+331
-164
lines changed

8 files changed

+331
-164
lines changed

include/swift/AST/ArchetypeBuilder.h

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -169,10 +169,15 @@ class ArchetypeBuilder {
169169
Type Superclass,
170170
RequirementSource Source);
171171

172-
/// \brief Add a new same-type requirement specifying that the given potential
173-
/// archetypes should map to the equivalent archetype.
174-
bool addSameTypeRequirement(Type T1, Type T2, RequirementSource Source,
175-
PotentialArchetype *basePA = nullptr);
172+
/// \brief Add a new same-type requirement specifying that the given two
173+
/// types should be the same.
174+
///
175+
/// \param diagnoseMismatch Callback invoked when the types in the same-type
176+
/// requirement mismatch.
177+
bool addSameTypeRequirement(
178+
Type T1, Type T2, RequirementSource Source,
179+
PotentialArchetype *basePA,
180+
llvm::function_ref<void(Type, Type)> diagnoseMismatch);
176181

177182
/// Add the requirements placed on the given abstract type parameter
178183
/// to the given potential archetype.
@@ -251,9 +256,12 @@ class ArchetypeBuilder {
251256
///
252257
/// Adding an already-checked requirement cannot fail. This is used to
253258
/// re-inject requirements from outer contexts.
254-
void addRequirement(const Requirement &req, RequirementSource source);
259+
///
260+
/// \returns true if this requirement makes the set of requirements
261+
/// inconsistent, in which case a diagnostic will have been issued.
262+
bool addRequirement(const Requirement &req, RequirementSource source);
255263

256-
void addRequirement(const Requirement &req, RequirementSource source,
264+
bool addRequirement(const Requirement &req, RequirementSource source,
257265
PotentialArchetype *basePA,
258266
llvm::SmallPtrSetImpl<ProtocolDecl *> &Visited);
259267

include/swift/AST/DiagnosticsSema.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1579,6 +1579,9 @@ ERROR(requires_same_type_conflict,none,
15791579
ERROR(requires_generic_param_same_type_does_not_conform,none,
15801580
"same-type constraint type %0 does not conform to required protocol %1",
15811581
(Type, Identifier))
1582+
ERROR(requires_same_concrete_type,none,
1583+
"generic signature requires types %0 and %1 to be the same", (Type, Type))
1584+
15821585
ERROR(mutiple_layout_constraints,none,
15831586
"multiple layout constraints cannot be used at the same time: %0 and %1",
15841587
(LayoutConstraint, LayoutConstraint))

include/swift/AST/TypeMatcher.h

Lines changed: 131 additions & 96 deletions
Large diffs are not rendered by default.

lib/AST/ArchetypeBuilder.cpp

Lines changed: 103 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "swift/AST/Module.h"
2525
#include "swift/AST/ParameterList.h"
2626
#include "swift/AST/ProtocolConformance.h"
27+
#include "swift/AST/TypeMatcher.h"
2728
#include "swift/AST/TypeRepr.h"
2829
#include "swift/AST/TypeWalker.h"
2930
#include "swift/Basic/Defer.h"
@@ -1315,13 +1316,17 @@ bool ArchetypeBuilder::addSameTypeRequirementBetweenArchetypes(
13151316
Type concrete2 = T2->getConcreteType();
13161317

13171318
if (concrete1 && concrete2) {
1318-
if (!concrete1->isEqual(concrete2)) {
1319-
Diags.diagnose(Source.getLoc(), diag::requires_same_type_conflict,
1320-
T1->getDependentType(/*FIXME: */{ }, true), concrete1,
1321-
concrete2);
1322-
return true;
1323-
1324-
}
1319+
bool mismatch =
1320+
addSameTypeRequirement(concrete1, concrete2, Source, nullptr,
1321+
[&](Type type1, Type type2) {
1322+
Diags.diagnose(Source.getLoc(),
1323+
diag::requires_same_type_conflict,
1324+
T1->getDependentType(/*FIXME: */{ }, true), type1,
1325+
type2);
1326+
1327+
});
1328+
1329+
if (mismatch) return true;
13251330
} else if (concrete1) {
13261331
assert(!T2->ConcreteType
13271332
&& "already formed archetype for concrete-constrained parameter");
@@ -1389,12 +1394,17 @@ bool ArchetypeBuilder::addSameTypeRequirementToConcrete(
13891394
// If we've already been bound to a type, we're either done, or we have a
13901395
// problem.
13911396
if (auto oldConcrete = T->getConcreteType()) {
1392-
if (!oldConcrete->isEqual(Concrete)) {
1393-
Diags.diagnose(Source.getLoc(), diag::requires_same_type_conflict,
1394-
T->getDependentType(/*FIXME: */{ }, true), oldConcrete,
1395-
Concrete);
1396-
return true;
1397-
}
1397+
bool mismatch =
1398+
addSameTypeRequirement(oldConcrete, Concrete, Source, nullptr,
1399+
[&](Type type1, Type type2) {
1400+
Diags.diagnose(Source.getLoc(),
1401+
diag::requires_same_type_conflict,
1402+
T->getDependentType(/*FIXME: */{ }, true), type1,
1403+
type2);
1404+
1405+
});
1406+
1407+
if (mismatch) return true;
13981408
return false;
13991409
}
14001410

@@ -1458,27 +1468,51 @@ bool ArchetypeBuilder::addSameTypeRequirementToConcrete(
14581468
return false;
14591469
}
14601470

1461-
bool ArchetypeBuilder::addSameTypeRequirement(Type Reqt1, Type Reqt2,
1462-
RequirementSource Source,
1463-
PotentialArchetype *basePA) {
1464-
// Find the potential archetypes.
1465-
PotentialArchetype *T1 = resolveArchetype(Reqt1, basePA);
1466-
PotentialArchetype *T2 = resolveArchetype(Reqt2, basePA);
1471+
bool ArchetypeBuilder::addSameTypeRequirement(
1472+
Type type1, Type type2,
1473+
RequirementSource source,
1474+
PotentialArchetype *basePA,
1475+
llvm::function_ref<void(Type, Type)> diagnoseMismatch) {
1476+
// Local class to handle matching the two sides of the same-type constraint.
1477+
class ReqTypeMatcher : public TypeMatcher<ReqTypeMatcher> {
1478+
ArchetypeBuilder &builder;
1479+
RequirementSource source;
1480+
PotentialArchetype *basePA;
1481+
llvm::function_ref<void(Type, Type)> diagnoseMismatch;
1482+
1483+
public:
1484+
ReqTypeMatcher(ArchetypeBuilder &builder,
1485+
RequirementSource source,
1486+
PotentialArchetype *basePA,
1487+
llvm::function_ref<void(Type, Type)> diagnoseMismatch)
1488+
: builder(builder), source(source), basePA(basePA),
1489+
diagnoseMismatch(diagnoseMismatch) { }
1490+
1491+
bool mismatch(TypeBase *firstType, TypeBase *secondType,
1492+
Type sugaredFirstType) {
1493+
// Find the potential archetypes.
1494+
PotentialArchetype *pa1 = builder.resolveArchetype(firstType, basePA);
1495+
PotentialArchetype *pa2 = builder.resolveArchetype(secondType, basePA);
1496+
1497+
// If both sides of the requirement are type parameters, equate them.
1498+
if (pa1 && pa2)
1499+
return !builder.addSameTypeRequirementBetweenArchetypes(pa1, pa2,
1500+
source);
1501+
1502+
// If just one side is a type parameter, map it to a concrete type.
1503+
if (pa1)
1504+
return !builder.addSameTypeRequirementToConcrete(pa1, secondType,
1505+
source);
1506+
if (pa2)
1507+
return !builder.addSameTypeRequirementToConcrete(pa2, sugaredFirstType,
1508+
source);
1509+
1510+
diagnoseMismatch(sugaredFirstType, secondType);
1511+
return false;
1512+
}
1513+
} matcher(*this, source, basePA, diagnoseMismatch);
14671514

1468-
// Require that at least one side of the requirement be a potential archetype.
1469-
if (!T1 && !T2) {
1470-
Diags.diagnose(Source.getLoc(), diag::requires_no_same_type_archetype);
1471-
return true;
1472-
}
1473-
1474-
// If both sides of the requirement are open archetypes, combine them.
1475-
if (T1 && T2)
1476-
return addSameTypeRequirementBetweenArchetypes(T1, T2, Source);
1477-
1478-
// Otherwise, we're binding an open archetype.
1479-
if (T1)
1480-
return addSameTypeRequirementToConcrete(T1, Reqt2, Source);
1481-
return addSameTypeRequirementToConcrete(T2, Reqt1, Source);
1515+
return !matcher.match(type1, type2);
14821516
}
14831517

14841518
// Local function to mark the given associated type as recursive,
@@ -1623,46 +1657,58 @@ bool ArchetypeBuilder::addRequirement(const RequirementRepr &Req) {
16231657
}
16241658

16251659
case RequirementReprKind::SameType:
1626-
return addSameTypeRequirement(Req.getFirstType(),
1627-
Req.getSecondType(),
1628-
RequirementSource(RequirementSource::Explicit,
1629-
Req.getEqualLoc()));
1660+
// Require that at least one side of the requirement contain a type
1661+
// parameter.
1662+
if (!Req.getFirstType()->hasTypeParameter() &&
1663+
!Req.getSecondType()->hasTypeParameter()) {
1664+
Diags.diagnose(Req.getEqualLoc(), diag::requires_no_same_type_archetype)
1665+
.highlight(Req.getFirstTypeLoc().getSourceRange())
1666+
.highlight(Req.getSecondTypeLoc().getSourceRange());
1667+
return true;
1668+
}
1669+
1670+
return addRequirement(Requirement(RequirementKind::SameType,
1671+
Req.getFirstType(),
1672+
Req.getSecondType()),
1673+
RequirementSource(RequirementSource::Explicit,
1674+
Req.getEqualLoc()));
16301675
}
16311676

16321677
llvm_unreachable("Unhandled requirement?");
16331678
}
16341679

1635-
void ArchetypeBuilder::addRequirement(const Requirement &req,
1680+
bool ArchetypeBuilder::addRequirement(const Requirement &req,
16361681
RequirementSource source) {
16371682
llvm::SmallPtrSet<ProtocolDecl *, 8> Visited;
1638-
addRequirement(req, source, nullptr, Visited);
1683+
return addRequirement(req, source, nullptr, Visited);
16391684
}
16401685

1641-
void ArchetypeBuilder::addRequirement(
1686+
bool ArchetypeBuilder::addRequirement(
16421687
const Requirement &req, RequirementSource source,
16431688
PotentialArchetype *basePA,
16441689
llvm::SmallPtrSetImpl<ProtocolDecl *> &Visited) {
16451690
switch (req.getKind()) {
16461691
case RequirementKind::Superclass: {
1692+
// FIXME: Diagnose this.
16471693
PotentialArchetype *pa = resolveArchetype(req.getFirstType(), basePA);
1648-
if (!pa) return;
1694+
if (!pa) return false;
16491695

16501696
assert(req.getSecondType()->getClassOrBoundGenericClass());
1651-
addSuperclassRequirement(pa, req.getSecondType(), source);
1652-
return;
1697+
return addSuperclassRequirement(pa, req.getSecondType(), source);
16531698
}
16541699

16551700
case RequirementKind::Layout: {
1701+
// FIXME: Diagnose this.
16561702
PotentialArchetype *pa = resolveArchetype(req.getFirstType(), basePA);
1657-
if (!pa) return;
1703+
if (!pa) return false;
16581704

1659-
(void)addLayoutRequirement(pa, req.getLayoutConstraint(), source);
1660-
return;
1705+
return addLayoutRequirement(pa, req.getLayoutConstraint(), source);
16611706
}
16621707

16631708
case RequirementKind::Conformance: {
1709+
// FIXME: Diagnose this.
16641710
PotentialArchetype *pa = resolveArchetype(req.getFirstType(), basePA);
1665-
if (!pa) return;
1711+
if (!pa) return false;
16661712

16671713
SmallVector<ProtocolDecl *, 4> conformsTo;
16681714
(void)req.getSecondType()->isExistentialType(conformsTo);
@@ -1673,16 +1719,21 @@ void ArchetypeBuilder::addRequirement(
16731719
markPotentialArchetypeRecursive(pa, proto, source);
16741720
continue;
16751721
}
1676-
(void)addConformanceRequirement(pa, proto, source, Visited);
1722+
if (addConformanceRequirement(pa, proto, source, Visited)) return true;
16771723
}
16781724

1679-
return;
1725+
return false;
16801726
}
16811727

16821728
case RequirementKind::SameType:
1683-
addSameTypeRequirement(req.getFirstType(), req.getSecondType(), source,
1684-
basePA);
1685-
return;
1729+
return addSameTypeRequirement(
1730+
req.getFirstType(), req.getSecondType(), source, basePA,
1731+
[&](Type type1, Type type2) {
1732+
if (source.getLoc().isValid())
1733+
Diags.diagnose(source.getLoc(),
1734+
diag::requires_same_concrete_type, type1,
1735+
type2);
1736+
});
16861737
}
16871738

16881739
llvm_unreachable("Unhandled requirement?");

lib/Sema/CSDiag.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1186,11 +1186,13 @@ static bool findGenericSubstitutions(DeclContext *dc, Type paramType,
11861186
GenericVisitor(DeclContext *dc, TypeSubstitutionMap &archetypesMap)
11871187
: dc(dc), archetypesMap(archetypesMap) {}
11881188

1189-
bool mismatch(TypeBase *paramType, TypeBase *argType) {
1189+
bool mismatch(TypeBase *paramType, TypeBase *argType,
1190+
Type sugaredFirstType) {
11901191
return paramType->isEqual(argType);
11911192
}
11921193

1193-
bool mismatch(SubstitutableType *paramType, TypeBase *argType) {
1194+
bool mismatch(SubstitutableType *paramType, TypeBase *argType,
1195+
Type sugaredFirstType) {
11941196
Type type = paramType;
11951197
if (type->is<GenericTypeParamType>()) {
11961198
assert(dc);

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3270,7 +3270,8 @@ ConformanceChecker::inferTypeWitnessesViaValueWitness(ValueDecl *req,
32703270
: Conformance(conformance), Inferred(inferred) { }
32713271

32723272
/// Structural mismatches imply that the witness cannot match.
3273-
bool mismatch(TypeBase *firstType, TypeBase *secondType) {
3273+
bool mismatch(TypeBase *firstType, TypeBase *secondType,
3274+
Type sugaredFirstType) {
32743275
// If either type hit an error, don't stop yet.
32753276
if (firstType->hasError() || secondType->hasError())
32763277
return true;
@@ -3281,7 +3282,7 @@ ConformanceChecker::inferTypeWitnessesViaValueWitness(ValueDecl *req,
32813282

32823283
/// Deduce associated types from dependent member types in the witness.
32833284
bool mismatch(DependentMemberType *firstDepMember,
3284-
TypeBase *secondType) {
3285+
TypeBase *secondType, Type sugaredFirstType) {
32853286
// If the second type is an error, don't look at it further.
32863287
if (secondType->hasError())
32873288
return true;
@@ -3298,7 +3299,7 @@ ConformanceChecker::inferTypeWitnessesViaValueWitness(ValueDecl *req,
32983299

32993300
/// FIXME: Recheck the type of Self against the second type?
33003301
bool mismatch(GenericTypeParamType *selfParamType,
3301-
TypeBase *secondType) {
3302+
TypeBase *secondType, Type sugaredFirstType) {
33023303
return true;
33033304
}
33043305
};

test/Constraints/same_types.swift

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ func test4<T: Barrable>(_ t: T) -> Y where T.Bar == Y {
7272
}
7373

7474
func fail3<T: Barrable>(_ t: T) -> X
75-
where T.Bar == X { // expected-error{{'X' does not conform to required protocol 'Fooable'}}
75+
where T.Bar == X { // expected-error {{'X' does not conform to required protocol 'Fooable'}}
7676
return t.bar // expected-error{{cannot convert return expression of type 'T.Bar' to return type 'X'}}
7777
}
7878

@@ -99,7 +99,7 @@ func fail4<T: Barrable>(_ t: T) -> (Y, Z)
9999
func fail5<T: Barrable>(_ t: T) -> (Y, Z)
100100
where
101101
T.Bar.Foo == Z,
102-
T.Bar == Y { // expected-error 2{{generic parameter 'T.Bar.Foo' cannot be equal to both 'Z' and 'Y.Foo' (aka 'X')}}
102+
T.Bar == Y { // expected-error 2{{generic parameter 'T.Bar.Foo' cannot be equal to both 'Z' and 'X'}}
103103
return (t.bar, t.bar.foo) // expected-error{{cannot convert return expression of type 'X' to return type 'Z'}}
104104
}
105105

@@ -161,7 +161,7 @@ struct S1<T : P> {
161161
S1<Q>().foo(x: 1, y: 2)
162162

163163
struct S2<T : P> where T.A == T.B {
164-
func foo<X, Y>(x: X, y: Y) where X == T.A, Y == T.B { // expected-error 2 {{same-type requirement makes generic parameters 'X' and 'Y' equivalent}}
164+
func foo<X, Y>(x: X, y: Y) where X == T.A, Y == T.B { // expected-error 2{{same-type requirement makes generic parameters 'X' and 'Y' equivalent}}
165165
print(X.self)
166166
print(Y.self)
167167
print(x)
@@ -201,5 +201,45 @@ struct S4<T : P> {
201201

202202
S4<QQ>().foo(x: SS())
203203

204+
// rdar://problem/29333056 - allow deeper matching of same-type constraints.
205+
struct X1<A, B> { }
206+
207+
protocol P1 {
208+
associatedtype Assoc
209+
}
210+
211+
func structuralSameType1<A: P1, B: P1, T, U, V, W>(_: A, _: B, _: T, _: U, _: V, _: W)
212+
where A.Assoc == X1<T, U>, B.Assoc == X1<V, W>, A.Assoc == B.Assoc { }
213+
// expected-error@-1{{same-type requirement makes generic parameters 'T' and 'V' equivalent}}
214+
// expected-error@-2{{same-type requirement makes generic parameters 'T' and 'V' equivalent}}
215+
// expected-error@-3{{same-type requirement makes generic parameters 'U' and 'W' equivalent}}
216+
// expected-error@-4{{same-type requirement makes generic parameters 'U' and 'W' equivalent}}
217+
218+
typealias Tuple2<T, U> = (T, U)
219+
220+
func structuralSameType2<A: P1, B: P1, T, U, V, W>(_: A, _: B, _: T, _: U, _: V, _: W)
221+
where A.Assoc == Tuple2<T, U>, B.Assoc == Tuple2<V, W>, A.Assoc == B.Assoc { }
222+
// expected-error@-1{{same-type requirement makes generic parameters 'T' and 'V' equivalent}}
223+
// expected-error@-2{{same-type requirement makes generic parameters 'T' and 'V' equivalent}}
224+
// expected-error@-3{{same-type requirement makes generic parameters 'U' and 'W' equivalent}}
225+
// expected-error@-4{{same-type requirement makes generic parameters 'U' and 'W' equivalent}}
226+
227+
func structuralSameType3<T, U, V, W>(_: T, _: U, _: V, _: W)
228+
where X1<T, U> == X1<V, W> { }
229+
// expected-error@-1{{same-type requirement makes generic parameters 'T' and 'V' equivalent}}
230+
// expected-error@-2{{same-type requirement makes generic parameters 'T' and 'V' equivalent}}
231+
// expected-error@-3{{same-type requirement makes generic parameters 'U' and 'W' equivalent}}
232+
// expected-error@-4{{same-type requirement makes generic parameters 'U' and 'W' equivalent}}
233+
234+
protocol P2 {
235+
associatedtype Assoc1
236+
associatedtype Assoc2
237+
}
238+
239+
func structuralSameTypeRecursive1<T: P2, U>(_: T, _: U)
240+
where T.Assoc1 == Tuple2<T.Assoc1, U> // expected-error 2{{same-type constraint 'T.Assoc1' == '(T.Assoc1, U)' is recursive}}
241+
{ }
242+
243+
204244
// FIXME: Remove -verify-ignore-unknown.
205245
// <unknown>:0: error: unexpected error produced: generic parameter τ_0_0.Bar.Foo cannot be equal to both 'Y.Foo' (aka 'X') and 'Z'

0 commit comments

Comments
 (0)