Skip to content
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

[Parse/Sema/SILGen] Variables in 'case' labels with multiple patterns. #1383

Merged
merged 1 commit into from
Feb 25, 2016

Conversation

gregomni
Copy link
Contributor

There was a thread on swift-evolution back on Jan 23rd about adding this, and the response from the team was that it was something desirable but that you just hadn’t had time to do it so far.

Parser now accepts multiple patterns in switch cases that contain variables. Every pattern must contain the same variable names, but can be in arbitrary positions. New error for variable that doesn't exist in all patterns.

Sema now checks cases with multiple patterns that each occurrence of a variable name is bound to the same type. New error for unexpected types.

SILGen now shares a basic block for a switch case that contains multiple patterns even if there are variables. That BB takes incoming arguments from each applicable pattern match emission with the specific variables for the pattern that matched.

Added simple tests for the Parse and Sema functionality. I’d greatly appreciate suggestions on what sort of SILGen tests would be helpful. (The existing dominator assertions of variable usage and BB assertions on predecessor args caught a bunch of my mistakes along the way, so I’m pretty confident this all works, but how do I help make everyone else be confident too…)

I’m also not terribly happy with the managed value cleanup stuff between the pattern BBs and case body BBs, which works, but seems like the code could be cleaner. Again, suggestions greatly appreciated.

@jckarter
Copy link
Contributor

This looks awesome. Do you have any SILGen tests?

@gregomni
Copy link
Contributor Author

@jckarter I've been looking at the generated SIL with various examples by hand, but I'm not sure what ought to actually be checked in as tests.

Here's a simple example: https://gist.github.com/gregomni/cfaef124bd943168f266
with resulting SIL: https://gist.github.com/gregomni/3fdf53b2c5bdb9f621ab

Mostly verifying that the correct tuple elements get passed into the case block, that the bits of the pattern that aren't used are still released in the pattern emission blocks. But clearly I need more work with the managed cleanups part, since I'm seeing now that %21 is still being leaked. Argh.

Is there an easier / better way I haven't found to transfer a +1 value from one BB as an arg to another BB? I'm having a tough time with wanting the SILGenPattern code structure (and thus the cleanup scopes) being different from the order I'd actually like to generate the SIL.

@gregomni gregomni changed the title [Parse/Sema/SILGen] Variables in 'case' labels with multiple patterns. WIP [Parse/Sema/SILGen] Variables in 'case' labels with multiple patterns. Feb 21, 2016
@jckarter
Copy link
Contributor

There's a ConditionalValue abstraction in Condition.h, though I don't think it'll be easy to fit into the framework of SILGenPattern. I think you want to separate the scope in which the patterns are evaluated from the scope of the case body they lead into. That way, you can forward the bindings selectively from the pattern evaluation to the case body basic block and let everything else get cleaned up when you leave the pattern's scope. @rjmccall might be able to help here too.

@gregomni
Copy link
Contributor Author

Yep, I think you are right, the single scope per case instead of also per pattern is at the root of my troubles. Thanks for looking, and sorry for being a bit premature!

@gregomni
Copy link
Contributor Author

Ah, I'd been staring at things too long last night. That example SIL is actually fine, what I was thinking of then as an extra retain_value of %21 is consumed by being an @owned argument to a call. So I'm back to still needing to write some SILGen tests.

@gregomni gregomni changed the title WIP [Parse/Sema/SILGen] Variables in 'case' labels with multiple patterns. [Parse/Sema/SILGen] Variables in 'case' labels with multiple patterns. Feb 22, 2016
@@ -945,6 +945,8 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction

void emitConditionalPBD(PatternBindingDecl *PBD, SILBasicBlock *FailBB);

void usingImplicitVariablesForPattern(Pattern *pattern, CaseStmt *stmt,
const std::function<void(void)> &f);
Copy link
Contributor

Choose a reason for hiding this comment

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

Nitpick: we have llvm::function_ref for generalized functions that don't need to be copied to the heap.

@jrose-apple
Copy link
Contributor

This is pretty great.

@jckarter
Copy link
Contributor

Another thing to test: What happens if you try to access a pattern variable from another pattern in the same case?

switch x {
case let a,
     a: // Ought to be an error
}

@jrose-apple
Copy link
Contributor

Can you also add code completion tests? They don't have to produce the best results, but they shouldn't crash. Interesting cases off the top of my head:

  • Completing in a second pattern
  • Completing in a body with multiple patterns with bindings
  • Completing in a body where there's an error in the patterns

Completion tests live in test/IDE/.

@gregomni
Copy link
Contributor Author

@jckarter Added test for accessing a pattern var from another pattern in the same case. This would be a problem if it made it through the parser, because it does think that the variable is defined, but it gets caught by the check for the second pattern needing to also define the variable.

Also improved that error message so that it says 'a' must appear "as a variable" so that the diagnosis makes more sense in this case.

@jrose-apple Fixed to use function_ref, thanks for the pointer! And added the code completion tests you suggested. They run and don't produce anything surprising, although ideally it wouldn't suggest using variables defined in the first pattern as an expression in the second (or subsequent) pattern. Added a FIXME for that. Are there docs or other info you could point me at to get started understanding swiftIDE? Not familiar with that part of the code yet, and nothing in the docs folder jumps out as relevant.

Thanks!

@jrose-apple
Copy link
Contributor

I think formally the umbrella term for var and let is "binding"; "variable" would just mean var. Maybe "%0 must be bound in every pattern"?

For the swiftIDE question…@nkcsgexi?

@gregomni
Copy link
Contributor Author

You're right, "must be bound" is a better way of putting it.

@gregomni
Copy link
Contributor Author

FWIW, the code completion including the previous pattern binding var decls while in the next pattern is not a regression, it's been that way forever.

}
}

// MULTI_PATTERN_2: Begin completions
Copy link
Contributor

Choose a reason for hiding this comment

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

Please check that a is part of the output here too!

@nkcsgexi
Copy link
Contributor

Sorry for missing this earlier. @gregomni A good start point to understand how Swift code completion works is by studying several straight-forward completion kinds. One example is completing import decl, you can find it at CodeCompletion.swift:4012.

@gregomni
Copy link
Contributor Author

Added the completion test checks @jrose-apple suggested and squashed.

Thanks for the pointer @nkcsgexi! I'll look into improving the completion for additional pattern next. To me it seems like it would make sense for that to be a separate pull.

@jrose-apple
Copy link
Contributor

Thumbs-up from me, leaving it to Joe to say whether it's fully ready. (I didn't look too closely at the code, but it looked okay from a skim.)

@jckarter
Copy link
Contributor

LGTM. Nice work!

@jckarter
Copy link
Contributor

@swift-ci Please test

@jckarter
Copy link
Contributor

Test failure looks unrelated. I'll try again when the builders are clear.

@jckarter
Copy link
Contributor

@swift-ci Please test

@jckarter
Copy link
Contributor

Or first, @gregomni, can you rebase? Are you able to build this branch locally?

Parser now accepts multiple patterns in switch cases that contain variables.
Every pattern must contain the same variable names, but can be in arbitrary
positions. New error for variable that doesn't exist in all patterns.

Sema now checks cases with multiple patterns that each occurence of a variable
name is bound to the same type. New error for unexpected types.

SILGen now shares basic blocks for switch cases that contain multiple
patterns. That BB takes incoming arguments from each applicable pattern match
emission with the specific var decls for the pattern that matched.

Added tests for all three of these, and some simple IDE completion
sanity tests.
@gregomni
Copy link
Contributor Author

This commit e249fd6 changed the order of arguments to RValue.forwardInto(). Should've rebased and retested when I was squashing, sorry! Reran all the tests locally to make sure, that looked like the only issue.

@gregomni
Copy link
Contributor Author

@jckarter Should be good to go now!

@jckarter
Copy link
Contributor

@swift-ci Please test

@jckarter
Copy link
Contributor

Thanks @gregomni!

@jckarter jckarter self-assigned this Feb 25, 2016
jckarter added a commit that referenced this pull request Feb 25, 2016
[Parse/Sema/SILGen] Variables in 'case' labels with multiple patterns.
@jckarter jckarter merged commit a416650 into swiftlang:master Feb 25, 2016
@jrose-apple
Copy link
Contributor

Hooray! This deserves an update to CHANGELOG.md. :-)

@lattner
Copy link
Contributor

lattner commented Mar 24, 2016

@gregomni Please update the changelog, and include a link to SE-0043, thanks!

@gregomni
Copy link
Contributor Author

@lattner Done, thanks!

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.

5 participants