Skip to content

Commit 99b0e77

Browse files
committed
Merge pull request #1160 from gregomni/generic-func-args
[Sema] Extend callee diagnosis to complex args including generics, e.g. (Void) -> T
2 parents ba02f12 + 6b30695 commit 99b0e77

File tree

6 files changed

+115
-28
lines changed

6 files changed

+115
-28
lines changed

include/swift/AST/TypeMatcher.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,12 @@ class TypeMatcher {
230230
/// FIXME: Split this out into cases?
231231
bool visitAnyFunctionType(CanAnyFunctionType firstFunc, Type secondType) {
232232
if (auto secondFunc = secondType->getAs<AnyFunctionType>()) {
233+
// FIXME: Compare throws()? Both existing subclasses would prefer
234+
// to mismatch on (!firstFunc->throws() && secondFunc->throws()), but
235+
// embedding that non-commutativity in this general matcher is icky.
236+
if (firstFunc->isNoEscape() != secondFunc->isNoEscape())
237+
return mismatch(firstFunc.getPointer(), secondFunc);
238+
233239
return this->visit(firstFunc.getInput(), secondFunc->getInput()) &&
234240
this->visit(firstFunc.getResult(), secondFunc->getResult());
235241
}

lib/Sema/CSDiag.cpp

Lines changed: 94 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
#include "ConstraintSystem.h"
1818
#include "llvm/Support/SaveAndRestore.h"
1919
#include "swift/AST/ASTWalker.h"
20+
#include "swift/AST/TypeWalker.h"
21+
#include "swift/AST/TypeMatcher.h"
2022

2123
using namespace swift;
2224
using namespace constraints;
@@ -836,7 +838,7 @@ namespace {
836838
/// obviously mismatching candidates and compute a "closeness" for the
837839
/// resultant set.
838840
std::pair<CandidateCloseness, CalleeCandidateInfo::FailedArgumentInfo>
839-
evaluateCloseness(Type candArgListType, ArrayRef<CallArgParam> actualArgs);
841+
evaluateCloseness(DeclContext *dc, Type candArgListType, ArrayRef<CallArgParam> actualArgs);
840842

841843
void filterList(ArrayRef<CallArgParam> actualArgs);
842844
void filterList(Type actualArgsType) {
@@ -980,11 +982,71 @@ static bool argumentMismatchIsNearMiss(Type argType, Type paramType) {
980982
return false;
981983
}
982984

985+
/// Given a parameter type that may contain generic type params and an actual
986+
/// argument type, decide whether the param and actual arg have the same shape
987+
/// and equal fixed type portions, and return by reference each archetype and
988+
/// the matching portion of the actual arg type where that archetype appears.
989+
static bool findGenericSubstitutions(DeclContext *dc, Type paramType, Type actualArgType,
990+
SmallVector<ArchetypeType *, 4> &archetypes,
991+
SmallVector<Type, 4> &substitutions) {
992+
class GenericVisitor : public TypeMatcher<GenericVisitor> {
993+
DeclContext *dc;
994+
SmallVector<ArchetypeType *, 4> &archetypes;
995+
SmallVector<Type, 4> &substitutions;
996+
997+
public:
998+
GenericVisitor(DeclContext *dc,
999+
SmallVector<ArchetypeType *, 4> &archetypes,
1000+
SmallVector<Type, 4> &substitutions)
1001+
: dc(dc), archetypes(archetypes), substitutions(substitutions) {}
1002+
1003+
bool mismatch(TypeBase *paramType, TypeBase *argType) {
1004+
return paramType->isEqual(argType);
1005+
}
1006+
1007+
bool mismatch(SubstitutableType *paramType, TypeBase *argType) {
1008+
Type type = paramType;
1009+
if (dc && type->isTypeParameter())
1010+
type = ArchetypeBuilder::mapTypeIntoContext(dc, paramType);
1011+
1012+
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);
1019+
return true;
1020+
}
1021+
return false;
1022+
}
1023+
};
1024+
1025+
// If paramType contains any substitutions already, find them and add them
1026+
// to our list before matching the two types to find more.
1027+
paramType.findIf([&](Type type) -> bool {
1028+
if (auto substitution = dyn_cast<SubstitutedType>(type.getPointer())) {
1029+
Type original = substitution->getOriginal();
1030+
if (dc && original->isTypeParameter())
1031+
original = ArchetypeBuilder::mapTypeIntoContext(dc, original);
1032+
1033+
if (auto archetype = original->getAs<ArchetypeType>()) {
1034+
archetypes.push_back(archetype);
1035+
substitutions.push_back(substitution->getReplacementType());
1036+
}
1037+
}
1038+
return false;
1039+
});
1040+
1041+
GenericVisitor visitor(dc, archetypes, substitutions);
1042+
return visitor.match(paramType, actualArgType);
1043+
}
1044+
9831045
/// Determine how close an argument list is to an already decomposed argument
9841046
/// list. If the closeness is a miss by a single argument, then this returns
9851047
/// information about that failure.
9861048
std::pair<CandidateCloseness, CalleeCandidateInfo::FailedArgumentInfo>
987-
CalleeCandidateInfo::evaluateCloseness(Type candArgListType,
1049+
CalleeCandidateInfo::evaluateCloseness(DeclContext *dc, Type candArgListType,
9881050
ArrayRef<CallArgParam> actualArgs) {
9891051
auto candArgs = decomposeArgParamType(candArgListType);
9901052

@@ -1064,16 +1126,28 @@ CalleeCandidateInfo::evaluateCloseness(Type candArgListType,
10641126
// more parameters.
10651127
// We can still do something more sophisticated with this.
10661128
// FIXME: Use TC.isConvertibleTo?
1067-
if (rArgType->isEqual(paramType))
1129+
1130+
SmallVector<ArchetypeType *, 4> archetypes;
1131+
SmallVector<Type, 4> substitutions;
1132+
bool matched;
1133+
if (paramType->is<UnresolvedType>())
1134+
matched = false;
1135+
else
1136+
matched = findGenericSubstitutions(dc, paramType, rArgType,
1137+
archetypes, substitutions);
1138+
1139+
if (matched && archetypes.size() == 0)
10681140
continue;
1069-
if (auto genericParam = paramType->getAs<GenericTypeParamType>())
1070-
paramType = genericParam->getDecl()->getArchetype();
1071-
if (paramType->is<ArchetypeType>() && !rArgType->hasTypeVariable()) {
1141+
if (matched && archetypes.size() == 1 && !rArgType->hasTypeVariable()) {
1142+
auto archetype = archetypes[0];
1143+
auto substitution = substitutions[0];
1144+
10721145
if (singleArchetype) {
1073-
if (!paramType->isEqual(singleArchetype))
1146+
if (!archetype->isEqual(singleArchetype))
10741147
// Multiple archetypes, too complicated.
10751148
return { CC_ArgumentMismatch, {}};
1076-
if (rArgType->isEqual(matchingArgType)) {
1149+
1150+
if (substitution->isEqual(matchingArgType)) {
10771151
if (nonSubstitutableArgs == 0)
10781152
continue;
10791153
++nonSubstitutableArgs;
@@ -1087,20 +1161,18 @@ CalleeCandidateInfo::evaluateCloseness(Type candArgListType,
10871161
// If we have only one nonSubstitutableArg so far, then this different
10881162
// type might be the one that we should be substituting for instead.
10891163
// Note that failureInfo is already set correctly for that case.
1090-
auto archetype = paramType->castTo<ArchetypeType>();
1091-
if (CS->TC.isSubstitutableFor(rArgType, archetype, CS->DC)) {
1092-
mismatchesAreNearMisses = argumentMismatchIsNearMiss(matchingArgType, rArgType);
1093-
matchingArgType = rArgType;
1164+
if (CS->TC.isSubstitutableFor(substitution, archetype, CS->DC)) {
1165+
mismatchesAreNearMisses = argumentMismatchIsNearMiss(matchingArgType, substitution);
1166+
matchingArgType = substitution;
10941167
continue;
10951168
}
10961169
}
10971170
}
10981171
} else {
1099-
matchingArgType = rArgType;
1100-
singleArchetype = paramType;
1172+
matchingArgType = substitution;
1173+
singleArchetype = archetype;
11011174

1102-
auto archetype = paramType->getAs<ArchetypeType>();
1103-
if (CS->TC.isSubstitutableFor(rArgType, archetype, CS->DC)) {
1175+
if (CS->TC.isSubstitutableFor(substitution, archetype, CS->DC)) {
11041176
continue;
11051177
}
11061178
++nonSubstitutableArgs;
@@ -1325,7 +1397,9 @@ void CalleeCandidateInfo::filterList(ArrayRef<CallArgParam> actualArgs) {
13251397
// If this isn't a function or isn't valid at this uncurry level, treat it
13261398
// as a general mismatch.
13271399
if (!inputType) return { CC_GeneralMismatch, {}};
1328-
return evaluateCloseness(inputType, actualArgs);
1400+
Decl *decl = candidate.getDecl();
1401+
return evaluateCloseness(decl ? decl->getInnermostDeclContext() : nullptr,
1402+
inputType, actualArgs);
13291403
});
13301404
}
13311405

@@ -3540,8 +3614,10 @@ bool FailureDiagnosis::visitSubscriptExpr(SubscriptExpr *SE) {
35403614
}
35413615

35423616
// Explode out multi-index subscripts to find the best match.
3617+
Decl *decl = cand.getDecl();
35433618
auto indexResult =
3544-
calleeInfo.evaluateCloseness(cand.getArgumentType(), decomposedIndexType);
3619+
calleeInfo.evaluateCloseness(decl ? decl->getInnermostDeclContext() : nullptr,
3620+
cand.getArgumentType(), decomposedIndexType);
35453621
if (selfConstraint > indexResult.first)
35463622
return {selfConstraint, {}};
35473623
return indexResult;

test/Constraints/diagnostics.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ i.wobble() // expected-error{{value of type 'Int' has no member 'wobble'}}
7171
// Generic member does not conform.
7272
extension Int {
7373
func wibble<T: P2>(x: T, _ y: T) -> T { return x }
74+
func wubble<T>(x: Int -> T) -> T { return x(self) }
7475
}
7576
i.wibble(3, 4) // expected-error {{argument type 'Int' does not conform to expected type 'P2'}}
7677

@@ -82,6 +83,15 @@ let a = A()
8283
for j in i.wibble(a, a) { // expected-error {{type 'A' does not conform to protocol 'SequenceType'}}
8384
}
8485

86+
// Generic as part of function/tuple types
87+
func f6<T:P2>(g: Void -> T) -> (c: Int, i: T) {
88+
return (c: 0, i: g())
89+
}
90+
func f7() -> (c: Int, v: A) {
91+
let g: Void -> A = { return A() }
92+
return f6(g) // expected-error {{cannot convert return expression of type '(c: Int, i: A)' to return type '(c: Int, v: A)'}}
93+
}
94+
8595
// <rdar://problem/19658691> QoI: Incorrect diagnostic for calling nonexistent members on literals
8696
1.doesntExist(0) // expected-error {{value of type 'Int' has no member 'doesntExist'}}
8797
[1, 2, 3].doesntExist(0) // expected-error {{value of type '[Int]' has no member 'doesntExist'}}

test/Generics/function_defs.swift

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,7 @@ func doCompare<T : EqualComparable, U : EqualComparable>(t1: T, t2: T, u: U) ->
1717
return true
1818
}
1919

20-
// FIXME: This is not ambiguous, the actual problem is that 'u' has the wrong
21-
// type - "U" is not the same as "T".
22-
return t1.isEqual(u) // expected-error {{type of expression is ambiguous without more context}}
20+
return t1.isEqual(u) // expected-error {{cannot invoke 'isEqual' with an argument list of type '(U)'}} expected-note {{expected an argument list of type '(T)'}}
2321
}
2422

2523
protocol MethodLessComparable {
@@ -171,9 +169,7 @@ func staticEqCheck<T : StaticEq, U : StaticEq>(t: T, u: U) {
171169
if T.isEqual(t, y: t) { return }
172170
if U.isEqual(u, y: u) { return }
173171

174-
// FIXME: This is not ambiguous, the actual problem is that 'u' has the wrong
175-
// type - "U" is not the same as "T".
176-
T.isEqual(t, y: u) // expected-error{{type of expression is ambiguous without more context}}
172+
T.isEqual(t, y: u) // expected-error{{cannot invoke 'isEqual' with an argument list of type '(T, y: U)'}} expected-note {{expected an argument list of type '(T, y: T)'}}
177173
}
178174

179175
//===----------------------------------------------------------------------===//

test/Misc/misc_diagnostics.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ func testIS1() -> Int { return 0 }
9191
let _: String = testIS1() // expected-error {{cannot convert value of type 'Int' to specified type 'String'}}
9292

9393
func insertA<T>(inout array : [T], elt : T) {
94-
array.append(T); // expected-error {{ambiguous reference to member 'append'}}
94+
array.append(T); // expected-error {{cannot invoke 'append' with an argument list of type '((T).Type)'}} expected-note {{expected an argument list of type '(T)'}}
9595
}
9696

9797
// <rdar://problem/17875634> can't append to array of tuples
@@ -130,6 +130,6 @@ func test17875634() {
130130

131131
// <rdar://problem/20770032> Pattern matching ranges against tuples crashes the compiler
132132
func test20770032() {
133-
if case let 1...10 = (1, 1) { // expected-warning{{'let' pattern has no effect; sub-pattern didn't bind any variables}} {{11-15=}} expected-error{{cannot convert value of type '(Int, Int)' to expected argument type 'Range<Int>'}}
133+
if case let 1...10 = (1, 1) { // expected-warning{{'let' pattern has no effect; sub-pattern didn't bind any variables}} {{11-15=}} expected-error{{expression pattern of type 'Range<Int>' cannot match values of type '(Int, Int)'}}
134134
}
135135
}

validation-test/stdlib/StdlibUnittestStaticAssertions.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ func test_expectType() {
2121

2222
func test_expectEqualType() {
2323
expectEqualType(S1.self, S1.self)
24-
expectEqualType(S1.self, S2.self) // expected-error {{cannot invoke 'expectEqualType' with an argument list of type '(S1.Type, S2.Type)'}}
25-
// expected-note @-1 {{expected an argument list of type '(T.Type, T.Type)'}}
24+
expectEqualType(S1.self, S2.self) // expected-error {{cannot convert value of type 'S2.Type' to expected argument type 'S1'}}
2625
}
2726

0 commit comments

Comments
 (0)