Skip to content

Sema: Support optional promotion of tuple patterns #63992

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

AnthonyLatsis
Copy link
Collaborator

As with enum element patterns — at least the unqualified ones — this allows tuple patterns to omit syntactic noise in the form of enclosing .some(...) or trailing ?.

enum E { case a }

let t: (E, E)?
switch t {
  case nil: break
  case (.a, .a): break
}

@AnthonyLatsis
Copy link
Collaborator Author

@swift-ci please smoke test macOS

@xedin
Copy link
Contributor

xedin commented Mar 1, 2023

A general note: Any changes made to TypeCheckPattern.cpp need to be reflected in CSGen otherwise the behavior in multi-statement closures and result builder transformed closures would not change…

@hamishknight
Copy link
Contributor

FWIW I'm also hoping to eventually remove coercePatternToType such that the constraint system pattern solving implementation is always used (main...hamishknight:swift:patte)

@AnthonyLatsis AnthonyLatsis force-pushed the tuple-pattern-optional-promotion branch 2 times, most recently from f3f6be7 to 85d8479 Compare March 7, 2023 19:54
@AnthonyLatsis
Copy link
Collaborator Author

@xedin I took care of all the closure stuff; thanks for the reminder.

@xedin
Copy link
Contributor

xedin commented Mar 7, 2023

Great! I'll try to take a look today.

@AnthonyLatsis AnthonyLatsis force-pushed the tuple-pattern-optional-promotion branch from 85d8479 to 9d9a385 Compare March 7, 2023 20:00
@AnthonyLatsis
Copy link
Collaborator Author

@swift-ci please smoke test macOS

Copy link
Contributor

@hamishknight hamishknight left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some initial comments from a quick look through, I can look in more detail tomorrow. I think you'll need to adjust the logic in getTypeForPattern rather than reversing the contextual conversion constraint though

Comment on lines +109 to +95
// FIXME: '.a' is OK but 'E.a' is not?
case E.a: () // expected-error {{enum case 'a' is not a member of type 'E?'}}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Heh yeah, this is something I'm planning on fixing

@AnthonyLatsis AnthonyLatsis force-pushed the tuple-pattern-optional-promotion branch from 9d9a385 to a53186e Compare April 11, 2023 18:09
@AnthonyLatsis
Copy link
Collaborator Author

@swift-ci please smoke test macOS

@AnthonyLatsis
Copy link
Collaborator Author

@xedin How does this look? As of now the only known issue is a failure in the case of a for-in loop where the sequence is an array literal. Haven’t figured that one out yet.

Comment on lines +14 to +22
contexts = ['do_block', 'closure', 'result_builder_closure']
def get_test_case_introducer(context):
if context == 'do_block':
return 'do'
elif context == 'closure':
return 'let _: () -> Void ='
elif context == 'result_builder_closure':
return '@DummyBuilder var x: Void'
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does anyone have an opinion on removing the result builder context now that constraint generation for closures has been unified? It makes these tests somewhat harder to codify.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fine with me, as long as you add a couple of simple test cases within result builders

@xedin
Copy link
Contributor

xedin commented Apr 15, 2023

@hamishknight could you please take a look? I will try to get some time for this next week.

@AnthonyLatsis AnthonyLatsis changed the title Sema: Support optional promotion of tuple patterns when pattern matching Sema: Support optional promotion of tuple patterns Apr 15, 2023
Copy link
Contributor

@hamishknight hamishknight left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! A few comments, but otherwise this LGTM. I'll let @xedin have the final say though.

Comment on lines +1232 to +1222
// switch 0 {
// case (int: 0): break
// default: break
// }
// Do we really want/need to support this?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Urgh no, I don't think we want to support that. The constraint system will already reject such cases (including if case, as that already goes through the constraint system even outside of closures). Hopefully we can drop it for switch statements outside of closures without much fallout.

Comment on lines +161 to +165
/// The first type is an optional of non-negative depth of the second type,
/// ignoring lvalueness (because the constraint is currently applied only to
/// pattern types, which are always rvalue). For example, given a RHS of
/// 'Int', a LHS of 'Int' or 'Int??' would satisfy this constraint.
EqualOrOptional,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't quite feel right to me to have Equal in the name since the constraint isn't doing rvalue conversions, it's just asserting that no lvalues are present. Additionally, I think if we did ever want to apply this constraint to something else that could have lvalues, I think it would probably make most sense to preserve lvalueness like OptionalObject, as lvalues are generally preserved through optional chains.

How about something like UnwrappingOptionals? I think the plurality also helps make it clear it unwraps an arbitrary number of optionals.

@AnthonyLatsis AnthonyLatsis force-pushed the tuple-pattern-optional-promotion branch from a53186e to 44b91a6 Compare April 18, 2023 13:56
TVO_CanBindToNoEscape);
CS.addConstraint(
ConstraintKind::EqualOrOptional, tyVar, patternTy,
locator.withPathElement(LocatorPathElt::PatternMatch(pattern)));
Copy link
Contributor

@xedin xedin May 1, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As far as I understand this problem, what we really want is not to look through optionals but disregard the structure of the tuple pattern and let context impose it. So how about instead of this new constraint we actually model it this way:

  1. Create a "tuple" pattern type variable (just like you did);
  2. For each pattern in the tuple create a member constraint to i.e. <tuple var>[member: .<index or name>] == <element-var>;
  3. Set tuple variable as a type of the tuple pattern.

This scheme should make it possible to infer tuple type from context and look through optionals in pattern matching context while resolving individual tuple elements.

WDYT?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the late reply here! I'm curious what the benefit of that approach would be, as wouldn't the new tuple member constraint need to handle the same optional unwrapping that the EqualOrOptional constraint currently handles, plus it would need to have tuple matching logic? Or is there something I'm missing here?

Or is the aim to have the <tuple var> be inferred as a non-optional? I should note that the constraint which attaches the tuple var to the surrounding pattern would fail with that, as it should only allow conversions that propagate into pattern, not out of them (though I'm becoming increasingly of the opinion that we should limit those to equality constraints where we can).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are no new constraints and existing member already handles unwrapping of optional base type.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I see! Sorry, I totally missed that. I guess one worry with that approach is we don't want to allow e.g (x: Int, y: Int) to match with (y: Int, x: Int), or even worse some nominal type T that just so happens to have the right members.

One other thing that we have to contend with is that we currently can use the tuple type of the pattern to introduce conversions, e.g this is currently allowed:

if case (x: 0, y: 0) = (0, 0) {}

Also just discovered that we currently allow duplicate labels in tuple patterns :(

if case (x: 0, x: 0) = (0, 0) {}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don’t think it should be too hard to maintain the structural matching, we can even add additional information about index/label to member constraint to make sure that the base tuple does indeed match structurally.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants