Skip to content

Commit 63f99a4

Browse files
committed
Move CallExpr diagnostics over to the same overload candidate diagnosis
facilities used by operators etc. This required a bunch of changes to make the diagnostics changes strictly an improvement: - Teach the new path about calls to TypeExprs. - Teach evaluateCloseness some simple things about varargs. - Make the generic diagnosis logic produce a better error when there is exactly one match. Overall, the resultant diagnostics are a step forward: we now produce candidate set notes more uniformly, and the messages about some existing ones are more specific. This is just another stepping stone towards progress though. Swift SVN r30057
1 parent 710df51 commit 63f99a4

25 files changed

+131
-138
lines changed

Diff for: include/swift/AST/DiagnosticsSema.def

+3-3
Original file line numberDiff line numberDiff line change
@@ -141,9 +141,6 @@ ERROR(cannot_apply_initializer_to_args,sema,none,
141141
"cannot invoke initializer for type '%0' with an argument list of type "
142142
"'%1'", (StringRef,StringRef))
143143

144-
NOTE(expected_certain_args,sema_nb,none,
145-
"expected an argument list of type '%0'", (StringRef))
146-
147144
ERROR(cannot_find_appropriate_overload_with_list,sema,none,
148145
"cannot find an overload for '%0' that accepts an argument list of"
149146
" type '%1'", (StringRef, StringRef))
@@ -170,6 +167,9 @@ NOTE(mult_stmt_closures_require_explicit_result,sema,none,
170167
ERROR(invalid_trailing_closure_target,sema,none,
171168
"unexpected trailing closure", ())
172169

170+
NOTE(suggest_expected_match,sema,none,
171+
"expected an argument list of type '%0'", (StringRef))
172+
173173
NOTE(suggest_partial_overloads,sema,none,
174174
"overloads for '%0' exist with these partially matching parameter "
175175
"lists: %1", (StringRef, StringRef))

Diff for: lib/Sema/CSDiag.cpp

+65-111
Original file line numberDiff line numberDiff line change
@@ -1716,12 +1716,10 @@ class FailureDiagnosis :public ASTVisitor<FailureDiagnosis, /*exprresult*/bool>{
17161716

17171717
/// Given a set of parameter lists from an overload group, and a list of
17181718
/// arguments, emit a diagnostic indicating any partially matching overloads.
1719-
void suggestPotentialOverloads(StringRef functionName, SourceLoc loc,
1720-
const SmallVectorImpl<Type> &paramLists,
1721-
Type argType);
17221719
void suggestPotentialOverloads(StringRef functionName, SourceLoc loc,
17231720
ArrayRef<ValueDecl*> candidates,
1724-
CandidateCloseness closeness);
1721+
CandidateCloseness closeness,
1722+
bool isCallExpr = false);
17251723

17261724
bool visitExpr(Expr *E);
17271725

@@ -2101,6 +2099,7 @@ Expr *FailureDiagnosis::typeCheckIndependentSubExpression(Expr *subExpr) {
21012099
if (!isa<ClosureExpr>(subExpr) &&
21022100
(isa<ApplyExpr>(subExpr) || isa<ArrayExpr>(subExpr) ||
21032101
isa<ForceValueExpr>(subExpr) ||
2102+
//isa<UnresolvedDotExpr>(subExpr) ||
21042103
typeIsNotSpecialized(subExpr->getType()))) {
21052104

21062105
// Store off the sub-expression, in case a new one is provided via the
@@ -2179,68 +2178,27 @@ bool FailureDiagnosis::diagnoseContextualConversionError(Type exprResultType) {
21792178
return true;
21802179
}
21812180

2182-
// FIXME: Remove this!
2183-
void FailureDiagnosis::suggestPotentialOverloads(StringRef functionName,
2184-
SourceLoc loc,
2185-
const SmallVectorImpl<Type> &paramLists,
2186-
Type argType) {
2187-
// FIXME: This is arbitrary.
2188-
if (argType->isVoid())
2189-
return;
2190-
2191-
auto argTypeElts = decomposeArgumentType(argType);
2192-
2193-
std::string suggestionText = "";
2194-
std::map<std::string, bool> dupes;
2195-
2196-
for (auto paramList : paramLists) {
2197-
auto paramTypeElts = decomposeArgumentType(paramList);
2198-
2199-
if (paramTypeElts.size() != argTypeElts.size())
2200-
continue;
2201-
/// FIXME: Right now, a "matching" overload is one with a parameter whose type
2202-
/// is identical to one of the argument types. We can obviously do something
2203-
/// more sophisticated with this.
2204-
for (size_t i = 0, e = paramTypeElts.size(); i != e; i++) {
2205-
auto pt = paramTypeElts[i];
2206-
auto at = argTypeElts[i];
2207-
if (pt->isEqual(at->getRValueType())) {
2208-
auto typeListString = getTypeListString(paramList);
2209-
if (!dupes[typeListString]) {
2210-
dupes[typeListString] = true;
2211-
if (!suggestionText.empty())
2212-
suggestionText += ", ";
2213-
suggestionText += typeListString;
2214-
}
2215-
break;
2216-
}
2217-
}
2218-
}
2219-
2220-
if (suggestionText.empty())
2221-
return;
2222-
2223-
CS->TC.diagnose(loc, diag::suggest_partial_overloads,
2224-
functionName, suggestionText);
2225-
}
2226-
22272181
void FailureDiagnosis::
22282182
suggestPotentialOverloads(StringRef functionName, SourceLoc loc,
22292183
ArrayRef<ValueDecl*> candidates,
2230-
CandidateCloseness closeness) {
2184+
CandidateCloseness closeness,
2185+
bool isCallExpr) {
22312186

22322187
// If the candidate list is has no near matches to the actual types, don't
22332188
// print out a candidate list, it will just be noise.
2234-
if (closeness == CC_ArgumentCountMismatch ||
2235-
closeness == CC_GeneralMismatch)
2236-
return;
2189+
if ((closeness == CC_ArgumentCountMismatch ||
2190+
closeness == CC_GeneralMismatch)) {
2191+
2192+
// FIXME: This is arbitrary.
2193+
if (candidates.size() != 1 || !isCallExpr)
2194+
return;
2195+
}
22372196

22382197
std::string suggestionText = "";
22392198
std::set<std::string> dupes;
22402199

22412200
// FIXME2: For (T,T) & (Self, Self), emit this as two candidates, one using
22422201
// the LHS and one using the RHS type for T's.
2243-
22442202
for (auto decl : candidates) {
22452203
Type paramListType;
22462204

@@ -2269,8 +2227,13 @@ suggestPotentialOverloads(StringRef functionName, SourceLoc loc,
22692227
if (suggestionText.empty())
22702228
return;
22712229

2272-
CS->TC.diagnose(loc, diag::suggest_partial_overloads,
2273-
functionName, suggestionText);
2230+
if (dupes.size() == 1) {
2231+
CS->TC.diagnose(loc, diag::suggest_expected_match,
2232+
suggestionText);
2233+
} else {
2234+
CS->TC.diagnose(loc, diag::suggest_partial_overloads,
2235+
functionName, suggestionText);
2236+
}
22742237
}
22752238

22762239

@@ -2309,9 +2272,23 @@ static FailureDiagnosis::CandidateCloseness
23092272
evaluateCloseness(Type candArgListType, ArrayRef<Type> actualArgs) {
23102273
auto candArgs = decomposeArgumentType(candArgListType);
23112274

2312-
// FIXME: This isn't handling varargs.
2313-
if (actualArgs.size() != candArgs.size())
2275+
// FIXME: This isn't handling varargs, and isn't handling default values
2276+
// either.
2277+
if (actualArgs.size() != candArgs.size()) {
2278+
// If the candidate is varargs, and if there are more arguments specified
2279+
// than required, consider this a general mismatch.
2280+
// TODO: we could catalog the remaining entries if they *do* match up.
2281+
if (auto *TT = candArgListType->getAs<TupleType>()) {
2282+
if (!TT->getElements().empty()) {
2283+
if (TT->getElements().back().isVararg() &&
2284+
actualArgs.size() >= TT->getElements().size()-1)
2285+
return FailureDiagnosis::CC_GeneralMismatch;
2286+
}
2287+
}
2288+
2289+
23142290
return FailureDiagnosis::CC_ArgumentCountMismatch;
2291+
}
23152292

23162293
// Count the number of mismatched arguments.
23172294
unsigned mismatchingArgs = 0;
@@ -2336,6 +2313,8 @@ evaluateCloseness(Type candArgListType, ArrayRef<Type> actualArgs) {
23362313
if (mismatchingArgs == 1)
23372314
return FailureDiagnosis::CC_OneArgumentMismatch;
23382315

2316+
// TODO: Keyword argument mismatches.
2317+
23392318
return FailureDiagnosis::CC_GeneralMismatch;
23402319
}
23412320

@@ -2351,6 +2330,16 @@ FailureDiagnosis::collectCalleeCandidateInfo(Expr *fn, Type actualArgsType,
23512330
} else if (auto overloadedDRE = dyn_cast<OverloadedDeclRefExpr>(fn)) {
23522331
result.append(overloadedDRE->getDecls().begin(),
23532332
overloadedDRE->getDecls().end());
2333+
} else if (auto TE = dyn_cast<TypeExpr>(fn)) {
2334+
// It's always a metatype type, so use the instance type name.
2335+
auto instanceType = TE->getType()->getAs<MetatypeType>()->getInstanceType();
2336+
2337+
// TODO: figure out right value for isKnownPrivate
2338+
if (!instanceType->getAs<TupleType>()) {
2339+
auto ctors = CS->TC.lookupConstructors(CS->DC, instanceType);
2340+
for (auto ctor : ctors)
2341+
result.push_back(ctor);
2342+
}
23542343
} else if (overloadConstraint) {
23552344
result.push_back(overloadConstraint->getOverloadChoice().getDecl());
23562345
} else {
@@ -2654,65 +2643,42 @@ bool FailureDiagnosis::visitSubscriptExpr(SubscriptExpr *SE) {
26542643
bool FailureDiagnosis::visitCallExpr(CallExpr *callExpr) {
26552644
CleanupIllFormedExpressionRAII cleanup(CS->getASTContext(), expr);
26562645

2646+
// FIXME: Should be calculating types of sub-exprs in a consistent way.
2647+
//auto fnExpr = typeCheckIndependentSubExpression(callExpr->getFn());
2648+
//if (!fnExpr) return true;
26572649
auto fnExpr = callExpr->getFn();
26582650
auto argExpr = callExpr->getArg();
26592651

26602652
// An error was posted elsewhere.
26612653
if (isErrorTypeKind(fnExpr->getType()))
26622654
return true;
26632655

2664-
26652656
std::string overloadName = "";
26662657

26672658
bool isClosureInvocation = false;
26682659
bool isInvalidTrailingClosureTarget = false;
26692660
bool isInitializer = false;
26702661
bool isOverloadedFn = false;
2671-
2672-
llvm::SmallVector<Type, 16> paramLists;
26732662

2663+
CandidateCloseness candidateCloseness;
2664+
auto Candidates = collectCalleeCandidateInfo(fnExpr, argExpr->getType(),
2665+
candidateCloseness);
2666+
2667+
26742668
// Obtain the function's name, and collect any parameter lists for diffing
26752669
// purposes.
26762670
if (auto DRE = dyn_cast<DeclRefExpr>(fnExpr)) {
26772671
overloadName = DRE->getDecl()->getNameStr();
2678-
2679-
if (auto fnType = DRE->getDecl()->getType()->getAs<AnyFunctionType>()) {
2680-
paramLists.push_back(fnType->getInput());
2681-
}
2682-
26832672
} else if (auto ODRE = dyn_cast<OverloadedDeclRefExpr>(fnExpr)) {
26842673
isOverloadedFn = true;
26852674
overloadName = ODRE->getDecls()[0]->getNameStr().str();
2686-
2687-
// Collect the parameters for later use.
2688-
for (auto D : ODRE->getDecls()) {
2689-
if (auto fnType = D->getType()->getAs<AnyFunctionType>()) {
2690-
paramLists.push_back(fnType->getInput());
2691-
}
2692-
}
2693-
2694-
} else if (auto TE = dyn_cast<TypeExpr>(fnExpr)) {
2675+
} else if (isa<TypeExpr>(fnExpr)) {
26952676
isInitializer = true;
2696-
2677+
isOverloadedFn = Candidates.size() > 1;
26972678
// It's always a metatype type, so use the instance type name.
2698-
auto instanceType = TE->getType()->getAs<MetatypeType>()->getInstanceType();
2679+
auto instanceType =
2680+
fnExpr->getType()->castTo<MetatypeType>()->getInstanceType();
26992681
overloadName = instanceType->getString();
2700-
2701-
// TODO: figure out right value for isKnownPrivate
2702-
if (!instanceType->getAs<TupleType>()) {
2703-
auto ctors = CS->TC.lookupConstructors(CS->DC, instanceType);
2704-
for (auto ctor : ctors) {
2705-
if (auto fnType = ctor->getType()->getAs<AnyFunctionType>()) {
2706-
// skip type argument
2707-
if (auto fnType2 = fnType->getResult()->getAs<AnyFunctionType>()) {
2708-
paramLists.push_back(fnType2->getInput());
2709-
}
2710-
}
2711-
}
2712-
}
2713-
if (paramLists.size() > 1)
2714-
isOverloadedFn = true;
2715-
27162682
} else if (auto UDE = dyn_cast<UnresolvedDotExpr>(fnExpr)) {
27172683
overloadName = UDE->getName().str().str();
27182684
} else if (isa<UnresolvedConstructorExpr>(fnExpr)) {
@@ -2724,7 +2690,7 @@ bool FailureDiagnosis::visitCallExpr(CallExpr *callExpr) {
27242690
isInvalidTrailingClosureTarget = !isa<ClosureExpr>(unwrappedExpr);
27252691
}
27262692
// TODO: Handle dot_syntax_call_expr "fn" as a non-closure value.
2727-
2693+
// TODO: need a concept of an uncurry level.
27282694

27292695
Type argType;
27302696

@@ -2746,7 +2712,8 @@ bool FailureDiagnosis::visitCallExpr(CallExpr *callExpr) {
27462712
SmallVector<TupleTypeElt, 4> resultElts;
27472713

27482714
for (unsigned i = 0, e = TE->getNumElements(); i != e; i++) {
2749-
auto elType = getTypeOfTypeCheckedIndependentSubExpression(TE->getElement(i));
2715+
auto elType =
2716+
getTypeOfTypeCheckedIndependentSubExpression(TE->getElement(i));
27502717
if (!elType)
27512718
return true; // already diagnosed.
27522719

@@ -2820,22 +2787,9 @@ bool FailureDiagnosis::visitCallExpr(CallExpr *callExpr) {
28202787
}
28212788

28222789
// Did the user intend on invoking a different overload?
2823-
if (!paramLists.empty()) {
2824-
if (!isOverloadedFn) {
2825-
std::string paramString = "";
2826-
2827-
if (!paramLists[0]->isVoid()) {
2828-
paramString = getTypeListString(paramLists[0]);
2829-
2830-
CS->TC.diagnose(argExpr->getLoc(),
2831-
diag::expected_certain_args,
2832-
paramString);
2833-
}
2834-
} else {
2835-
suggestPotentialOverloads(overloadName, fnExpr->getLoc(),
2836-
paramLists, argType);
2837-
}
2838-
}
2790+
suggestPotentialOverloads(overloadName, fnExpr->getLoc(),
2791+
Candidates, candidateCloseness,
2792+
/*isCallExpr*/true);
28392793

28402794
expr->setType(ErrorType::get(CS->getASTContext()));
28412795
return true;

Diff for: test/1_stdlib/UnicodeScalarDiagnostics.swift

+5-5
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,12 @@ func test_UnicodeScalarDoesNotImplementArithmetic(us: UnicodeScalar, i: Int) {
1616

1717
let c1 = us + i // expected-error {{binary operator '+' cannot be applied to operands of type 'UnicodeScalar' and 'Int'}} expected-note{{overloads for '+' exist with these partially matching parameter lists:}}
1818
let c2 = us - i // expected-error {{binary operator '-' cannot be applied to operands of type 'UnicodeScalar' and 'Int'}} expected-note{{overloads for '-' exist with these partially matching parameter lists:}}
19-
let c3 = us * i // expected-error {{binary operator '*' cannot be applied to operands of type 'UnicodeScalar' and 'Int'}} expected-note{{overloads for '*' exist with these partially matching parameter lists:}}
20-
let c4 = us / i // expected-error {{binary operator '/' cannot be applied to operands of type 'UnicodeScalar' and 'Int'}} expected-note{{overloads for '/' exist with these partially matching parameter lists:}}
19+
let c3 = us * i // expected-error {{binary operator '*' cannot be applied to operands of type 'UnicodeScalar' and 'Int'}} expected-note{{expected an argument list of type '(Int, Int)'}}
20+
let c4 = us / i // expected-error {{binary operator '/' cannot be applied to operands of type 'UnicodeScalar' and 'Int'}} expected-note{{expected an argument list of type '(Int, Int)'}}
2121

2222
let d1 = i + us // expected-error {{binary operator '+' cannot be applied to operands of type 'Int' and 'UnicodeScalar'}} expected-note{{overloads for '+' exist with these partially matching parameter lists:}}
23-
let d2 = i - us // expected-error {{binary operator '-' cannot be applied to operands of type 'Int' and 'UnicodeScalar'}} expected-note{{overloads for '-' exist with these partially matching parameter lists:}}
24-
let d3 = i * us // expected-error {{binary operator '*' cannot be applied to operands of type 'Int' and 'UnicodeScalar'}} expected-note{{overloads for '*' exist with these partially matching parameter lists:}}
25-
let d4 = i / us // expected-error {{binary operator '/' cannot be applied to operands of type 'Int' and 'UnicodeScalar'}} expected-note{{overloads for '/' exist with these partially matching parameter lists:}}
23+
let d2 = i - us // expected-error {{binary operator '-' cannot be applied to operands of type 'Int' and 'UnicodeScalar'}} expected-note{{expected an argument list of type '(Int, Int)'}}
24+
let d3 = i * us // expected-error {{binary operator '*' cannot be applied to operands of type 'Int' and 'UnicodeScalar'}} expected-note{{expected an argument list of type '(Int, Int)'}}
25+
let d4 = i / us // expected-error {{binary operator '/' cannot be applied to operands of type 'Int' and 'UnicodeScalar'}} expected-note{{expected an argument list of type '(Int, Int)'}}
2626
}
2727

Diff for: test/ClangModules/cfuncs_parse.swift

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import cfuncs
77
func test_cfunc1(i: Int) {
88
cfunc1() // okay
99
cfunc1(i) // expected-error{{cannot invoke 'cfunc1' with an argument list of type '(Int)'}}
10+
// expected-note @-1 {{expected an argument list of type '()'}}
1011
}
1112

1213
func test_cfunc2(i: Int) {

Diff for: test/ClangModules/objc_diags.swift

+1
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,5 @@ import ObjectiveC
66

77
func instanceMethod(b: B) {
88
b.method(1, 2.5) // expected-error {{cannot invoke 'method' with an argument list of type '(Int, Double)'}}
9+
// expected-note @-1 {{expected an argument list of type '(Int32, onExtB: Double)'}}
910
}

Diff for: test/ClangModules/objc_factory_method.swift

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ func testInstanceTypeFactoryMethodInherited() {
2020
_ = NSObjectFactorySub(double: 314159)
2121
_ = NSObjectFactorySub(float: 314159) // expected-error{{incorrect argument label in call (have 'float:', expected 'integer:')}}
2222
let a = NSObjectFactorySub(buildingWidgets: ()) // expected-error{{cannot find an initializer for type 'NSObjectFactorySub' that accepts an argument list of type '(buildingWidgets: ())'}}
23+
// expected-note @-1 {{overloads for 'NSObjectFactorySub' exist with these partially matching parameter lists: (integer: Int), (double: Double), ()}}
2324
_ = a
2425
}
2526

Diff for: test/ClangModules/objc_implicit_properties.swift

+12
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,23 @@ func implicitProperties(obj: ImplicitProperties, other: AnyObject) {
1818

1919
func badImplicitProperties(obj: BadImplicitProperties) {
2020
acceptsInt(obj.nonVoidReturn) // expected-error {{cannot find an overload for 'acceptsInt' that accepts an argument list of type '(() -> Int32)'}}
21+
// expected-note @-1 {{overloads for 'acceptsInt' exist with these partially matching parameter lists: (Int), (UInt)}}
22+
2123
acceptsInt(obj.nonMatchingType) // expected-error {{cannot find an overload for 'acceptsInt' that accepts an argument list of type '(() -> Int32)'}}
24+
// expected-note @-1 {{overloads for 'acceptsInt' exist with these partially matching parameter lists: (Int), (UInt)}}
25+
2226
acceptsInt(obj.wrongGetterArgs) // expected-error {{cannot find an overload for 'acceptsInt' that accepts an argument list of type '((Int32) -> Int32)'}}
27+
// expected-note @-1 {{overloads for 'acceptsInt' exist with these partially matching parameter lists: (Int), (UInt)}}
28+
2329
acceptsInt(obj.wrongSetterArgs) // expected-error {{cannot find an overload for 'acceptsInt' that accepts an argument list of type '(() -> Int32)'}}
30+
// expected-note @-1 {{overloads for 'acceptsInt' exist with these partially matching parameter lists: (Int), (UInt)}}
31+
2432
acceptsInt(obj.wrongSetterArgs2) // expected-error {{cannot find an overload for 'acceptsInt' that accepts an argument list of type '(() -> Int32)'}}
33+
// expected-note @-1 {{overloads for 'acceptsInt' exist with these partially matching parameter lists: (Int), (UInt)}}
34+
2535
acceptsInt(obj.getterOnly) // expected-error {{cannot find an overload for 'acceptsInt' that accepts an argument list of type '(() -> Int32)'}}
36+
// expected-note @-1 {{overloads for 'acceptsInt' exist with these partially matching parameter lists: (Int), (UInt)}}
37+
2638
acceptsInt(obj.setterOnly) // expected-error {{'BadImplicitProperties' does not have a member named 'setterOnly'}}
2739

2840
// But we should still import all of the methods as methods.

Diff for: test/ClangModules/objc_implicit_with.swift

+1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ func testInstanceTypeFactoryMethodInherited() {
3636
// FIXME: Awful diagnostic
3737
_ = NSObjectFactorySub(float: 314159) // expected-error{{incorrect argument label in call (have 'float:', expected 'integer:')}}
3838
let a = NSObjectFactorySub(buildingWidgets: ()) // expected-error{{cannot find an initializer for type 'NSObjectFactorySub' that accepts an argument list of type '(buildingWidgets: ())'}}
39+
// expected-note @-1 {{overloads for 'NSObjectFactorySub' exist with these partially matching parameter lists: (integer: Int), (double: Double), ()}}
3940
_ = a
4041
}
4142

Diff for: test/ClangModules/objc_parse.swift

+1
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ func classMethods(b: B, other: NSObject) {
7171
B.description()
7272
B.instanceTakesObjectClassTakesFloat(2.0)
7373
B.instanceTakesObjectClassTakesFloat(other) // expected-error{{cannot invoke 'instanceTakesObjectClassTakesFloat' with an argument list of type '(NSObject)'}}
74+
// expected-note @-1 {{expected an argument list of type '(Float)'}}
7475

7576
// Call an instance method of NSObject.
7677
var c: AnyClass = B.myClass() // no-warning

0 commit comments

Comments
 (0)