support pattern matching in closure parameters - #2899
Conversation
tjhance
left a comment
There was a problem hiding this comment.
Thanks for working on this!
It looks like there are some problems if the pattern contains any ref mut bindings (vir::ast::MutRef). For one thing, you won't be able to lower such patterns in spec code (as you'd need to for the requires and ensures). For another, it needs support in the rustc_mir_build fork.
Specifically, if I try:
fn test() {
let y = |(x, y): &mut (u64, u64)| {
};
}
I get a error: Verus Internal Error: setup_verus_ctxt_for_thir_erasure failed, var lookup failed.
IMO, you should just disable this case for now, i.e., check if there are any ByRef::Mut in the Pattern and if so, return a 'not supported' error.
Thank you so much for the review! Hope that everything is fixed now. I'll be more considerable next time :) |
|
OK, I pulled your commit to experiment locally and see what's going on with the span ids. I think I found the circumstances that led you add this line, but it's a real mess. First of all, you shouldn't need to add any mapping ids for "adjusted patterns". This isn't the case for any other patterns (in match expressions or let decls) so it shouldn't be the case here. The reason it seems like you do is that there are duplicate AstIds. The You need to use - let init = SpannedTyped::new(&pattern.span, typ, PlaceX::Local(name.clone()));
+ let init = bctx.spanned_typed_new(pat.span, typ, PlaceX::Local(name.clone()));In rust_to_vir, always use the Okay, now there's still the issue that I think if you make these fixes, everything should be working again. However, there are still a couple of other things I'm worried about:
|
|
Thank you for the review! I’ll address the remaining issues as follows:
|
| let mut span = span.clone(); | ||
| span.id = self.spans.get_next_span_id(); | ||
| self.erasure_info.borrow_mut().hir_vir_ids.push((None, span.id)); | ||
| span |
There was a problem hiding this comment.
This only applies to statements, but not expressions or patterns. (I'm confused how the temporary test passes, since the test seems to be checking the pattern IDs.)
There was a problem hiding this comment.
This only applies to statements, but not expressions or patterns. (I'm confused how the temporary test passes, since the test seems to be checking the pattern IDs.)
Thanks for the comment! I think map_stmt_spans only uses a statement as the entry point and it recursively maps its child nodes. It takes a statement and a span-transforming closure, f_span, which handles new ID here.
The call chain in source/vir/src/ast_visitor.rs is shown below and expressions or patterns are visited.
map_stmt_spans
->map_stmt_visitor_env
-> visit_stmt
-> visit_stmt_rec
-> visit_pattern / visit_place / visit_exprFP is added to MapExprStmtTypVisitor following other type like FE. Its visit_pattern first calls visit_pattern_rec to process nested patterns, then applies fp to the resulting pattern. In map_stmt_spans, the fp closure calls f_span on the pattern’s span, so each visited pattern receives a fresh ID. The expression and place callbacks apply f_span similarly.
That's why I think it passes the test checking pattern IDs.
|
|
||
| impl<'tcx> crate::context::ContextX<'tcx> { | ||
| /// Clones generated statements with fresh IDs, while preserving diagnostic locations. | ||
| pub(crate) fn clone_stmts_with_fresh_ids(&self, stmts: &[vir::ast::Stmt]) -> vir::ast::Stmts { |
There was a problem hiding this comment.
I'm not sure where to put this helper function. I put it here just because I want to call it conveniently :-(
| let mut span = span.clone(); | ||
| span.id = self.spans.get_next_span_id(); | ||
| self.erasure_info.borrow_mut().hir_vir_ids.push((None, span.id)); | ||
| span |
There was a problem hiding this comment.
This only applies to statements, but not expressions or patterns. (I'm confused how the temporary test passes, since the test seems to be checking the pattern IDs.)
Thanks for the comment! I think map_stmt_spans only uses a statement as the entry point and it recursively maps its child nodes. It takes a statement and a span-transforming closure, f_span, which handles new ID here.
The call chain in source/vir/src/ast_visitor.rs is shown below and expressions or patterns are visited.
map_stmt_spans
->map_stmt_visitor_env
-> visit_stmt
-> visit_stmt_rec
-> visit_pattern / visit_place / visit_exprFP is added to MapExprStmtTypVisitor following other type like FE. Its visit_pattern first calls visit_pattern_rec to process nested patterns, then applies fp to the resulting pattern. In map_stmt_spans, the fp closure calls f_span on the pattern’s span, so each visited pattern receives a fresh ID. The expression and place callbacks apply f_span similarly.
That's why I think it passes the test checking pattern IDs.
tjhance
left a comment
There was a problem hiding this comment.
Thanks for working on this, and for dealing with the id madness!
Follow discussion #1540
The PR supports pattern matching in closure parameters, including reference, tuple, struct, and tuple-struct patterns (all covered by regression tests).
pattern_stmtsto store destructuring declarationsclosure_pat_to_mut_varpat_to_mut_varpattern_stmts.PatKind::Refbranch inpattern_to_vir_unadjustedto handle ordinary reference patterns, respecting rustc’sskipped_ref_patsand continuing to reject pinned reference patterns.requiresandensuresexpression, and the closure body, with the generated destructuring declarations so pattern-bound variables are available in each scope.The implementation preserves VIR’s existing representation of closure parameters as individual variables, along with the original argument types and closure arity.
Assisted-by: GPT-5.6 Sol, GPT-6 Astra
By submitting this pull request, I confirm that my contribution is made under the terms of the MIT license.