Skip to content

Commit ea8d143

Browse files
committed
[Constraint system] Introduce a one-way constraint for a switch subject.
The normal type checking of switch statements checks the switch subject first, without context, then evaluates the cases. Introduce a one-way constraint into the type checking of switch statements within function builders to provide this same behavior. The difference can be observed in code such as: enum E { case a case b(Int, String?) } enum E2 { case b(Int, String?) } func getSomeEnumOverloaded(_: Double) -> E { return .a } func getSomeEnumOverloaded(_: Int) -> E2 { return .b(0, nil) } func f() { switch getSomeEnumOverloaded(17) { case .a: // error: no member named "a" in E2 print("a") default: print("default") } } When the subject expression `getSomeEnumOverloaded(17)` is resolved without consider cases, it will select the second `getSomeEnumOverloaded(_:)`, because the literal 17 prefers to be an `Int`. The type checking of the first case would then fail because E2 does not contain a member named "a". Prior to this change, the same expression within a function builder would succeed in type checking, because the lack of case named "a" within "E2" would make the second getSomeEnumOverloaded() unusable. Making this code work by considering the cases along with the subject expression is not unreasonable, and may be the right long term direction for the language. However, it's a feature that should be discussed separately, and the semantics should agree between function builders and normal statements. Big thanks to John McCall for noting the missing one-way constraint!
1 parent 4b43573 commit ea8d143

File tree

2 files changed

+25
-0
lines changed

2 files changed

+25
-0
lines changed

lib/Sema/BuilderTransform.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -627,6 +627,9 @@ class BuilderClosureVisitor
627627
// type for use in matching the various patterns.
628628
Expr *subjectExpr = switchStmt->getSubjectExpr();
629629
if (cs) {
630+
// Form a one-way constraint to prevent backward propagation.
631+
subjectExpr = new (ctx) OneWayExpr(subjectExpr);
632+
630633
// FIXME: Add contextual type purpose for switch subjects?
631634
SolutionApplicationTarget target(subjectExpr, dc, CTP_Unused, Type(),
632635
/*isDiscarded=*/false);

test/Constraints/function_builder_diags.swift

+22
Original file line numberDiff line numberDiff line change
@@ -377,3 +377,25 @@ func testSwitch(e: E) {
377377
}
378378
}
379379

380+
// Ensure that we don't back-propagate constraints to the subject
381+
// expression. This is a potential avenue for future exploration, but
382+
// is currently not supported by switch statements outside of function
383+
// builders. It's better to be consistent for now.
384+
enum E2 {
385+
case b(Int, String?) // expected-note{{'b' declared here}}
386+
}
387+
388+
func getSomeEnumOverloaded(_: Double) -> E { return .a }
389+
func getSomeEnumOverloaded(_: Int) -> E2 { return .b(0, nil) }
390+
391+
func testOverloadedSwitch() {
392+
tuplify(true) { c in
393+
// FIXME: Bad source location.
394+
switch getSomeEnumOverloaded(17) { // expected-error{{type 'E2' has no member 'a'; did you mean 'b'?}}
395+
case .a:
396+
"a"
397+
default:
398+
"default"
399+
}
400+
}
401+
}

0 commit comments

Comments
 (0)