Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-def/src/expr_store/lower.rs')
| -rw-r--r-- | crates/hir-def/src/expr_store/lower.rs | 895 |
1 files changed, 100 insertions, 795 deletions
diff --git a/crates/hir-def/src/expr_store/lower.rs b/crates/hir-def/src/expr_store/lower.rs index f374dd2cc9..42b076abb2 100644 --- a/crates/hir-def/src/expr_store/lower.rs +++ b/crates/hir-def/src/expr_store/lower.rs @@ -2,6 +2,7 @@ //! representation. mod asm; +mod format_args; mod generics; mod path; @@ -19,7 +20,7 @@ use intern::{Symbol, sym}; use rustc_hash::FxHashMap; use stdx::never; use syntax::{ - AstNode, AstPtr, AstToken as _, SyntaxNodePtr, + AstNode, AstPtr, SyntaxNodePtr, ast::{ self, ArrayExprKind, AstChildren, BlockExpr, HasArgList, HasAttrs, HasGenericArgs, HasGenericParams, HasLoopBody, HasName, HasTypeBounds, IsString, RangeItem, @@ -31,10 +32,9 @@ use triomphe::Arc; use tt::TextRange; use crate::{ - AdtId, BlockId, BlockIdLt, DefWithBodyId, FunctionId, GenericDefId, ImplId, MacroId, + AdtId, BlockId, BlockLoc, DefWithBodyId, FunctionId, GenericDefId, ImplId, MacroId, ModuleDefId, ModuleId, TraitId, TypeAliasId, UnresolvedMacro, attrs::AttrFlags, - builtin_type::BuiltinUint, db::DefDatabase, expr_store::{ Body, BodySourceMap, ExprPtr, ExpressionStore, ExpressionStoreBuilder, @@ -47,13 +47,7 @@ use crate::{ hir::{ Array, Binding, BindingAnnotation, BindingId, BindingProblems, CaptureBy, ClosureKind, Expr, ExprId, Item, Label, LabelId, Literal, MatchArm, Movability, OffsetOf, Pat, PatId, - RecordFieldPat, RecordLitField, Statement, - format_args::{ - self, FormatAlignment, FormatArgs, FormatArgsPiece, FormatArgument, FormatArgumentKind, - FormatArgumentsCollector, FormatCount, FormatDebugHex, FormatOptions, - FormatPlaceholder, FormatSign, FormatTrait, - }, - generics::GenericParams, + RecordFieldPat, RecordLitField, Statement, generics::GenericParams, }, item_scope::BuiltinShadowMode, item_tree::FieldsShape, @@ -434,10 +428,12 @@ pub struct ExprCollector<'db> { current_try_block_label: Option<LabelId>, label_ribs: Vec<LabelRib>, - current_binding_owner: Option<ExprId>, + unowned_bindings: Vec<BindingId>, awaitable_context: Option<Awaitable>, krate: base_db::Crate, + + name_generator_index: usize, } #[derive(Clone, Debug)] @@ -538,14 +534,21 @@ impl<'db> ExprCollector<'db> { current_try_block_label: None, is_lowering_coroutine: false, label_ribs: Vec::new(), - current_binding_owner: None, + unowned_bindings: Vec::new(), awaitable_context: None, current_block_legacy_macro_defs_count: FxHashMap::default(), outer_impl_trait: false, krate, + name_generator_index: 0, } } + fn generate_new_name(&mut self) -> Name { + let index = self.name_generator_index; + self.name_generator_index += 1; + Name::generate_new_name(index) + } + #[inline] pub(crate) fn lang_items(&self) -> &'db LangItems { self.lang_items.get_or_init(|| crate::lang_item::lang_items(self.db, self.def_map.krate())) @@ -949,7 +952,8 @@ impl<'db> ExprCollector<'db> { node: ast::TypeBound, impl_trait_lower_fn: ImplTraitLowerFn<'_>, ) -> TypeBound { - match node.kind() { + let Some(kind) = node.kind() else { return TypeBound::Error }; + match kind { ast::TypeBoundKind::PathType(binder, path_type) => { let binder = match binder.and_then(|it| it.generic_param_list()) { Some(gpl) => gpl @@ -1065,12 +1069,10 @@ impl<'db> ExprCollector<'db> { Some(ast::BlockModifier::Const(_)) => { self.with_label_rib(RibKind::Constant, |this| { this.with_awaitable_block(Awaitable::No("constant block"), |this| { - let (result_expr_id, prev_binding_owner) = - this.initialize_binding_owner(syntax_ptr); - let inner_expr = this.collect_block(e); - this.store.exprs[result_expr_id] = Expr::Const(inner_expr); - this.current_binding_owner = prev_binding_owner; - result_expr_id + this.with_binding_owner(|this| { + let inner_expr = this.collect_block(e); + this.alloc_expr(Expr::Const(inner_expr), syntax_ptr) + }) }) }) } @@ -1281,64 +1283,65 @@ impl<'db> ExprCollector<'db> { } } ast::Expr::ClosureExpr(e) => self.with_label_rib(RibKind::Closure, |this| { - let (result_expr_id, prev_binding_owner) = - this.initialize_binding_owner(syntax_ptr); - let mut args = Vec::new(); - let mut arg_types = Vec::new(); - if let Some(pl) = e.param_list() { - let num_params = pl.params().count(); - args.reserve_exact(num_params); - arg_types.reserve_exact(num_params); - for param in pl.params() { - let pat = this.collect_pat_top(param.pat()); - let type_ref = - param.ty().map(|it| this.lower_type_ref_disallow_impl_trait(it)); - args.push(pat); - arg_types.push(type_ref); + this.with_binding_owner(|this| { + let mut args = Vec::new(); + let mut arg_types = Vec::new(); + if let Some(pl) = e.param_list() { + let num_params = pl.params().count(); + args.reserve_exact(num_params); + arg_types.reserve_exact(num_params); + for param in pl.params() { + let pat = this.collect_pat_top(param.pat()); + let type_ref = + param.ty().map(|it| this.lower_type_ref_disallow_impl_trait(it)); + args.push(pat); + arg_types.push(type_ref); + } } - } - let ret_type = e - .ret_type() - .and_then(|r| r.ty()) - .map(|it| this.lower_type_ref_disallow_impl_trait(it)); + let ret_type = e + .ret_type() + .and_then(|r| r.ty()) + .map(|it| this.lower_type_ref_disallow_impl_trait(it)); - let prev_is_lowering_coroutine = mem::take(&mut this.is_lowering_coroutine); - let prev_try_block_label = this.current_try_block_label.take(); + let prev_is_lowering_coroutine = mem::take(&mut this.is_lowering_coroutine); + let prev_try_block_label = this.current_try_block_label.take(); - let awaitable = if e.async_token().is_some() { - Awaitable::Yes - } else { - Awaitable::No("non-async closure") - }; - let body = - this.with_awaitable_block(awaitable, |this| this.collect_expr_opt(e.body())); + let awaitable = if e.async_token().is_some() { + Awaitable::Yes + } else { + Awaitable::No("non-async closure") + }; + let body = this + .with_awaitable_block(awaitable, |this| this.collect_expr_opt(e.body())); - let closure_kind = if this.is_lowering_coroutine { - let movability = if e.static_token().is_some() { - Movability::Static + let closure_kind = if this.is_lowering_coroutine { + let movability = if e.static_token().is_some() { + Movability::Static + } else { + Movability::Movable + }; + ClosureKind::Coroutine(movability) + } else if e.async_token().is_some() { + ClosureKind::Async } else { - Movability::Movable + ClosureKind::Closure }; - ClosureKind::Coroutine(movability) - } else if e.async_token().is_some() { - ClosureKind::Async - } else { - ClosureKind::Closure - }; - let capture_by = - if e.move_token().is_some() { CaptureBy::Value } else { CaptureBy::Ref }; - this.is_lowering_coroutine = prev_is_lowering_coroutine; - this.current_binding_owner = prev_binding_owner; - this.current_try_block_label = prev_try_block_label; - this.store.exprs[result_expr_id] = Expr::Closure { - args: args.into(), - arg_types: arg_types.into(), - ret_type, - body, - closure_kind, - capture_by, - }; - result_expr_id + let capture_by = + if e.move_token().is_some() { CaptureBy::Value } else { CaptureBy::Ref }; + this.is_lowering_coroutine = prev_is_lowering_coroutine; + this.current_try_block_label = prev_try_block_label; + this.alloc_expr( + Expr::Closure { + args: args.into(), + arg_types: arg_types.into(), + ret_type, + body, + closure_kind, + capture_by, + }, + syntax_ptr, + ) + }) }), ast::Expr::BinExpr(e) => { let op = e.op_kind(); @@ -1374,11 +1377,7 @@ impl<'db> ExprCollector<'db> { let initializer = self.collect_expr_opt(initializer); let repeat = self.with_label_rib(RibKind::Constant, |this| { if let Some(repeat) = repeat { - let syntax_ptr = AstPtr::new(&repeat); - this.collect_as_a_binding_owner_bad( - |this| this.collect_expr(repeat), - syntax_ptr, - ) + this.with_binding_owner(|this| this.collect_expr(repeat)) } else { this.missing_expr() } @@ -1635,31 +1634,13 @@ impl<'db> ExprCollector<'db> { } } - fn initialize_binding_owner( - &mut self, - syntax_ptr: AstPtr<ast::Expr>, - ) -> (ExprId, Option<ExprId>) { - let result_expr_id = self.alloc_expr(Expr::Missing, syntax_ptr); - let prev_binding_owner = self.current_binding_owner.take(); - self.current_binding_owner = Some(result_expr_id); - - (result_expr_id, prev_binding_owner) - } - - /// FIXME: This function is bad. It will produce a dangling `Missing` expr which wastes memory. Currently - /// it is used only for const blocks and repeat expressions, which are also hacky and ideally should have - /// their own body. Don't add more usage for this function so that we can remove this function after - /// separating those bodies. - fn collect_as_a_binding_owner_bad( - &mut self, - job: impl FnOnce(&mut ExprCollector<'_>) -> ExprId, - syntax_ptr: AstPtr<ast::Expr>, - ) -> ExprId { - let (id, prev_owner) = self.initialize_binding_owner(syntax_ptr); - let tmp = job(self); - self.store.exprs[id] = mem::replace(&mut self.store.exprs[tmp], Expr::Missing); - self.current_binding_owner = prev_owner; - id + fn with_binding_owner(&mut self, create_expr: impl FnOnce(&mut Self) -> ExprId) -> ExprId { + let prev_unowned_bindings_len = self.unowned_bindings.len(); + let expr_id = create_expr(self); + for binding in self.unowned_bindings.drain(prev_unowned_bindings_len..) { + self.store.binding_owners.insert(binding, expr_id); + } + expr_id } /// Desugar `try { <stmts>; <expr> }` into `'<new_label>: { <stmts>; ::std::ops::Try::from_output(<expr>) }`, @@ -1667,9 +1648,8 @@ impl<'db> ExprCollector<'db> { /// and save the `<new_label>` to use it as a break target for desugaring of the `?` operator. fn desugar_try_block(&mut self, e: BlockExpr) -> ExprId { let try_from_output = self.lang_path(self.lang_items().TryTraitFromOutput); - let label = self.alloc_label_desugared(Label { - name: Name::generate_new_name(self.store.labels.len()), - }); + let label = self.generate_new_name(); + let label = self.alloc_label_desugared(Label { name: label }); let old_label = self.current_try_block_label.replace(label); let ptr = AstPtr::new(&e).upcast(); @@ -1797,7 +1777,7 @@ impl<'db> ExprCollector<'db> { this.collect_expr_opt(e.loop_body().map(|it| it.into())) }), }; - let iter_name = Name::generate_new_name(self.store.exprs.len()); + let iter_name = self.generate_new_name(); let iter_expr = self.alloc_expr(Expr::Path(Path::from(iter_name.clone())), syntax_ptr); let iter_expr_mut = self.alloc_expr( Expr::Ref { expr: iter_expr, rawness: Rawness::Ref, mutability: Mutability::Mut }, @@ -1858,7 +1838,7 @@ impl<'db> ExprCollector<'db> { let try_branch = self.alloc_expr(try_branch.map_or(Expr::Missing, Expr::Path), syntax_ptr); let expr = self .alloc_expr(Expr::Call { callee: try_branch, args: Box::new([operand]) }, syntax_ptr); - let continue_name = Name::generate_new_name(self.store.bindings.len()); + let continue_name = self.generate_new_name(); let continue_binding = self.alloc_binding( continue_name.clone(), BindingAnnotation::Unannotated, @@ -1876,7 +1856,7 @@ impl<'db> ExprCollector<'db> { guard: None, expr: self.alloc_expr(Expr::Path(Path::from(continue_name)), syntax_ptr), }; - let break_name = Name::generate_new_name(self.store.bindings.len()); + let break_name = self.generate_new_name(); let break_binding = self.alloc_binding(break_name.clone(), BindingAnnotation::Unannotated, HygieneId::ROOT); let break_bpat = self.alloc_pat_desugared(Pat::Bind { id: break_binding, subpat: None }); @@ -2114,7 +2094,7 @@ impl<'db> ExprCollector<'db> { ) -> ExprId { let block_id = self.expander.ast_id_map().ast_id_for_block(&block).map(|file_local_id| { let ast_id = self.expander.in_file(file_local_id); - unsafe { BlockIdLt::new(self.db, ast_id, self.module).to_static() } + self.db.intern_block(BlockLoc { ast_id, module: self.module }) }); let (module, def_map) = @@ -2371,11 +2351,7 @@ impl<'db> ExprCollector<'db> { ast::Pat::ConstBlockPat(const_block_pat) => { if let Some(block) = const_block_pat.block_expr() { let expr_id = self.with_label_rib(RibKind::Constant, |this| { - let syntax_ptr = AstPtr::new(&block.clone().into()); - this.collect_as_a_binding_owner_bad( - |this| this.collect_block(block), - syntax_ptr, - ) + this.with_binding_owner(|this| this.collect_block(block)) }); Pat::ConstBlock(expr_id) } else { @@ -2635,7 +2611,6 @@ impl<'db> ExprCollector<'db> { } // endregion: labels - // region: format fn expand_macros_to_string(&mut self, expr: ast::Expr) -> Option<(ast::String, bool)> { let m = match expr { ast::Expr::MacroExpr(m) => m, @@ -2655,676 +2630,6 @@ impl<'db> ExprCollector<'db> { Some((exp, false)) } - fn collect_format_args( - &mut self, - f: ast::FormatArgsExpr, - syntax_ptr: AstPtr<ast::Expr>, - ) -> ExprId { - let mut args = FormatArgumentsCollector::default(); - f.args().for_each(|arg| { - args.add(FormatArgument { - kind: match arg.name() { - Some(name) => FormatArgumentKind::Named(name.as_name()), - None => FormatArgumentKind::Normal, - }, - expr: self.collect_expr_opt(arg.expr()), - }); - }); - let template = f.template(); - let fmt_snippet = template.as_ref().and_then(|it| match it { - ast::Expr::Literal(literal) => match literal.kind() { - ast::LiteralKind::String(s) => Some(s.text().to_owned()), - _ => None, - }, - _ => None, - }); - let mut mappings = vec![]; - let (fmt, hygiene) = match template.and_then(|template| { - self.expand_macros_to_string(template.clone()).map(|it| (it, template)) - }) { - Some(((s, is_direct_literal), template)) => { - let call_ctx = self.expander.call_syntax_ctx(); - let hygiene = self.hygiene_id_for(s.syntax().text_range()); - let fmt = format_args::parse( - &s, - fmt_snippet, - args, - is_direct_literal, - |name, range| { - let expr_id = self.alloc_expr_desugared(Expr::Path(Path::from(name))); - if let Some(range) = range { - self.store - .template_map - .get_or_insert_with(Default::default) - .implicit_capture_to_source - .insert( - expr_id, - self.expander.in_file((AstPtr::new(&template), range)), - ); - } - if !hygiene.is_root() { - self.store.ident_hygiene.insert(expr_id.into(), hygiene); - } - expr_id - }, - |name, span| { - if let Some(span) = span { - mappings.push((span, name)) - } - }, - call_ctx, - ); - (fmt, hygiene) - } - None => ( - FormatArgs { - template: Default::default(), - arguments: args.finish(), - orphans: Default::default(), - }, - HygieneId::ROOT, - ), - }; - - // Create a list of all _unique_ (argument, format trait) combinations. - // E.g. "{0} {0:x} {0} {1}" -> [(0, Display), (0, LowerHex), (1, Display)] - let mut argmap = FxIndexSet::default(); - for piece in fmt.template.iter() { - let FormatArgsPiece::Placeholder(placeholder) = piece else { continue }; - if let Ok(index) = placeholder.argument.index { - argmap.insert((index, ArgumentType::Format(placeholder.format_trait))); - } - } - - let lit_pieces = fmt - .template - .iter() - .enumerate() - .filter_map(|(i, piece)| { - match piece { - FormatArgsPiece::Literal(s) => { - Some(self.alloc_expr_desugared(Expr::Literal(Literal::String(s.clone())))) - } - &FormatArgsPiece::Placeholder(_) => { - // Inject empty string before placeholders when not already preceded by a literal piece. - if i == 0 || matches!(fmt.template[i - 1], FormatArgsPiece::Placeholder(_)) - { - Some(self.alloc_expr_desugared(Expr::Literal(Literal::String( - Symbol::empty(), - )))) - } else { - None - } - } - } - }) - .collect(); - let lit_pieces = - self.alloc_expr_desugared(Expr::Array(Array::ElementList { elements: lit_pieces })); - let lit_pieces = self.alloc_expr_desugared(Expr::Ref { - expr: lit_pieces, - rawness: Rawness::Ref, - mutability: Mutability::Shared, - }); - let format_options = { - // Generate: - // &[format_spec_0, format_spec_1, format_spec_2] - let elements = fmt - .template - .iter() - .filter_map(|piece| { - let FormatArgsPiece::Placeholder(placeholder) = piece else { return None }; - Some(self.make_format_spec(placeholder, &mut argmap)) - }) - .collect(); - let array = self.alloc_expr_desugared(Expr::Array(Array::ElementList { elements })); - self.alloc_expr_desugared(Expr::Ref { - expr: array, - rawness: Rawness::Ref, - mutability: Mutability::Shared, - }) - }; - - // Assume that rustc version >= 1.89.0 iff lang item `format_arguments` exists - // but `format_unsafe_arg` does not - let lang_items = self.lang_items(); - let fmt_args = lang_items.FormatArguments; - let fmt_unsafe_arg = lang_items.FormatUnsafeArg; - let use_format_args_since_1_89_0 = fmt_args.is_some() && fmt_unsafe_arg.is_none(); - - let idx = if use_format_args_since_1_89_0 { - self.collect_format_args_impl(syntax_ptr, fmt, argmap, lit_pieces, format_options) - } else { - self.collect_format_args_before_1_89_0_impl( - syntax_ptr, - fmt, - argmap, - lit_pieces, - format_options, - ) - }; - - self.store - .template_map - .get_or_insert_with(Default::default) - .format_args_to_captures - .insert(idx, (hygiene, mappings)); - idx - } - - /// `format_args!` expansion implementation for rustc versions < `1.89.0` - fn collect_format_args_before_1_89_0_impl( - &mut self, - syntax_ptr: AstPtr<ast::Expr>, - fmt: FormatArgs, - argmap: FxIndexSet<(usize, ArgumentType)>, - lit_pieces: ExprId, - format_options: ExprId, - ) -> ExprId { - let arguments = &*fmt.arguments.arguments; - - let args = if arguments.is_empty() { - let expr = self - .alloc_expr_desugared(Expr::Array(Array::ElementList { elements: Box::default() })); - self.alloc_expr_desugared(Expr::Ref { - expr, - rawness: Rawness::Ref, - mutability: Mutability::Shared, - }) - } else { - // Generate: - // &match (&arg0, &arg1, &…) { - // args => [ - // <core::fmt::Argument>::new_display(args.0), - // <core::fmt::Argument>::new_lower_hex(args.1), - // <core::fmt::Argument>::new_debug(args.0), - // … - // ] - // } - let args = argmap - .iter() - .map(|&(arg_index, ty)| { - let arg = self.alloc_expr_desugared(Expr::Ref { - expr: arguments[arg_index].expr, - rawness: Rawness::Ref, - mutability: Mutability::Shared, - }); - self.make_argument(arg, ty) - }) - .collect(); - let array = - self.alloc_expr_desugared(Expr::Array(Array::ElementList { elements: args })); - self.alloc_expr_desugared(Expr::Ref { - expr: array, - rawness: Rawness::Ref, - mutability: Mutability::Shared, - }) - }; - - // Generate: - // <core::fmt::Arguments>::new_v1_formatted( - // lit_pieces, - // args, - // format_options, - // unsafe { ::core::fmt::UnsafeArg::new() } - // ) - - let lang_items = self.lang_items(); - let new_v1_formatted = self.ty_rel_lang_path( - lang_items.FormatArguments, - Name::new_symbol_root(sym::new_v1_formatted), - ); - let unsafe_arg_new = - self.ty_rel_lang_path(lang_items.FormatUnsafeArg, Name::new_symbol_root(sym::new)); - let new_v1_formatted = - self.alloc_expr_desugared(new_v1_formatted.map_or(Expr::Missing, Expr::Path)); - - let unsafe_arg_new = - self.alloc_expr_desugared(unsafe_arg_new.map_or(Expr::Missing, Expr::Path)); - let unsafe_arg_new = - self.alloc_expr_desugared(Expr::Call { callee: unsafe_arg_new, args: Box::default() }); - let mut unsafe_arg_new = self.alloc_expr_desugared(Expr::Unsafe { - id: None, - statements: Box::new([]), - tail: Some(unsafe_arg_new), - }); - if !fmt.orphans.is_empty() { - unsafe_arg_new = self.alloc_expr_desugared(Expr::Block { - id: None, - // We collect the unused expressions here so that we still infer them instead of - // dropping them out of the expression tree. We cannot store them in the `Unsafe` - // block because then unsafe blocks within them will get a false "unused unsafe" - // diagnostic (rustc has a notion of builtin unsafe blocks, but we don't). - statements: fmt - .orphans - .into_iter() - .map(|expr| Statement::Expr { expr, has_semi: true }) - .collect(), - tail: Some(unsafe_arg_new), - label: None, - }); - } - - self.alloc_expr( - Expr::Call { - callee: new_v1_formatted, - args: Box::new([lit_pieces, args, format_options, unsafe_arg_new]), - }, - syntax_ptr, - ) - } - - /// `format_args!` expansion implementation for rustc versions >= `1.89.0`, - /// especially since [this PR](https://github.com/rust-lang/rust/pull/140748) - fn collect_format_args_impl( - &mut self, - syntax_ptr: AstPtr<ast::Expr>, - fmt: FormatArgs, - argmap: FxIndexSet<(usize, ArgumentType)>, - lit_pieces: ExprId, - format_options: ExprId, - ) -> ExprId { - let arguments = &*fmt.arguments.arguments; - - let (let_stmts, args) = if arguments.is_empty() { - ( - // Generate: - // [] - vec![], - self.alloc_expr_desugared(Expr::Array(Array::ElementList { - elements: Box::default(), - })), - ) - } else if argmap.len() == 1 && arguments.len() == 1 { - // Only one argument, so we don't need to make the `args` tuple. - // - // Generate: - // super let args = [<core::fmt::Arguments>::new_display(&arg)]; - let args = argmap - .iter() - .map(|&(arg_index, ty)| { - let ref_arg = self.alloc_expr_desugared(Expr::Ref { - expr: arguments[arg_index].expr, - rawness: Rawness::Ref, - mutability: Mutability::Shared, - }); - self.make_argument(ref_arg, ty) - }) - .collect(); - let args = - self.alloc_expr_desugared(Expr::Array(Array::ElementList { elements: args })); - let args_name = Name::new_symbol_root(sym::args); - let args_binding = self.alloc_binding( - args_name.clone(), - BindingAnnotation::Unannotated, - HygieneId::ROOT, - ); - let args_pat = self.alloc_pat_desugared(Pat::Bind { id: args_binding, subpat: None }); - self.add_definition_to_binding(args_binding, args_pat); - // TODO: We don't have `super let` yet. - let let_stmt = Statement::Let { - pat: args_pat, - type_ref: None, - initializer: Some(args), - else_branch: None, - }; - (vec![let_stmt], self.alloc_expr_desugared(Expr::Path(args_name.into()))) - } else { - // Generate: - // super let args = (&arg0, &arg1, &...); - let args_name = Name::new_symbol_root(sym::args); - let args_binding = self.alloc_binding( - args_name.clone(), - BindingAnnotation::Unannotated, - HygieneId::ROOT, - ); - let args_pat = self.alloc_pat_desugared(Pat::Bind { id: args_binding, subpat: None }); - self.add_definition_to_binding(args_binding, args_pat); - let elements = arguments - .iter() - .map(|arg| { - self.alloc_expr_desugared(Expr::Ref { - expr: arg.expr, - rawness: Rawness::Ref, - mutability: Mutability::Shared, - }) - }) - .collect(); - let args_tuple = self.alloc_expr_desugared(Expr::Tuple { exprs: elements }); - // TODO: We don't have `super let` yet - let let_stmt1 = Statement::Let { - pat: args_pat, - type_ref: None, - initializer: Some(args_tuple), - else_branch: None, - }; - - // Generate: - // super let args = [ - // <core::fmt::Argument>::new_display(args.0), - // <core::fmt::Argument>::new_lower_hex(args.1), - // <core::fmt::Argument>::new_debug(args.0), - // … - // ]; - let args = argmap - .iter() - .map(|&(arg_index, ty)| { - let args_ident_expr = - self.alloc_expr_desugared(Expr::Path(args_name.clone().into())); - let arg = self.alloc_expr_desugared(Expr::Field { - expr: args_ident_expr, - name: Name::new_tuple_field(arg_index), - }); - self.make_argument(arg, ty) - }) - .collect(); - let array = - self.alloc_expr_desugared(Expr::Array(Array::ElementList { elements: args })); - let args_binding = self.alloc_binding( - args_name.clone(), - BindingAnnotation::Unannotated, - HygieneId::ROOT, - ); - let args_pat = self.alloc_pat_desugared(Pat::Bind { id: args_binding, subpat: None }); - self.add_definition_to_binding(args_binding, args_pat); - let let_stmt2 = Statement::Let { - pat: args_pat, - type_ref: None, - initializer: Some(array), - else_branch: None, - }; - (vec![let_stmt1, let_stmt2], self.alloc_expr_desugared(Expr::Path(args_name.into()))) - }; - - // Generate: - // &args - let args = self.alloc_expr_desugared(Expr::Ref { - expr: args, - rawness: Rawness::Ref, - mutability: Mutability::Shared, - }); - - let call_block = { - // Generate: - // unsafe { - // <core::fmt::Arguments>::new_v1_formatted( - // lit_pieces, - // args, - // format_options, - // ) - // } - - let new_v1_formatted = self.ty_rel_lang_path( - self.lang_items().FormatArguments, - Name::new_symbol_root(sym::new_v1_formatted), - ); - let new_v1_formatted = - self.alloc_expr_desugared(new_v1_formatted.map_or(Expr::Missing, Expr::Path)); - let args = [lit_pieces, args, format_options]; - let call = self - .alloc_expr_desugared(Expr::Call { callee: new_v1_formatted, args: args.into() }); - - Expr::Unsafe { id: None, statements: Box::default(), tail: Some(call) } - }; - - if !let_stmts.is_empty() { - // Generate: - // { - // super let … - // super let … - // <core::fmt::Arguments>::new_…(…) - // } - let call = self.alloc_expr_desugared(call_block); - self.alloc_expr( - Expr::Block { - id: None, - statements: let_stmts.into(), - tail: Some(call), - label: None, - }, - syntax_ptr, - ) - } else { - self.alloc_expr(call_block, syntax_ptr) - } - } - - /// Generate a hir expression for a format_args placeholder specification. - /// - /// Generates - /// - /// ```text - /// <core::fmt::rt::Placeholder::new( - /// …usize, // position - /// '…', // fill - /// <core::fmt::rt::Alignment>::…, // alignment - /// …u32, // flags - /// <core::fmt::rt::Count::…>, // width - /// <core::fmt::rt::Count::…>, // precision - /// ) - /// ``` - fn make_format_spec( - &mut self, - placeholder: &FormatPlaceholder, - argmap: &mut FxIndexSet<(usize, ArgumentType)>, - ) -> ExprId { - let lang_items = self.lang_items(); - let position = match placeholder.argument.index { - Ok(arg_index) => { - let (i, _) = - argmap.insert_full((arg_index, ArgumentType::Format(placeholder.format_trait))); - self.alloc_expr_desugared(Expr::Literal(Literal::Uint( - i as u128, - Some(BuiltinUint::Usize), - ))) - } - Err(_) => self.missing_expr(), - }; - let &FormatOptions { - ref width, - ref precision, - alignment, - fill, - sign, - alternate, - zero_pad, - debug_hex, - } = &placeholder.format_options; - - let precision_expr = self.make_count(precision, argmap); - let width_expr = self.make_count(width, argmap); - - if self.krate.workspace_data(self.db).is_atleast_187() { - // These need to match the constants in library/core/src/fmt/rt.rs. - let align = match alignment { - Some(FormatAlignment::Left) => 0, - Some(FormatAlignment::Right) => 1, - Some(FormatAlignment::Center) => 2, - None => 3, - }; - // This needs to match `Flag` in library/core/src/fmt/rt.rs. - let flags = fill.unwrap_or(' ') as u32 - | ((sign == Some(FormatSign::Plus)) as u32) << 21 - | ((sign == Some(FormatSign::Minus)) as u32) << 22 - | (alternate as u32) << 23 - | (zero_pad as u32) << 24 - | ((debug_hex == Some(FormatDebugHex::Lower)) as u32) << 25 - | ((debug_hex == Some(FormatDebugHex::Upper)) as u32) << 26 - | (width.is_some() as u32) << 27 - | (precision.is_some() as u32) << 28 - | align << 29 - | 1 << 31; // Highest bit always set. - let flags = self.alloc_expr_desugared(Expr::Literal(Literal::Uint( - flags as u128, - Some(BuiltinUint::U32), - ))); - - let position = - RecordLitField { name: Name::new_symbol_root(sym::position), expr: position }; - let flags = RecordLitField { name: Name::new_symbol_root(sym::flags), expr: flags }; - let precision = RecordLitField { - name: Name::new_symbol_root(sym::precision), - expr: precision_expr, - }; - let width = - RecordLitField { name: Name::new_symbol_root(sym::width), expr: width_expr }; - self.alloc_expr_desugared(Expr::RecordLit { - path: self.lang_path(lang_items.FormatPlaceholder).map(Box::new), - fields: Box::new([position, flags, precision, width]), - spread: None, - }) - } else { - let format_placeholder_new = { - let format_placeholder_new = self.ty_rel_lang_path( - lang_items.FormatPlaceholder, - Name::new_symbol_root(sym::new), - ); - match format_placeholder_new { - Some(path) => self.alloc_expr_desugared(Expr::Path(path)), - None => self.missing_expr(), - } - }; - // This needs to match `Flag` in library/core/src/fmt/rt.rs. - let flags: u32 = ((sign == Some(FormatSign::Plus)) as u32) - | (((sign == Some(FormatSign::Minus)) as u32) << 1) - | ((alternate as u32) << 2) - | ((zero_pad as u32) << 3) - | (((debug_hex == Some(FormatDebugHex::Lower)) as u32) << 4) - | (((debug_hex == Some(FormatDebugHex::Upper)) as u32) << 5); - let flags = self.alloc_expr_desugared(Expr::Literal(Literal::Uint( - flags as u128, - Some(BuiltinUint::U32), - ))); - let fill = self.alloc_expr_desugared(Expr::Literal(Literal::Char(fill.unwrap_or(' ')))); - let align = { - let align = self.ty_rel_lang_path( - lang_items.FormatAlignment, - match alignment { - Some(FormatAlignment::Left) => Name::new_symbol_root(sym::Left), - Some(FormatAlignment::Right) => Name::new_symbol_root(sym::Right), - Some(FormatAlignment::Center) => Name::new_symbol_root(sym::Center), - None => Name::new_symbol_root(sym::Unknown), - }, - ); - match align { - Some(path) => self.alloc_expr_desugared(Expr::Path(path)), - None => self.missing_expr(), - } - }; - self.alloc_expr_desugared(Expr::Call { - callee: format_placeholder_new, - args: Box::new([position, fill, align, flags, precision_expr, width_expr]), - }) - } - } - - /// Generate a hir expression for a format_args Count. - /// - /// Generates: - /// - /// ```text - /// <core::fmt::rt::Count>::Is(…) - /// ``` - /// - /// or - /// - /// ```text - /// <core::fmt::rt::Count>::Param(…) - /// ``` - /// - /// or - /// - /// ```text - /// <core::fmt::rt::Count>::Implied - /// ``` - fn make_count( - &mut self, - count: &Option<FormatCount>, - argmap: &mut FxIndexSet<(usize, ArgumentType)>, - ) -> ExprId { - let lang_items = self.lang_items(); - match count { - Some(FormatCount::Literal(n)) => { - let args = self.alloc_expr_desugared(Expr::Literal(Literal::Uint( - *n as u128, - // FIXME: Change this to Some(BuiltinUint::U16) once we drop support for toolchains < 1.88 - None, - ))); - let count_is = match self - .ty_rel_lang_path(lang_items.FormatCount, Name::new_symbol_root(sym::Is)) - { - Some(count_is) => self.alloc_expr_desugared(Expr::Path(count_is)), - None => self.missing_expr(), - }; - self.alloc_expr_desugared(Expr::Call { callee: count_is, args: Box::new([args]) }) - } - Some(FormatCount::Argument(arg)) => { - if let Ok(arg_index) = arg.index { - let (i, _) = argmap.insert_full((arg_index, ArgumentType::Usize)); - - let args = self.alloc_expr_desugared(Expr::Literal(Literal::Uint( - i as u128, - Some(BuiltinUint::Usize), - ))); - let count_param = match self - .ty_rel_lang_path(lang_items.FormatCount, Name::new_symbol_root(sym::Param)) - { - Some(count_param) => self.alloc_expr_desugared(Expr::Path(count_param)), - None => self.missing_expr(), - }; - self.alloc_expr_desugared(Expr::Call { - callee: count_param, - args: Box::new([args]), - }) - } else { - // FIXME: This drops arg causing it to potentially not be resolved/type checked - // when typing? - self.missing_expr() - } - } - None => match self - .ty_rel_lang_path(lang_items.FormatCount, Name::new_symbol_root(sym::Implied)) - { - Some(count_param) => self.alloc_expr_desugared(Expr::Path(count_param)), - None => self.missing_expr(), - }, - } - } - - /// Generate a hir expression representing an argument to a format_args invocation. - /// - /// Generates: - /// - /// ```text - /// <core::fmt::Argument>::new_…(arg) - /// ``` - fn make_argument(&mut self, arg: ExprId, ty: ArgumentType) -> ExprId { - use ArgumentType::*; - use FormatTrait::*; - - let new_fn = match self.ty_rel_lang_path( - self.lang_items().FormatArgument, - Name::new_symbol_root(match ty { - Format(Display) => sym::new_display, - Format(Debug) => sym::new_debug, - Format(LowerExp) => sym::new_lower_exp, - Format(UpperExp) => sym::new_upper_exp, - Format(Octal) => sym::new_octal, - Format(Pointer) => sym::new_pointer, - Format(Binary) => sym::new_binary, - Format(LowerHex) => sym::new_lower_hex, - Format(UpperHex) => sym::new_upper_hex, - Usize => sym::from_usize, - }), - ) { - Some(new_fn) => self.alloc_expr_desugared(Expr::Path(new_fn)), - None => self.missing_expr(), - }; - self.alloc_expr_desugared(Expr::Call { callee: new_fn, args: Box::new([arg]) }) - } - - // endregion: format - fn lang_path(&self, lang: Option<impl Into<LangItemTarget>>) -> Option<Path> { Some(Path::LangItem(lang?.into(), None)) } @@ -3332,9 +2637,17 @@ impl<'db> ExprCollector<'db> { fn ty_rel_lang_path( &self, lang: Option<impl Into<LangItemTarget>>, - relative_name: Name, + relative_name: Symbol, ) -> Option<Path> { - Some(Path::LangItem(lang?.into(), Some(relative_name))) + Some(Path::LangItem(lang?.into(), Some(Name::new_symbol_root(relative_name)))) + } + + fn ty_rel_lang_path_expr( + &self, + lang: Option<impl Into<LangItemTarget>>, + relative_name: Symbol, + ) -> Expr { + self.ty_rel_lang_path(lang, relative_name).map_or(Expr::Missing, Expr::Path) } } @@ -3379,9 +2692,7 @@ impl ExprCollector<'_> { hygiene: HygieneId, ) -> BindingId { let binding = self.store.bindings.alloc(Binding { name, mode, problems: None, hygiene }); - if let Some(owner) = self.current_binding_owner { - self.store.binding_owners.insert(binding, owner); - } + self.unowned_bindings.push(binding); binding } @@ -3453,12 +2764,6 @@ fn comma_follows_token(t: Option<syntax::SyntaxToken>) -> bool { .is_some_and(|it| it.kind() == syntax::T![,]) } -#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] -enum ArgumentType { - Format(FormatTrait), - Usize, -} - /// This function find the AST fragment that corresponds to an `AssociatedTypeBinding` in the HIR. pub fn hir_assoc_type_binding_to_ast( segment_args: &ast::GenericArgList, |