Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-def/src/body/lower.rs')
| -rw-r--r-- | crates/hir-def/src/body/lower.rs | 157 |
1 files changed, 130 insertions, 27 deletions
diff --git a/crates/hir-def/src/body/lower.rs b/crates/hir-def/src/body/lower.rs index e9cb51d5bf..acc9943481 100644 --- a/crates/hir-def/src/body/lower.rs +++ b/crates/hir-def/src/body/lower.rs @@ -31,8 +31,8 @@ use crate::{ expander::Expander, hir::{ dummy_expr_id, Array, Binding, BindingAnnotation, BindingId, CaptureBy, ClosureKind, Expr, - ExprId, Label, LabelId, Literal, MatchArm, Movability, Pat, PatId, RecordFieldPat, - RecordLitField, Statement, + ExprId, Label, LabelId, Literal, LiteralOrConst, MatchArm, Movability, Pat, PatId, + RecordFieldPat, RecordLitField, Statement, }, item_scope::BuiltinShadowMode, lang_item::LangItem, @@ -295,13 +295,7 @@ impl ExprCollector<'_> { self.alloc_expr(Expr::While { condition, body, label }, syntax_ptr) } - ast::Expr::ForExpr(e) => { - let label = e.label().map(|label| self.collect_label(label)); - let iterable = self.collect_expr_opt(e.iterable()); - let pat = self.collect_pat_top(e.pat()); - let body = self.collect_labelled_block_opt(label, e.loop_body()); - self.alloc_expr(Expr::For { iterable, pat, body, label }, syntax_ptr) - } + ast::Expr::ForExpr(e) => self.collect_for_loop(syntax_ptr, e), ast::Expr::CallExpr(e) => { let is_rustc_box = { let attrs = e.attrs(); @@ -703,6 +697,91 @@ impl ExprCollector<'_> { expr_id } + /// Desugar `ast::ForExpr` from: `[opt_ident]: for <pat> in <head> <body>` into: + /// ```ignore (pseudo-rust) + /// match IntoIterator::into_iter(<head>) { + /// mut iter => { + /// [opt_ident]: loop { + /// match Iterator::next(&mut iter) { + /// None => break, + /// Some(<pat>) => <body>, + /// }; + /// } + /// } + /// } + /// ``` + fn collect_for_loop(&mut self, syntax_ptr: AstPtr<ast::Expr>, e: ast::ForExpr) -> ExprId { + let (into_iter_fn, iter_next_fn, option_some, option_none) = 'if_chain: { + if let Some(into_iter_fn) = LangItem::IntoIterIntoIter.path(self.db, self.krate) { + if let Some(iter_next_fn) = LangItem::IteratorNext.path(self.db, self.krate) { + if let Some(option_some) = LangItem::OptionSome.path(self.db, self.krate) { + if let Some(option_none) = LangItem::OptionNone.path(self.db, self.krate) { + break 'if_chain (into_iter_fn, iter_next_fn, option_some, option_none); + } + } + } + } + // Some of the needed lang items are missing, so we can't desugar + return self.alloc_expr(Expr::Missing, syntax_ptr); + }; + let head = self.collect_expr_opt(e.iterable()); + let into_iter_fn_expr = self.alloc_expr(Expr::Path(into_iter_fn), syntax_ptr.clone()); + let iterator = self.alloc_expr( + Expr::Call { + callee: into_iter_fn_expr, + args: Box::new([head]), + is_assignee_expr: false, + }, + syntax_ptr.clone(), + ); + let none_arm = MatchArm { + pat: self.alloc_pat_desugared(Pat::Path(Box::new(option_none))), + guard: None, + expr: self.alloc_expr(Expr::Break { expr: None, label: None }, syntax_ptr.clone()), + }; + let some_pat = Pat::TupleStruct { + path: Some(Box::new(option_some)), + args: Box::new([self.collect_pat_top(e.pat())]), + ellipsis: None, + }; + let some_arm = MatchArm { + pat: self.alloc_pat_desugared(some_pat), + guard: None, + expr: self.collect_expr_opt(e.loop_body().map(|x| x.into())), + }; + let iter_name = Name::generate_new_name(); + let iter_binding = self.alloc_binding(iter_name.clone(), BindingAnnotation::Mutable); + let iter_expr = self.alloc_expr(Expr::Path(Path::from(iter_name)), syntax_ptr.clone()); + let iter_expr_mut = self.alloc_expr( + Expr::Ref { expr: iter_expr, rawness: Rawness::Ref, mutability: Mutability::Mut }, + syntax_ptr.clone(), + ); + let iter_next_fn_expr = self.alloc_expr(Expr::Path(iter_next_fn), syntax_ptr.clone()); + let iter_next_expr = self.alloc_expr( + Expr::Call { + callee: iter_next_fn_expr, + args: Box::new([iter_expr_mut]), + is_assignee_expr: false, + }, + syntax_ptr.clone(), + ); + let loop_inner = self.alloc_expr( + Expr::Match { expr: iter_next_expr, arms: Box::new([none_arm, some_arm]) }, + syntax_ptr.clone(), + ); + let label = e.label().map(|label| self.collect_label(label)); + let loop_outer = + self.alloc_expr(Expr::Loop { body: loop_inner, label }, syntax_ptr.clone()); + let iter_pat = self.alloc_pat_desugared(Pat::Bind { id: iter_binding, subpat: None }); + self.alloc_expr( + Expr::Match { + expr: iterator, + arms: Box::new([MatchArm { pat: iter_pat, guard: None, expr: loop_outer }]), + }, + syntax_ptr.clone(), + ) + } + /// Desugar `ast::TryExpr` from: `<expr>?` into: /// ```ignore (pseudo-rust) /// match Try::branch(<expr>) { @@ -1159,22 +1238,12 @@ impl ExprCollector<'_> { } #[rustfmt::skip] // https://github.com/rust-lang/rustfmt/issues/5676 ast::Pat::LiteralPat(lit) => 'b: { - if let Some(ast_lit) = lit.literal() { - let mut hir_lit: Literal = ast_lit.kind().into(); - if lit.minus_token().is_some() { - let Some(h) = hir_lit.negate() else { - break 'b Pat::Missing; - }; - hir_lit = h; - } - let expr = Expr::Literal(hir_lit); - let expr_ptr = AstPtr::new(&ast::Expr::Literal(ast_lit)); - let expr_id = self.alloc_expr(expr, expr_ptr); - Pat::Lit(expr_id) - } else { - Pat::Missing - } - }, + let Some((hir_lit, ast_lit)) = pat_literal_to_hir(lit) else { break 'b Pat::Missing }; + let expr = Expr::Literal(hir_lit); + let expr_ptr = AstPtr::new(&ast::Expr::Literal(ast_lit)); + let expr_id = self.alloc_expr(expr, expr_ptr); + Pat::Lit(expr_id) + } ast::Pat::RestPat(_) => { // `RestPat` requires special handling and should not be mapped // to a Pat. Here we are using `Pat::Missing` as a fallback for @@ -1215,8 +1284,30 @@ impl ExprCollector<'_> { } None => Pat::Missing, }, - // FIXME: implement - ast::Pat::RangePat(_) => Pat::Missing, + // FIXME: implement in a way that also builds source map and calculates assoc resolutions in type inference. + ast::Pat::RangePat(p) => { + let mut range_part_lower = |p: Option<ast::Pat>| { + p.and_then(|x| match &x { + ast::Pat::LiteralPat(x) => { + Some(Box::new(LiteralOrConst::Literal(pat_literal_to_hir(x)?.0))) + } + ast::Pat::IdentPat(p) => { + let name = + p.name().map(|nr| nr.as_name()).unwrap_or_else(Name::missing); + Some(Box::new(LiteralOrConst::Const(name.into()))) + } + ast::Pat::PathPat(p) => p + .path() + .and_then(|path| self.expander.parse_path(self.db, path)) + .map(LiteralOrConst::Const) + .map(Box::new), + _ => None, + }) + }; + let start = range_part_lower(p.start()); + let end = range_part_lower(p.end()); + Pat::Range { start, end } + } }; let ptr = AstPtr::new(&pat); self.alloc_pat(pattern, Either::Left(ptr)) @@ -1338,6 +1429,18 @@ impl ExprCollector<'_> { // endregion: labels } +fn pat_literal_to_hir(lit: &ast::LiteralPat) -> Option<(Literal, ast::Literal)> { + let ast_lit = lit.literal()?; + let mut hir_lit: Literal = ast_lit.kind().into(); + if lit.minus_token().is_some() { + let Some(h) = hir_lit.negate() else { + return None; + }; + hir_lit = h; + } + Some((hir_lit, ast_lit)) +} + impl ExprCollector<'_> { fn alloc_expr(&mut self, expr: Expr, ptr: ExprPtr) -> ExprId { let src = self.expander.to_source(ptr); |