From e172383e2fa5e501d7ff00aac245c52ea0a578d7 Mon Sep 17 00:00:00 2001 From: Paul Meng Date: Fri, 5 Aug 2016 23:12:36 +0800 Subject: [PATCH] [Sema] Implement SE-0110 This commit built upon the work of Pull Request 3895. Apart from the work to make the following work ```swift let f: (Int, Int) -> Void = { x in } // this is now an error ``` This patch also implement the part 2 mentioned in the #3895 ```swift let g: ((Int, Int)) -> Void = { y in } // y should have type (Int, Int) ``` --- include/swift/AST/DiagnosticsSema.def | 5 ++++- lib/Sema/CSApply.cpp | 2 +- lib/Sema/CSDiag.cpp | 4 ++-- lib/Sema/TypeCheckConstraints.cpp | 18 ++++++++++++++++ lib/Sema/TypeCheckPattern.cpp | 31 ++++++++++++++++++++------- lib/Sema/TypeChecker.h | 2 +- test/Constraints/closures.swift | 8 +++---- test/expr/closure/closures.swift | 4 ++-- test/expr/closure/default_args.swift | 4 ++-- test/expr/expressions.swift | 2 +- 10 files changed, 58 insertions(+), 22 deletions(-) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 8e974907dbd12..78f63ec7b80cc 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -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)) diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index fe596c29184b8..affc3218d2c1f 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -6464,7 +6464,7 @@ namespace { // Coerce the pattern, in case we resolved something. auto fnType = closure->getType()->castTo(); 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 diff --git a/lib/Sema/CSDiag.cpp b/lib/Sema/CSDiag.cpp index 4c1001a7195ba..9fc164a453ede 100644 --- a/lib/Sema/CSDiag.cpp +++ b/lib/Sema/CSDiag.cpp @@ -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(); diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index 1141990321804..ef25419234fe3 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -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(expr)) { + auto *P = closure->getParameters(); + + if (P->size() == 1 && convertType.getType()->is()) { + auto hintFnType = convertType.getType()->castTo(); + auto hintFnInputType = hintFnType->getInput(); + + // Cannot use hintFnInputType->is() since it would desugar ParenType + if (isa(hintFnInputType.getPointer())) { + TupleType *tupleTy = hintFnInputType->castTo(); + + if (tupleTy->getNumElements() >= 2) { + diagnose(P->getStartLoc(), diag::closure_argument_list_single_tuple, + hintFnInputType, P->size(), P->size() > 1); + return true; + } + } + } } } diff --git a/lib/Sema/TypeCheckPattern.cpp b/lib/Sema/TypeCheckPattern.cpp index 91bb12d6f0f18..a313144fce962 100644 --- a/lib/Sema/TypeCheckPattern.cpp +++ b/lib/Sema/TypeCheckPattern.cpp @@ -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(); // Sometimes a scalar type gets applied to a single-argument parameter list. @@ -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 @@ -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(paramListType.getPointer())) { + auto underlyingTy = paramListType->getCanonicalType(); + + if (underlyingTy->is()) { + if (P->size() == 1) { + return handleParameter(P->get(0), underlyingTy); + } + } + + //pass + } // The context type must be a tuple. TupleType *tupleTy = paramListType->getAs(); @@ -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; } diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index db8064b348053..b0163abfc0955 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -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. diff --git a/test/Constraints/closures.swift b/test/Constraints/closures.swift index a5f9b2ce320a4..afdb6ffd37792 100644 --- a/test/Constraints/closures.swift +++ b/test/Constraints/closures.swift @@ -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 {} @@ -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} @@ -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}} } @@ -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}} diff --git a/test/expr/closure/closures.swift b/test/expr/closure/closures.swift index 1769beb98494b..f738b08a6b7e4 100644 --- a/test/expr/closure/closures.swift +++ b/test/expr/closure/closures.swift @@ -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}} @@ -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'}} diff --git a/test/expr/closure/default_args.swift b/test/expr/closure/default_args.swift index 4297ac66505fa..8211d4180201e 100644 --- a/test/expr/closure/default_args.swift +++ b/test/expr/closure/default_args.swift @@ -3,8 +3,8 @@ func simple_default_args() { // 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() { diff --git a/test/expr/expressions.swift b/test/expr/expressions.swift index f60462b1c40b0..697efe007a795 100644 --- a/test/expr/expressions.swift +++ b/test/expr/expressions.swift @@ -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)'}}