Skip to content

Commit 046010d

Browse files
authored
Merge pull request #16582 from xedin/rdar-40002266-reapply-4.2
[4.2] [Diagnostics] Hint contextual type diagnostics with expression type if known
2 parents 9f28451 + d8cca72 commit 046010d

File tree

5 files changed

+42
-19
lines changed

5 files changed

+42
-19
lines changed

lib/Sema/CSDiag.cpp

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -886,7 +886,8 @@ class FailureDiagnosis :public ASTVisitor<FailureDiagnosis, /*exprresult*/bool>{
886886
/// Attempt to produce a diagnostic for a mismatch between an expression's
887887
/// type and its assumed contextual type.
888888
bool diagnoseContextualConversionError(Expr *expr, Type contextualType,
889-
ContextualTypePurpose CTP);
889+
ContextualTypePurpose CTP,
890+
Type suggestedType = Type());
890891

891892
/// For an expression being type checked with a CTP_CalleeResult contextual
892893
/// type, try to diagnose a problem.
@@ -2780,7 +2781,8 @@ static bool tryDiagnoseNonEscapingParameterToEscaping(
27802781
}
27812782

27822783
bool FailureDiagnosis::diagnoseContextualConversionError(
2783-
Expr *expr, Type contextualType, ContextualTypePurpose CTP) {
2784+
Expr *expr, Type contextualType, ContextualTypePurpose CTP,
2785+
Type suggestedType) {
27842786
// If the constraint system has a contextual type, then we can test to see if
27852787
// this is the problem that prevents us from solving the system.
27862788
if (!contextualType) {
@@ -2799,11 +2801,16 @@ bool FailureDiagnosis::diagnoseContextualConversionError(
27992801
if (contextualType->is<InOutType>())
28002802
options |= TCC_AllowLValue;
28012803

2802-
auto recheckedExpr = typeCheckChildIndependently(expr, options);
2804+
auto *recheckedExpr = typeCheckChildIndependently(expr, options);
28032805
auto exprType = recheckedExpr ? CS.getType(recheckedExpr) : Type();
28042806

2807+
// If there is a suggested type and re-typecheck failed, let's use it.
2808+
if (!exprType)
2809+
exprType = suggestedType;
2810+
28052811
// If it failed and diagnosed something, then we're done.
2806-
if (!exprType) return true;
2812+
if (!exprType)
2813+
return CS.TC.Diags.hadAnyError();
28072814

28082815
// If we contextually had an inout type, and got a non-lvalue result, then
28092816
// we fail with a mutability error.
@@ -5263,7 +5270,7 @@ bool FailureDiagnosis::diagnoseTrailingClosureErrors(ApplyExpr *callExpr) {
52635270
}
52645271
};
52655272

5266-
SmallVector<Type, 4> possibleTypes;
5273+
SmallPtrSet<TypeBase *, 4> possibleTypes;
52675274
auto currentType = CS.getType(fnExpr);
52685275

52695276
// If current type has type variables or unresolved types
@@ -5283,10 +5290,10 @@ bool FailureDiagnosis::diagnoseTrailingClosureErrors(ApplyExpr *callExpr) {
52835290
return diagnoseContextualConversionError(callExpr, contextualType,
52845291
CS.getContextualTypePurpose());
52855292
} else {
5286-
possibleTypes.push_back(currentType);
5293+
possibleTypes.insert(currentType.getPointer());
52875294
}
52885295

5289-
for (auto type : possibleTypes) {
5296+
for (Type type : possibleTypes) {
52905297
auto *fnType = type->getAs<AnyFunctionType>();
52915298
if (!fnType)
52925299
continue;
@@ -5377,7 +5384,7 @@ bool FailureDiagnosis::diagnoseCallContextualConversionErrors(
53775384
auto *DC = CS.DC;
53785385

53795386
auto typeCheckExpr = [](TypeChecker &TC, Expr *expr, DeclContext *DC,
5380-
SmallVectorImpl<Type> &types,
5387+
SmallPtrSetImpl<TypeBase *> &types,
53815388
Type contextualType = Type()) {
53825389
CalleeListener listener(contextualType);
53835390
TC.getPossibleTypesOfExpressionWithoutApplying(
@@ -5387,7 +5394,7 @@ bool FailureDiagnosis::diagnoseCallContextualConversionErrors(
53875394
// First let's type-check expression without contextual type, and
53885395
// see if that's going to produce a type, if so, let's type-check
53895396
// again, this time using given contextual type.
5390-
SmallVector<Type, 4> withoutContextual;
5397+
SmallPtrSet<TypeBase *, 4> withoutContextual;
53915398
typeCheckExpr(TC, callExpr, DC, withoutContextual);
53925399

53935400
// If there are no types returned, it means that problem was
@@ -5396,12 +5403,17 @@ bool FailureDiagnosis::diagnoseCallContextualConversionErrors(
53965403
if (withoutContextual.empty())
53975404
return false;
53985405

5399-
SmallVector<Type, 4> withContextual;
5406+
SmallPtrSet<TypeBase *, 4> withContextual;
54005407
typeCheckExpr(TC, callExpr, DC, withContextual, contextualType);
54015408
// If type-checking with contextual type didn't produce any results
54025409
// it means that we have a contextual mismatch.
5403-
if (withContextual.empty())
5404-
return diagnoseContextualConversionError(callExpr, contextualType, CTP);
5410+
if (withContextual.empty()) {
5411+
// If there is just a single choice, we can hit contextual diagnostics
5412+
// about it in case re-typecheck fails.
5413+
Type exprType = withoutContextual.size() == 1 ? *withoutContextual.begin() : Type();
5414+
return diagnoseContextualConversionError(callExpr, contextualType, CTP,
5415+
exprType);
5416+
}
54055417

54065418
// If call produces a single type when type-checked with contextual
54075419
// expression, it means that the problem is elsewhere, any other
@@ -5420,15 +5432,15 @@ static bool shouldTypeCheckFunctionExpr(TypeChecker &TC, DeclContext *DC,
54205432
if (!isa<UnresolvedDotExpr>(fnExpr))
54215433
return true;
54225434

5423-
SmallVector<Type, 4> fnTypes;
5435+
SmallPtrSet<TypeBase *, 4> fnTypes;
54245436
TC.getPossibleTypesOfExpressionWithoutApplying(fnExpr, DC, fnTypes,
54255437
FreeTypeVariableBinding::UnresolvedType);
54265438

54275439
if (fnTypes.size() == 1) {
54285440
// Some member types depend on the arguments to produce a result type,
54295441
// type-checking such expressions without associated arguments is
54305442
// going to produce unrelated diagnostics.
5431-
if (auto fn = fnTypes[0]->getAs<AnyFunctionType>()) {
5443+
if (auto fn = (*fnTypes.begin())->getAs<AnyFunctionType>()) {
54325444
auto resultType = fn->getResult();
54335445
if (resultType->hasUnresolvedType() || resultType->hasTypeVariable())
54345446
return false;
@@ -5483,7 +5495,7 @@ bool FailureDiagnosis::visitApplyExpr(ApplyExpr *callExpr) {
54835495
isa<UnresolvedDotExpr>(callExpr->getFn())) {
54845496
fnExpr = callExpr->getFn();
54855497

5486-
SmallVector<Type, 4> types;
5498+
SmallPtrSet<TypeBase *, 4> types;
54875499
CS.TC.getPossibleTypesOfExpressionWithoutApplying(fnExpr, CS.DC, types);
54885500

54895501
auto isFunctionType = [getFuncType](Type type) -> bool {

lib/Sema/TypeCheckConstraints.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2010,7 +2010,7 @@ getTypeOfExpressionWithoutApplying(Expr *&expr, DeclContext *dc,
20102010
}
20112011

20122012
void TypeChecker::getPossibleTypesOfExpressionWithoutApplying(
2013-
Expr *&expr, DeclContext *dc, SmallVectorImpl<Type> &types,
2013+
Expr *&expr, DeclContext *dc, SmallPtrSetImpl<TypeBase *> &types,
20142014
FreeTypeVariableBinding allowFreeTypeVariables,
20152015
ExprTypeCheckListener *listener) {
20162016
PrettyStackTraceExpr stackTrace(Context, "type-checking", expr);
@@ -2044,7 +2044,7 @@ void TypeChecker::getPossibleTypesOfExpressionWithoutApplying(
20442044
for (auto &solution : viable) {
20452045
auto exprType = solution.simplifyType(cs.getType(expr));
20462046
assert(exprType && !exprType->hasTypeVariable());
2047-
types.push_back(exprType);
2047+
types.insert(exprType.getPointer());
20482048
}
20492049
}
20502050

lib/Sema/TypeChecker.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1745,7 +1745,7 @@ class TypeChecker final : public LazyResolver {
17451745
ExprTypeCheckListener *listener = nullptr);
17461746

17471747
void getPossibleTypesOfExpressionWithoutApplying(
1748-
Expr *&expr, DeclContext *dc, SmallVectorImpl<Type> &types,
1748+
Expr *&expr, DeclContext *dc, SmallPtrSetImpl<TypeBase *> &types,
17491749
FreeTypeVariableBinding allowFreeTypeVariables =
17501750
FreeTypeVariableBinding::Disallow,
17511751
ExprTypeCheckListener *listener = nullptr);

test/Constraints/diagnostics.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -501,7 +501,7 @@ func r21684487() {
501501
var closures = Array<MyClosure>()
502502
let testClosure = {(list: [Int]) -> Bool in return true}
503503

504-
let closureIndex = closures.index{$0 === testClosure} // expected-error {{cannot check reference equality of functions; operands here have types '_' and '([Int]) -> Bool'}}
504+
let closureIndex = closures.index{$0 === testClosure} // expected-error {{cannot check reference equality of functions;}}
505505
}
506506

507507
// <rdar://problem/18397777> QoI: special case comparisons with nil

test/Constraints/rdar40002266.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -verify %s
2+
// REQUIRES: objc_interop
3+
4+
import Foundation
5+
6+
struct S {
7+
init<T: NSNumber>(_ num: T) {
8+
self.init(num != 0) // expected-error {{binary operator '!=' cannot be applied to operands of type 'T' and 'Int'}}
9+
// expected-note@-1 {{expected an argument list of type '(Int, Int)'}}
10+
}
11+
}

0 commit comments

Comments
 (0)