From 9d5ee7a081768410c117c48b21d774626a61bb49 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Mon, 7 Mar 2022 10:51:38 -0800 Subject: [PATCH 1/6] [ConstraintLocator] Add a new element - pattern binding element The new element points to an index of a particular pattern in a pattern binding declaration. --- include/swift/Sema/ConstraintLocator.h | 13 +++++++++++++ include/swift/Sema/ConstraintLocatorPathElts.def | 3 +++ lib/Sema/ConstraintLocator.cpp | 9 +++++++++ 3 files changed, 25 insertions(+) diff --git a/include/swift/Sema/ConstraintLocator.h b/include/swift/Sema/ConstraintLocator.h index d5996d53aea09..5c9adad004409 100644 --- a/include/swift/Sema/ConstraintLocator.h +++ b/include/swift/Sema/ConstraintLocator.h @@ -1058,6 +1058,19 @@ class LocatorPathElt::ClosureBodyElement final } }; +class LocatorPathElt::PatternBindingElement final + : public StoredIntegerElement<1> { +public: + PatternBindingElement(unsigned index) + : StoredIntegerElement(ConstraintLocator::PatternBindingElement, index) {} + + unsigned getIndex() const { return getValue(); } + + static bool classof(const LocatorPathElt *elt) { + return elt->getKind() == ConstraintLocator::PatternBindingElement; + } +}; + namespace details { template class PathElement { diff --git a/include/swift/Sema/ConstraintLocatorPathElts.def b/include/swift/Sema/ConstraintLocatorPathElts.def index 35c819d0035d8..0d9d8b4f5ceaa 100644 --- a/include/swift/Sema/ConstraintLocatorPathElts.def +++ b/include/swift/Sema/ConstraintLocatorPathElts.def @@ -232,6 +232,9 @@ SIMPLE_LOCATOR_PATH_ELT(ImplicitDynamicMemberSubscript) /// The element of the closure body e.g. statement, declaration, or expression. CUSTOM_LOCATOR_PATH_ELT(ClosureBodyElement) +/// The element of the pattern binding declaration. +CUSTOM_LOCATOR_PATH_ELT(PatternBindingElement) + #undef LOCATOR_PATH_ELT #undef CUSTOM_LOCATOR_PATH_ELT #undef SIMPLE_LOCATOR_PATH_ELT diff --git a/lib/Sema/ConstraintLocator.cpp b/lib/Sema/ConstraintLocator.cpp index 215cee2c1da58..c5d8de2184214 100644 --- a/lib/Sema/ConstraintLocator.cpp +++ b/lib/Sema/ConstraintLocator.cpp @@ -96,6 +96,7 @@ unsigned LocatorPathElt::getNewSummaryFlags() const { case ConstraintLocator::ClosureBodyElement: case ConstraintLocator::PackType: case ConstraintLocator::PackElement: + case ConstraintLocator::PatternBindingElement: return 0; case ConstraintLocator::FunctionArgument: @@ -578,6 +579,14 @@ void ConstraintLocator::dump(SourceManager *sm, raw_ostream &out) const { out << "pack element #" << llvm::utostr(packElt.getIndex()); break; } + + case PatternBindingElement: { + auto patternBindingElt = + elt.castTo(); + out << "pattern binding element #" + << llvm::utostr(patternBindingElt.getIndex()); + break; + } } } out << ']'; From 966f58f0440585604031a2ac804bc8bae62c4b75 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Mon, 7 Mar 2022 18:20:03 -0800 Subject: [PATCH 2/6] [Tests] NFC: Adjust all the test-cases improved by multi-statement inference --- test/Constraints/closures.swift | 17 +++++++------- test/Constraints/diagnostics.swift | 4 ++-- test/Constraints/members.swift | 14 +++++++++--- test/Constraints/patterns.swift | 4 ++-- test/Constraints/rdar46544601.swift | 2 +- test/Constraints/rdar65320500.swift | 8 +++---- test/Constraints/result_builder_diags.swift | 21 +++--------------- test/Constraints/tuple.swift | 6 ++--- test/Constraints/tuple_arguments.swift | 22 ++++++++++++++++--- .../without_actually_escaping.swift | 2 +- test/Sema/diag_ambiguous_overloads.swift | 16 ++++++-------- .../pretty-printed-diagnostics.swift | 13 ----------- test/expr/closure/anonymous.swift | 4 ---- test/expr/closure/closures.swift | 12 +++++----- test/expr/closure/inference.swift | 7 +++--- test/expr/closure/let.swift | 4 ++-- test/expr/expressions.swift | 4 +--- test/expr/unary/keypath/keypath.swift | 2 -- test/stmt/statements.swift | 9 +++++++- .../0119-rdar33613329.swift | 5 ++--- 20 files changed, 82 insertions(+), 94 deletions(-) diff --git a/test/Constraints/closures.swift b/test/Constraints/closures.swift index 42616c02e36bc..cb4a32e5aa3e4 100644 --- a/test/Constraints/closures.swift +++ b/test/Constraints/closures.swift @@ -252,7 +252,7 @@ struct CC {} func callCC(_ f: (CC) -> U) -> () {} func typeCheckMultiStmtClosureCrash() { - callCC { // expected-error {{cannot infer return type for closure with multiple statements; add explicit type to disambiguate}} {{none}} + callCC { _ = $0 return 1 } @@ -312,9 +312,8 @@ func testAcceptNothingToInt(ac1: @autoclosure () -> Int) { struct Thing { init?() {} } -// This throws a compiler error -let things = Thing().map { thing in // expected-error {{cannot infer return type for closure with multiple statements; add explicit type to disambiguate}} {{34-34=-> <#Result#> }} - // Commenting out this makes it compile + +let things = Thing().map { thing in _ = thing return thing } @@ -322,14 +321,14 @@ let things = Thing().map { thing in // expected-error {{cannot infer return typ // QoI: [Closure return type inference] Swift cannot find members for the result of inlined lambdas with branches func r21675896(file : String) { - let x: String = { // expected-error {{cannot infer return type for closure with multiple statements; add explicit type to disambiguate}} {{20-20= () -> <#Result#> in }} + let x: String = { if true { return "foo" } else { return file } - }().pathExtension + }().pathExtension // expected-error {{value of type 'String' has no member 'pathExtension'}} } @@ -360,7 +359,7 @@ func someGeneric19997471(_ x: T) { // Swift fails to compile: [0].map() { _ in let r = (1,2).0; return r } -[0].map { // expected-error {{cannot infer return type for closure with multiple statements; add explicit type to disambiguate}} {{5-5=-> <#Result#> }} +let _ = [0].map { _ in let r = (1,2).0 return r @@ -408,7 +407,7 @@ func r20789423() { print(p.f(p)()) // expected-error {{cannot convert value of type 'C' to expected argument type 'Int'}} // expected-error@-1:11 {{cannot call value of non-function type '()'}} - let _f = { (v: Int) in // expected-error {{cannot infer return type for closure with multiple statements; add explicit type to disambiguate}} {{23-23=-> <#Result#> }} + let _f = { (v: Int) in print("a") return "hi" } @@ -1127,7 +1126,7 @@ func rdar76058892() { func experiment(arr: [S]?) { test { // expected-error {{contextual closure type '() -> String' expects 0 arguments, but 1 was used in closure body}} if let arr = arr { - arr.map($0.test) // expected-note {{anonymous closure parameter '$0' is used here}} + arr.map($0.test) // expected-note {{anonymous closure parameter '$0' is used here}} // expected-error {{generic parameter 'T' could not be inferred}} } } } diff --git a/test/Constraints/diagnostics.swift b/test/Constraints/diagnostics.swift index 6b7aa27c3f2f7..2ebd3e52c8a1d 100644 --- a/test/Constraints/diagnostics.swift +++ b/test/Constraints/diagnostics.swift @@ -148,7 +148,7 @@ func ***~(_: Int, _: String) { } i ***~ i // expected-error{{cannot convert value of type 'Int' to expected argument type 'String'}} @available(*, unavailable, message: "call the 'map()' method on the sequence") -public func myMap( +public func myMap( // expected-note {{'myMap' has been explicitly marked unavailable here}} _ source: C, _ transform: (C.Iterator.Element) -> T ) -> [T] { fatalError("unavailable function can't be called") @@ -161,7 +161,7 @@ public func myMap(_ x: T?, _ f: (T) -> U) -> U? { // func rdar20142523() { - myMap(0..<10, { x in // expected-error{{cannot infer return type for closure with multiple statements; add explicit type to disambiguate}} {{21-21=-> <#Result#> }} {{educational-notes=complex-closure-inference}} + _ = myMap(0..<10, { x in // expected-error {{'myMap' is unavailable: call the 'map()' method on the sequence}} () return x }) diff --git a/test/Constraints/members.swift b/test/Constraints/members.swift index 61e58d891c9b5..5dda70ee0d26b 100644 --- a/test/Constraints/members.swift +++ b/test/Constraints/members.swift @@ -595,10 +595,10 @@ func rdar50679161() { func foo() { _ = { () -> Void in + // Missing `.self` or `init` is not diagnosed here because there are errors in + // `if let` statement and `MiscDiagnostics` only run if the body is completely valid. var foo = S - // expected-error@-1 {{expected member name or constructor call after type name}} - // expected-note@-2 {{add arguments after the type to construct a value of the type}} - // expected-note@-3 {{use '.self' to reference the type object}} + if let v = Int?(1) { var _ = Q( a: v + foo.w, @@ -610,6 +610,14 @@ func rdar50679161() { ) } } + + _ = { () -> Void in + var foo = S + // expected-error@-1 {{expected member name or constructor call after type name}} + // expected-note@-2 {{add arguments after the type to construct a value of the type}} + // expected-note@-3 {{use '.self' to reference the type object}} + print(foo) + } } } diff --git a/test/Constraints/patterns.swift b/test/Constraints/patterns.swift index 40577ded3e629..0dd5c677991e2 100644 --- a/test/Constraints/patterns.swift +++ b/test/Constraints/patterns.swift @@ -230,14 +230,14 @@ func good(_ a: A) -> Int { } func bad(_ a: A) { - a.map { // expected-error {{cannot infer return type for closure with multiple statements; add explicit type to disambiguate}} {{none}} + let _ = a.map { let _: EE = $0 return 1 } } func ugly(_ a: A) { - a.map { // expected-error {{cannot infer return type for closure with multiple statements; add explicit type to disambiguate}} {{none}} + let _ = a.map { switch $0 { case .A: return 1 diff --git a/test/Constraints/rdar46544601.swift b/test/Constraints/rdar46544601.swift index e633bda722ee2..28e5882d6772e 100644 --- a/test/Constraints/rdar46544601.swift +++ b/test/Constraints/rdar46544601.swift @@ -26,6 +26,6 @@ func crash(_ p: P, payload: [UInt8]) throws { p.foo(arr: arr, data: []).and(result: (id, arr)) }.then { args0 in let (parentID, args1) = args0 - p.bar(root: parentID, from: p).and(args1) + p.bar(root: parentID, from: p).and(result: args1) }.whenFailure { _ in } } diff --git a/test/Constraints/rdar65320500.swift b/test/Constraints/rdar65320500.swift index 25b08ac7aad5e..2d9dc9f9311a4 100644 --- a/test/Constraints/rdar65320500.swift +++ b/test/Constraints/rdar65320500.swift @@ -31,13 +31,13 @@ test_builder { test_builder { test(doesntExist()) // expected-error {{cannot find 'doesntExist' in scope}} - if let result = doesntExist() { + if let result = doesntExist() { // expected-error {{cannot find 'doesntExist' in scope}} } - if bar = test(42) {} + if bar = test(42) {} // expected-error {{cannot find 'bar' in scope}} - let foo = bar() + let foo = bar() // expected-error {{cannot find 'bar' in scope}} - switch (doesntExist()) { + switch (doesntExist()) { // expected-error {{cannot find 'doesntExist' in scope}} } } diff --git a/test/Constraints/result_builder_diags.swift b/test/Constraints/result_builder_diags.swift index ce189a976d472..8f91ff8294032 100644 --- a/test/Constraints/result_builder_diags.swift +++ b/test/Constraints/result_builder_diags.swift @@ -78,7 +78,7 @@ struct TupleBuilderWithoutIf { // expected-note 3{{struct 'TupleBuilderWithoutIf static func buildDo(_ value: T) -> T { return value } } -func tuplify(_ cond: Bool, @TupleBuilder body: (Bool) -> T) { +func tuplify(_ cond: Bool, @TupleBuilder body: (Bool) -> T) { // expected-note {{in call to function 'tuplify(_:body:)'}} print(body(cond)) } @@ -307,21 +307,6 @@ struct MyTuplifiedStruct { } } -func test_invalid_return_type_in_body() { - tuplify(true) { _ -> (Void, Int) in - tuplify(false) { condition in - if condition { - return 42 // expected-error {{cannot use explicit 'return' statement in the body of result builder 'TupleBuilder'}} - // expected-note@-1 {{remove 'return' statements to apply the result builder}} {{9-16=}} - } else { - 1 - } - } - - 42 - } -} - // Check that we're performing syntactic use diagnostics. func acceptMetatype(_: T.Type) -> Bool { true } @@ -481,7 +466,7 @@ struct TestConstraintGenerationErrors { func buildTupleClosure() { tuplify(true) { _ in let a = nothing // expected-error {{cannot find 'nothing' in scope}} - String(nothing) + String(nothing) // expected-error {{cannot find 'nothing' in scope}} } } } @@ -522,7 +507,7 @@ enum E3 { } func testCaseMutabilityMismatches(e: E3) { - tuplify(true) { c in + tuplify(true) { c in // expected-error {{generic parameter 'T' could not be inferred}} "testSwitch" switch e { case .a(let x, var y), diff --git a/test/Constraints/tuple.swift b/test/Constraints/tuple.swift index d89ee2b5d1251..5c2965e6b4b2b 100644 --- a/test/Constraints/tuple.swift +++ b/test/Constraints/tuple.swift @@ -218,14 +218,14 @@ extension r25271859 { func map(f: (T) -> U) -> r25271859 { } - func andThen(f: (T) -> r25271859) { // expected-note {{in call to function 'andThen(f:)'}} + func andThen(f: (T) -> r25271859) { } } func f(a : r25271859<(Float, Int)>) { - a.map { $0.0 } // expected-error {{generic parameter 'U' could not be inferred}} (This is related to how solver is setup with multiple statements) + a.map { $0.0 } .andThen { _ in - print("hello") // comment this out and it runs, leave any form of print in and it doesn't + print("hello") return r25271859() } } diff --git a/test/Constraints/tuple_arguments.swift b/test/Constraints/tuple_arguments.swift index ec14a257c96aa..a568be0bc5206 100644 --- a/test/Constraints/tuple_arguments.swift +++ b/test/Constraints/tuple_arguments.swift @@ -1407,25 +1407,41 @@ func processArrayOfFunctions(f1: [((Bool, Bool)) -> ()], } f2.forEach { block in - // expected-note@-1 2{{'block' declared here}} + // expected-note@-1 {{'block' declared here}} block(p) // expected-error {{parameter 'block' expects 2 separate arguments}} + } + + f2.forEach { block in + // expected-note@-1 {{'block' declared here}} block((c, c)) // expected-error {{parameter 'block' expects 2 separate arguments; remove extra parentheses to change tuple into separate arguments}} {{11-12=}} {{16-17=}} block(c, c) } + f2.forEach { block in + block(c, c) + } + f2.forEach { (block: ((Bool, Bool)) -> ()) in // expected-error@-1 {{cannot convert value of type '(((Bool, Bool)) -> ()) -> Void' to expected argument type '(@escaping (Bool, Bool) -> ()) throws -> Void'}} block(p) block((c, c)) - block(c, c) + block(c, c) // expected-error {{parameter 'block' expects a single parameter of type '(Bool, Bool)'}} } f2.forEach { (block: (Bool, Bool) -> ()) in - // expected-note@-1 2{{'block' declared here}} + // expected-note@-1 {{'block' declared here}} block(p) // expected-error {{parameter 'block' expects 2 separate arguments}} + } + + f2.forEach { (block: (Bool, Bool) -> ()) in + // expected-note@-1 {{'block' declared here}} block((c, c)) // expected-error {{parameter 'block' expects 2 separate arguments; remove extra parentheses to change tuple into separate arguments}} {{11-12=}} {{16-17=}} block(c, c) } + + f2.forEach { (block: (Bool, Bool) -> ()) in + block(c, c) + } } // expected-error@+1 {{cannot create a single-element tuple with an element label}} diff --git a/test/Constraints/without_actually_escaping.swift b/test/Constraints/without_actually_escaping.swift index 12752c08e8c2e..d6ce858c24801 100644 --- a/test/Constraints/without_actually_escaping.swift +++ b/test/Constraints/without_actually_escaping.swift @@ -10,7 +10,7 @@ func escapeX(_ xx: (Int) -> Int, _ value: Int) { // expected-note* {{non-escapin withoutActuallyEscaping(xx) { escapableXX in x = xx // expected-error{{non-escaping parameter}} x = escapableXX - x = xx // expected-error{{non-escaping parameter}} + x = xx _ = x(value) _ = xx(value) diff --git a/test/Sema/diag_ambiguous_overloads.swift b/test/Sema/diag_ambiguous_overloads.swift index 1aa56b1b0bece..e351f3885d935 100644 --- a/test/Sema/diag_ambiguous_overloads.swift +++ b/test/Sema/diag_ambiguous_overloads.swift @@ -132,15 +132,13 @@ func SR12689(_ u: UnsafeBufferPointer) {} let array : [UInt16] = [1, 2] array.withUnsafeBufferPointer { - SR12689(UnsafeRawPointer($0).bindMemory(to: UInt16.self, capacity: 1)) // expected-error {{cannot convert value of type 'UnsafePointer' to expected argument type 'UnsafeBufferPointer'}} - // expected-error@-1 {{no exact matches in call to initializer}} - // expected-note@-2 {{candidate expects value of type 'UnsafeRawPointer' for parameter #1}} - // expected-note@-3 {{candidate expects value of type 'UnsafeMutableRawPointer' for parameter #1}} - - UnsafeRawPointer($0) as UnsafeBufferPointer // expected-error {{cannot convert value of type 'UnsafeRawPointer' to type 'UnsafeBufferPointer' in coercion}} - // expected-error@-1 {{no exact matches in call to initializer}} - // expected-note@-2 {{found candidate with type '(UnsafeRawPointer) -> UnsafeRawPointer'}} - // expected-note@-3 {{found candidate with type '(UnsafeMutableRawPointer) -> UnsafeRawPointer'}} + _ = SR12689(UnsafeRawPointer($0).bindMemory(to: UInt16.self, capacity: 1)) // expected-error {{cannot convert value of type 'UnsafePointer' to expected argument type 'UnsafeBufferPointer'}} + // expected-error@-1 {{cannot convert value of type 'UnsafeBufferPointer' to expected argument type 'UnsafeMutableRawPointer'}} +} + +array.withUnsafeBufferPointer { + _ = UnsafeRawPointer($0) as UnsafeBufferPointer // expected-error {{cannot convert value of type 'UnsafeRawPointer' to type 'UnsafeBufferPointer' in coercion}} + // expected-error@-1 {{cannot convert value of type 'UnsafeBufferPointer' to expected argument type 'UnsafeMutableRawPointer'}} } func SR12689_1(_ u: Int) -> String { "" } // expected-note {{found this candidate}} expected-note {{candidate expects value of type 'Int' for parameter #1 (got 'Double')}} diff --git a/test/diagnostics/pretty-printed-diagnostics.swift b/test/diagnostics/pretty-printed-diagnostics.swift index b110ecfa45c23..6ff1e65045cd1 100644 --- a/test/diagnostics/pretty-printed-diagnostics.swift +++ b/test/diagnostics/pretty-printed-diagnostics.swift @@ -124,13 +124,6 @@ foo(b: // CHECK: | ^ note: Remove '=' to make 'x' a computed property [remove '= ' and replace 'let' with 'var'] // CHECK: [[#LINE+1]] | } -// CHECK: SOURCE_DIR{{[/\]+}}test{{[/\]+}}diagnostics{{[/\]+}}pretty-printed-diagnostics.swift:[[#LINE:]]:9 -// CHECK: [[#LINE-1]] | -// CHECK: [[#LINE]] | let x = { () -> Result in -// CHECK: | +++++++++++++++++ -// CHECK: | ^ error: cannot infer return type for closure with multiple statements; add explicit type to disambiguate -// CHECK: [[#LINE+1]] | let y = 1 - // CHECK: SOURCE_DIR{{[/\]+}}test{{[/\]+}}diagnostics{{[/\]+}}pretty-printed-diagnostics.swift:[[#LINE:]]:8 // CHECK: [[#LINE-1]] | // CHECK: [[#LINE]] | struct B: Decodable { @@ -149,12 +142,6 @@ foo(b: // CHECK: | ^ error: argument 'a' must precede argument 'b' [remove ', a: 2' and insert 'a: 2, '] // CHECK: [[#LINE+1]] | -// CHECK: SOURCE_DIR{{[/\]+}}test{{[/\]+}}diagnostics{{[/\]+}}pretty-printed-diagnostics.swift:[[#LINE:]]:20 -// CHECK: [[#LINE-1]] | -// CHECK: [[#LINE]] | let 👍👍👍 = { -// CHECK: | --> error: cannot infer return type for closure with multiple statements; add explicit type to disambiguate [insert ' () -> <#Result#> in '] -// CHECK: [[#LINE+1]] | let y = 1 - // CHECK: SOURCE_DIR{{[/\]+}}test{{[/\]+}}diagnostics{{[/\]+}}pretty-printed-diagnostics.swift:[[#LINE:]]:5 // CHECK: [[#LINE-2]] | // Multi-line fix-its // CHECK: [[#LINE-1]] | foo(a: 2, b: 1, diff --git a/test/expr/closure/anonymous.swift b/test/expr/closure/anonymous.swift index 3a9caf47f7537..e5de36e8d2412 100644 --- a/test/expr/closure/anonymous.swift +++ b/test/expr/closure/anonymous.swift @@ -29,11 +29,7 @@ func variadic() { takesVariadicGeneric({takesIntArray($0)}) - // FIXME: Problem here is related to multi-statement closure body not being type-checked together with - // enclosing context. We could have inferred `$0` to be `[Int]` if `let` was a part of constraint system. takesVariadicGeneric({let _: [Int] = $0}) - // expected-error@-1 {{unable to infer type of a closure parameter '$0' in the current context}} - takesVariadicIntInt({_ = $0; takesIntArray($1)}) takesVariadicIntInt({_ = $0; let _: [Int] = $1}) } diff --git a/test/expr/closure/closures.swift b/test/expr/closure/closures.swift index 49508cc7a8fd2..658f5f3586bf3 100644 --- a/test/expr/closure/closures.swift +++ b/test/expr/closure/closures.swift @@ -120,9 +120,8 @@ var selfRef = { selfRef() } // expected-note@-1 2{{through reference here}} // expected-error@-2 {{circular reference}} -var nestedSelfRef = { +var nestedSelfRef = { // expected-error {{circular reference}} expected-note 2 {{through reference here}} var recursive = { nestedSelfRef() } - // expected-warning@-1 {{variable 'recursive' was never mutated; consider changing to 'let' constant}} recursive() } @@ -140,12 +139,11 @@ func anonymousClosureArgsInClosureWithArgs() { var a3 = { (z: Int) in $0 } // expected-error {{anonymous closure arguments cannot be used inside a closure that has explicit arguments; did you mean 'z'?}} {{26-28=z}} var a4 = { (z: [Int], w: [Int]) in f($0.count) // expected-error {{anonymous closure arguments cannot be used inside a closure that has explicit arguments; did you mean 'z'?}} {{7-9=z}} expected-error {{cannot convert value of type 'Int' to expected argument type 'String'}} - f($1.count) // expected-error {{anonymous closure arguments cannot be used inside a closure that has explicit arguments; did you mean 'w'?}} {{7-9=w}} expected-error {{cannot convert value of type 'Int' to expected argument type 'String'}} + f($1.count) // expected-error {{anonymous closure arguments cannot be used inside a closure that has explicit arguments; did you mean 'w'?}} {{7-9=w}} } var a5 = { (_: [Int], w: [Int]) in f($0.count) // expected-error {{anonymous closure arguments cannot be used inside a closure that has explicit arguments}} f($1.count) // expected-error {{anonymous closure arguments cannot be used inside a closure that has explicit arguments; did you mean 'w'?}} {{7-9=w}} - // expected-error@-1 {{cannot convert value of type 'Int' to expected argument type 'String'}} } } @@ -403,7 +401,7 @@ Void(0) // expected-error{{argument passed to call that takes no arguments}} _ = {0} // "multi-statement closures require an explicit return type" should be an error not a note -let samples = { // expected-error {{cannot infer return type for closure with multiple statements; add explicit type to disambiguate}} {{16-16= () -> <#Result#> in }} +let samples = { if (i > 10) { return true } else { return false } }() @@ -485,8 +483,8 @@ func lvalueCapture(c: GenericClass) { } // Don't expose @lvalue-ness in diagnostics. -let closure = { // expected-error {{cannot infer return type for closure with multiple statements; add explicit type to disambiguate}} {{16-16= () -> <#Result#> in }} - var helper = true +let closure = { + var helper = true // expected-warning {{variable 'helper' was never mutated; consider changing to 'let' constant}} return helper } diff --git a/test/expr/closure/inference.swift b/test/expr/closure/inference.swift index b1b536c53991f..0e9ac2a943f4b 100644 --- a/test/expr/closure/inference.swift +++ b/test/expr/closure/inference.swift @@ -34,10 +34,9 @@ func unnamed() { // Regression tests. var nestedClosuresWithBrokenInference = { f: Int in {} } - // expected-error@-1 {{closure expression is unused}} expected-note@-1 {{did you mean to use a 'do' statement?}} {{53-53=do }} - // expected-error@-2 {{consecutive statements on a line must be separated by ';'}} {{44-44=;}} - // expected-error@-3 {{expected expression}} - // expected-error@-4 {{cannot find 'f' in scope}} + // expected-error@-1 {{consecutive statements on a line must be separated by ';'}} {{44-44=;}} + // expected-error@-2 {{expected expression}} + // expected-error@-3 {{cannot find 'f' in scope}} // SR-11540 diff --git a/test/expr/closure/let.swift b/test/expr/closure/let.swift index 176629f7c9517..4d4bfdfd86846 100644 --- a/test/expr/closure/let.swift +++ b/test/expr/closure/let.swift @@ -9,11 +9,11 @@ func foo() { _ = { frob(x: x) }() // expected-error{{'x' is a 'let'}} _ = { x = 0 }() // expected-error{{'x' is a 'let'}} - _ = { frob(x: x); x = 0 }() // expected-error 2 {{'x' is a 'let'}} + _ = { frob(x: x); x = 0 }() // expected-error {{'x' is a 'let'}} } let a: Int { 1 } // expected-error{{'let' declarations cannot be computed properties}} let b: Int = 1 -{ didSet { print("didSet") } } // expected-error{{'let' declarations cannot be observing properties}} \ No newline at end of file +{ didSet { print("didSet") } } // expected-error{{'let' declarations cannot be observing properties}} diff --git a/test/expr/expressions.swift b/test/expr/expressions.swift index 78a5897cbbfce..1c2bb47a4ee4d 100644 --- a/test/expr/expressions.swift +++ b/test/expr/expressions.swift @@ -246,11 +246,9 @@ func test_as_2() { func test_lambda() { // A simple closure. var a = { (value: Int) -> () in markUsed(value+1) } - // expected-warning@-1 {{initialization of variable 'a' was never used; consider replacing with assignment to '_' or removing it}} // A recursive lambda. - var fib = { (n: Int) -> Int in - // expected-warning@-1 {{variable 'fib' was never mutated; consider changing to 'let' constant}} + var fib = { (n: Int) -> Int in // expected-error {{circular reference}} expected-note 2 {{through reference here}} if (n < 2) { return n } diff --git a/test/expr/unary/keypath/keypath.swift b/test/expr/unary/keypath/keypath.swift index f50823e037828..ab9f6ea21afae 100644 --- a/test/expr/unary/keypath/keypath.swift +++ b/test/expr/unary/keypath/keypath.swift @@ -1093,8 +1093,6 @@ func rdar74711236() { // `isSupported` should be an invalid declaration to trigger a crash in `map(\.option)` let isSupported = context!.supported().contains(type) return (isSupported ? [type] : []).map(\.option) - // expected-error@-1 {{value of type 'Any' has no member 'option'}} - // expected-note@-2 {{cast 'Any' to 'AnyObject' or use 'as!' to force downcast to a more specific type to access members}} } return [] }() diff --git a/test/stmt/statements.swift b/test/stmt/statements.swift index 1f05959eb4448..c3348cce6ffc9 100644 --- a/test/stmt/statements.swift +++ b/test/stmt/statements.swift @@ -400,13 +400,20 @@ func test_is_as_patterns() { } // Fuzzing SourceKit: crash in Parser::parseStmtForEach(...) -func matching_pattern_recursion() { +func matching_pattern_recursion(zs: [Int]) { // expected-note {{'zs' declared here}} switch 42 { case { // expected-error {{expression pattern of type '() -> ()' cannot match values of type 'Int'}} for i in zs { } }: break } + + switch 42 { + case { + for i in ws { // expected-error {{cannot find 'ws' in scope; did you mean 'zs'?}} + } + }: break + } } // Swift's break operator in switch should be indicated in errors diff --git a/validation-test/compiler_crashers_2_fixed/0119-rdar33613329.swift b/validation-test/compiler_crashers_2_fixed/0119-rdar33613329.swift index 176928ec6781b..36d9906941ced 100644 --- a/validation-test/compiler_crashers_2_fixed/0119-rdar33613329.swift +++ b/validation-test/compiler_crashers_2_fixed/0119-rdar33613329.swift @@ -17,7 +17,7 @@ struct M { } } -protocol P { // expected-note {{where 'Self' = 'M, R>'}} +protocol P { // expected-note {{where 'Self' = 'M, Int>'}} associatedtype A associatedtype B @@ -42,5 +42,4 @@ extension WritableKeyPath : P { struct X { var y: Int = 0 } var x = X() x ~> \X.y ≈> { a in a += 1; return 3 } -// expected-error@-1 {{referencing operator function '~>' on 'P' requires that 'M, R>' conform to 'P'}} -// expected-error@-2 {{cannot infer return type for closure with multiple statements; add explicit type to disambiguate}} +//expected-error@-1 {{referencing operator function '~>' on 'P' requires that 'M, Int>' conform to 'P'}} From a5cff5afb56d257f1a6820583b9d50e96c8c436f Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Tue, 8 Mar 2022 11:33:27 -0800 Subject: [PATCH 3/6] [CSGen] Always record a type of named pattern declaration This is going to be used by multi-statement closure inference because patterns could be declarated and referenced in the body via the associated declaration. --- lib/Sema/CSGen.cpp | 66 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 64 insertions(+), 2 deletions(-) diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index 9d292b284a53b..6249f3bff491c 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -2299,9 +2299,71 @@ namespace { locator); } - // If we have a type to ascribe to the variable, do so now. - if (oneWayVarType) + // Ascribe a type to the declaration so it's always available to + // constraint system. + if (oneWayVarType) { CS.setType(var, oneWayVarType); + } else if (externalPatternType) { + // If there is an externally imposed type, that's what the + // declaration is going to be bound to. + CS.setType(var, externalPatternType); + } else { + // Otherwise, let's use the type of the pattern. The type + // of the declaration has to be r-value, so let's add an + // equality constraint if pattern type has any type variables + // that are allowed to be l-value. + bool foundLValueVars = false; + + // Note that it wouldn't be always correct to allocate a single type + // variable, that disallows l-value types, to use as a declaration + // type because equality constraint would drop TVO_CanBindToLValue + // from the right-hand side (which is not the case for `OneWayEqual`) + // e.g.: + // + // sturct S { var x, y: Int } + // + // func test(s: S) { + // let (x, y) = (s.x, s.y) + // } + // + // Single type variable approach results in the following constraint: + // `$T_x_y = ($T_s_x, $T_s_y)` where both `$T_s_x` and `$T_s_y` have + // to allow l-value, but `$T_x_y` does not. Early simplication of `=` + // constraint (due to right-hand side being a "concrete" tuple type) + // would drop l-value option from `$T_s_x` and `$T_s_y` which leads to + // a failure during member lookup because `x` and `y` are both + // `@lvalue Int`. To avoid that, declaration type would mimic pattern + // type with all l-value options stripped, so the equality constraint + // becomes `($T_x, $_T_y) = ($T_s_x, $T_s_y)` which doesn't result in + // stripping of l-value flag from the right-hand side since + // simplification can only happen when either side is resolved. + auto declTy = varType.transform([&](Type type) -> Type { + if (auto *typeVar = type->getAs()) { + if (typeVar->getImpl().canBindToLValue()) { + foundLValueVars = true; + + // Drop l-value from the options but preserve the rest. + auto options = typeVar->getImpl().getRawOptions(); + options &= ~TVO_CanBindToLValue; + + return CS.createTypeVariable(typeVar->getImpl().getLocator(), + options); + } + } + return type; + }); + + // If pattern types allows l-value types, let's create an + // equality constraint between r-value only declaration type + // and l-value pattern type that would take care of looking + // through l-values when necessary. + if (foundLValueVars) { + CS.addConstraint(ConstraintKind::Equal, declTy, varType, + CS.getConstraintLocator(locator)); + } + + CS.setType(var, declTy); + } return setType(varType); } From ceb78c091c870d4f303214d3c66ba3f3b0caee4a Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Tue, 8 Mar 2022 11:35:42 -0800 Subject: [PATCH 4/6] [Constraint] NFC: Improve debug output of pattern binding element constraints --- lib/Sema/Constraint.cpp | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/lib/Sema/Constraint.cpp b/lib/Sema/Constraint.cpp index 1001ab23be28e..1dcef91a86354 100644 --- a/lib/Sema/Constraint.cpp +++ b/lib/Sema/Constraint.cpp @@ -388,8 +388,21 @@ void Constraint::print(llvm::raw_ostream &Out, SourceManager *sm) const { } if (Kind == ConstraintKind::ClosureBodyElement) { - Out << "closure body element "; - getClosureElement().dump(Out); + auto *locator = getLocator(); + auto element = getClosureElement(); + + if (auto patternBindingElt = + locator + ->getLastElementAs()) { + auto *patternBinding = cast(element.get()); + Out << "pattern binding element @ "; + Out << patternBindingElt->getIndex() << " : "; + patternBinding->getPattern(patternBindingElt->getIndex())->dump(Out); + } else { + Out << "closure body element "; + getClosureElement().dump(Out); + } + return; } From 5c3fb222e1397b7a0b939df6994ad9a15847f8c4 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Tue, 8 Mar 2022 11:52:16 -0800 Subject: [PATCH 5/6] [CSClosure] Explode pattern binding declarations into conjunctions `one-way` constraints disable some optimizations related to component selection because they imply strict ordering. This is a problem for multi-statement closures because variable declarations could involve complex operator expressions that rely on aforementioned optimizations. In order to fix that, let's move away from solving whole pattern binding declaration into scheme that explodes such declarations into indvidual elements and inlines them into a conjunction. For example: ``` let x = 42, y = x + 1, z = (x, test()) ``` Would result in a conjunction of three elements: ``` x = 42 y = x + 1 z = (x, test()) ``` Each element is solved indepedently, which eliminates the need for `one-way` constraints and re-enables component selection optimizations. --- lib/Sema/CSClosure.cpp | 93 ++++++++++++++++++- test/expr/closure/closures.swift | 9 +- test/expr/expressions.swift | 4 +- ...statement_closure_with_simd_variable.swift | 18 ++++ 4 files changed, 116 insertions(+), 8 deletions(-) create mode 100644 validation-test/Sema/type_checker_perf/fast/multi_statement_closure_with_simd_variable.swift diff --git a/lib/Sema/CSClosure.cpp b/lib/Sema/CSClosure.cpp index c94a9f0b614a3..13bee8a11518a 100644 --- a/lib/Sema/CSClosure.cpp +++ b/lib/Sema/CSClosure.cpp @@ -483,12 +483,81 @@ class ClosureConstraintGenerator }); } + void visitPatternBinding(PatternBindingDecl *patternBinding, + SmallVectorImpl &patterns) { + auto *baseLoc = cs.getConstraintLocator( + locator, LocatorPathElt::ClosureBodyElement(patternBinding)); + + for (unsigned index : range(patternBinding->getNumPatternEntries())) { + auto *pattern = TypeChecker::resolvePattern( + patternBinding->getPattern(index), patternBinding->getDeclContext(), + /*isStmtCondition=*/true); + + if (!pattern) { + hadError = true; + return; + } + + // Reset binding to point to the resolved pattern. This is required + // before calling `forPatternBindingDecl`. + patternBinding->setPattern(index, pattern, + patternBinding->getInitContext(index)); + + patterns.push_back(makeElement( + patternBinding, + cs.getConstraintLocator( + baseLoc, LocatorPathElt::PatternBindingElement(index)))); + } + } + + void visitPatternBindingElement(PatternBindingDecl *patternBinding) { + assert(locator->isLastElement()); + + auto index = + locator->castLastElementTo() + .getIndex(); + + auto contextualPattern = + ContextualPattern::forPatternBindingDecl(patternBinding, index); + Type patternType = TypeChecker::typeCheckPattern(contextualPattern); + + // Fail early if pattern couldn't be type-checked. + if (!patternType || patternType->hasError()) { + hadError = true; + return; + } + + auto *pattern = patternBinding->getPattern(index); + auto *init = patternBinding->getInit(index); + + if (!init && patternBinding->isDefaultInitializable(index) && + pattern->hasStorage()) { + init = TypeChecker::buildDefaultInitializer(patternType); + } + + auto target = init ? SolutionApplicationTarget::forInitialization( + init, patternBinding->getDeclContext(), + patternType, patternBinding, index, + /*bindPatternVarsOneWay=*/false) + : SolutionApplicationTarget::forUninitializedVar( + patternBinding, index, patternType); + + if (cs.generateConstraints(target, FreeTypeVariableBinding::Disallow)) { + hadError = true; + return; + } + + // Keep track of this binding entry. + cs.setSolutionApplicationTarget({patternBinding, index}, target); + } + void visitDecl(Decl *decl) { if (isSupportedMultiStatementClosure()) { if (auto patternBinding = dyn_cast(decl)) { - SolutionApplicationTarget target(patternBinding); - if (cs.generateConstraints(target, FreeTypeVariableBinding::Disallow)) - hadError = true; + if (locator->isLastElement()) + visitPatternBindingElement(patternBinding); + else + llvm_unreachable("cannot visit pattern binding directly"); return; } } @@ -788,6 +857,13 @@ class ClosureConstraintGenerator element.is() && (!ctx.LangOpts.Playground && !ctx.LangOpts.DebuggerSupport); + if (auto *decl = element.dyn_cast()) { + if (auto *PDB = dyn_cast(decl)) { + visitPatternBinding(PDB, elements); + continue; + } + } + elements.push_back(makeElement( element, cs.getConstraintLocator( @@ -1600,6 +1676,17 @@ void ConjunctionElement::findReferencedVariables( TypeVariableRefFinder refFinder(cs, locator->getAnchor(), typeVars); + if (auto *patternBinding = + dyn_cast_or_null(element.dyn_cast())) { + if (auto patternBindingElt = + locator + ->getLastElementAs()) { + if (auto *init = patternBinding->getInit(patternBindingElt->getIndex())) + init->walk(refFinder); + return; + } + } + if (element.is() || element.is() || element.is() || element.isStmt(StmtKind::Return)) element.walk(refFinder); diff --git a/test/expr/closure/closures.swift b/test/expr/closure/closures.swift index 658f5f3586bf3..0c2c821665539 100644 --- a/test/expr/closure/closures.swift +++ b/test/expr/closure/closures.swift @@ -115,13 +115,14 @@ func t() { func f0(_ a: Any) -> Int { return 1 } assert(f0(1) == 1) - +// TODO(diagnostics): Bad diagnostic - should be `circular reference` var selfRef = { selfRef() } -// expected-note@-1 2{{through reference here}} -// expected-error@-2 {{circular reference}} +// expected-error@-1 {{unable to infer closure type in the current context}} -var nestedSelfRef = { // expected-error {{circular reference}} expected-note 2 {{through reference here}} +// TODO: should be an error `circular reference` but it's diagnosed via overlapped requests +var nestedSelfRef = { var recursive = { nestedSelfRef() } + // expected-warning@-1 {{variable 'recursive' was never mutated; consider changing to 'let' constant}} recursive() } diff --git a/test/expr/expressions.swift b/test/expr/expressions.swift index 1c2bb47a4ee4d..78a5897cbbfce 100644 --- a/test/expr/expressions.swift +++ b/test/expr/expressions.swift @@ -246,9 +246,11 @@ func test_as_2() { func test_lambda() { // A simple closure. var a = { (value: Int) -> () in markUsed(value+1) } + // expected-warning@-1 {{initialization of variable 'a' was never used; consider replacing with assignment to '_' or removing it}} // A recursive lambda. - var fib = { (n: Int) -> Int in // expected-error {{circular reference}} expected-note 2 {{through reference here}} + var fib = { (n: Int) -> Int in + // expected-warning@-1 {{variable 'fib' was never mutated; consider changing to 'let' constant}} if (n < 2) { return n } diff --git a/validation-test/Sema/type_checker_perf/fast/multi_statement_closure_with_simd_variable.swift b/validation-test/Sema/type_checker_perf/fast/multi_statement_closure_with_simd_variable.swift new file mode 100644 index 0000000000000..294ff03cb814c --- /dev/null +++ b/validation-test/Sema/type_checker_perf/fast/multi_statement_closure_with_simd_variable.swift @@ -0,0 +1,18 @@ +// RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 +// REQUIRES: objc_interop,no_asan + +import simd + +func test(_: () -> Void) {} + +test { + let a: simd_float2 = .init() + let b: simd_float2 = .init() + let c: simd_float2 = .init() + let d: simd_float2 = .init() + + let width: Float = 2 + + let p = Int(max(20, min(1000, (simd_distance(a, b) + simd_distance(b, c) + simd_distance(c, d)) / width / 4))) + print(p) +} From 5e4c964c36d69fba5c1b172e9e8cea8c5da1f2b6 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Tue, 8 Mar 2022 12:02:15 -0800 Subject: [PATCH 6/6] [TypeChecker] SE-0326: Enable multi-statement closure inference by default --- include/swift/Basic/LangOptions.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/swift/Basic/LangOptions.h b/include/swift/Basic/LangOptions.h index 93ca008b25c25..d2144d14d3383 100644 --- a/include/swift/Basic/LangOptions.h +++ b/include/swift/Basic/LangOptions.h @@ -724,7 +724,7 @@ namespace swift { /// Enable experimental support for type inference through multi-statement /// closures. - bool EnableMultiStatementClosureInference = false; + bool EnableMultiStatementClosureInference = true; /// Enable experimental support for generic parameter inference in /// parameter positions from associated default expressions.