Skip to content

[4.2] [Diagnostics] Hint contextual type diagnostics with expression type if known #16582

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 13, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 27 additions & 15 deletions lib/Sema/CSDiag.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -886,7 +886,8 @@ class FailureDiagnosis :public ASTVisitor<FailureDiagnosis, /*exprresult*/bool>{
/// Attempt to produce a diagnostic for a mismatch between an expression's
/// type and its assumed contextual type.
bool diagnoseContextualConversionError(Expr *expr, Type contextualType,
ContextualTypePurpose CTP);
ContextualTypePurpose CTP,
Type suggestedType = Type());

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

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

auto recheckedExpr = typeCheckChildIndependently(expr, options);
auto *recheckedExpr = typeCheckChildIndependently(expr, options);
auto exprType = recheckedExpr ? CS.getType(recheckedExpr) : Type();

// If there is a suggested type and re-typecheck failed, let's use it.
if (!exprType)
exprType = suggestedType;

// If it failed and diagnosed something, then we're done.
if (!exprType) return true;
if (!exprType)
return CS.TC.Diags.hadAnyError();

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

SmallVector<Type, 4> possibleTypes;
SmallPtrSet<TypeBase *, 4> possibleTypes;
auto currentType = CS.getType(fnExpr);

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

for (auto type : possibleTypes) {
for (Type type : possibleTypes) {
auto *fnType = type->getAs<AnyFunctionType>();
if (!fnType)
continue;
Expand Down Expand Up @@ -5377,7 +5384,7 @@ bool FailureDiagnosis::diagnoseCallContextualConversionErrors(
auto *DC = CS.DC;

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

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

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

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

SmallVector<Type, 4> fnTypes;
SmallPtrSet<TypeBase *, 4> fnTypes;
TC.getPossibleTypesOfExpressionWithoutApplying(fnExpr, DC, fnTypes,
FreeTypeVariableBinding::UnresolvedType);

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

SmallVector<Type, 4> types;
SmallPtrSet<TypeBase *, 4> types;
CS.TC.getPossibleTypesOfExpressionWithoutApplying(fnExpr, CS.DC, types);

auto isFunctionType = [getFuncType](Type type) -> bool {
Expand Down
4 changes: 2 additions & 2 deletions lib/Sema/TypeCheckConstraints.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2010,7 +2010,7 @@ getTypeOfExpressionWithoutApplying(Expr *&expr, DeclContext *dc,
}

void TypeChecker::getPossibleTypesOfExpressionWithoutApplying(
Expr *&expr, DeclContext *dc, SmallVectorImpl<Type> &types,
Expr *&expr, DeclContext *dc, SmallPtrSetImpl<TypeBase *> &types,
FreeTypeVariableBinding allowFreeTypeVariables,
ExprTypeCheckListener *listener) {
PrettyStackTraceExpr stackTrace(Context, "type-checking", expr);
Expand Down Expand Up @@ -2044,7 +2044,7 @@ void TypeChecker::getPossibleTypesOfExpressionWithoutApplying(
for (auto &solution : viable) {
auto exprType = solution.simplifyType(cs.getType(expr));
assert(exprType && !exprType->hasTypeVariable());
types.push_back(exprType);
types.insert(exprType.getPointer());
}
}

Expand Down
2 changes: 1 addition & 1 deletion lib/Sema/TypeChecker.h
Original file line number Diff line number Diff line change
Expand Up @@ -1745,7 +1745,7 @@ class TypeChecker final : public LazyResolver {
ExprTypeCheckListener *listener = nullptr);

void getPossibleTypesOfExpressionWithoutApplying(
Expr *&expr, DeclContext *dc, SmallVectorImpl<Type> &types,
Expr *&expr, DeclContext *dc, SmallPtrSetImpl<TypeBase *> &types,
FreeTypeVariableBinding allowFreeTypeVariables =
FreeTypeVariableBinding::Disallow,
ExprTypeCheckListener *listener = nullptr);
Expand Down
2 changes: 1 addition & 1 deletion test/Constraints/diagnostics.swift
Original file line number Diff line number Diff line change
Expand Up @@ -501,7 +501,7 @@ func r21684487() {
var closures = Array<MyClosure>()
let testClosure = {(list: [Int]) -> Bool in return true}

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

// <rdar://problem/18397777> QoI: special case comparisons with nil
Expand Down
11 changes: 11 additions & 0 deletions test/Constraints/rdar40002266.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -verify %s
// REQUIRES: objc_interop

import Foundation

struct S {
init<T: NSNumber>(_ num: T) {
self.init(num != 0) // expected-error {{binary operator '!=' cannot be applied to operands of type 'T' and 'Int'}}
// expected-note@-1 {{expected an argument list of type '(Int, Int)'}}
}
}