Skip to content

Commit 83f2eb0

Browse files
authored
Merge pull request #4102 from MnO2/master
2 parents af1a815 + e172383 commit 83f2eb0

File tree

10 files changed

+58
-22
lines changed

10 files changed

+58
-22
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2489,7 +2489,10 @@ ERROR(tuple_pattern_in_non_tuple_context,none,
24892489
"tuple pattern cannot match values of the non-tuple type %0", (Type))
24902490
ERROR(closure_argument_list_tuple,none,
24912491
"contextual closure type %0 expects %1 argument%s1, "
2492-
"but %2 were used in closure body", (Type, unsigned, unsigned))
2492+
"but %2 %select{were|was}3 used in closure body", (Type, unsigned, unsigned, bool))
2493+
ERROR(closure_argument_list_single_tuple,none,
2494+
"contextual closure type specifies %0, but %1 %select{was|were}2 used in closure body, "
2495+
"try adding extra parentheses around the single tuple argument", (Type, unsigned, bool))
24932496
ERROR(closure_argument_list_missing,none,
24942497
"contextual type for closure argument list expects %0 argument%s0, "
24952498
"which cannot be implicitly ignored", (unsigned))

lib/Sema/CSApply.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6464,7 +6464,7 @@ namespace {
64646464
// Coerce the pattern, in case we resolved something.
64656465
auto fnType = closure->getType()->castTo<FunctionType>();
64666466
auto *params = closure->getParameters();
6467-
if (tc.coerceParameterListToType(params, closure, fnType->getInput()))
6467+
if (tc.coerceParameterListToType(params, closure, fnType))
64686468
return { false, nullptr };
64696469

64706470
// If this is a single-expression closure, convert the expression

lib/Sema/CSDiag.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5614,11 +5614,11 @@ bool FailureDiagnosis::visitClosureExpr(ClosureExpr *CE) {
56145614
// don't confuse the issue.
56155615
fnType = FunctionType::get(fnType->getInput(), fnType->getResult());
56165616
diagnose(params->getStartLoc(), diag::closure_argument_list_tuple,
5617-
fnType, inferredArgCount, actualArgCount);
5617+
fnType, inferredArgCount, actualArgCount, (actualArgCount == 1));
56185618
return true;
56195619
}
56205620

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

56245624
expectedResultType = fnType->getResult();

lib/Sema/TypeCheckConstraints.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1523,6 +1523,24 @@ bool TypeChecker::typeCheckExpression(Expr *&expr, DeclContext *dc,
15231523
convertType.getType()->hasUnresolvedType())) {
15241524
convertType = TypeLoc();
15251525
convertTypePurpose = CTP_Unused;
1526+
} else if (auto closure = dyn_cast<ClosureExpr>(expr)) {
1527+
auto *P = closure->getParameters();
1528+
1529+
if (P->size() == 1 && convertType.getType()->is<FunctionType>()) {
1530+
auto hintFnType = convertType.getType()->castTo<FunctionType>();
1531+
auto hintFnInputType = hintFnType->getInput();
1532+
1533+
// Cannot use hintFnInputType->is<TupleType>() since it would desugar ParenType
1534+
if (isa<TupleType>(hintFnInputType.getPointer())) {
1535+
TupleType *tupleTy = hintFnInputType->castTo<TupleType>();
1536+
1537+
if (tupleTy->getNumElements() >= 2) {
1538+
diagnose(P->getStartLoc(), diag::closure_argument_list_single_tuple,
1539+
hintFnInputType, P->size(), P->size() > 1);
1540+
return true;
1541+
}
1542+
}
1543+
}
15261544
}
15271545
}
15281546

lib/Sema/TypeCheckPattern.cpp

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1526,8 +1526,9 @@ bool TypeChecker::coercePatternToType(Pattern *&P, DeclContext *dc, Type type,
15261526
/// TODO: These diagnostics should be a lot better now that we know this is
15271527
/// all specific to closures.
15281528
///
1529-
bool TypeChecker::coerceParameterListToType(ParameterList *P, DeclContext *DC,
1530-
Type paramListType) {
1529+
bool TypeChecker::coerceParameterListToType(ParameterList *P, ClosureExpr *CE,
1530+
AnyFunctionType *FN) {
1531+
Type paramListType = FN->getInput();
15311532
bool hadError = paramListType->is<ErrorType>();
15321533

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

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

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

1568+
// Check if paramListType only contains one single tuple.
1569+
// If it is, then paramListType would be sugared ParenType
1570+
// with a single underlying TupleType. In that case, check if
1571+
// the closure argument is also one to avoid the tuple splat
1572+
// from happening.
1573+
if (!hadError && isa<ParenType>(paramListType.getPointer())) {
1574+
auto underlyingTy = paramListType->getCanonicalType();
1575+
1576+
if (underlyingTy->is<TupleType>()) {
1577+
if (P->size() == 1) {
1578+
return handleParameter(P->get(0), underlyingTy);
1579+
}
1580+
}
1581+
1582+
//pass
1583+
}
15671584

15681585
// The context type must be a tuple.
15691586
TupleType *tupleTy = paramListType->getAs<TupleType>();
@@ -1578,11 +1595,9 @@ bool TypeChecker::coerceParameterListToType(ParameterList *P, DeclContext *DC,
15781595
// The number of elements must match exactly.
15791596
// TODO: incomplete tuple patterns, with some syntax.
15801597
if (!hadError && tupleTy->getNumElements() != P->size()) {
1581-
if (P->size() == 1)
1582-
return handleParameter(P->get(0), paramListType);
1583-
1584-
diagnose(P->getStartLoc(), diag::tuple_pattern_length_mismatch,
1585-
paramListType);
1598+
auto fnType = FunctionType::get(paramListType->getDesugaredType(), FN->getResult());
1599+
diagnose(P->getStartLoc(), diag::closure_argument_list_tuple,
1600+
fnType, tupleTy->getNumElements(), P->size(), (P->size() == 1));
15861601
hadError = true;
15871602
}
15881603

lib/Sema/TypeChecker.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1404,7 +1404,7 @@ class TypeChecker final : public LazyResolver {
14041404
/// contextual type.
14051405
///
14061406
/// \returns true if an error occurred, false otherwise.
1407-
bool coerceParameterListToType(ParameterList *P, DeclContext *dc, Type type);
1407+
bool coerceParameterListToType(ParameterList *P, ClosureExpr *CE, AnyFunctionType *FN);
14081408

14091409

14101410
/// Type-check an initialized variable pattern declaration.

test/Constraints/closures.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ _ = myMap(intArray, { String($0) })
88
_ = myMap(intArray, { x -> String in String(x) } )
99

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

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

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

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

@@ -323,4 +323,4 @@ func r20789423() {
323323

324324
}
325325

326-
326+
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}}

test/expr/closure/closures.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// RUN: %target-parse-verify-swift
22

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

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

7070

7171

test/expr/closure/default_args.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
func simple_default_args() {
44
// <rdar://problem/22753605> QoI: bad diagnostic when closure has default argument
55
let _ : (Int) -> Int = {(x : Int = 1) in x+1} // expected-error{{default arguments are not allowed in closures}} {{36-39=}}
6-
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=}}
7-
let _ : () -> Int = {(_ x : Int) in x+1} // expected-error{{cannot convert value of type '(Int) -> Int' to specified type '() -> Int'}}
6+
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=}}
7+
let _ : () -> Int = {(_ x : Int) in x+1} // expected-error{{contextual closure type '() -> Int' expects 0 arguments, but 1 was used in closure body}}
88
}
99

1010
func func_default_args() {

test/expr/expressions.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ func funcdecl7(_ a: Int, b: (c: Int, d: Int), third: (c: Int, d: Int)) -> Int {
113113
}
114114

115115
// Error recovery.
116-
func testfunc2 (_: ((), Int) -> Int) -> Int {}
116+
func testfunc2 (_: (((), Int)) -> Int) -> Int {}
117117
func errorRecovery() {
118118
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)'}}
119119

0 commit comments

Comments
 (0)