Skip to content

Commit e75dae4

Browse files
committed
Don't diagnose failures to call symmetrically-typed binary operators
as a failure to convert the individual operand, since the operator is likely conceptually generic in some way and the choice of any specific overload is probably arbitrary. Since we now fall back to a better-informed diagnostics point, take advantage of this to generate a specialized diagnostic when trying to compare values of function type with ===. Fixes rdar://25666129. This reverts commit 073f427, i.e. it reapplies 35ba809 with a test fix to expect an extra note in one place.
1 parent 073f427 commit e75dae4

File tree

13 files changed

+85
-30
lines changed

13 files changed

+85
-30
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,11 @@ ERROR(cannot_match_expr_pattern_with_value,none,
138138
"expression pattern of type %0 cannot match values of type %1",
139139
(Type, Type))
140140

141+
ERROR(cannot_reference_compare_types,none,
142+
"cannot check reference equality of functions; operands here have types "
143+
"%1 and %2",
144+
(StringRef, Type, Type))
145+
141146
ERROR(cannot_apply_binop_to_args,none,
142147
"binary operator '%0' cannot be applied to operands of type "
143148
"%1 and %2",

lib/Sema/CSDiag.cpp

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1806,8 +1806,8 @@ class FailureDiagnosis :public ASTVisitor<FailureDiagnosis, /*exprresult*/bool>{
18061806

18071807
/// Diagnose common failures due to applications of an argument list to an
18081808
/// ApplyExpr or SubscriptExpr.
1809-
bool diagnoseParameterErrors(CalleeCandidateInfo &CCI, Expr *fnExpr,
1810-
Expr *argExpr);
1809+
bool diagnoseParameterErrors(CalleeCandidateInfo &CCI,
1810+
Expr *fnExpr, Expr *argExpr);
18111811

18121812
/// Attempt to diagnose a specific failure from the info we've collected from
18131813
/// the failed constraint system.
@@ -2904,6 +2904,12 @@ typeCheckChildIndependently(Expr *subExpr, Type convertType,
29042904
TCEOptions |= TypeCheckExprFlags::PreferForceUnwrapToOptional;
29052905
}
29062906

2907+
// Ensure that the expression we're about to type-check doesn't have
2908+
// anything that the type-checker doesn't expect to see. This can happen
2909+
// because of repeated type-checking; the removal below, while independently
2910+
// important, isn't itself sufficient because of AST mutation.
2911+
eraseOpenedExistentials(subExpr);
2912+
29072913
bool hadError = CS->TC.typeCheckExpression(subExpr, CS->DC,
29082914
TypeLoc::withoutLoc(convertType),
29092915
convertTypePurpose, TCEOptions,
@@ -3334,6 +3340,28 @@ void ConstraintSystem::diagnoseAssignmentFailure(Expr *dest, Type destTy,
33343340
diag::assignment_lhs_not_lvalue);
33353341
}
33363342

3343+
static bool isSymmetricBinaryOperator(const CalleeCandidateInfo &CCI) {
3344+
// If we don't have at least one known candidate, don't trigger.
3345+
if (CCI.candidates.empty()) return false;
3346+
3347+
for (auto &candidate : CCI.candidates) {
3348+
// Each candidate must be a non-assignment operator function.
3349+
auto decl = dyn_cast_or_null<FuncDecl>(candidate.getDecl());
3350+
if (!decl) return false;
3351+
auto op = dyn_cast_or_null<InfixOperatorDecl>(decl->getOperatorDecl());
3352+
if (!op || op->isAssignment()) return false;
3353+
3354+
// It must have exactly two parameters.
3355+
auto params = decl->getParameterLists().back();
3356+
if (params->size() != 2) return false;
3357+
3358+
// Require the types to be the same.
3359+
if (!params->get(0)->getType()->isEqual(params->get(1)->getType()))
3360+
return false;
3361+
}
3362+
3363+
return true;
3364+
}
33373365

33383366
/// Special magic to handle inout exprs and tuples in argument lists.
33393367
Expr *FailureDiagnosis::
@@ -3364,6 +3392,11 @@ typeCheckArgumentChildIndependently(Expr *argExpr, Type argType,
33643392
if (decl->isInstanceMember() && candidates[0].level == 0 &&
33653393
!isa<SubscriptDecl>(decl))
33663394
argType = Type();
3395+
3396+
// Similarly, we get better results when we don't push argument types down
3397+
// to symmetric operators.
3398+
if (argType && isSymmetricBinaryOperator(candidates))
3399+
argType = Type();
33673400

33683401

33693402
// FIXME: This should all just be a matter of getting the type of the
@@ -3735,12 +3768,17 @@ bool FailureDiagnosis::diagnoseParameterErrors(CalleeCandidateInfo &CCI,
37353768
// (often because there is only one candidate in the set), then diagnose this
37363769
// as a specific problem of passing something of the wrong type into a
37373770
// parameter.
3771+
//
3772+
// We don't generally want to use this path to diagnose calls to
3773+
// symmetrically-typed binary operators because it's likely that both
3774+
// operands contributed to the type.
37383775
if ((CCI.closeness == CC_OneArgumentMismatch ||
37393776
CCI.closeness == CC_OneArgumentNearMismatch ||
37403777
CCI.closeness == CC_OneGenericArgumentMismatch ||
37413778
CCI.closeness == CC_OneGenericArgumentNearMismatch ||
37423779
CCI.closeness == CC_GenericNonsubstitutableMismatch) &&
3743-
CCI.failedArgument.isValid()) {
3780+
CCI.failedArgument.isValid() &&
3781+
!isSymmetricBinaryOperator(CCI)) {
37443782
// Map the argument number into an argument expression.
37453783
TCCOptions options = TCC_ForceRecheck;
37463784
if (CCI.failedArgument.parameterType->is<InOutType>())
@@ -4161,6 +4199,18 @@ bool FailureDiagnosis::visitApplyExpr(ApplyExpr *callExpr) {
41614199
.highlight(rhsExpr->getSourceRange());
41624200
return true;
41634201
}
4202+
4203+
// Diagnose attempts to compare reference equality of certain types.
4204+
if (overloadName == "===" || overloadName == "!==") {
4205+
// Functions.
4206+
if (lhsType->is<AnyFunctionType>() || rhsType->is<AnyFunctionType>()) {
4207+
diagnose(callExpr->getLoc(), diag::cannot_reference_compare_types,
4208+
overloadName, lhsType, rhsType)
4209+
.highlight(lhsExpr->getSourceRange())
4210+
.highlight(rhsExpr->getSourceRange());
4211+
return true;
4212+
}
4213+
}
41644214

41654215
// If we found an exact match, this must be a problem with a conversion from
41664216
// the result of the call to the expected type. Diagnose this as a

test/1_stdlib/UnicodeScalarDiagnostics.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,12 @@ func test_UnicodeScalarDoesNotImplementArithmetic(_ us: UnicodeScalar, i: Int) {
2323

2424
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:}}
2525
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:}}
26-
let c3 = us * i // expected-error {{cannot convert value of type 'UnicodeScalar' to expected argument type 'Int'}}
27-
let c4 = us / i // expected-error {{cannot convert value of type 'UnicodeScalar' to expected argument type 'Int'}}
26+
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)'}}
27+
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)'}}
2828

2929
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:}}
30-
let d2 = i - us // expected-error {{cannot convert value of type 'UnicodeScalar' to expected argument type 'Int'}}
31-
let d3 = i * us // expected-error {{cannot convert value of type 'UnicodeScalar' to expected argument type 'Int'}}
32-
let d4 = i / us // expected-error {{cannot convert value of type 'UnicodeScalar' to expected argument type 'Int'}}
30+
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)'}}
31+
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)'}}
32+
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)'}}
3333
}
3434

test/Constraints/bridging.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ let d2: Double = 3.14159
205205
inferDouble2 = d2
206206

207207
// rdar://problem/18269449
208-
var i1: Int = 1.5 * 3.5 // expected-error{{cannot convert value of type 'Double' to expected argument type 'Int'}}
208+
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)'}}
209209

210210
// rdar://problem/18330319
211211
func rdar18330319(_ s: String, d: [String : AnyObject]) {
@@ -266,12 +266,12 @@ func rdar19831919() {
266266
// <rdar://problem/19831698> Incorrect 'as' fixits offered for invalid literal expressions
267267
func rdar19831698() {
268268
var v70 = true + 1 // expected-error{{binary operator '+' cannot be applied to operands of type 'Bool' and 'Int'}} expected-note {{overloads for '+' exist with these partially matching parameter lists: (Int, Int), (UnsafeMutablePointer<Pointee>, Int), (UnsafePointer<Pointee>, Int)}}
269-
var v71 = true + 1.0 // expected-error{{cannot convert value of type 'Bool' to expected argument type 'Double'}}
269+
var v71 = true + 1.0 // expected-error {{binary operator '+' cannot be applied to operands of type 'Bool' and 'Double'}} expected-note {{expected an argument list of type '(Double, Double)'}}
270270
var v72 = true + true // expected-error{{binary operator '+' cannot be applied to two 'Bool' operands}}
271271
// expected-note @-1 {{overloads for '+' exist with these partially matching parameter lists:}}
272272
var v73 = true + [] // expected-error{{binary operator '+' cannot be applied to operands of type 'Bool' and '[_]'}}
273273
// expected-note @-1 {{overloads for '+' exist with these partially matching parameter lists:}}
274-
var v75 = true + "str" // expected-error{{cannot convert value of type 'Bool' to expected argument type 'String'}}
274+
var v75 = true + "str" // expected-error {{binary operator '+' cannot be applied to operands of type 'Bool' and 'String'}} expected-note {{expected an argument list of type '(String, String)'}}
275275
}
276276

277277
// <rdar://problem/19836341> Incorrect fixit for NSString? to String? conversions

test/Constraints/diagnostics.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -583,7 +583,7 @@ func r21684487() {
583583
var closures = Array<MyClosure>()
584584
let testClosure = {(list: [Int]) -> Bool in return true}
585585

586-
let closureIndex = closures.index{$0 === testClosure} // expected-error {{cannot convert value of type '([Int]) -> Bool' to expected argument type 'AnyObject?'}}
586+
let closureIndex = closures.index{$0 === testClosure} // expected-error {{cannot check reference equality of functions; operands here have types '_' and '([Int]) -> Bool'}}
587587
}
588588

589589
// <rdar://problem/18397777> QoI: special case comparisons with nil
@@ -749,8 +749,8 @@ if AssocTest.one(1) == AssocTest.one(1) {} // expected-error{{binary operator '=
749749
func r24251022() {
750750
var a = 1
751751
var b: UInt32 = 2
752-
a += a +
753-
b // expected-error {{cannot convert value of type 'UInt32' to expected argument type 'Int'}}
752+
a += a + // expected-error {{binary operator '+' cannot be applied to operands of type 'Int' and 'UInt32'}} expected-note {{expected an argument list of type '(Int, Int)'}}
753+
b
754754
}
755755

756756
func overloadSetResultType(_ a : Int, b : Int) -> Int {

test/Constraints/patterns.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ default: break
140140

141141
// <rdar://problem/21995744> QoI: Binary operator '~=' cannot be applied to operands of type 'String' and 'String?'
142142
switch ("foo" as String?) {
143-
case "what": break // expected-error{{value of optional type 'String?' not unwrapped; did you mean to use '!' or '?'?}}
143+
case "what": break // expected-error{{expression pattern of type 'String' cannot match values of type 'String?'}}
144144
default: break
145145
}
146146

test/Misc/misc_diagnostics.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ if let realRoomName = roomName as! NSString { // expected-error {{initializer fo
1313

1414
var pi = 3.14159265358979
1515
var d: CGFloat = 2.0
16-
var dpi:CGFloat = d*pi // expected-error{{cannot convert value of type 'Double' to expected argument type 'CGFloat'}}
16+
var dpi:CGFloat = d*pi // expected-error{{binary operator '*' cannot be applied to operands of type 'CGFloat' and 'Double'}} // expected-note{{expected an argument list of type '(CGFloat, CGFloat)'}}
1717

1818
let ff: CGFloat = floorf(20.0) // expected-error{{cannot convert value of type 'Float' to specified type 'CGFloat'}}
1919

@@ -30,7 +30,7 @@ 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{{cannot convert value of type 'Float' to expected argument type 'Double'}}
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)'}}
3434

3535
func f() -> Bool {
3636
return 1 + 1 // expected-error{{no '+' candidates produce the expected contextual result type 'Bool'}}
@@ -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 {{cannot convert value of type '[String]' to expected argument type 'Int'}}
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)'}}
6464
}
6565

6666
func bad_return1() {
@@ -81,7 +81,7 @@ class MyBadReturnClass {
8181
}
8282

8383
func ==(lhs:MyBadReturnClass, rhs:MyBadReturnClass) {
84-
return MyBadReturnClass.intProperty == MyBadReturnClass.intProperty // expected-error {{cannot convert value of type 'Int' to expected argument type '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)'}}
8585
}
8686

8787

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// RUN: %target-swift-frontend -parse -enable-source-import -primary-file %s %S/Inputs/multi-file-with-main/main.swift -module-name=MultiFile -sdk "" -verify
22

33
func testOperator() {
4-
let x: Int = 1 +++ "abc" // expected-error {{cannot convert value of type 'String' to expected argument type 'Int'}}
4+
let x: Int = 1 +++ "abc" // expected-error {{binary operator '+++' cannot be applied to operands of type 'Int' and 'String'}} expected-note {{expected an argument list of type '(Int, Int)'}}
55

66
_ = x
77
}

test/Parse/recovery.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -616,7 +616,7 @@ protocol B23086402 {
616616
617617
// <rdar://problem/23550816> QoI: Poor diagnostic in argument list of "print" (varargs related)
618618
func test23086402(a: A23086402) {
619-
print(a.b.c + "") // expected-error {{cannot convert value of type '[String]' to expected argument type 'String'}}
619+
print(a.b.c + "") // expected-error {{binary operator '+' cannot be applied to operands of type '[String]' and 'String'}} expected-note {{expected an argument list of type '(String, String)'}}
620620
}
621621

622622
// <rdar://problem/23719432> [practicalswift] Compiler crashes on &(Int:_)

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 {{cannot convert value of type 'Double' to expected argument type '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)'}}
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(fn: {$0 + $1}) // Closure with two named anonymous arguments
3535
func6(fn: {($0) + $1}) // Closure with sequence expr inferred type
36-
func6(fn: {($0) + $0}) // expected-error{{cannot convert value of type '(Int, Int)' to expected argument type 'Int'}}
36+
func6(fn: {($0) + $0}) // // expected-error {{binary operator '+' cannot be applied to two '(Int, Int)' operands}} expected-note {{expected an argument list of type '(Int, Int)'}}
3737

3838

3939
var testfunc : ((), Int) -> Int

0 commit comments

Comments
 (0)