Skip to content

Commit e0c028d

Browse files
committed
Merge pull request #1319 from gregomni/generic-func-args
[Sema] Extend callee diagnosis to multiple generics
2 parents b39cebd + f8dbb20 commit e0c028d

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)