Skip to content

Commit ed45116

Browse files
authored
Merge pull request #39458 from xedin/result-builder-labeled-tuple-pattern
[ResultBuilders] A couple pattern matching fixes to make it consistent with `TypeCheckPattern`
2 parents 6de4ee8 + 8c40106 commit ed45116

File tree

3 files changed

+94
-4
lines changed

3 files changed

+94
-4
lines changed

lib/Sema/CSGen.cpp

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2405,7 +2405,19 @@ namespace {
24052405
Type memberType = CS.createTypeVariable(
24062406
CS.getConstraintLocator(locator),
24072407
TVO_CanBindToLValue | TVO_CanBindToNoEscape);
2408-
FunctionRefKind functionRefKind = FunctionRefKind::Compound;
2408+
2409+
// Tuple splat is still allowed for patterns (with a warning in Swift 5)
2410+
// so we need to start here from single-apply to make sure that e.g.
2411+
// `case test(x: Int, y: Int)` gets the labels preserved when matched
2412+
// with `case let .test(tuple)`.
2413+
FunctionRefKind functionRefKind = FunctionRefKind::SingleApply;
2414+
// If sub-pattern is a tuple we'd need to mark reference as compound,
2415+
// that would make sure that the labels are dropped in cases
2416+
// when `case` has a single tuple argument (tuple explosion) or multiple
2417+
// arguments (tuple-to-tuple conversion).
2418+
if (dyn_cast_or_null<TuplePattern>(enumPattern->getSubPattern()))
2419+
functionRefKind = FunctionRefKind::Compound;
2420+
24092421
if (enumPattern->getParentType() || enumPattern->getParentTypeRepr()) {
24102422
// Resolve the parent type.
24112423
const auto parentType = [&] {

lib/Sema/CSSimplify.cpp

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2284,9 +2284,29 @@ ConstraintSystem::matchFunctionTypes(FunctionType *func1, FunctionType *func2,
22842284
increaseScore(SK_FunctionConversion);
22852285
}
22862286
}
2287-
} else if (last->getKind() == ConstraintLocator::PatternMatch &&
2288-
isa<EnumElementPattern>(
2289-
last->castTo<LocatorPathElt::PatternMatch>().getPattern())) {
2287+
} else if (last->is<LocatorPathElt::PatternMatch>() &&
2288+
isa<EnumElementPattern>(
2289+
last->castTo<LocatorPathElt::PatternMatch>()
2290+
.getPattern())) {
2291+
// A single paren pattern becomes a labeled tuple pattern
2292+
// e.g. `case .test(let value):` should be able to match
2293+
// `case test(result: Int)`. Note that it also means that:
2294+
// `cast test(result: (String, Int))` would be matched against
2295+
// e.g. `case .test((let x, let y))` but that fails during
2296+
// pattern coercion (behavior consistent with what happens in
2297+
// `TypeCheckPattern`).
2298+
if (func1Params.size() == 1 && !func1Params.front().hasLabel() &&
2299+
func2Params.size() == 1 && func2Params.front().hasLabel()) {
2300+
auto param = func1Params.front();
2301+
auto label = func2Params.front().getLabel();
2302+
2303+
auto labeledParam = FunctionType::Param(param.getPlainType(), label,
2304+
param.getParameterFlags());
2305+
2306+
func1Params.clear();
2307+
func1Params.push_back(labeledParam);
2308+
}
2309+
22902310
// Consider following example:
22912311
//
22922312
// enum E {

test/Constraints/result_builder.swift

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -513,6 +513,64 @@ func getE(_ i: Int) -> E {
513513
}
514514
}
515515

516+
func test_labeled_splats() {
517+
enum E {
518+
case multi(a: String, b: String)
519+
case tuple((a: Int, b: Int))
520+
case single(result: Int)
521+
case single_multi(result: (a: Int, q: String))
522+
}
523+
524+
func test_answer(_: String) -> Int { 42 }
525+
func test_question(_: Int) -> String { "ultimate question" }
526+
527+
let e: E = E.single(result: 42)
528+
529+
tuplify(true) { _ in
530+
switch e {
531+
case .single(let result):
532+
test_question(result)
533+
case let .single_multi(result):
534+
test_answer(result.q)
535+
test_question(result.a)
536+
case let .multi(value): // tuple splat preserves labels
537+
test_answer(value.a)
538+
test_answer(value.b)
539+
case let .tuple(a: a, b: b): // un-splat preserves labels too
540+
test_question(a)
541+
test_question(b)
542+
}
543+
544+
// compound names still work with and without splat
545+
switch e {
546+
case .single(_): 42
547+
case .single_multi(result: (let a, let q)):
548+
test_answer(q)
549+
test_question(a)
550+
case let .multi(a: a, b: b):
551+
test_answer(a)
552+
test_answer(b)
553+
case let .tuple((a: a, b: b)):
554+
test_question(a)
555+
test_question(b)
556+
}
557+
558+
// no labels, no problem regardless of splatting
559+
switch e {
560+
case .single(_): 42
561+
case let .single_multi(result: (a, q)):
562+
test_question(a)
563+
test_answer(q)
564+
case let .multi(a, b):
565+
test_answer(a)
566+
test_answer(b)
567+
case let .tuple((a, b)):
568+
test_question(a)
569+
test_question(b)
570+
}
571+
}
572+
}
573+
516574
tuplify(true) { c in
517575
"testIfLetMatching"
518576
if let theValue = getOptionalInt(c) {

0 commit comments

Comments
 (0)