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
4 changes: 4 additions & 0 deletions lib/Sema/CSSimplify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7393,6 +7393,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<T>` 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) {
Expand All @@ -7403,6 +7406,7 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
};
if (isa<TupleType>(desugar1) != isa<TupleType>(desugar2) &&
!isa<InOutType>(desugar1) && !isa<InOutType>(desugar2) &&
!isa<LValueType>(desugar1) && !isa<LValueType>(desugar2) &&
!isTypeVariableWrappedInOptional(desugar1) &&
!isTypeVariableWrappedInOptional(desugar2) &&
!desugar1->isAny() &&
Expand Down
316 changes: 316 additions & 0 deletions test/Constraints/pack_expansion_types.swift
Original file line number Diff line number Diff line change
Expand Up @@ -300,3 +300,319 @@ func test_one_element_tuple_vs_non_tuple_matching() {
test(V<Int>.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<each A, each B>(_ 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<each A, B>(_ 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, each B>(_ 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)
}()
}
27 changes: 27 additions & 0 deletions test/Sema/issue-85837.swift
Original file line number Diff line number Diff line change
@@ -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<T>(first: T) -> (T) {
return first
}

public static func buildPartialBlock<each A, B>(accumulated: (repeat each A), next: B) -> (repeat each A, B) {
return (repeat each accumulated, next)
}
}

func builder<each A>(@TupleBuilder content: ()->(repeat each A)) -> (repeat each A) {
return content()
}

// Ensure this optimally packs parameters
let built: (String, Int, String) = builder {
"a"
2
"c"
}