Skip to content

Commit f8dbb20

Browse files
committed
[Sema] Extend callee diagnosis to multiple generics
Straightforward extension of the previous work here to handle callees with multiple generic parameters. Same type requirements are already handled by the ArchetypeBuilder, and protocol conformance is handled explicitly. This is still bailing on nested archetypes. Pre-existing tests updated that now give better diagnoses.
1 parent 10370a5 commit f8dbb20

File tree

5 files changed

+113
-96
lines changed

5 files changed

+113
-96
lines changed

lib/Sema/CSDiag.cpp

Lines changed: 101 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -987,18 +987,14 @@ static bool argumentMismatchIsNearMiss(Type argType, Type paramType) {
987987
/// and equal fixed type portions, and return by reference each archetype and
988988
/// the matching portion of the actual arg type where that archetype appears.
989989
static bool findGenericSubstitutions(DeclContext *dc, Type paramType, Type actualArgType,
990-
SmallVector<ArchetypeType *, 4> &archetypes,
991-
SmallVector<Type, 4> &substitutions) {
990+
TypeSubstitutionMap &archetypesMap) {
992991
class GenericVisitor : public TypeMatcher<GenericVisitor> {
993992
DeclContext *dc;
994-
SmallVector<ArchetypeType *, 4> &archetypes;
995-
SmallVector<Type, 4> &substitutions;
993+
TypeSubstitutionMap &archetypesMap;
996994

997995
public:
998-
GenericVisitor(DeclContext *dc,
999-
SmallVector<ArchetypeType *, 4> &archetypes,
1000-
SmallVector<Type, 4> &substitutions)
1001-
: dc(dc), archetypes(archetypes), substitutions(substitutions) {}
996+
GenericVisitor(DeclContext *dc, TypeSubstitutionMap &archetypesMap)
997+
: dc(dc), archetypesMap(archetypesMap) {}
1002998

1003999
bool mismatch(TypeBase *paramType, TypeBase *argType) {
10041000
return paramType->isEqual(argType);
@@ -1010,12 +1006,10 @@ static bool findGenericSubstitutions(DeclContext *dc, Type paramType, Type actua
10101006
type = ArchetypeBuilder::mapTypeIntoContext(dc, paramType);
10111007

10121008
if (auto archetype = type->getAs<ArchetypeType>()) {
1013-
for (unsigned i = 0, c = archetypes.size(); i < c; i++) {
1014-
if (archetypes[i]->isEqual(archetype))
1015-
return substitutions[i]->isEqual(argType);
1016-
}
1017-
archetypes.push_back(archetype);
1018-
substitutions.push_back(argType);
1009+
auto existing = archetypesMap[archetype];
1010+
if (existing)
1011+
return existing->isEqual(argType);
1012+
archetypesMap[archetype] = argType;
10191013
return true;
10201014
}
10211015
return false;
@@ -1030,15 +1024,22 @@ static bool findGenericSubstitutions(DeclContext *dc, Type paramType, Type actua
10301024
if (dc && original->isTypeParameter())
10311025
original = ArchetypeBuilder::mapTypeIntoContext(dc, original);
10321026

1033-
if (auto archetype = original->getAs<ArchetypeType>()) {
1034-
archetypes.push_back(archetype);
1035-
substitutions.push_back(substitution->getReplacementType());
1036-
}
1027+
Type replacement = substitution->getReplacementType();
1028+
// If the replacement is itself an archetype, then the constraint
1029+
// system was asserting equivalencies between different levels of
1030+
// generics, rather than binding a generic to a concrete type (and we
1031+
// don't/won't have a concrete type). In which case, it is the
1032+
// replacement we are interested in, since it is the one in our current
1033+
// context. That generic type should equal itself.
1034+
if (auto ourGeneric = replacement->getAs<ArchetypeType>())
1035+
archetypesMap[ourGeneric] = replacement;
1036+
else if (auto archetype = original->getAs<ArchetypeType>())
1037+
archetypesMap[archetype] = replacement;
10371038
}
10381039
return false;
10391040
});
10401041

1041-
GenericVisitor visitor(dc, archetypes, substitutions);
1042+
GenericVisitor visitor(dc, archetypesMap);
10421043
return visitor.match(paramType, actualArgType);
10431044
}
10441045

@@ -1085,18 +1086,20 @@ CalleeCandidateInfo::evaluateCloseness(DeclContext *dc, Type candArgListType,
10851086
// their type and count the number of mismatched arguments.
10861087
unsigned mismatchingArgs = 0;
10871088

1088-
// Checking of archetypes.
1089-
// FIXME: For now just trying to verify applicability of arguments with only
1090-
// a single generic variable. Ideally we'd create a ConstraintSystem with
1091-
// type variables for all generics and solve it with the given argument types.
1092-
Type singleArchetype = nullptr;
1093-
Type matchingArgType = nullptr;
1089+
// Known mapping of archetypes in all arguments so far. An archetype may map
1090+
// to another archetype if the constraint system substituted one for another.
1091+
TypeSubstitutionMap allGenericSubstitutions;
10941092

1095-
// Number of args of generic archetype which are mismatched because
1093+
// Number of args of one generic archetype which are mismatched because
10961094
// isSubstitutableFor() has failed. If all mismatches are of this type, we'll
10971095
// return a different closeness for better diagnoses.
1096+
Type nonSubstitutableArchetype = nullptr;
10981097
unsigned nonSubstitutableArgs = 0;
10991098

1099+
// The type of failure is that multiple occurrences of the same generic are
1100+
// being passed arguments with different concrete types.
1101+
bool genericWithDifferingConcreteTypes = false;
1102+
11001103
// We classify an argument mismatch as being a "near" miss if it is a very
11011104
// likely match due to a common sort of problem (e.g. wrong flags on a
11021105
// function type, optional where none was expected, etc). This allows us to
@@ -1122,75 +1125,77 @@ CalleeCandidateInfo::evaluateCloseness(DeclContext *dc, Type candArgListType,
11221125

11231126
// FIXME: Right now, a "matching" overload is one with a parameter whose
11241127
// type is identical to the argument type, or substitutable via handling
1125-
// of functions with a single archetype in one or more parameters.
1128+
// of functions with primary archetypes in one or more parameters.
11261129
// We can still do something more sophisticated with this.
11271130
// FIXME: Use TC.isConvertibleTo?
11281131

1129-
SmallVector<ArchetypeType *, 4> archetypes;
1130-
SmallVector<Type, 4> substitutions;
1132+
TypeSubstitutionMap archetypesMap;
11311133
bool matched;
1132-
if (paramType->is<UnresolvedType>())
1134+
if (paramType->is<UnresolvedType>() || rArgType->hasTypeVariable())
11331135
matched = false;
11341136
else
1135-
matched = findGenericSubstitutions(dc, paramType, rArgType,
1136-
archetypes, substitutions);
1137+
matched = findGenericSubstitutions(dc, paramType, rArgType, archetypesMap);
11371138

1138-
if (matched && archetypes.size() == 0)
1139-
continue;
1140-
if (matched && archetypes.size() == 1 && !rArgType->hasTypeVariable()) {
1141-
auto archetype = archetypes[0];
1142-
auto substitution = substitutions[0];
1143-
1144-
if (singleArchetype) {
1145-
if (!archetype->isEqual(singleArchetype))
1146-
// Multiple archetypes, too complicated.
1147-
return { CC_ArgumentMismatch, {}};
1139+
if (matched) {
1140+
for (auto pair : archetypesMap) {
1141+
auto archetype = pair.first->castTo<ArchetypeType>();
1142+
auto substitution = pair.second;
11481143

1149-
if (substitution->isEqual(matchingArgType)) {
1150-
if (nonSubstitutableArgs == 0)
1151-
continue;
1152-
++nonSubstitutableArgs;
1153-
mismatchesAreNearMisses = false;
1144+
auto existingSubstitution = allGenericSubstitutions[archetype];
1145+
if (!existingSubstitution) {
1146+
// New substitution for this callee.
1147+
allGenericSubstitutions[archetype] = substitution;
1148+
1149+
// Not yet handling nested archetypes.
1150+
if (!archetype->isPrimary())
1151+
return { CC_ArgumentMismatch, {}};
1152+
1153+
if (!CS->TC.isSubstitutableFor(substitution, archetype, CS->DC)) {
1154+
// If we have multiple non-substitutable types, this is just a mismatched mess.
1155+
if (!nonSubstitutableArchetype.isNull())
1156+
return { CC_ArgumentMismatch, {}};
1157+
1158+
if (auto argOptType = argType->getOptionalObjectType())
1159+
mismatchesAreNearMisses &= CS->TC.isSubstitutableFor(argOptType, archetype, CS->DC);
1160+
else
1161+
mismatchesAreNearMisses = false;
1162+
1163+
nonSubstitutableArchetype = archetype;
1164+
nonSubstitutableArgs = 1;
1165+
matched = false;
1166+
}
11541167
} else {
1155-
if (nonSubstitutableArgs == 1) {
1168+
// Substitution for the same archetype as in a previous argument.
1169+
bool isNonSubstitutableArchetype = !nonSubstitutableArchetype.isNull() &&
1170+
nonSubstitutableArchetype->isEqual(archetype);
1171+
if (substitution->isEqual(existingSubstitution)) {
1172+
if (isNonSubstitutableArchetype) {
1173+
++nonSubstitutableArgs;
1174+
matched = false;
1175+
}
1176+
} else {
11561177
// If we have only one nonSubstitutableArg so far, then this different
11571178
// type might be the one that we should be substituting for instead.
11581179
// Note that failureInfo is already set correctly for that case.
1159-
if (CS->TC.isSubstitutableFor(substitution, archetype, CS->DC)) {
1160-
mismatchesAreNearMisses = argumentMismatchIsNearMiss(matchingArgType, substitution);
1161-
matchingArgType = substitution;
1162-
continue;
1180+
if (isNonSubstitutableArchetype && nonSubstitutableArgs == 1 &&
1181+
CS->TC.isSubstitutableFor(substitution, archetype, CS->DC)) {
1182+
mismatchesAreNearMisses = argumentMismatchIsNearMiss(existingSubstitution, substitution);
1183+
allGenericSubstitutions[archetype] = substitution;
1184+
} else {
1185+
genericWithDifferingConcreteTypes = true;
1186+
matched = false;
11631187
}
11641188
}
1165-
1166-
// This substitution doesn't match a previous substitution. Set up the nearMiss
1167-
// and failureInfo.paramType with the expected substitution inserted.
1168-
// (Note that this transform assumes only a single archetype.)
1169-
mismatchesAreNearMisses &= argumentMismatchIsNearMiss(substitution, matchingArgType);
1170-
paramType = paramType.transform(([&](Type type) -> Type {
1171-
if (type->is<SubstitutableType>())
1172-
return matchingArgType;
1173-
return type;
1174-
}));
11751189
}
1176-
} else {
1177-
matchingArgType = substitution;
1178-
singleArchetype = archetype;
1179-
1180-
if (CS->TC.isSubstitutableFor(substitution, archetype, CS->DC))
1181-
continue;
1182-
1183-
if (auto argOptType = argType->getOptionalObjectType())
1184-
mismatchesAreNearMisses &= CS->TC.isSubstitutableFor(argOptType, archetype, CS->DC);
1185-
else
1186-
mismatchesAreNearMisses = false;
1187-
++nonSubstitutableArgs;
11881190
}
1189-
} else {
1190-
// Keep track of whether this argument was a near miss or not.
1191-
mismatchesAreNearMisses &= argumentMismatchIsNearMiss(argType, paramType);
11921191
}
11931192

1193+
if (matched)
1194+
continue;
1195+
1196+
if (archetypesMap.empty())
1197+
mismatchesAreNearMisses &= argumentMismatchIsNearMiss(argType, paramType);
1198+
11941199
++mismatchingArgs;
11951200

11961201
failureInfo.argumentNumber = argNo;
@@ -1213,10 +1218,23 @@ CalleeCandidateInfo::evaluateCloseness(DeclContext *dc, Type candArgListType,
12131218
// close matches are prioritized against obviously wrong ones.
12141219
if (mismatchingArgs == 1) {
12151220
CandidateCloseness closeness;
1216-
if (singleArchetype.isNull()) {
1221+
if (allGenericSubstitutions.empty()) {
12171222
closeness = mismatchesAreNearMisses ? CC_OneArgumentNearMismatch
12181223
: CC_OneArgumentMismatch;
12191224
} else {
1225+
// If the failure is that different occurrences of the same generic have
1226+
// different concrete types, substitute in all the concrete types we've found
1227+
// into the failureInfo to improve diagnosis.
1228+
if (genericWithDifferingConcreteTypes) {
1229+
auto newType = failureInfo.parameterType.transform([&](Type type) -> Type {
1230+
if (auto archetype = type->getAs<ArchetypeType>())
1231+
if (auto replacement = allGenericSubstitutions[archetype])
1232+
return replacement;
1233+
return type;
1234+
});
1235+
failureInfo.parameterType = newType;
1236+
}
1237+
12201238
closeness = mismatchesAreNearMisses ? CC_OneGenericArgumentNearMismatch
12211239
: CC_OneGenericArgumentMismatch;
12221240
}
@@ -1589,16 +1607,15 @@ bool CalleeCandidateInfo::diagnoseGenericParameterErrors(Expr *badArgExpr) {
15891607
return false;
15901608

15911609
bool foundFailure = false;
1592-
SmallVector<ArchetypeType *, 4> archetypes;
1593-
SmallVector<Type, 4> substitutions;
1610+
TypeSubstitutionMap archetypesMap;
15941611

15951612
if (!findGenericSubstitutions(failedArgument.declContext, failedArgument.parameterType,
1596-
argType, archetypes, substitutions))
1613+
argType, archetypesMap))
15971614
return false;
15981615

1599-
for (unsigned i = 0, c = archetypes.size(); i < c; i++) {
1600-
auto archetype = archetypes[i];
1601-
auto substitution = substitutions[i];
1616+
for (auto pair : archetypesMap) {
1617+
auto archetype = pair.first->castTo<ArchetypeType>();
1618+
auto substitution = pair.second;
16021619

16031620
// FIXME: Add specific error for not subclass, if the archetype has a superclass?
16041621

test/Constraints/generics.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,7 @@ func min<T : Comparable>(x: T, y: T) -> T {
1414
func weirdConcat<T : ConcatToAnything, U>(t: T, u: U) {
1515
t +++ u
1616
t +++ 1
17-
u +++ t // expected-error{{binary operator '+++' cannot be applied to operands of type 'U' and 'T'}}
18-
// expected-note @-1 {{expected an argument list of type '(Self, T)'}}
17+
u +++ t // expected-error{{argument type 'U' does not conform to expected type 'ConcatToAnything'}}
1918
}
2019

2120
// Make sure that the protocol operators don't get in the way.

test/Generics/deduction.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ func takeTuples<T, U>(_: (T, U), _: (U, T)) {
6565
func useTuples(x: Int, y: Float, z: (Float, Int)) {
6666
takeTuples((x, y), (y, x))
6767

68-
takeTuples((x, y), (x, y)) // expected-error{{cannot convert value of type '(Int, Float)' to expected argument type '(_, _)'}}
68+
takeTuples((x, y), (x, y)) // expected-error{{cannot convert value of type 'Int' to expected argument type 'Float'}}
6969

7070
// FIXME: Use 'z', which requires us to fix our tuple-conversion
7171
// representation.
@@ -75,7 +75,7 @@ func acceptFunction<T, U>(f: (T) -> U, _ t: T, _ u: U) {}
7575

7676
func passFunction(f: (Int) -> Float, x: Int, y: Float) {
7777
acceptFunction(f, x, y)
78-
acceptFunction(f, y, y) // expected-error{{cannot convert value of type '(Int) -> Float' to expected argument type '(_) -> _'}}
78+
acceptFunction(f, y, y) // expected-error{{cannot convert value of type 'Float' to expected argument type 'Int'}}
7979
}
8080

8181
func returnTuple<T, U>(_: T) -> (T, U) { } // expected-note {{in call to function 'returnTuple'}}

test/Generics/function_defs.swift

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,8 @@ protocol Overload {
7575
associatedtype B
7676
func getA() -> A
7777
func getB() -> B
78-
func f1(_: A) -> A // expected-note{{found this candidate}}
79-
func f1(_: B) -> B // expected-note{{found this candidate}}
78+
func f1(_: A) -> A
79+
func f1(_: B) -> B
8080
func f2(_: Int) -> A // expected-note{{found this candidate}}
8181
func f2(_: Int) -> B // expected-note{{found this candidate}}
8282
func f3(_: Int) -> Int // expected-note {{found this candidate}}
@@ -106,7 +106,8 @@ func testOverload<Ovl : Overload, OtherOvl : Overload>(ovl: Ovl, ovl2: Ovl,
106106
a = ovl2.f2(17)
107107
a = ovl2.f1(a)
108108

109-
other.f1(a) // expected-error{{ambiguous reference to member 'f1'}}
109+
other.f1(a) // expected-error{{cannot invoke 'f1' with an argument list of type '(Ovl.A)'}}
110+
// expected-note @-1 {{overloads for 'f1' exist with these partially matching parameter lists: (Self.A), (Self.B)}}
110111

111112
// Overloading based on context
112113
var f3i : (Int) -> Int = ovl.f3
@@ -132,15 +133,15 @@ protocol Subscriptable {
132133
func getIndex() -> Index
133134
func getValue() -> Value
134135

135-
subscript (index : Index) -> Value { get set } // expected-note{{found this candidate}}
136+
subscript (index : Index) -> Value { get set }
136137
}
137138

138139
protocol IntSubscriptable {
139140
associatedtype ElementType
140141

141142
func getElement() -> ElementType
142143

143-
subscript (index : Int) -> ElementType { get } // expected-note{{found this candidate}}
144+
subscript (index : Int) -> ElementType { get }
144145
}
145146

146147
func subscripting<T : protocol<Subscriptable, IntSubscriptable>>(t: T) {
@@ -153,7 +154,8 @@ func subscripting<T : protocol<Subscriptable, IntSubscriptable>>(t: T) {
153154
element = t[17]
154155
t[42] = element // expected-error{{cannot assign through subscript: subscript is get-only}}
155156

156-
t[value] = 17 // expected-error{{ambiguous reference to member 'subscript'}}
157+
// Suggests the Int form because we prefer concrete matches to generic matches in diagnosis.
158+
t[value] = 17 // expected-error{{cannot convert value of type 'T.Value' to expected argument type 'Int'}}
157159
}
158160

159161
//===----------------------------------------------------------------------===//

test/Generics/generic_types.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -200,8 +200,7 @@ func useNested(ii: Int, hni: HasNested<Int>,
200200

201201
// Generic constructor of a generic struct
202202
HNI(1, 2.71828) // expected-warning{{unused}}
203-
// FIXME: Should report this error: {{cannot convert the expression's type 'HNI' to type 'Int'}}
204-
HNI(1.5, 2.71828) // expected-error{{cannot invoke initializer for type 'HNI' with an argument list of type '(Double, Double)'}} expected-note{{expected an argument list of type '(T, U)'}}
203+
HNI(1.5, 2.71828) // expected-error{{'Double' is not convertible to 'Int'}}
205204

206205
// Generic function in a nested generic struct
207206
var ids = xis.g(1, u: "Hello", v: 3.14159)

0 commit comments

Comments
 (0)