diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index a1f64680131..d07f10b4a80 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -7407,6 +7407,9 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind, // Notable exceptions here are: `Any` which doesn't require wrapping and // would be handled by an existential promotion in cases where it's allowed, // and `Optional` which would be handled by optional injection. + // + // `LValueType`s are also ignored at this stage to avoid accidentally wrapping them. If they + // are valid wrapping targets, they will be tuple-wrapped after the lvalue is converted. if (isTupleWithUnresolvedPackExpansion(origType1) || isTupleWithUnresolvedPackExpansion(origType2)) { auto isTypeVariableWrappedInOptional = [](Type type) { @@ -7417,6 +7420,7 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind, }; if (isa(desugar1) != isa(desugar2) && !isa(desugar1) && !isa(desugar2) && + !isa(desugar1) && !isa(desugar2) && !isTypeVariableWrappedInOptional(desugar1) && !isTypeVariableWrappedInOptional(desugar2) && !desugar1->isAny() && diff --git a/test/Constraints/pack_expansion_types.swift b/test/Constraints/pack_expansion_types.swift index c7868d47a26..f3343611c80 100644 --- a/test/Constraints/pack_expansion_types.swift +++ b/test/Constraints/pack_expansion_types.swift @@ -300,3 +300,319 @@ func test_one_element_tuple_vs_non_tuple_matching() { test(V.self) // Ok } } + +// Ensure correct behavior of lvalue tuple parameters + +/** + Previously `var`-backed parameters would end up wrapped + in an extraneous tuple level, leading to leading to incorrect + nesting in the output type due to the `LValueType` not being unwrapped. + + https://github.com/swiftlang/swift/issues/85924 + */ +func test_var_let_tuple_merge_equivalence() { + func merge(_ a: (repeat each A), _ b: (repeat each B)) -> (repeat each A, repeat each B) { + return (repeat each a, repeat each b) + } + + // allLets, TupleFirst + let _: (String, Int, String) = { + let a = ("a", 2) // (String, Int) + let b = "c" // String + return merge(a, b) + }() + + // Before #85924 was fixed, this would type as ((String, Int), String) + // varFirst, TupleFirst + let _: (String, Int, String) = { + // expected-warning@+1{{variable 'a' was never mutated; consider changing to 'let' constant}} + var a = ("a", 2) // @lvalue (String, Int) + let b = "c" // String + return merge(a, b) + }() + + // varSecond, TupleFirst + let _: (String, Int, String) = { + let a = ("a", 2) // (String, Int) + // expected-warning@+1{{variable 'b' was never mutated; consider changing to 'let' constant}} + var b = "c" // @lvalue String + return merge(a, b) + }() + + // allVars, TupleFirst + let _: (String, Int, String) = { + // expected-warning@+1{{variable 'a' was never mutated; consider changing to 'let' constant}} + var a = ("a", 2) // @lvalue (String, Int) + // expected-warning@+1{{variable 'b' was never mutated; consider changing to 'let' constant}} + var b = "c" // @lvalue String + return merge(a, b) + }() + + // allLets, TupleSecond + let _: (String, Int, String) = { + let a = "a" + let b = (2, "c") + return merge(a, b) + }() + + // varFirst, TupleSecond + let _: (String, Int, String) = { + // expected-warning@+1{{variable 'a' was never mutated; consider changing to 'let' constant}} + var a = "a" + let b = (2, "c") + return merge(a, b) + }() + + // varSecond, TupleSecond + let _: (String, Int, String) = { + let a = "a" + // expected-warning@+1{{variable 'b' was never mutated; consider changing to 'let' constant}} + var b = (2, "c") + return merge(a, b) + }() + + // allVars, TupleSecond + let _: (String, Int, String) = { + // expected-warning@+1{{variable 'a' was never mutated; consider changing to 'let' constant}} + var a = "a" + // expected-warning@+1{{variable 'b' was never mutated; consider changing to 'let' constant}} + var b = (2, "c") + return merge(a, b) + }() + + // allLets, MultiTuple + let _: (String, Int, String) = { + let a = ("a") + let b = (2, "c") + return merge(a, b) + }() + + // varFirst, MultiTuple + let _: (String, Int, String) = { + // expected-warning@+1{{variable 'a' was never mutated; consider changing to 'let' constant}} + var a = ("a") + let b = (2, "c") + return merge(a, b) + }() + + // varSecond, MultiTuple + let _: (String, Int, String) = { + let a = ("a") + // expected-warning@+1{{variable 'b' was never mutated; consider changing to 'let' constant}} + var b = (2, "c") + return merge(a, b) + }() + + // allVars, MultiTuple + let _: (String, Int, String) = { + // expected-warning@+1{{variable 'a' was never mutated; consider changing to 'let' constant}} + var a = ("a") + // expected-warning@+1{{variable 'b' was never mutated; consider changing to 'let' constant}} + var b = (2, "c") + return merge(a, b) + }() +} + +func test_var_let_tuple_append_equivalence() { + func append(_ a: (repeat each A), _ b: B) -> (repeat each A, B) { + return (repeat each a, b) + } + + // allLets, TupleFirst + let _: (String, Int, String) = { + let a = ("a", 2) // (String, Int) + let b = "c" // String + return append(a, b) + }() + + // varFirst, TupleFirst + let _: (String, Int, String) = { + // expected-warning@+1{{variable 'a' was never mutated; consider changing to 'let' constant}} + var a = ("a", 2) // @lvalue (String, Int) + let b = "c" // String + return append(a, b) + }() + + // varSecond, TupleFirst + let _: (String, Int, String) = { + let a = ("a", 2) // (String, Int) + // expected-warning@+1{{variable 'b' was never mutated; consider changing to 'let' constant}} + var b = "c" // @lvalue String + return append(a, b) + }() + + // allVars, TupleFirst + let _: (String, Int, String) = { + // expected-warning@+1{{variable 'a' was never mutated; consider changing to 'let' constant}} + var a = ("a", 2) // @lvalue (String, Int) + // expected-warning@+1{{variable 'b' was never mutated; consider changing to 'let' constant}} + var b = "c" // @lvalue String + return append(a, b) + }() + + // allLets, TupleSecond + let _: (String, (Int, String)) = { + let a = "a" + let b = (2, "c") + return append(a, b) + }() + + // varFirst, TupleSecond + let _: (String, (Int, String)) = { + // expected-warning@+1{{variable 'a' was never mutated; consider changing to 'let' constant}} + var a = "a" + let b = (2, "c") + return append(a, b) + }() + + // varSecond, TupleSecond + let _: (String, (Int, String)) = { + let a = "a" + // expected-warning@+1{{variable 'b' was never mutated; consider changing to 'let' constant}} + var b = (2, "c") + return append(a, b) + }() + + // allVars, TupleSecond + let _: (String, (Int, String)) = { + // expected-warning@+1{{variable 'a' was never mutated; consider changing to 'let' constant}} + var a = "a" + // expected-warning@+1{{variable 'b' was never mutated; consider changing to 'let' constant}} + var b = (2, "c") + return append(a, b) + }() + + // allLets, MultiTuple + let _: (String, (Int, String)) = { + let a = ("a") + let b = (2, "c") + return append(a, b) + }() + + // varFirst, MultiTuple + let _: (String, (Int, String)) = { + // expected-warning@+1{{variable 'a' was never mutated; consider changing to 'let' constant}} + var a = ("a") + let b = (2, "c") + return append(a, b) + }() + + // varSecond, MultiTuple + let _: (String, (Int, String)) = { + let a = ("a") + // expected-warning@+1{{variable 'b' was never mutated; consider changing to 'let' constant}} + var b = (2, "c") + return append(a, b) + }() + + // allVars, MultiTuple + let _: (String, (Int, String)) = { + // expected-warning@+1{{variable 'a' was never mutated; consider changing to 'let' constant}} + var a = ("a") + // expected-warning@+1{{variable 'b' was never mutated; consider changing to 'let' constant}} + var b = (2, "c") + return append(a, b) + }() +} + +func test_var_let_tuple_prefixOnto_equivalence() { + func prefixOnto(_ a: A, _ b: (repeat each B)) -> (A, repeat each B) { + return (a, repeat each b) + } + + // allLets, TupleFirst + let _: ((String, Int), String) = { + let a = ("a", 2) + let b = "c" + return prefixOnto(a, b) + }() + + // varFirst, TupleFirst + let _: ((String, Int), String) = { + // expected-warning@+1{{variable 'a' was never mutated; consider changing to 'let' constant}} + var a = ("a", 2) + let b = "c" + return prefixOnto(a, b) + }() + + // varSecond, TupleFirst + let _: ((String, Int), String) = { + let a = ("a", 2) + // expected-warning@+1{{variable 'b' was never mutated; consider changing to 'let' constant}} + var b = "c" + return prefixOnto(a, b) + }() + + // allVars, TupleFirst + let _: ((String, Int), String) = { + // expected-warning@+1{{variable 'a' was never mutated; consider changing to 'let' constant}} + var a = ("a", 2) + // expected-warning@+1{{variable 'b' was never mutated; consider changing to 'let' constant}} + var b = "c" + return prefixOnto(a, b) + }() + + // allLets, TupleSecond + let _: (String, Int, String) = { + let a = "a" + let b = (2, "c") + return prefixOnto(a, b) + }() + + // varFirst, TupleSecond + let _: (String, Int, String) = { + // expected-warning@+1{{variable 'a' was never mutated; consider changing to 'let' constant}} + var a = "a" + let b = (2, "c") + return prefixOnto(a, b) + }() + + // varSecond, TupleSecond + let _: (String, Int, String) = { + let a = "a" + // expected-warning@+1{{variable 'b' was never mutated; consider changing to 'let' constant}} + var b = (2, "c") + return prefixOnto(a, b) + }() + + // allVars, TupleSecond + let _: (String, Int, String) = { + // expected-warning@+1{{variable 'a' was never mutated; consider changing to 'let' constant}} + var a = "a" + // expected-warning@+1{{variable 'b' was never mutated; consider changing to 'let' constant}} + var b = (2, "c") + return prefixOnto(a, b) + }() + + // allLets, MultiTuple + let _: (String, Int, String) = { + let a = ("a") + let b = (2, "c") + return prefixOnto(a, b) + }() + + // varFirst, MultiTuple + let _: (String, Int, String) = { + // expected-warning@+1{{variable 'a' was never mutated; consider changing to 'let' constant}} + var a = ("a") + let b = (2, "c") + return prefixOnto(a, b) + }() + + // varSecond, MultiTuple + let _: (String, Int, String) = { + let a = ("a") + // expected-warning@+1{{variable 'b' was never mutated; consider changing to 'let' constant}} + var b = (2, "c") + return prefixOnto(a, b) + }() + + // allVars, MultiTuple + let _: (String, Int, String) = { + // expected-warning@+1 {{variable 'a' was never mutated; consider changing to 'let' constant}} + var a = ("a") + // expected-warning@+1 {{variable 'b' was never mutated; consider changing to 'let' constant}} + var b = (2, "c") + return prefixOnto(a, b) + }() +} diff --git a/test/Sema/issue-85837.swift b/test/Sema/issue-85837.swift new file mode 100644 index 00000000000..21b559427a5 --- /dev/null +++ b/test/Sema/issue-85837.swift @@ -0,0 +1,27 @@ +// RUN: %target-typecheck-verify-swift + +// Verifies fix for Github Issue #85837 +// https://github.com/swiftlang/swift/issues/85837 +// +// + +@resultBuilder public enum TupleBuilder { + public static func buildPartialBlock(first: T) -> (T) { + return first + } + + public static func buildPartialBlock(accumulated: (repeat each A), next: B) -> (repeat each A, B) { + return (repeat each accumulated, next) + } +} + +func builder(@TupleBuilder content: ()->(repeat each A)) -> (repeat each A) { + return content() +} + +// Ensure this optimally packs parameters +let built: (String, Int, String) = builder { + "a" + 2 + "c" + }