Skip to content

Commit 9358671

Browse files
authored
Merge pull request #11042 from xedin/rdar-32711708
[QoI] Improve diagnostics for assignment expression
2 parents eb005c2 + e8744af commit 9358671

File tree

8 files changed

+64
-34
lines changed

8 files changed

+64
-34
lines changed

lib/Sema/CSDiag.cpp

Lines changed: 48 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2134,7 +2134,8 @@ class FailureDiagnosis :public ASTVisitor<FailureDiagnosis, /*exprresult*/bool>{
21342134

21352135
/// Attempt to produce a diagnostic for a mismatch between an expression's
21362136
/// type and its assumed contextual type.
2137-
bool diagnoseContextualConversionError(Expr *expr, Type contextualType);
2137+
bool diagnoseContextualConversionError(Expr *expr, Type contextualType,
2138+
ContextualTypePurpose CTP);
21382139

21392140
/// For an expression being type checked with a CTP_CalleeResult contextual
21402141
/// type, try to diagnose a problem.
@@ -2143,7 +2144,8 @@ class FailureDiagnosis :public ASTVisitor<FailureDiagnosis, /*exprresult*/bool>{
21432144
/// Attempt to produce a diagnostic for a mismatch between a call's
21442145
/// type and its assumed contextual type.
21452146
bool diagnoseCallContextualConversionErrors(ApplyExpr *callEpxr,
2146-
Type contextualType);
2147+
Type contextualType,
2148+
ContextualTypePurpose CTP);
21472149

21482150
private:
21492151
/// Validate potential contextual type for type-checking one of the
@@ -3899,9 +3901,9 @@ static bool addTypeCoerceFixit(InFlightDiagnostic &diag, ConstraintSystem &CS,
38993901
/// of function type, giving more specific and simpler diagnostics, attaching
39003902
/// notes on the parameter, and offering fixits to insert @escaping. Returns
39013903
/// true if it detects and issues an error, false if it does nothing.
3902-
static bool tryDiagnoseNonEscapingParameterToEscaping(Expr *expr, Type srcType,
3903-
Type dstType,
3904-
ConstraintSystem &CS) {
3904+
static bool tryDiagnoseNonEscapingParameterToEscaping(
3905+
Expr *expr, Type srcType, Type dstType, ContextualTypePurpose dstPurpose,
3906+
ConstraintSystem &CS) {
39053907
assert(expr);
39063908
// Need to be referencing a parameter of function type
39073909
auto declRef = dyn_cast<DeclRefExpr>(expr);
@@ -3921,7 +3923,7 @@ static bool tryDiagnoseNonEscapingParameterToEscaping(Expr *expr, Type srcType,
39213923

39223924
// Pick a specific diagnostic for the specific use
39233925
auto paramDecl = cast<ParamDecl>(declRef->getDecl());
3924-
switch (CS.getContextualTypePurpose()) {
3926+
switch (dstPurpose) {
39253927
case CTP_CallArgument:
39263928
CS.TC.diagnose(declRef->getLoc(), diag::passing_noescape_to_escaping,
39273929
paramDecl->getName());
@@ -3949,13 +3951,13 @@ static bool tryDiagnoseNonEscapingParameterToEscaping(Expr *expr, Type srcType,
39493951
return true;
39503952
}
39513953

3952-
bool FailureDiagnosis::diagnoseContextualConversionError(Expr *expr,
3953-
Type contextualType) {
3954+
bool FailureDiagnosis::diagnoseContextualConversionError(
3955+
Expr *expr, Type contextualType, ContextualTypePurpose CTP) {
39543956
// If the constraint system has a contextual type, then we can test to see if
39553957
// this is the problem that prevents us from solving the system.
39563958
if (!contextualType) {
39573959
// This contextual conversion constraint doesn't install an actual type.
3958-
if (CS.getContextualTypePurpose() == CTP_CalleeResult)
3960+
if (CTP == CTP_CalleeResult)
39593961
return diagnoseCalleeResultContextualConversionError();
39603962

39613963
return false;
@@ -3995,7 +3997,7 @@ bool FailureDiagnosis::diagnoseContextualConversionError(Expr *expr,
39953997
// If this is conversion failure due to a return statement with an argument
39963998
// that cannot be coerced to the result type of the function, emit a
39973999
// specific error.
3998-
switch (CS.getContextualTypePurpose()) {
4000+
switch (CTP) {
39994001
case CTP_Unused:
40004002
case CTP_CannotFail:
40014003
llvm_unreachable("These contextual type purposes cannot fail with a "
@@ -4156,8 +4158,7 @@ bool FailureDiagnosis::diagnoseContextualConversionError(Expr *expr,
41564158

41574159
// If this is a conversion from T to () in a call argument context, it is
41584160
// almost certainly an extra argument being passed in.
4159-
if (CS.getContextualTypePurpose() == CTP_CallArgument &&
4160-
contextualType->isVoid()) {
4161+
if (CTP == CTP_CallArgument && contextualType->isVoid()) {
41614162
diagnose(expr->getLoc(), diag::extra_argument_to_nullary_call)
41624163
.highlight(expr->getSourceRange());
41634164
return true;
@@ -4212,7 +4213,7 @@ bool FailureDiagnosis::diagnoseContextualConversionError(Expr *expr,
42124213

42134214
// Try for better/more specific diagnostics for non-escaping to @escaping
42144215
if (tryDiagnoseNonEscapingParameterToEscaping(expr, exprType, contextualType,
4215-
CS))
4216+
CTP, CS))
42164217
return true;
42174218

42184219
// Don't attempt fixits if we have an unsolved type variable, since
@@ -4261,7 +4262,7 @@ bool FailureDiagnosis::diagnoseContextualConversionError(Expr *expr,
42614262
}
42624263

42634264
// Attempt to add a fixit for the error.
4264-
switch (CS.getContextualTypePurpose()) {
4265+
switch (CTP) {
42654266
case CTP_CallArgument:
42664267
case CTP_ArrayElement:
42674268
case CTP_DictionaryKey:
@@ -6303,7 +6304,8 @@ bool FailureDiagnosis::diagnoseTrailingClosureErrors(ApplyExpr *callExpr) {
63036304
// related to function type, let's try to diagnose it.
63046305
if (possibleTypes.empty() && contextualType &&
63056306
!contextualType->hasUnresolvedType())
6306-
return diagnoseContextualConversionError(callExpr, contextualType);
6307+
return diagnoseContextualConversionError(callExpr, contextualType,
6308+
CS.getContextualTypePurpose());
63076309
} else {
63086310
possibleTypes.push_back(currentType);
63096311
}
@@ -6404,7 +6406,7 @@ bool FailureDiagnosis::diagnoseTrailingClosureErrors(ApplyExpr *callExpr) {
64046406
/// Check if there failure associated with expression is related
64056407
/// to given contextual type.
64066408
bool FailureDiagnosis::diagnoseCallContextualConversionErrors(
6407-
ApplyExpr *callExpr, Type contextualType) {
6409+
ApplyExpr *callExpr, Type contextualType, ContextualTypePurpose CTP) {
64086410
if (!contextualType || contextualType->hasUnresolvedType())
64096411
return false;
64106412

@@ -6436,7 +6438,7 @@ bool FailureDiagnosis::diagnoseCallContextualConversionErrors(
64366438
// If type-checking with contextual type didn't produce any results
64376439
// it means that we have a contextual mismatch.
64386440
if (withContextual.empty())
6439-
return diagnoseContextualConversionError(callExpr, contextualType);
6441+
return diagnoseContextualConversionError(callExpr, contextualType, CTP);
64406442

64416443
// If call produces a single type when type-checked with contextual
64426444
// expression, it means that the problem is elsewhere, any other
@@ -6482,7 +6484,8 @@ bool FailureDiagnosis::visitApplyExpr(ApplyExpr *callExpr) {
64826484
if (diagnoseTrailingClosureErrors(callExpr))
64836485
return true;
64846486

6485-
if (diagnoseCallContextualConversionErrors(callExpr, CS.getContextualType()))
6487+
if (diagnoseCallContextualConversionErrors(callExpr, CS.getContextualType(),
6488+
CS.getContextualTypePurpose()))
64866489
return true;
64876490

64886491
auto *fnExpr = callExpr->getFn();
@@ -7004,11 +7007,31 @@ bool FailureDiagnosis::visitAssignExpr(AssignExpr *assignExpr) {
70047007
return true;
70057008
}
70067009

7007-
// If the source type is already an error type, we've already posted an error.
7008-
auto srcExpr = typeCheckChildIndependently(assignExpr->getSrc(),
7009-
destType->getRValueType(),
7010-
CTP_AssignSource);
7011-
if (!srcExpr) return true;
7010+
auto *srcExpr = assignExpr->getSrc();
7011+
auto contextualType = destType->getRValueType();
7012+
7013+
// Let's try to type-check assignment source expression without using
7014+
// destination as a contextual type, that allows us to diagnose
7015+
// contextual problems related to source much easier.
7016+
//
7017+
// If source expression requires contextual type to be present,
7018+
// let's avoid this step because it's always going to fail.
7019+
{
7020+
auto *srcExpr = assignExpr->getSrc();
7021+
ExprTypeSaverAndEraser eraser(srcExpr);
7022+
7023+
ConcreteDeclRef ref = nullptr;
7024+
auto type = CS.TC.getTypeOfExpressionWithoutApplying(srcExpr, CS.DC, ref);
7025+
7026+
if (type && !type->isEqual(contextualType))
7027+
return diagnoseContextualConversionError(
7028+
assignExpr->getSrc(), contextualType, CTP_AssignSource);
7029+
}
7030+
7031+
srcExpr = typeCheckChildIndependently(assignExpr->getSrc(), contextualType,
7032+
CTP_AssignSource);
7033+
if (!srcExpr)
7034+
return true;
70127035

70137036
// If we are assigning to _ and have unresolved types on the RHS, then we have
70147037
// an ambiguity problem.
@@ -8860,7 +8883,8 @@ void ConstraintSystem::diagnoseFailureForExpr(Expr *expr) {
88608883
return;
88618884

88628885
// If this is a contextual conversion problem, dig out some information.
8863-
if (diagnosis.diagnoseContextualConversionError(expr, getContextualType()))
8886+
if (diagnosis.diagnoseContextualConversionError(expr, getContextualType(),
8887+
getContextualTypePurpose()))
88648888
return;
88658889

88668890
// If we can diagnose a problem based on the constraints left laying around in

test/ClangImporter/objc_parse.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ func keyedSubscripting(_ b: B, idx: A, a: A) {
175175
dict[NSString()] = a
176176
let value = dict[NSString()]
177177

178-
dict[nil] = a // expected-error {{ambiguous reference}}
178+
dict[nil] = a // expected-error {{cannot assign value of type 'A' to type 'Any?'}}
179179
let q = dict[nil] // expected-error {{ambiguous subscript}}
180180
_ = q
181181
}

test/Constraints/diagnostics.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1085,3 +1085,9 @@ fun_31849281(a: { !$0 }, c: [nil, 42], b: { "\($0)" })
10851085

10861086
func f_31849281(x: Int, y: Int, z: Int) {}
10871087
f_31849281(42, y: 10, x: 20) // expected-error {{argument 'x' must precede unnamed argument #1}} {{12-12=x: 20, }} {{21-28=}}
1088+
1089+
func sr5081() {
1090+
var a = ["1", "2", "3", "4", "5"]
1091+
var b = [String]()
1092+
b = a[2...4] // expected-error {{cannot assign value of type 'ArraySlice<String>' to type '[String]'}}
1093+
}

test/Constraints/generics.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -429,7 +429,7 @@ class GenericClass<A> {}
429429
func genericFunc<T>(t: T) {
430430
_ = [T: GenericClass] // expected-error {{generic parameter 'A' could not be inferred}}
431431
// expected-note@-1 {{explicitly specify the generic arguments to fix this issue}}
432-
// expected-error@-2 2 {{type 'T' does not conform to protocol 'Hashable'}}
432+
// expected-error@-2 3 {{type 'T' does not conform to protocol 'Hashable'}}
433433
}
434434

435435
struct SR_3525<T> {}

test/Generics/inheritance.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ func f0<T : A>(_ obji: T, _ ai: A, _ bi: B) {
2525
a = obj
2626

2727
// Invalid assignments
28-
obj = a // expected-error{{'A' is not convertible to 'T'}}
29-
obj = b // expected-error{{'B' is not convertible to 'T'}}
28+
obj = a // expected-error{{cannot assign value of type 'A' to type 'T'}}
29+
obj = b // expected-error{{cannot assign value of type 'B' to type 'T'}}
3030

3131
// Downcast that is actually a coercion
3232
a = (obj as? A)! // expected-warning{{conditional cast from 'T' to 'A' always succeeds}}

test/Parse/pointer_conversion.swift.gyb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ func mutablePointerArguments(_ p: UnsafeMutablePointer<Int>,
6060

6161
// We don't allow these conversions outside of function arguments.
6262
var x: UnsafeMutablePointer<Int> = &i // expected-error{{cannot pass immutable value as inout argument: 'i' is immutable}}
63-
x = &ii // expected-error{{cannot assign value of type '[Int]' to type 'Int'}}
63+
x = &ii // expected-error{{cannot assign value of type 'inout [Int]' to type 'UnsafeMutablePointer<Int>'}}
6464
_ = x
6565
}
6666

test/TypeCoercion/overload_noncall.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ func test_inout() {
5252
x = accept_XY(&xy);
5353

5454
x = xy
55-
x = &xy; // expected-error{{'&' used with non-inout argument of type 'X'}}
55+
x = &xy; // expected-error{{cannot assign value of type 'inout X' to type 'X'}}
5656
accept_Z(&xy); // expected-error{{cannot convert value of type 'X' to expected argument type 'Z'}}
5757
}
5858

test/stdlib/UnsafePointerDiagnostics.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,17 +56,17 @@ func unsafePointerConversionAvailability(
5656
_ = UnsafeRawPointer(oumps)
5757
_ = UnsafeRawPointer(oups)
5858

59-
_ = UnsafeMutablePointer<Void>(rp) // expected-warning 3 {{UnsafeMutablePointer<Void> has been replaced by UnsafeMutableRawPointer}} expected-error {{cannot invoke initializer for type 'UnsafeMutablePointer<Void>' with an argument list of type '(UnsafeRawPointer)'}} expected-note {{overloads for 'UnsafeMutablePointer<Void>' exist with these partially matching parameter lists: (RawPointer), (OpaquePointer), (OpaquePointer?), (UnsafeMutablePointer<Pointee>), (UnsafeMutablePointer<Pointee>?)}} expected-note{{Pointer conversion restricted: use '.assumingMemoryBound(to:)' or '.bindMemory(to:capacity:)' to view memory as a type}}
60-
_ = UnsafeMutablePointer<Void>(mrp) // expected-error {{cannot invoke initializer for type 'UnsafeMutablePointer<Void>' with an argument list of type '(UnsafeMutableRawPointer)'}} expected-note {{}} expected-warning 3 {{UnsafeMutablePointer<Void> has been replaced by UnsafeMutableRawPointer}} expected-note{{overloads for 'UnsafeMutablePointer<Void>' exist with these partially matching parameter lists: (RawPointer), (OpaquePointer), (OpaquePointer?), (UnsafeMutablePointer<Pointee>), (UnsafeMutablePointer<Pointee>?)}}
59+
_ = UnsafeMutablePointer<Void>(rp) // expected-warning 4 {{UnsafeMutablePointer<Void> has been replaced by UnsafeMutableRawPointer}} expected-error {{cannot invoke initializer for type 'UnsafeMutablePointer<Void>' with an argument list of type '(UnsafeRawPointer)'}} expected-note {{overloads for 'UnsafeMutablePointer<Void>' exist with these partially matching parameter lists: (RawPointer), (OpaquePointer), (OpaquePointer?), (UnsafeMutablePointer<Pointee>), (UnsafeMutablePointer<Pointee>?)}} expected-note{{Pointer conversion restricted: use '.assumingMemoryBound(to:)' or '.bindMemory(to:capacity:)' to view memory as a type}}
60+
_ = UnsafeMutablePointer<Void>(mrp) // expected-error {{cannot invoke initializer for type 'UnsafeMutablePointer<Void>' with an argument list of type '(UnsafeMutableRawPointer)'}} expected-note {{}} expected-warning 4 {{UnsafeMutablePointer<Void> has been replaced by UnsafeMutableRawPointer}} expected-note{{overloads for 'UnsafeMutablePointer<Void>' exist with these partially matching parameter lists: (RawPointer), (OpaquePointer), (OpaquePointer?), (UnsafeMutablePointer<Pointee>), (UnsafeMutablePointer<Pointee>?)}}
6161
_ = UnsafeMutablePointer<Void>(umpv) // expected-warning {{UnsafeMutablePointer<Void> has been replaced by UnsafeMutableRawPointer}}
6262
_ = UnsafeMutablePointer<Void>(upv) // expected-error {{'init' has been renamed to 'init(mutating:)'}} expected-warning {{UnsafeMutablePointer<Void> has been replaced by UnsafeMutableRawPointer}}
6363
_ = UnsafeMutablePointer<Void>(umpi) // expected-warning {{UnsafeMutablePointer<Void> has been replaced by UnsafeMutableRawPointer}}
6464
_ = UnsafeMutablePointer<Void>(upi) // expected-error {{'init' has been renamed to 'init(mutating:)'}} expected-warning {{UnsafeMutablePointer<Void> has been replaced by UnsafeMutableRawPointer}}
6565
_ = UnsafeMutablePointer<Void>(umps) // expected-warning {{UnsafeMutablePointer<Void> has been replaced by UnsafeMutableRawPointer}}
6666
_ = UnsafeMutablePointer<Void>(ups) // expected-error {{'init' has been renamed to 'init(mutating:)'}} expected-warning {{UnsafeMutablePointer<Void> has been replaced by UnsafeMutableRawPointer}}
6767

68-
_ = UnsafePointer<Void>(rp) // expected-error {{cannot invoke initializer for type 'UnsafePointer<Void>' with an argument list of type '(UnsafeRawPointer)'}} expected-note {{}} expected-warning 3 {{UnsafePointer<Void> has been replaced by UnsafeRawPointer}} expected-note{{overloads for 'UnsafePointer<Void>' exist with these partially matching parameter lists: (RawPointer), (OpaquePointer), (OpaquePointer?), (UnsafePointer<Pointee>), (UnsafePointer<Pointee>?), (UnsafeMutablePointer<Pointee>), (UnsafeMutablePointer<Pointee>?)}}
69-
_ = UnsafePointer<Void>(mrp) // expected-error {{cannot invoke initializer for type 'UnsafePointer<Void>' with an argument list of type '(UnsafeMutableRawPointer)'}} expected-note {{}} expected-warning 3 {{UnsafePointer<Void> has been replaced by UnsafeRawPointer}} expected-note{{overloads for 'UnsafePointer<Void>' exist with these partially matching parameter lists: (RawPointer), (OpaquePointer), (OpaquePointer?), (UnsafePointer<Pointee>), (UnsafePointer<Pointee>?), (UnsafeMutablePointer<Pointee>), (UnsafeMutablePointer<Pointee>?)}}
68+
_ = UnsafePointer<Void>(rp) // expected-error {{cannot invoke initializer for type 'UnsafePointer<Void>' with an argument list of type '(UnsafeRawPointer)'}} expected-note {{}} expected-warning 4 {{UnsafePointer<Void> has been replaced by UnsafeRawPointer}} expected-note{{overloads for 'UnsafePointer<Void>' exist with these partially matching parameter lists: (RawPointer), (OpaquePointer), (OpaquePointer?), (UnsafePointer<Pointee>), (UnsafePointer<Pointee>?), (UnsafeMutablePointer<Pointee>), (UnsafeMutablePointer<Pointee>?)}}
69+
_ = UnsafePointer<Void>(mrp) // expected-error {{cannot invoke initializer for type 'UnsafePointer<Void>' with an argument list of type '(UnsafeMutableRawPointer)'}} expected-note {{}} expected-warning 4 {{UnsafePointer<Void> has been replaced by UnsafeRawPointer}} expected-note{{overloads for 'UnsafePointer<Void>' exist with these partially matching parameter lists: (RawPointer), (OpaquePointer), (OpaquePointer?), (UnsafePointer<Pointee>), (UnsafePointer<Pointee>?), (UnsafeMutablePointer<Pointee>), (UnsafeMutablePointer<Pointee>?)}}
7070
_ = UnsafePointer<Void>(umpv) // expected-warning {{UnsafePointer<Void> has been replaced by UnsafeRawPointer}}
7171
_ = UnsafePointer<Void>(upv) // expected-warning {{UnsafePointer<Void> has been replaced by UnsafeRawPointer}}
7272
_ = UnsafePointer<Void>(umpi) // expected-warning {{UnsafePointer<Void> has been replaced by UnsafeRawPointer}}

0 commit comments

Comments
 (0)