From 98973e45811a53418247ff350d83eb61ac9919ed Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Thu, 2 Jul 2026 14:56:42 +0300 Subject: [PATCH 1/4] Support mapping of all arguments with `Self` type --- compiler/rustc_ast_lowering/src/delegation.rs | 123 +++++++++++++----- compiler/rustc_ast_lowering/src/lib.rs | 56 ++++++-- .../delegation/self-coercion-static-free.rs | 2 - .../self-coercion-static-free.stderr | 46 ++----- .../self-mapping-arguments-errors.rs | 43 ++++++ .../self-mapping-arguments-errors.stderr | 54 ++++++++ tests/ui/delegation/self-mapping-arguments.rs | 59 +++++++++ .../self-mapping-arguments.run.stdout | 5 + 8 files changed, 303 insertions(+), 85 deletions(-) create mode 100644 tests/ui/delegation/self-mapping-arguments-errors.rs create mode 100644 tests/ui/delegation/self-mapping-arguments-errors.stderr create mode 100644 tests/ui/delegation/self-mapping-arguments.rs create mode 100644 tests/ui/delegation/self-mapping-arguments.run.stdout diff --git a/compiler/rustc_ast_lowering/src/delegation.rs b/compiler/rustc_ast_lowering/src/delegation.rs index a2a7b542258d1..df6b783cfe41a 100644 --- a/compiler/rustc_ast_lowering/src/delegation.rs +++ b/compiler/rustc_ast_lowering/src/delegation.rs @@ -64,6 +64,7 @@ use crate::diagnostics::{ }; use crate::{ AllowReturnTypeNotation, ImplTraitContext, ImplTraitPosition, LoweringContext, ParamMode, + re_lowering, }; mod generics; @@ -248,6 +249,17 @@ impl<'hir> LoweringContext<'_, 'hir> { return false; } + // If there are definitions inside and we can't delete target expression, so report an error. + // FIXME(fn_delegation): support deletion of target expression with defs inside. + if !should_generate_block && self.any_defs_in_block(block) { + self.dcx().emit_err(DelegationAttemptedBlockWithDefsDeletion { span: block.span }); + return false; + } + + true + } + + fn any_defs_in_block(&self, block: &Block) -> bool { struct DefinitionsFinder<'a> { all_owners: &'a NodeMap>, // `self.owner.node_id_to_def_id` @@ -284,16 +296,7 @@ impl<'hir> LoweringContext<'_, 'hir> { nested_def_ids: &self.owner.node_id_to_def_id, }; - let contains_defs = collector.visit_block(block).is_break(); - - // If there are definitions inside and we can't delete target expression, so report an error. - // FIXME(fn_delegation): support deletion of target expression with defs inside. - if !should_generate_block && contains_defs { - self.dcx().emit_err(DelegationAttemptedBlockWithDefsDeletion { span: block.span }); - return false; - } - - true + collector.visit_block(block).is_break() } fn should_generate_block( @@ -529,7 +532,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let block_id = self.lower_body(|this| { let mut parameters: Vec> = Vec::with_capacity(param_count); let mut args: Vec> = Vec::with_capacity(param_count); - let mut stmts: &[hir::Stmt<'hir>] = &[]; + let mut stmts = vec![]; let is_method = this.is_method(sig_id, span); let should_generate_block = this.should_generate_block(delegation, sig_id, is_method); @@ -546,25 +549,20 @@ impl<'hir> LoweringContext<'_, 'hir> { let generate_arg = |this: &mut Self| this.generate_arg(is_method, idx, param.pat.hir_id, span); + let param_local_id = param.pat.hir_id.local_id; let arg = if let Some(block) = block - && idx == 0 - && should_generate_block + && (idx == 0 && should_generate_block + || this.can_map_argument(delegation, sig_id, idx) + && !this.any_defs_in_block(block)) { - let mut self_resolver = SelfResolver { - ctxt: this, - path_id: delegation.id, - self_param_id: pat_node_id, - }; - self_resolver.visit_block(block); - // Target expr needs to lower `self` path. - this.ident_and_label_to_local_id.insert(pat_node_id, param.pat.hir_id.local_id); - - // Lower with `HirId::INVALID` as we will use only expr and stmts. - // FIXME(fn_delegation): Alternatives for target expression lowering: - // https://github.com/rust-lang/rfcs/pull/3530#issuecomment-2197170600. - let block = this.lower_block_noalloc(HirId::INVALID, block, false); - - stmts = block.stmts; + let block = this.lower_block_maybe_more_than_once( + block, + pat_node_id, + param_local_id, + delegation.id, + ); + + stmts.push(block.stmts); // The behavior of the delegation's target expression differs from the // behavior of the usual block, where if there is no final expression @@ -579,8 +577,14 @@ impl<'hir> LoweringContext<'_, 'hir> { args.push(arg); } - let (final_expr, hir_id) = - this.finalize_body_lowering(delegation, stmts, args, generics, span); + let (final_expr, hir_id) = this.finalize_body_lowering( + delegation, + sig_id, + this.arena.alloc_from_iter(stmts.into_iter().flatten().copied()), + args, + generics, + span, + ); call_expr_id = hir_id; @@ -592,9 +596,50 @@ impl<'hir> LoweringContext<'_, 'hir> { (block_id, call_expr_id, unused_target_expr) } + fn can_map_argument(&self, delegation: &Delegation, sig_id: DefId, arg_index: usize) -> bool { + self.can_perform_mapping(delegation) + .filter(|_| { + self.tcx.fn_sig(sig_id).skip_binder().input(arg_index).skip_binder().is_param(0) + }) + .is_some() + } + + fn lower_block_maybe_more_than_once( + &mut self, + block: &Block, + pat_node_id: NodeId, + param_local_id: hir::ItemLocalId, + delegation_id: NodeId, + ) -> hir::Block<'hir> { + let mut self_resolver = SelfResolver { + ctxt: self, + path_id: delegation_id, + self_param_id: pat_node_id, + overwrites: vec![], + }; + + self_resolver.visit_block(block); + + let overwrites = self_resolver.overwrites; + + // Target expr needs to lower `self` path. + self.ident_and_label_to_local_id.insert(pat_node_id, param_local_id); + + let block = re_lowering::ReloweringChecker::allow_relowering(self, |this| { + this.lower_block_noalloc(HirId::INVALID, block, false) + }); + + for id in overwrites { + self.partial_res_overrides.remove(&id); + } + + block + } + fn finalize_body_lowering( &mut self, delegation: &Delegation, + sig_id: DefId, stmts: &'hir [hir::Stmt<'hir>], args: Vec>, generics: &mut GenericsGenerationResults<'hir>, @@ -666,7 +711,9 @@ impl<'hir> LoweringContext<'_, 'hir> { let args = self.arena.alloc_from_iter(args); let call = self.mk_expr(hir::ExprKind::Call(callee_path, args), span); - let expr = if let Some((parent, of_trait)) = self.should_wrap_return_value(delegation) { + let expr = if let Some((parent, of_trait)) = + self.should_wrap_return_value(delegation, sig_id) + { let res = Res::SelfTyAlias { alias_to: parent.to_def_id(), is_trait_impl: of_trait }; let ident = Ident::new(kw::SelfUpper, span); let path = self.create_resolved_path(res, ident, span); @@ -701,7 +748,7 @@ impl<'hir> LoweringContext<'_, 'hir> { (self.mk_expr(hir::ExprKind::Block(block, None), span), call.hir_id) } - fn should_wrap_return_value(&self, delegation: &Delegation) -> Option<(LocalDefId, bool)> { + fn can_perform_mapping(&self, delegation: &Delegation) -> Option<(LocalDefId, bool)> { // Heuristic: don't do wrapping if there is no target expression. if delegation.body.is_none() { return None; @@ -735,11 +782,19 @@ impl<'hir> LoweringContext<'_, 'hir> { // After previous check we are sure that `sig_id` and `delegation.id` // point to the same function. && tcx.def_kind(tcx.parent(id)) == DefKind::Trait - && tcx.fn_sig(id).skip_binder().output().skip_binder().is_param(0) }) }) } + fn should_wrap_return_value( + &self, + delegation: &Delegation, + sig_id: DefId, + ) -> Option<(LocalDefId, bool)> { + self.can_perform_mapping(delegation) + .filter(|_| self.tcx.fn_sig(sig_id).skip_binder().output().skip_binder().is_param(0)) + } + fn process_segment( &mut self, span: Span, @@ -851,6 +906,7 @@ struct SelfResolver<'a, 'b, 'hir> { ctxt: &'a mut LoweringContext<'b, 'hir>, path_id: NodeId, self_param_id: NodeId, + overwrites: Vec, } impl SelfResolver<'_, '_, '_> { @@ -859,6 +915,7 @@ impl SelfResolver<'_, '_, '_> { && let Some(Res::Local(sig_id)) = res.full_res() && sig_id == self.path_id { + self.overwrites.push(id); self.ctxt.partial_res_overrides.insert(id, self.self_param_id); } } diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 35e16ce78a8a8..72df87af79da6 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -100,6 +100,42 @@ pub fn provide(providers: &mut Providers) { providers.lower_to_hir = lower_to_hir; } +pub(crate) mod re_lowering { + use rustc_ast::NodeId; + use rustc_ast::node_id::NodeMap; + use rustc_hir::{self as hir}; + + use crate::LoweringContext; + + #[derive(Debug, Default)] + pub(crate) struct ReloweringChecker { + node_id_to_local_id: NodeMap, + relowering_permissions_count: usize, + } + + impl ReloweringChecker { + pub(crate) fn check(&mut self, ast_node_id: NodeId, local_id: hir::ItemLocalId) { + if self.relowering_permissions_count == 0 { + let old = self.node_id_to_local_id.insert(ast_node_id, local_id); + assert_eq!(old, None); + } + } + + pub(crate) fn allow_relowering<'a, 'hir, TRes>( + ctx: &mut LoweringContext<'a, 'hir>, + op: impl FnOnce(&mut LoweringContext<'a, 'hir>) -> TRes, + ) -> TRes { + ctx.relowering_checker.relowering_permissions_count += 1; + + let res = op(ctx); + + ctx.relowering_checker.relowering_permissions_count -= 1; + + res + } + } +} + struct LoweringContext<'a, 'hir> { tcx: TyCtxt<'hir>, resolver: &'a ResolverAstLowering<'hir>, @@ -146,7 +182,7 @@ struct LoweringContext<'a, 'hir> { ident_and_label_to_local_id: NodeMap, /// NodeIds that are lowered inside the current HIR owner. Only used for duplicate lowering check. #[cfg(debug_assertions)] - node_id_to_local_id: NodeMap, + relowering_checker: re_lowering::ReloweringChecker, /// The `NodeId` space is split in two. /// `0..resolver.next_node_id` are created by the resolver on the AST. /// The higher part `resolver.next_node_id..next_node_id` are created during lowering. @@ -205,8 +241,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // and we never call `lower_node_id(owner)`. item_local_id_counter: hir::ItemLocalId::new(1), ident_and_label_to_local_id: Default::default(), + #[cfg(debug_assertions)] - node_id_to_local_id: Default::default(), + relowering_checker: Default::default(), + trait_map: Default::default(), next_node_id: resolver.next_node_id, node_id_to_def_id: NodeMap::default(), @@ -808,7 +846,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let current_ident_and_label_to_local_id = mem::take(&mut self.ident_and_label_to_local_id); #[cfg(debug_assertions)] - let current_node_id_to_local_id = mem::take(&mut self.node_id_to_local_id); + let current_relowering_checker = mem::take(&mut self.relowering_checker); let current_trait_map = mem::take(&mut self.trait_map); let current_owner = mem::replace(&mut self.current_hir_id_owner, owner_id); let current_local_counter = @@ -824,10 +862,7 @@ impl<'hir> LoweringContext<'_, 'hir> { // Always allocate the first `HirId` for the owner itself. #[cfg(debug_assertions)] - { - let _old = self.node_id_to_local_id.insert(owner, hir::ItemLocalId::ZERO); - debug_assert_eq!(_old, None); - } + self.relowering_checker.check(owner, hir::ItemLocalId::ZERO); let item = f(self); assert_eq!(owner_id, item.def_id()); @@ -845,7 +880,7 @@ impl<'hir> LoweringContext<'_, 'hir> { #[cfg(debug_assertions)] { - self.node_id_to_local_id = current_node_id_to_local_id; + self.relowering_checker = current_relowering_checker; } self.trait_map = current_trait_map; self.current_hir_id_owner = current_owner; @@ -936,10 +971,7 @@ impl<'hir> LoweringContext<'_, 'hir> { // Check whether the same `NodeId` is lowered more than once. #[cfg(debug_assertions)] - { - let old = self.node_id_to_local_id.insert(ast_node_id, local_id); - assert_eq!(old, None); - } + self.relowering_checker.check(ast_node_id, local_id); hir_id } diff --git a/tests/ui/delegation/self-coercion-static-free.rs b/tests/ui/delegation/self-coercion-static-free.rs index 408b29a12cde0..47448209e45e9 100644 --- a/tests/ui/delegation/self-coercion-static-free.rs +++ b/tests/ui/delegation/self-coercion-static-free.rs @@ -22,7 +22,6 @@ impl Trait for S { reuse ::{static_value, static_mut_ref, static_ref} { //~^ ERROR: mismatched types //~| ERROR: mismatched types - //~| ERROR: mismatched types //~| ERROR: unused target expression is specified for glob or list delegation let _ = self; S::static_self() @@ -35,7 +34,6 @@ impl Trait for S1 { reuse ::{static_value, static_mut_ref, static_ref} { //~^ ERROR: mismatched types //~| ERROR: mismatched types - //~| ERROR: mismatched types //~| ERROR: unused target expression is specified for glob or list delegation let _ = self; S1::static_self() diff --git a/tests/ui/delegation/self-coercion-static-free.stderr b/tests/ui/delegation/self-coercion-static-free.stderr index 870bd6bf3073b..e8f9c3d6e656b 100644 --- a/tests/ui/delegation/self-coercion-static-free.stderr +++ b/tests/ui/delegation/self-coercion-static-free.stderr @@ -5,26 +5,11 @@ LL | reuse ::{static_value, static_mut_ref, static_ref} { | ^^^^^^^^^^^^ error: unused target expression is specified for glob or list delegation - --> $DIR/self-coercion-static-free.rs:35:26 + --> $DIR/self-coercion-static-free.rs:34:26 | LL | reuse ::{static_value, static_mut_ref, static_ref} { | ^^^^^^^^^^^^ -error[E0308]: mismatched types - --> $DIR/self-coercion-static-free.rs:22:26 - | -LL | reuse ::{static_value, static_mut_ref, static_ref} { - | ^^^^^^^^^^^^ - | | - | expected `F`, found `S` - | arguments to this function are incorrect - | -note: associated function defined here - --> $DIR/self-coercion-static-free.rs:10:8 - | -LL | fn static_value(_: Self) -> i32 { 1 } - | ^^^^^^^^^^^^ ------- - error[E0308]: mismatched types --> $DIR/self-coercion-static-free.rs:22:40 | @@ -60,22 +45,7 @@ LL | fn static_ref(_: &Self) -> i32 { 3 } | ^^^^^^^^^^ -------- error[E0308]: mismatched types - --> $DIR/self-coercion-static-free.rs:35:26 - | -LL | reuse ::{static_value, static_mut_ref, static_ref} { - | ^^^^^^^^^^^^ - | | - | expected `F`, found `S1` - | arguments to this function are incorrect - | -note: associated function defined here - --> $DIR/self-coercion-static-free.rs:10:8 - | -LL | fn static_value(_: Self) -> i32 { 1 } - | ^^^^^^^^^^^^ ------- - -error[E0308]: mismatched types - --> $DIR/self-coercion-static-free.rs:35:40 + --> $DIR/self-coercion-static-free.rs:34:40 | LL | reuse ::{static_value, static_mut_ref, static_ref} { | ^^^^^^^^^^^^^^ @@ -92,7 +62,7 @@ LL | fn static_mut_ref(_: &mut Self) -> i32 { 2 } | ^^^^^^^^^^^^^^ ------------ error[E0308]: mismatched types - --> $DIR/self-coercion-static-free.rs:35:56 + --> $DIR/self-coercion-static-free.rs:34:56 | LL | reuse ::{static_value, static_mut_ref, static_ref} { | ^^^^^^^^^^ @@ -109,7 +79,7 @@ LL | fn static_ref(_: &Self) -> i32 { 3 } | ^^^^^^^^^^ -------- error[E0308]: mismatched types - --> $DIR/self-coercion-static-free.rs:52:43 + --> $DIR/self-coercion-static-free.rs:50:43 | LL | reuse to_reuse::{value, mut_ref, r#ref} { F } | ------- ^ expected `&mut _`, found `F` @@ -119,7 +89,7 @@ LL | reuse to_reuse::{value, mut_ref, r#ref} { F } = note: expected mutable reference `&mut _` found struct `F` note: function defined here - --> $DIR/self-coercion-static-free.rs:48:12 + --> $DIR/self-coercion-static-free.rs:46:12 | LL | pub fn mut_ref(_: &mut impl Trait) -> i32 { 2 } | ^^^^^^^ ------------------ @@ -129,7 +99,7 @@ LL | reuse to_reuse::{value, mut_ref, r#ref} { &mut F } | ++++ error[E0308]: mismatched types - --> $DIR/self-coercion-static-free.rs:52:43 + --> $DIR/self-coercion-static-free.rs:50:43 | LL | reuse to_reuse::{value, mut_ref, r#ref} { F } | ----- ^ expected `&_`, found `F` @@ -139,7 +109,7 @@ LL | reuse to_reuse::{value, mut_ref, r#ref} { F } = note: expected reference `&_` found struct `F` note: function defined here - --> $DIR/self-coercion-static-free.rs:49:12 + --> $DIR/self-coercion-static-free.rs:47:12 | LL | pub fn r#ref(_: &impl Trait) -> i32 { 3 } | ^^^^^ -------------- @@ -148,6 +118,6 @@ help: consider borrowing here LL | reuse to_reuse::{value, mut_ref, r#ref} { &F } | + -error: aborting due to 10 previous errors +error: aborting due to 8 previous errors For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/delegation/self-mapping-arguments-errors.rs b/tests/ui/delegation/self-mapping-arguments-errors.rs new file mode 100644 index 0000000000000..0213e656ce178 --- /dev/null +++ b/tests/ui/delegation/self-mapping-arguments-errors.rs @@ -0,0 +1,43 @@ +#![feature(fn_delegation)] + +mod target_expr_doesnt_relower_when_defs_inside { + trait MyAdd { + fn add(self, other: Self) -> Self; + } + + impl MyAdd for usize { + fn add(self, other: usize) -> usize { self + other } + } + + #[derive(Eq, PartialEq, Debug)] + struct W(usize); + reuse impl MyAdd for W { //~ ERROR: mismatched types + println!("{self:?}"); + fn foo() { + println!("hello"); + } + + reuse foo as bar; + bar(); + bar(); + + self.0 + } +} + +mod complex_Self_doesnt_map { + trait MyAdd { + fn add(self, other: Box) -> Self; + } + + impl MyAdd for usize { + fn add(self, other: Box) -> usize { self + *other.as_ref() } + } + + #[derive(Eq, PartialEq, Debug)] + struct W(usize); + reuse impl MyAdd for W { self.0 } + //~^ ERROR: mismatched types +} + +fn main() {} diff --git a/tests/ui/delegation/self-mapping-arguments-errors.stderr b/tests/ui/delegation/self-mapping-arguments-errors.stderr new file mode 100644 index 0000000000000..da4a11bdabaf7 --- /dev/null +++ b/tests/ui/delegation/self-mapping-arguments-errors.stderr @@ -0,0 +1,54 @@ +error[E0308]: mismatched types + --> $DIR/self-mapping-arguments-errors.rs:14:5 + | +LL | / reuse impl MyAdd for W { +LL | | println!("{self:?}"); +LL | | fn foo() { +LL | | println!("hello"); +... | +LL | | self.0 +LL | | } + | | ^ + | | | + | | expected `usize`, found `W` + | |_____arguments to this function are incorrect + | this return type influences the call expression's return type + | +help: the return type of this call is `target_expr_doesnt_relower_when_defs_inside::W` due to the type of the argument passed + --> $DIR/self-mapping-arguments-errors.rs:14:5 + | +LL | / reuse impl MyAdd for W { +LL | | println!("{self:?}"); +LL | | fn foo() { +LL | | println!("hello"); +... | +LL | | self.0 +LL | | } + | |_____^ this argument influences the return type of `add` +note: method defined here + --> $DIR/self-mapping-arguments-errors.rs:5:12 + | +LL | fn add(self, other: Self) -> Self; + | ^^^ ----- + +error[E0308]: mismatched types + --> $DIR/self-mapping-arguments-errors.rs:39:5 + | +LL | reuse impl MyAdd for W { self.0 } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | expected `Box`, found `Box` + | arguments to this function are incorrect + | this return type influences the call expression's return type + | + = note: expected struct `Box` + found struct `Box` +note: method defined here + --> $DIR/self-mapping-arguments-errors.rs:30:12 + | +LL | fn add(self, other: Box) -> Self; + | ^^^ ----- + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/delegation/self-mapping-arguments.rs b/tests/ui/delegation/self-mapping-arguments.rs new file mode 100644 index 0000000000000..f1b5279927af7 --- /dev/null +++ b/tests/ui/delegation/self-mapping-arguments.rs @@ -0,0 +1,59 @@ +//@ run-pass +//@ check-run-results + +#![feature(fn_delegation)] + + +mod default_test { + trait MyAdd { + fn add(self, other: Self) -> Self; + } + + impl MyAdd for usize { + fn add(self, other: usize) -> usize { self + other } + } + + #[derive(Eq, PartialEq, Debug)] + struct W(usize); + reuse impl MyAdd for W { + println!("{self:?}"); + let _x = 213; + + self.0 + } + + pub fn check() { + assert_eq!(W(1).add(W(2)), W(3)) + } +} + +mod arguments_mapping_works_without_return_self { + trait MyAdd { + fn add(self, other: Self); + } + + impl MyAdd for usize { + fn add(self, other: usize) { + let result = self + other; + println!("{result}"); + } + } + + #[derive(Eq, PartialEq, Debug)] + struct W(usize); + reuse impl MyAdd for W { + println!("{self:?}"); + let _x = 213; + + self.0 + } + + pub fn check() { + W(2).add(W(10)); + } +} + +fn main() { + default_test::check(); + arguments_mapping_works_without_return_self::check(); +} diff --git a/tests/ui/delegation/self-mapping-arguments.run.stdout b/tests/ui/delegation/self-mapping-arguments.run.stdout new file mode 100644 index 0000000000000..d8177420647a8 --- /dev/null +++ b/tests/ui/delegation/self-mapping-arguments.run.stdout @@ -0,0 +1,5 @@ +W(1) +W(2) +W(2) +W(10) +12 From 8f0fe761a928262e51cf3c685e59faa9883eefb3 Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Thu, 2 Jul 2026 15:19:17 +0300 Subject: [PATCH 2/4] Add #[cfg(debug_assertions)] --- compiler/rustc_ast_lowering/src/lib.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 72df87af79da6..ae9c23ab75ffe 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -125,11 +125,17 @@ pub(crate) mod re_lowering { ctx: &mut LoweringContext<'a, 'hir>, op: impl FnOnce(&mut LoweringContext<'a, 'hir>) -> TRes, ) -> TRes { - ctx.relowering_checker.relowering_permissions_count += 1; + #[cfg(debug_assertions)] + { + ctx.relowering_checker.relowering_permissions_count += 1; + } let res = op(ctx); - ctx.relowering_checker.relowering_permissions_count -= 1; + #[cfg(debug_assertions)] + { + ctx.relowering_checker.relowering_permissions_count -= 1; + } res } From 2469963c48ccc3ded327a202c6600c4f2e3bb700 Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Fri, 3 Jul 2026 12:51:59 +0300 Subject: [PATCH 3/4] Do not allow reentrant relowering, add `#[cfg(debug_assertions)]` --- compiler/rustc_ast_lowering/src/lib.rs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index ae9c23ab75ffe..384aae1be1ebc 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -109,13 +109,16 @@ pub(crate) mod re_lowering { #[derive(Debug, Default)] pub(crate) struct ReloweringChecker { + #[cfg(debug_assertions)] node_id_to_local_id: NodeMap, - relowering_permissions_count: usize, + #[cfg(debug_assertions)] + can_relower: bool, } impl ReloweringChecker { + #[cfg(debug_assertions)] pub(crate) fn check(&mut self, ast_node_id: NodeId, local_id: hir::ItemLocalId) { - if self.relowering_permissions_count == 0 { + if !self.can_relower { let old = self.node_id_to_local_id.insert(ast_node_id, local_id); assert_eq!(old, None); } @@ -127,14 +130,19 @@ pub(crate) mod re_lowering { ) -> TRes { #[cfg(debug_assertions)] { - ctx.relowering_checker.relowering_permissions_count += 1; + assert!( + !ctx.relowering_checker.can_relower, + "Reentrant relowering is not supported" + ); + + ctx.relowering_checker.can_relower = true; } let res = op(ctx); #[cfg(debug_assertions)] { - ctx.relowering_checker.relowering_permissions_count -= 1; + ctx.relowering_checker.can_relower = false; } res From ee2d2ea3efb6c40fcfe54843cd6b1b995cb251ea Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Fri, 3 Jul 2026 13:05:13 +0300 Subject: [PATCH 4/4] Add more `#[cfg(debug_assertions)]` --- compiler/rustc_ast_lowering/src/lib.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 384aae1be1ebc..f348c41c10428 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -101,8 +101,11 @@ pub fn provide(providers: &mut Providers) { } pub(crate) mod re_lowering { + #[cfg(debug_assertions)] use rustc_ast::NodeId; + #[cfg(debug_assertions)] use rustc_ast::node_id::NodeMap; + #[cfg(debug_assertions)] use rustc_hir::{self as hir}; use crate::LoweringContext;