Skip to content

Commit

Permalink
improve diagnostics relating to closure result types, both when they are
Browse files Browse the repository at this point in the history
explicitly written and disagree with context, and when context provides a
non-explicitly written type that disagrees with the body of the closure.



Swift SVN r30984
  • Loading branch information
lattner committed Aug 4, 2015
1 parent 7e31460 commit 7a73392
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 15 deletions.
10 changes: 10 additions & 0 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,9 @@ ERROR(cannot_infer_closure_type,sema,none,
ERROR(cannot_infer_closure_result_type,sema,none,
"unable to infer closure return type in current context", ())

ERROR(incorrect_explicit_closure_result,sema,none,
"declared closure result %0 is incompatible with contextual type %1",
(Type, Type))

ERROR(cannot_call_function_value,sema,none,
"cannot invoke value of function type with argument list '%0'",
Expand Down Expand Up @@ -243,6 +246,13 @@ ERROR(cannot_convert_argument_value_protocol,sema_tcd,none,
ERROR(cannot_convert_argument_value_nil,sema,none,
"nil is not compatible with expected argument type %0", (Type))

ERROR(cannot_convert_closure_result,sema,none,
"cannot convert value of type %0 to closure result type %1",
(Type,Type))
ERROR(cannot_convert_closure_result_protocol,sema_tcd,none,
"argument type %0 does not conform to closure result type %1", (Type, Type))
ERROR(cannot_convert_closure_result_nil,sema,none,
"nil is not compatible with closure result type %0", (Type))



Expand Down
58 changes: 50 additions & 8 deletions lib/Sema/CSDiag.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2865,6 +2865,9 @@ bool FailureDiagnosis::diagnoseContextualConversionError(Type exprType) {
case CTP_CallArgument:
nilDiag = diag::cannot_convert_argument_value_nil;
break;
case CTP_ClosureResult:
nilDiag = diag::cannot_convert_closure_result_nil;
break;
}

if (isa<NilLiteralExpr>(expr->getSemanticsProvidingExpr())) {
Expand Down Expand Up @@ -2928,6 +2931,10 @@ bool FailureDiagnosis::diagnoseContextualConversionError(Type exprType) {
diagID = diag::cannot_convert_argument_value;
diagIDProtocol = diag::cannot_convert_argument_value_protocol;
break;
case CTP_ClosureResult:
diagID = diag::cannot_convert_closure_result;
diagIDProtocol = diag::cannot_convert_closure_result_protocol;
break;
}

// When complaining about conversion to a protocol type, complain about
Expand Down Expand Up @@ -3728,12 +3735,14 @@ bool FailureDiagnosis::visitClosureExpr(ClosureExpr *CE) {
if (!CE->hasSingleExpressionBody())
return diagnoseGeneralFailure();

Type expectedResultType;

// If we have a contextual type available for this closure, apply it to the
// ParamDecls in our parameter list. This ensures that any uses of them get
// appropriate types.
if (CS->getContextualType() &&
CS->getContextualType()->is<FunctionType>()) {
auto fnType = CS->getContextualType()->castTo<FunctionType>();
CS->getContextualType()->is<AnyFunctionType>()) {
auto fnType = CS->getContextualType()->castTo<AnyFunctionType>();
Pattern *params = CE->getParams();
TypeResolutionOptions TROptions;
TROptions |= TR_OverrideType;
Expand All @@ -3743,7 +3752,10 @@ bool FailureDiagnosis::visitClosureExpr(ClosureExpr *CE) {
if (CS->TC.coercePatternToType(params, CE, fnType->getInput(), TROptions))
return true;
CE->setParams(params);

expectedResultType = fnType->getResult();
} else {

// Defend against type variables from our constraint system leaking into
// recursive constraints systems formed when checking the body of the
// closure. These typevars come into them when the body does name
Expand All @@ -3757,22 +3769,52 @@ bool FailureDiagnosis::visitClosureExpr(ClosureExpr *CE) {
});
}

// If the closure had an expected result type, use it.
if (CE->hasExplicitResultType())
expectedResultType = CE->getExplicitResultTypeLoc().getType();

// When we're type checking a single-expression closure, we need to reset the
// DeclContext to this closure for the recursive type checking. Otherwise,
// if there is a closure in the subexpression, we can violate invariants.
{
llvm::SaveAndRestore<DeclContext*> SavedDC(CS->DC, CE);
if (!typeCheckChildIndependently(CE->getSingleExpressionBody()))

auto CTP = expectedResultType ? CTP_ClosureResult : CTP_Unused;

if (!typeCheckChildIndependently(CE->getSingleExpressionBody(),
expectedResultType, CTP))
return true;
}

// Check for a contextual type error. This is necessary because
// FailureDiagnosis::diagnoseFailure doesn't do this for closures.

// If the body of the closure looked ok, then look for a contextual type
// error. This is necessary because FailureDiagnosis::diagnoseFailure doesn't
// do this for closures.
if (CS->getContextualType() &&
!CS->getContextualType()->isEqual(CE->getType()))
!CS->getContextualType()->isEqual(CE->getType())) {

auto fnType = CS->getContextualType()->getAs<AnyFunctionType>();

// If the closure had an explicitly written return type incompatible with
// the contextual type, diagnose that.
if (CE->hasExplicitResultType() &&
CE->getExplicitResultTypeLoc().getTypeRepr()) {
auto explicitResultTy = CE->getExplicitResultTypeLoc().getType();
if (!explicitResultTy->isEqual(fnType->getResult())) {
auto repr = CE->getExplicitResultTypeLoc().getTypeRepr();
diagnose(repr->getStartLoc(), diag::incorrect_explicit_closure_result,
explicitResultTy, fnType->getResult())
.fixItReplace(repr->getSourceRange(),fnType->getResult().getString());
return true;
}
}

// Otherwise, diagnose a general error talking about the type of the closure
// in-aggregate.
if (diagnoseContextualConversionError(CE->getType()))
return true;

}


// Otherwise, produce a generic error.
return diagnoseGeneralFailure();
}
Expand Down
2 changes: 1 addition & 1 deletion lib/Sema/TypeChecker.h
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ enum ContextualTypePurpose {
CTP_DefaultParameter, ///< Default value in parameter 'foo(a : Int = 42)'.

CTP_CallArgument, ///< Call to function or operator requires type.

CTP_ClosureResult, ///< Closure result expects a specific type.

CTP_CannotFail, ///< Conversion can never fail. abort() if it does.
};
Expand Down
2 changes: 1 addition & 1 deletion test/Constraints/closures.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ myMap(intArray, { x -> String in String(x) } )

// Closures with too few parameters.
func foo(x: (Int, Int) -> Int) {}
foo({$0}) // expected-error{{cannot convert value of type '(_) -> Int' to expected argument type '(Int, Int) -> Int'}}
foo({$0}) // expected-error{{cannot convert value of type '(Int, Int)' to closure result type 'Int'}}

struct X {}
func mySort(array: [String], _ predicate: (String, String) -> Bool) -> [String] {}
Expand Down
13 changes: 8 additions & 5 deletions test/expr/closure/closures.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,14 @@ func funcdecl5(a: Int, _ y: Int) {
func6(fn: { a,b in a+b })

// Infer incompatible type.
func6(fn: {a,b->Float in 4.0 }) // expected-error {{cannot convert value of type '(_, _) -> Float' to expected argument type '(Int, Int) -> Int'}}

// Pattern doesn't need to name arguments.
func6(fn: {a,b->Float in 4.0 }) // expected-error {{declared closure result 'Float' is incompatible with contextual type 'Int'}} {{19-24=Int}} // Pattern doesn't need to name arguments.
func6(fn: { _,_ in 4 })

func6(fn: {a,b in 4.0 }) // expected-error {{cannot convert value of type 'Double' to closure result type 'Int'}}
func6(fn: {(a : Float, b) in 4 }) // expected-error {{cannot convert value of type '(Float, _) -> Int' to expected argument type '(Int, Int) -> Int'}}



var fn = {}
var fn2 = { 4 }

Expand Down Expand Up @@ -246,8 +249,8 @@ var x = {return $0}(1)

func returnsInt() -> Int { return 0 }
takesVoidFunc(returnsInt) // expected-error {{cannot convert value of type '() -> Int' to expected argument type '() -> ()'}}
takesVoidFunc({()->Int in 0}) // expected-error {{cannot convert value of type '() -> Int' to expected argument type '() -> ()'}}

takesVoidFunc({()->Int in 0}) // expected-error {{declared closure result 'Int' is incompatible with contextual type '()'}} {{20-23=()}}
// These used to crash the compiler, but were fixed to support the implemenation of rdar://problem/17228969
Void(0) // expected-error{{cannot invoke initializer for type 'Void' with an argument list of type '(Int)'}}
_ = {0}
Expand Down

0 comments on commit 7a73392

Please sign in to comment.