Skip to content
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
5 changes: 4 additions & 1 deletion include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -2489,7 +2489,10 @@ ERROR(tuple_pattern_in_non_tuple_context,none,
"tuple pattern cannot match values of the non-tuple type %0", (Type))
ERROR(closure_argument_list_tuple,none,
"contextual closure type %0 expects %1 argument%s1, "
"but %2 were used in closure body", (Type, unsigned, unsigned))
"but %2 %select{were|was}3 used in closure body", (Type, unsigned, unsigned, bool))
ERROR(closure_argument_list_single_tuple,none,
"contextual closure type specifies %0, but %1 %select{was|were}2 used in closure body, "
"try adding extra parentheses around the single tuple argument", (Type, unsigned, bool))
ERROR(closure_argument_list_missing,none,
"contextual type for closure argument list expects %0 argument%s0, "
"which cannot be implicitly ignored", (unsigned))
Expand Down
2 changes: 1 addition & 1 deletion lib/Sema/CSApply.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6464,7 +6464,7 @@ namespace {
// Coerce the pattern, in case we resolved something.
auto fnType = closure->getType()->castTo<FunctionType>();
auto *params = closure->getParameters();
if (tc.coerceParameterListToType(params, closure, fnType->getInput()))
if (tc.coerceParameterListToType(params, closure, fnType))
return { false, nullptr };

// If this is a single-expression closure, convert the expression
Expand Down
4 changes: 2 additions & 2 deletions lib/Sema/CSDiag.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5614,11 +5614,11 @@ bool FailureDiagnosis::visitClosureExpr(ClosureExpr *CE) {
// don't confuse the issue.
fnType = FunctionType::get(fnType->getInput(), fnType->getResult());
diagnose(params->getStartLoc(), diag::closure_argument_list_tuple,
fnType, inferredArgCount, actualArgCount);
fnType, inferredArgCount, actualArgCount, (actualArgCount == 1));
return true;
}

if (CS->TC.coerceParameterListToType(params, CE, inferredArgType))
if (CS->TC.coerceParameterListToType(params, CE, fnType))
return true;

expectedResultType = fnType->getResult();
Expand Down
18 changes: 18 additions & 0 deletions lib/Sema/TypeCheckConstraints.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1523,6 +1523,24 @@ bool TypeChecker::typeCheckExpression(Expr *&expr, DeclContext *dc,
convertType.getType()->hasUnresolvedType())) {
convertType = TypeLoc();
convertTypePurpose = CTP_Unused;
} else if (auto closure = dyn_cast<ClosureExpr>(expr)) {
auto *P = closure->getParameters();

if (P->size() == 1 && convertType.getType()->is<FunctionType>()) {
auto hintFnType = convertType.getType()->castTo<FunctionType>();
auto hintFnInputType = hintFnType->getInput();

// Cannot use hintFnInputType->is<TupleType>() since it would desugar ParenType
if (isa<TupleType>(hintFnInputType.getPointer())) {
TupleType *tupleTy = hintFnInputType->castTo<TupleType>();

if (tupleTy->getNumElements() >= 2) {
diagnose(P->getStartLoc(), diag::closure_argument_list_single_tuple,
hintFnInputType, P->size(), P->size() > 1);
return true;
}
}
}
}
}

Expand Down
31 changes: 23 additions & 8 deletions lib/Sema/TypeCheckPattern.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1526,8 +1526,9 @@ bool TypeChecker::coercePatternToType(Pattern *&P, DeclContext *dc, Type type,
/// TODO: These diagnostics should be a lot better now that we know this is
/// all specific to closures.
///
bool TypeChecker::coerceParameterListToType(ParameterList *P, DeclContext *DC,
Type paramListType) {
bool TypeChecker::coerceParameterListToType(ParameterList *P, ClosureExpr *CE,
AnyFunctionType *FN) {
Type paramListType = FN->getInput();
bool hadError = paramListType->is<ErrorType>();

// Sometimes a scalar type gets applied to a single-argument parameter list.
Expand All @@ -1536,7 +1537,7 @@ bool TypeChecker::coerceParameterListToType(ParameterList *P, DeclContext *DC,

// Check that the type, if explicitly spelled, is ok.
if (param->getTypeLoc().getTypeRepr()) {
hadError |= validateParameterType(param, DC, TypeResolutionOptions(),
hadError |= validateParameterType(param, CE, TypeResolutionOptions(),
nullptr, *this);

// Now that we've type checked the explicit argument type, see if it
Expand Down Expand Up @@ -1564,6 +1565,22 @@ bool TypeChecker::coerceParameterListToType(ParameterList *P, DeclContext *DC,
return hadError;
};

// Check if paramListType only contains one single tuple.
// If it is, then paramListType would be sugared ParenType
// with a single underlying TupleType. In that case, check if
// the closure argument is also one to avoid the tuple splat
// from happening.
if (!hadError && isa<ParenType>(paramListType.getPointer())) {
auto underlyingTy = paramListType->getCanonicalType();

if (underlyingTy->is<TupleType>()) {
if (P->size() == 1) {
return handleParameter(P->get(0), underlyingTy);
}
}

//pass
}

// The context type must be a tuple.
TupleType *tupleTy = paramListType->getAs<TupleType>();
Expand All @@ -1578,11 +1595,9 @@ bool TypeChecker::coerceParameterListToType(ParameterList *P, DeclContext *DC,
// The number of elements must match exactly.
// TODO: incomplete tuple patterns, with some syntax.
if (!hadError && tupleTy->getNumElements() != P->size()) {
if (P->size() == 1)
return handleParameter(P->get(0), paramListType);

diagnose(P->getStartLoc(), diag::tuple_pattern_length_mismatch,
paramListType);
auto fnType = FunctionType::get(paramListType->getDesugaredType(), FN->getResult());
diagnose(P->getStartLoc(), diag::closure_argument_list_tuple,
fnType, tupleTy->getNumElements(), P->size(), (P->size() == 1));
hadError = true;
}

Expand Down
2 changes: 1 addition & 1 deletion lib/Sema/TypeChecker.h
Original file line number Diff line number Diff line change
Expand Up @@ -1404,7 +1404,7 @@ class TypeChecker final : public LazyResolver {
/// contextual type.
///
/// \returns true if an error occurred, false otherwise.
bool coerceParameterListToType(ParameterList *P, DeclContext *dc, Type type);
bool coerceParameterListToType(ParameterList *P, ClosureExpr *CE, AnyFunctionType *FN);


/// Type-check an initialized variable pattern declaration.
Expand Down
8 changes: 4 additions & 4 deletions test/Constraints/closures.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ _ = myMap(intArray, { String($0) })
_ = myMap(intArray, { x -> String in String(x) } )

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

struct X {}
Expand Down Expand Up @@ -130,7 +130,7 @@ var _: (Int) -> Int = {a,b in 0}
// expected-error @+1 {{contextual closure type '(Int) -> Int' expects 1 argument, but 3 were used in closure body}}
var _: (Int) -> Int = {a,b,c in 0}

var _: (Int, Int) -> Int = {a in 0}
var _: ((Int, Int)) -> Int = {a in 0}

// expected-error @+1 {{contextual closure type '(Int, Int, Int) -> Int' expects 3 arguments, but 2 were used in closure body}}
var _: (Int, Int, Int) -> Int = {a, b in a+b}
Expand Down Expand Up @@ -202,7 +202,7 @@ func acceptNothingToInt (_: () -> Int) {}
func testAcceptNothingToInt(ac1: @autoclosure () -> Int) {
// expected-note@-1{{parameter 'ac1' is implicitly non-escaping because it was declared @autoclosure}}
acceptNothingToInt({ac1($0)})
// expected-error@-1{{cannot convert value of type '(_) -> Int' to expected argument type '() -> Int'}}
// expected-error@-1{{contextual closure type '() -> Int' expects 0 arguments, but 1 was used in closure body}}
// FIXME: expected-error@-2{{closure use of non-escaping parameter 'ac1' may allow it to escape}}
}

Expand Down Expand Up @@ -323,4 +323,4 @@ func r20789423() {

}


let f: (Int, Int) -> Void = { x in } // expected-error {{contextual closure type specifies '(Int, Int)', but 1 was used in closure body, try adding extra parentheses around the single tuple argument}}
4 changes: 2 additions & 2 deletions test/expr/closure/closures.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// RUN: %target-parse-verify-swift

var func6 : (_ fn : (Int,Int) -> Int) -> ()
var func6 : (_ fn : ((Int, Int)) -> Int) -> ()
var func6a : ((Int, Int) -> Int) -> ()
var func6b : (Int, (Int, Int) -> Int) -> ()
func func6c(_ f: (Int, Int) -> Int, _ n: Int = 0) {} // expected-warning{{prior to parameters}}
Expand Down Expand Up @@ -65,7 +65,7 @@ func funcdecl5(_ a: Int, _ y: Int) {
func6({a,b in 4.0 }) // expected-error {{cannot convert value of type 'Double' to closure result type 'Int'}}

// TODO: This diagnostic can be improved: rdar://22128205
func6({(a : Float, b) in 4 }) // expected-error {{cannot convert value of type '(Float, _) -> Int' to expected argument type '(Int, Int) -> Int'}}
func6({(a : Float, b) in 4 }) // expected-error {{cannot convert value of type '(Float, _) -> Int' to expected argument type '((Int, Int)) -> Int'}}



Expand Down
4 changes: 2 additions & 2 deletions test/expr/closure/default_args.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
func simple_default_args() {
// <rdar://problem/22753605> QoI: bad diagnostic when closure has default argument
let _ : (Int) -> Int = {(x : Int = 1) in x+1} // expected-error{{default arguments are not allowed in closures}} {{36-39=}}
let _ : () -> Int = {(_ x : Int = 1) in x+1} // expected-error{{cannot convert value of type '(Int) -> Int' to specified type '() -> Int'}} expected-error {{default arguments are not allowed in closures}} {{35-38=}}
let _ : () -> Int = {(_ x : Int) in x+1} // expected-error{{cannot convert value of type '(Int) -> Int' to specified type '() -> Int'}}
let _ : () -> Int = {(_ x : Int = 1) in x+1} // expected-error{{contextual closure type '() -> Int' expects 0 arguments, but 1 was used in closure body}} expected-error {{default arguments are not allowed in closures}} {{35-38=}}
let _ : () -> Int = {(_ x : Int) in x+1} // expected-error{{contextual closure type '() -> Int' expects 0 arguments, but 1 was used in closure body}}
}

func func_default_args() {
Expand Down
2 changes: 1 addition & 1 deletion test/expr/expressions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ func funcdecl7(_ a: Int, b: (c: Int, d: Int), third: (c: Int, d: Int)) -> Int {
}

// Error recovery.
func testfunc2 (_: ((), Int) -> Int) -> Int {}
func testfunc2 (_: (((), Int)) -> Int) -> Int {}
func errorRecovery() {
testfunc2({ $0 + 1 }) // expected-error {{binary operator '+' cannot be applied to operands of type '((), Int)' and 'Int'}} expected-note {{expected an argument list of type '(Int, Int)'}}

Expand Down