Skip to content

Commit dd01b7e

Browse files
committed
[Diagnostics] SR-2208: Improve failure diagnostics for apply expressions
1 parent 4b0e0b2 commit dd01b7e

File tree

10 files changed

+138
-46
lines changed

10 files changed

+138
-46
lines changed

lib/Sema/CSDiag.cpp

Lines changed: 75 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5229,6 +5229,8 @@ bool FailureDiagnosis::visitApplyExpr(ApplyExpr *callExpr) {
52295229
callExpr->setFn(operatorRef);
52305230
};
52315231

5232+
auto fnType = fnExpr->getType()->getRValueType();
5233+
52325234
// If we have a contextual type, and if we have an ambiguously typed function
52335235
// result from our previous check, we re-type-check it using this contextual
52345236
// type to inform the result type of the callee.
@@ -5241,14 +5243,33 @@ bool FailureDiagnosis::visitApplyExpr(ApplyExpr *callExpr) {
52415243
(isUnresolvedOrTypeVarType(fnExpr->getType()) ||
52425244
(fnExpr->getType()->is<AnyFunctionType>() &&
52435245
fnExpr->getType()->hasUnresolvedType()))) {
5246+
// FIXME: Prevent typeCheckChildIndependently from transforming expressions,
5247+
// because if we try to typecheck OSR expression with contextual type,
5248+
// it'll end up converting it into DeclRefExpr based on contextual info,
5249+
// instead let's try to get a type without applying and filter callee
5250+
// candidates later on.
52445251
CalleeListener listener(CS->getContextualType());
5245-
fnExpr = typeCheckChildIndependently(callExpr->getFn(), Type(),
5246-
CTP_CalleeResult, TCC_ForceRecheck,
5247-
&listener);
5248-
if (!fnExpr) return true;
5252+
5253+
if (auto OSR = dyn_cast<OverloadSetRefExpr>(fnExpr)) {
5254+
assert(!OSR->getReferencedDecl() && "unexpected declaration reference");
5255+
5256+
ConcreteDeclRef decl = nullptr;
5257+
Optional<Type> type = CS->TC.getTypeOfExpressionWithoutApplying(
5258+
fnExpr, CS->DC, decl, FreeTypeVariableBinding::UnresolvedType,
5259+
&listener);
5260+
5261+
if (type.hasValue())
5262+
fnType = type.getValue()->getRValueType();
5263+
} else {
5264+
fnExpr = typeCheckChildIndependently(callExpr->getFn(), Type(),
5265+
CTP_CalleeResult, TCC_ForceRecheck,
5266+
&listener);
5267+
if (!fnExpr)
5268+
return true;
5269+
5270+
fnType = fnExpr->getType()->getRValueType();
5271+
}
52495272
}
5250-
5251-
auto fnType = fnExpr->getType()->getRValueType();
52525273

52535274
// If we resolved a concrete expression for the callee, and it has
52545275
// non-function/non-metatype type, then we cannot call it!
@@ -5291,7 +5312,37 @@ bool FailureDiagnosis::visitApplyExpr(ApplyExpr *callExpr) {
52915312
// Collect a full candidate list of callees based on the partially type
52925313
// checked function.
52935314
CalleeCandidateInfo calleeInfo(fnExpr, hasTrailingClosure, CS);
5294-
5315+
5316+
// Filter list of the candidates based on the known function type.
5317+
if (auto fn = fnType->getAs<AnyFunctionType>()) {
5318+
using Closeness = CalleeCandidateInfo::ClosenessResultTy;
5319+
5320+
auto isGenericType = [](Type type) -> bool {
5321+
if (type->hasError() || type->hasTypeVariable() ||
5322+
type->hasUnresolvedType())
5323+
return false;
5324+
5325+
return type->isCanonical() && type->isUnspecializedGeneric();
5326+
};
5327+
5328+
calleeInfo.filterList([&](UncurriedCandidate candidate) -> Closeness {
5329+
auto resultType = candidate.getResultType();
5330+
if (!resultType)
5331+
return {CC_GeneralMismatch, {}};
5332+
5333+
// FIXME: Handle matching of the generic types properly.
5334+
// Currently we don't filter result types containing generic parametes
5335+
// because there is no easy way to do that, and candidate set is going
5336+
// to be pruned by matching of the argument types later on anyway, so
5337+
// it's better to over report than to be too conservative.
5338+
if ((isGenericType(resultType) && isGenericType(fn->getResult())) ||
5339+
resultType->isEqual(fn->getResult()))
5340+
return {CC_ExactMatch, {}};
5341+
5342+
return {CC_GeneralMismatch, {}};
5343+
});
5344+
}
5345+
52955346
// Filter the candidate list based on the argument we may or may not have.
52965347
calleeInfo.filterContextualMemberList(callExpr->getArg());
52975348

@@ -5433,7 +5484,23 @@ bool FailureDiagnosis::visitApplyExpr(ApplyExpr *callExpr) {
54335484
// conversion failure.
54345485
if (calleeInfo.closeness == CC_ExactMatch)
54355486
return false;
5436-
5487+
5488+
// If this is not a specific structural problem we can diagnose,
5489+
// let's check if this is contextual type conversion error.
5490+
if (CS->getContextualType() &&
5491+
calleeInfo.closeness == CC_ArgumentMismatch) {
5492+
CalleeCandidateInfo candidates(fnExpr, hasTrailingClosure, CS);
5493+
5494+
// Filter original list of choices based on the deduced type of
5495+
// argument expression after force re-check.
5496+
candidates.filterContextualMemberList(argTuple);
5497+
5498+
// One of the candidates matches exactly, which means that
5499+
// this is a contextual type conversion failure, we can't diagnose here.
5500+
if (candidates.closeness == CC_ExactMatch)
5501+
return false;
5502+
}
5503+
54375504
if (!lhsType->isEqual(rhsType)) {
54385505
diagnose(callExpr->getLoc(), diag::cannot_apply_binop_to_args,
54395506
overloadName, lhsType, rhsType)

test/Constraints/bridging.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ let d2: Double = 3.14159
199199
inferDouble2 = d2
200200

201201
// rdar://problem/18269449
202-
var i1: Int = 1.5 * 3.5 // expected-error {{binary operator '*' cannot be applied to two 'Double' operands}} expected-note {{expected an argument list of type '(Int, Int)'}}
202+
var i1: Int = 1.5 * 3.5 // expected-error {{cannot convert value of type 'Double' to specified type 'Int'}}
203203

204204
// rdar://problem/18330319
205205
func rdar18330319(_ s: String, d: [String : AnyObject]) {

test/Constraints/diagnostics.swift

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -178,8 +178,7 @@ func perform<T>() {} // expected-error {{generic parameter 'T' is not used in f
178178

179179
// <rdar://problem/17080659> Error Message QOI - wrong return type in an overload
180180
func recArea(_ h: Int, w : Int) {
181-
return h * w // expected-error {{no '*' candidates produce the expected contextual result type '()'}}
182-
// expected-note @-1 {{overloads for '*' exist with these result types: UInt8, Int8, UInt16, Int16, UInt32, Int32, UInt64, Int64, UInt, Int, Float, Double}}
181+
return h * w // expected-error {{unexpected non-void return value in void function}}
183182
}
184183

185184
// <rdar://problem/17224804> QoI: Error In Ternary Condition is Wrong
@@ -720,7 +719,8 @@ secondArgumentNotLabeled(10, 20)
720719
func testImplConversion(a : Float?) -> Bool {}
721720
func testImplConversion(a : Int?) -> Bool {
722721
let someInt = 42
723-
let a : Int = testImplConversion(someInt) // expected-error {{'testImplConversion' produces 'Bool', not the expected contextual result type 'Int'}}
722+
let a : Int = testImplConversion(someInt) // expected-error {{argument labels '(_:)' do not match any available overloads}}
723+
// expected-note @-1 {{overloads for 'testImplConversion' exist with these partially matching parameter lists: (a: Float?), (a: Int?)}}
724724
}
725725

726726
// <rdar://problem/23752537> QoI: Bogus error message: Binary operator '&&' cannot be applied to two 'Bool' operands
@@ -754,7 +754,7 @@ func f23213302() {
754754

755755
// <rdar://problem/24202058> QoI: Return of call to overloaded function in void-return context
756756
func rdar24202058(a : Int) {
757-
return a <= 480 // expected-error {{'<=' produces 'Bool', not the expected contextual result type '()'}}
757+
return a <= 480 // expected-error {{unexpected non-void return value in void function}}
758758
}
759759

760760
// SR-1752: Warning about unused result with ternary operator
@@ -847,3 +847,24 @@ class C2_2505: P_2505 {
847847
}
848848

849849
let c_2505 = C_2505(arg: [C2_2505()]) // expected-error {{argument labels '(arg:)' do not match any available overloads}} expected-note {{overloads for 'C_2505' exist}}
850+
851+
// Diagnostic message for initialization with binary operations as right side
852+
let foo1255_3: String = 1 + 2 + 3 // expected-error {{cannot convert value of type 'Int' to specified type 'String'}}
853+
let foo1255_4: Dictionary<String, String> = ["hello": 1 + 2] // expected-error {{cannot convert value of type 'Int' to expected dictionary value type 'String'}}
854+
let foo1255_5: Dictionary<String, String> = [(1 + 2): "wolrd"] // expected-error {{cannot convert value of type 'Int' to expected dictionary key type 'String'}}
855+
let foo1255_6: [String] = [1 + 2 + 3] // expected-error {{cannot convert value of type 'Int' to expected element type 'String'}}
856+
857+
// SR-2208
858+
struct Foo2208 {
859+
func bar(value: UInt) {}
860+
}
861+
862+
func test2208() {
863+
let foo = Foo2208()
864+
let a: Int = 1
865+
let b: Int = 2
866+
let result = a / b
867+
foo.bar(value: a / b) // expected-error {{cannot convert value of type 'Int' to expected argument type 'UInt'}}
868+
foo.bar(value: result) // expected-error {{cannot convert value of type 'Int' to expected argument type 'UInt'}}
869+
foo.bar(value: UInt(result)) // Ok
870+
}

test/Constraints/overload.swift

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -129,9 +129,13 @@ func test20886179(_ handlers: [(Int) -> Void], buttonIndex: Int) {
129129
func overloaded_identity(_ a : Int) -> Int {}
130130
func overloaded_identity(_ b : Float) -> Float {}
131131

132-
func test_contextual_result() {
133-
return overloaded_identity() // expected-error {{no 'overloaded_identity' candidates produce the expected contextual result type '()'}}
134-
// expected-note @-1 {{overloads for 'overloaded_identity' exist with these result types: Int, Float}}
132+
func test_contextual_result_1() {
133+
return overloaded_identity() // expected-error {{cannot invoke 'overloaded_identity' with no arguments}}
134+
// expected-note @-1 {{overloads for 'overloaded_identity' exist with these partially matching parameter lists: (Int), (Float)}}
135+
}
136+
137+
func test_contextual_result_2() {
138+
return overloaded_identity(1) // expected-error {{unexpected non-void return value in void function}}
135139
}
136140

137141
// rdar://problem/24128153

test/Misc/misc_diagnostics.swift

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,10 @@ var b: Int = [1, 2, 3] // expected-error{{contextual type 'Int' cannot be used w
3030
var f1: Float = 2.0
3131
var f2: Float = 3.0
3232

33-
var dd: Double = f1 - f2 // expected-error{{binary operator '-' cannot be applied to two 'Float' operands}} // expected-note{{expected an argument list of type '(Double, Double)'}}
33+
var dd: Double = f1 - f2 // expected-error{{cannot convert value of type 'Float' to specified type 'Double'}}
3434

3535
func f() -> Bool {
36-
return 1 + 1 // expected-error{{no '+' candidates produce the expected contextual result type 'Bool'}}
37-
// expected-note @-1 {{overloads for '+' exist with these result types: UInt8, Int8, UInt16, Int16, UInt32, Int32, UInt64, Int64, UInt, Int, Float, Double}}
36+
return 1 + 1 // expected-error{{cannot convert return expression of type 'Int' to return type 'Bool'}}
3837
}
3938

4039
// Test that nested diagnostics are properly surfaced.
@@ -51,7 +50,8 @@ struct MyArray<Element> {}
5150
class A {
5251
var a: MyArray<Int>
5352
init() {
54-
a = MyArray<Int // expected-error{{'<' produces 'Bool', not the expected contextual result type 'MyArray<Int>'}}
53+
a = MyArray<Int // expected-error{{binary operator '<' cannot be applied to operands of type 'MyArray<_>.Type' and 'Int.Type'}}
54+
// expected-note @-1 {{overloads for '<' exist with these partially matching parameter lists:}}
5555
}
5656
}
5757

@@ -60,7 +60,7 @@ func retV() { return true } // expected-error {{unexpected non-void return value
6060
func retAI() -> Int {
6161
let a = [""]
6262
let b = [""]
63-
return (a + b) // expected-error{{binary operator '+' cannot be applied to two '[String]' operands}} // expected-note{{expected an argument list of type '(Int, Int)'}}
63+
return (a + b) // expected-error{{cannot convert return expression of type '[String]' to return type 'Int'}}
6464
}
6565

6666
func bad_return1() {
@@ -73,15 +73,15 @@ func bad_return2() -> (Int, Int) {
7373

7474
// <rdar://problem/14096697> QoI: Diagnostics for trying to return values from void functions
7575
func bad_return3(lhs: Int, rhs: Int) {
76-
return lhs != 0 // expected-error {{'!=' produces 'Bool', not the expected contextual result type '()'}}
76+
return lhs != 0 // expected-error {{unexpected non-void return value in void function}}
7777
}
7878

7979
class MyBadReturnClass {
8080
static var intProperty = 42
8181
}
8282

83-
func ==(lhs: MyBadReturnClass, rhs: MyBadReturnClass) {
84-
return MyBadReturnClass.intProperty == MyBadReturnClass.intProperty // expected-error{{binary operator '==' cannot be applied to two 'Int' operands}} // expected-note{{expected an argument list of type '(MyBadReturnClass, MyBadReturnClass)'}}
83+
func ==(lhs:MyBadReturnClass, rhs:MyBadReturnClass) {
84+
return MyBadReturnClass.intProperty == MyBadReturnClass.intProperty // expected-error{{unexpected non-void return value in void function}}
8585
}
8686

8787

test/Parse/recovery.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,13 @@ protocol FooProtocol {}
99
func garbage() -> () {
1010
var a : Int
1111
] this line is invalid, but we will stop at the keyword below... // expected-error{{expected expression}}
12-
return a + "a" // expected-error{{no '+' candidates produce the expected contextual result type '()'}} expected-note {{overloads for '+' exist with these result types: UInt8, Int8, UInt16, Int16, UInt32, Int32, UInt64, Int64, UInt, Int, Float, Double}}
12+
return a + "a" // expected-error{{binary operator '+' cannot be applied to operands of type 'Int' and 'String'}} expected-note {{overloads for '+' exist with these partially matching parameter lists: (Int, Int), (String, String), (Int, UnsafeMutablePointer<Pointee>), (Int, UnsafePointer<Pointee>)}}
1313
}
1414

1515
func moreGarbage() -> () {
1616
) this line is invalid, but we will stop at the declaration... // expected-error{{expected expression}}
1717
func a() -> Int { return 4 }
18-
return a() + "a" // expected-error{{no '+' candidates produce the expected contextual result type '()'}} expected-note {{overloads for '+' exist with these result types: UInt8, Int8, UInt16, Int16, UInt32, Int32, UInt64, Int64, UInt, Int, Float, Double}}
18+
return a() + "a" // expected-error{{binary operator '+' cannot be applied to operands of type 'Int' and 'String'}} expected-note {{overloads for '+' exist with these partially matching parameter lists: (Int, Int), (String, String), (Int, UnsafeMutablePointer<Pointee>), (Int, UnsafePointer<Pointee>)}}
1919
}
2020

2121

test/expr/closure/closures.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ var closure3a : () -> () -> (Int,Int) = {{ (4, 2) }} // multi-level closing.
1414
var closure3b : (Int,Int) -> (Int) -> (Int,Int) = {{ (4, 2) }} // expected-error{{contextual type for closure argument list expects 2 arguments, which cannot be implicitly ignored}} {{52-52=_,_ in }}
1515
var closure4 : (Int,Int) -> Int = { $0 + $1 }
1616
var closure5 : (Double) -> Int = {
17-
$0 + 1.0 // expected-error {{binary operator '+' cannot be applied to two 'Double' operands}} expected-note {{expected an argument list of type '(Int, Int)'}}
17+
$0 + 1.0 // expected-error {{cannot convert value of type 'Double' to closure result type 'Int'}}
1818
}
1919

2020
var closure6 = $0 // expected-error {{anonymous closure argument not contained in a closure}}
@@ -33,7 +33,7 @@ func funcdecl5(_ a: Int, _ y: Int) {
3333

3434
func6({$0 + $1}) // Closure with two named anonymous arguments
3535
func6({($0) + $1}) // Closure with sequence expr inferred type
36-
func6({($0) + $0}) // // expected-error {{binary operator '+' cannot be applied to two '(Int, Int)' operands}} expected-note {{expected an argument list of type '(Int, Int)'}}
36+
func6({($0) + $0}) // // expected-error {{binary operator '+' cannot be applied to two '(Int, Int)' operands}} expected-note {{overloads for '+' exist with these partially matching parameter lists}}
3737

3838

3939
var testfunc : ((), Int) -> Int // expected-note {{'testfunc' declared here}}

test/expr/expressions.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ func basictest() {
5151
// expected-note @-1 {{overloads for '+' exist with these partially matching parameter lists:}}
5252

5353

54-
var x9 : Int16 = x8 + 1 // expected-error {{binary operator '+' cannot be applied to operands of type 'Int8' and 'Int'}} expected-note {{expected an argument list of type '(Int16, Int16)'}}
54+
var x9 : Int16 = x8 + 1 // expected-error {{cannot convert value of type 'Int8' to specified type 'Int16'}}
5555

5656
// Various tuple types.
5757
var tuple1 : ()
@@ -757,8 +757,8 @@ func testParenExprInTheWay() {
757757

758758
if (x & 4.0) {} // expected-error {{binary operator '&' cannot be applied to operands of type 'Int' and 'Double'}} expected-note {{expected an argument list of type '(Int, Int)'}}
759759

760-
if !(x & 4.0) {} // expected-error {{no '&' candidates produce the expected contextual result type 'Bool'}}
761-
//expected-note @-1 {{overloads for '&' exist with these result types: UInt8, Int8, UInt16, Int16, UInt32, Int32, UInt64, Int64, UInt, Int, T, Self}}
760+
if !(x & 4.0) {} // expected-error {{binary operator '&' cannot be applied to operands of type 'Int' and 'Double'}}
761+
//expected-note @-1 {{expected an argument list of type '(Int, Int)'}}
762762

763763

764764
if x & x {} // expected-error {{'Int' is not convertible to 'Bool'}}

test/stdlib/RangeDiagnostics.swift

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ func typeInference_Comparable<C : Comparable>(v: C) {
2323
expectType(ClosedRange<C>.self, &range)
2424
}
2525
do {
26-
let r1: Range<C> = v...v // expected-error {{no '...' candidates produce the expected contextual result type 'Range<C>'}} expected-note {{}}
27-
let r2: ClosedRange<C> = v..<v // expected-error {{no '..<' candidates produce the expected contextual result type 'ClosedRange<C>'}} expected-note {{}}
26+
let r1: Range<C> = v...v // expected-error {{cannot convert value of type 'ClosedRange<C>' to specified type 'Range<C>'}}
27+
let r2: ClosedRange<C> = v..<v // expected-error {{cannot convert value of type 'Range<C>' to specified type 'ClosedRange<C>'}}
2828
let r3: CountableRange<C> = v..<v // expected-error {{type 'C' does not conform to protocol '_Strideable'}}
2929
let r4: CountableClosedRange<C> = v...v // expected-error {{type 'C' does not conform to protocol '_Strideable'}}
3030
let r5: CountableRange<C> = v...v // expected-error {{type 'C' does not conform to protocol '_Strideable'}}
@@ -42,8 +42,8 @@ func typeInference_Strideable<S : Strideable>(v: S) {
4242
expectType(ClosedRange<S>.self, &range)
4343
}
4444
do {
45-
let r1: Range<S> = v...v // expected-error {{no '...' candidates produce the expected contextual result type 'Range<S>'}} expected-note {{}}
46-
let r2: ClosedRange<S> = v..<v // expected-error {{no '..<' candidates produce the expected contextual result type 'ClosedRange<S>'}} expected-note {{}}
45+
let r1: Range<S> = v...v // expected-error {{cannot convert value of type 'ClosedRange<S>' to specified type 'Range<S>'}}
46+
let r2: ClosedRange<S> = v..<v // expected-error {{cannot convert value of type 'Range<S>' to specified type 'ClosedRange<S>'}}
4747
let r3: CountableRange<S> = v..<v // expected-error {{type 'S.Stride' does not conform to protocol 'SignedInteger'}}
4848
let r4: CountableClosedRange<S> = v...v // expected-error {{type 'S.Stride' does not conform to protocol 'SignedInteger'}}
4949
let r5: CountableRange<S> = v...v // expected-error {{type 'S.Stride' does not conform to protocol 'SignedInteger'}}
@@ -62,16 +62,16 @@ func typeInference_StrideableWithSignedIntegerStride<S : Strideable>(v: S)
6262
expectType(CountableClosedRange<S>.self, &range)
6363
}
6464
do {
65-
let range: Range<S> = v..<v
65+
let _: Range<S> = v..<v
6666
}
6767
do {
68-
let range: ClosedRange<S> = v...v
68+
let _: ClosedRange<S> = v...v
6969
}
7070
do {
71-
let r1: Range<S> = v...v // expected-error {{no '...' candidates produce the expected contextual result type 'Range<S>'}} expected-note {{}}
72-
let r2: ClosedRange<S> = v..<v // expected-error {{no '..<' candidates produce the expected contextual result type 'ClosedRange<S>'}} expected-note {{}}
73-
let r3: CountableRange<S> = v...v // expected-error {{no '...' candidates produce the expected contextual result type 'CountableRange<S>'}} expected-note {{}}
74-
let r4: CountableClosedRange<S> = v..<v // expected-error {{no '..<' candidates produce the expected contextual result type 'CountableClosedRange<S>'}} expected-note {{}}
71+
let _: Range<S> = v...v // expected-error {{cannot convert value of type 'CountableClosedRange<S>' to specified type 'Range<S>'}}
72+
let _: ClosedRange<S> = v..<v // expected-error {{cannot convert value of type 'CountableRange<S>' to specified type 'ClosedRange<S>'}}
73+
let _: CountableRange<S> = v...v // expected-error {{cannot convert value of type 'CountableClosedRange<S>' to specified type 'CountableRange<S>'}}
74+
let _: CountableClosedRange<S> = v..<v // expected-error {{cannot convert value of type 'CountableRange<S>' to specified type 'CountableClosedRange<S>'}}
7575
}
7676
}
7777

0 commit comments

Comments
 (0)