Skip to content

Commit e3a4b68

Browse files
authored
Merge pull request #16556 from xedin/rdar-40002266
[Diagnostics] Hint contextual type diagnostics with expression type if known
2 parents de89db5 + d102f7d commit e3a4b68

File tree

4 files changed

+41
-18
lines changed

4 files changed

+41
-18
lines changed

lib/Sema/CSDiag.cpp

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

898899
/// For an expression being type checked with a CTP_CalleeResult contextual
899900
/// type, try to diagnose a problem.
@@ -2788,7 +2789,8 @@ static bool tryDiagnoseNonEscapingParameterToEscaping(
27882789
}
27892790

27902791
bool FailureDiagnosis::diagnoseContextualConversionError(
2791-
Expr *expr, Type contextualType, ContextualTypePurpose CTP) {
2792+
Expr *expr, Type contextualType, ContextualTypePurpose CTP,
2793+
Type suggestedType) {
27922794
// If the constraint system has a contextual type, then we can test to see if
27932795
// this is the problem that prevents us from solving the system.
27942796
if (!contextualType) {
@@ -2807,11 +2809,16 @@ bool FailureDiagnosis::diagnoseContextualConversionError(
28072809
if (contextualType->is<InOutType>())
28082810
options |= TCC_AllowLValue;
28092811

2810-
auto recheckedExpr = typeCheckChildIndependently(expr, options);
2812+
auto *recheckedExpr = typeCheckChildIndependently(expr, options);
28112813
auto exprType = recheckedExpr ? CS.getType(recheckedExpr) : Type();
28122814

2815+
// If there is a suggested type and re-typecheck failed, let's use it.
2816+
if (!exprType)
2817+
exprType = suggestedType;
2818+
28132819
// If it failed and diagnosed something, then we're done.
2814-
if (!exprType) return true;
2820+
if (!exprType)
2821+
return CS.TC.Diags.hadAnyError();
28152822

28162823
// If we contextually had an inout type, and got a non-lvalue result, then
28172824
// we fail with a mutability error.
@@ -5274,7 +5281,7 @@ bool FailureDiagnosis::diagnoseTrailingClosureErrors(ApplyExpr *callExpr) {
52745281
}
52755282
};
52765283

5277-
SmallVector<Type, 4> possibleTypes;
5284+
SmallPtrSet<TypeBase *, 4> possibleTypes;
52785285
auto currentType = CS.getType(fnExpr);
52795286

52805287
// If current type has type variables or unresolved types
@@ -5294,10 +5301,10 @@ bool FailureDiagnosis::diagnoseTrailingClosureErrors(ApplyExpr *callExpr) {
52945301
return diagnoseContextualConversionError(callExpr, contextualType,
52955302
CS.getContextualTypePurpose());
52965303
} else {
5297-
possibleTypes.push_back(currentType);
5304+
possibleTypes.insert(currentType.getPointer());
52985305
}
52995306

5300-
for (auto type : possibleTypes) {
5307+
for (Type type : possibleTypes) {
53015308
auto *fnType = type->getAs<AnyFunctionType>();
53025309
if (!fnType)
53035310
continue;
@@ -5388,7 +5395,7 @@ bool FailureDiagnosis::diagnoseCallContextualConversionErrors(
53885395
auto *DC = CS.DC;
53895396

53905397
auto typeCheckExpr = [](TypeChecker &TC, Expr *expr, DeclContext *DC,
5391-
SmallVectorImpl<Type> &types,
5398+
SmallPtrSetImpl<TypeBase *> &types,
53925399
Type contextualType = Type()) {
53935400
CalleeListener listener(contextualType);
53945401
TC.getPossibleTypesOfExpressionWithoutApplying(
@@ -5398,7 +5405,7 @@ bool FailureDiagnosis::diagnoseCallContextualConversionErrors(
53985405
// First let's type-check expression without contextual type, and
53995406
// see if that's going to produce a type, if so, let's type-check
54005407
// again, this time using given contextual type.
5401-
SmallVector<Type, 4> withoutContextual;
5408+
SmallPtrSet<TypeBase *, 4> withoutContextual;
54025409
typeCheckExpr(TC, callExpr, DC, withoutContextual);
54035410

54045411
// If there are no types returned, it means that problem was
@@ -5407,12 +5414,17 @@ bool FailureDiagnosis::diagnoseCallContextualConversionErrors(
54075414
if (withoutContextual.empty())
54085415
return false;
54095416

5410-
SmallVector<Type, 4> withContextual;
5417+
SmallPtrSet<TypeBase *, 4> withContextual;
54115418
typeCheckExpr(TC, callExpr, DC, withContextual, contextualType);
54125419
// If type-checking with contextual type didn't produce any results
54135420
// it means that we have a contextual mismatch.
5414-
if (withContextual.empty())
5415-
return diagnoseContextualConversionError(callExpr, contextualType, CTP);
5421+
if (withContextual.empty()) {
5422+
// If there is just a single choice, we can hit contextual diagnostics
5423+
// about it in case re-typecheck fails.
5424+
Type exprType = withoutContextual.size() == 1 ? *withoutContextual.begin() : Type();
5425+
return diagnoseContextualConversionError(callExpr, contextualType, CTP,
5426+
exprType);
5427+
}
54165428

54175429
// If call produces a single type when type-checked with contextual
54185430
// expression, it means that the problem is elsewhere, any other
@@ -5431,15 +5443,15 @@ static bool shouldTypeCheckFunctionExpr(TypeChecker &TC, DeclContext *DC,
54315443
if (!isa<UnresolvedDotExpr>(fnExpr))
54325444
return true;
54335445

5434-
SmallVector<Type, 4> fnTypes;
5446+
SmallPtrSet<TypeBase *, 4> fnTypes;
54355447
TC.getPossibleTypesOfExpressionWithoutApplying(fnExpr, DC, fnTypes,
54365448
FreeTypeVariableBinding::UnresolvedType);
54375449

54385450
if (fnTypes.size() == 1) {
54395451
// Some member types depend on the arguments to produce a result type,
54405452
// type-checking such expressions without associated arguments is
54415453
// going to produce unrelated diagnostics.
5442-
if (auto fn = fnTypes[0]->getAs<AnyFunctionType>()) {
5454+
if (auto fn = (*fnTypes.begin())->getAs<AnyFunctionType>()) {
54435455
auto resultType = fn->getResult();
54445456
if (resultType->hasUnresolvedType() || resultType->hasTypeVariable())
54455457
return false;
@@ -5494,7 +5506,7 @@ bool FailureDiagnosis::visitApplyExpr(ApplyExpr *callExpr) {
54945506
isa<UnresolvedDotExpr>(callExpr->getFn())) {
54955507
fnExpr = callExpr->getFn();
54965508

5497-
SmallVector<Type, 4> types;
5509+
SmallPtrSet<TypeBase *, 4> types;
54985510
CS.TC.getPossibleTypesOfExpressionWithoutApplying(fnExpr, CS.DC, types);
54995511

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

lib/Sema/TypeCheckConstraints.cpp

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

19961996
void TypeChecker::getPossibleTypesOfExpressionWithoutApplying(
1997-
Expr *&expr, DeclContext *dc, SmallVectorImpl<Type> &types,
1997+
Expr *&expr, DeclContext *dc, SmallPtrSetImpl<TypeBase *> &types,
19981998
FreeTypeVariableBinding allowFreeTypeVariables,
19991999
ExprTypeCheckListener *listener) {
20002000
PrettyStackTraceExpr stackTrace(Context, "type-checking", expr);
@@ -2028,7 +2028,7 @@ void TypeChecker::getPossibleTypesOfExpressionWithoutApplying(
20282028
for (auto &solution : viable) {
20292029
auto exprType = solution.simplifyType(cs.getType(expr));
20302030
assert(exprType && !exprType->hasTypeVariable());
2031-
types.push_back(exprType);
2031+
types.insert(exprType.getPointer());
20322032
}
20332033
}
20342034

lib/Sema/TypeChecker.h

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

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

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)