From 99c6d263a95c02c9d9434fb05a2b59532b6fee39 Mon Sep 17 00:00:00 2001 From: lcnr Date: Thu, 21 Nov 2024 16:31:13 +0100 Subject: [PATCH 1/2] def_collector: rework trivial const-arg handling see the comment on `handle_lazy_anon_const_def` --- compiler/rustc_ast/src/ast.rs | 7 +- compiler/rustc_ast_lowering/src/asm.rs | 2 +- compiler/rustc_ast_lowering/src/expr.rs | 2 +- compiler/rustc_ast_lowering/src/lib.rs | 37 ++- compiler/rustc_resolve/src/def_collector.rs | 270 ++++++++-------- compiler/rustc_resolve/src/late.rs | 4 +- compiler/rustc_resolve/src/lib.rs | 15 +- ...=> const_arg_trivial_macro_expansion-1.rs} | 287 +++++++++++++++--- ...onst_arg_trivial_macro_expansion-3-pass.rs | 46 +++ .../const_arg_trivial_macro_expansion-4.rs | 18 ++ ...const_arg_trivial_macro_expansion-4.stderr | 37 +++ 11 files changed, 528 insertions(+), 197 deletions(-) rename tests/ui/const-generics/early/{const_arg_trivial_macro_expansion.rs => const_arg_trivial_macro_expansion-1.rs} (56%) create mode 100644 tests/ui/const-generics/early/const_arg_trivial_macro_expansion-3-pass.rs create mode 100644 tests/ui/const-generics/early/const_arg_trivial_macro_expansion-4.rs create mode 100644 tests/ui/const-generics/early/const_arg_trivial_macro_expansion-4.stderr diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 5f71fb97d768c..4905fa4993340 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -1187,14 +1187,15 @@ pub struct Expr { } impl Expr { - /// Is this expr either `N`, or `{ N }`. + /// Could this expr be either `N`, or `{ N }`, where `N` is a const parameter. /// /// If this is not the case, name resolution does not resolve `N` when using /// `min_const_generics` as more complex expressions are not supported. /// /// Does not ensure that the path resolves to a const param, the caller should check this. - pub fn is_potential_trivial_const_arg(&self, strip_identity_block: bool) -> bool { - let this = if strip_identity_block { self.maybe_unwrap_block() } else { self }; + /// This also does not consider macros, so it's only correct after macro-expansion. + pub fn is_potential_trivial_const_arg(&self) -> bool { + let this = self.maybe_unwrap_block(); if let ExprKind::Path(None, path) = &this.kind && path.is_potential_trivial_const_arg() diff --git a/compiler/rustc_ast_lowering/src/asm.rs b/compiler/rustc_ast_lowering/src/asm.rs index 215e6d84d0f0a..e4b67a7cf8997 100644 --- a/compiler/rustc_ast_lowering/src/asm.rs +++ b/compiler/rustc_ast_lowering/src/asm.rs @@ -223,7 +223,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let parent_def_id = self.current_def_id_parent; let node_id = self.next_node_id(); // HACK(min_generic_const_args): see lower_anon_const - if !expr.is_potential_trivial_const_arg(true) { + if !expr.is_potential_trivial_const_arg() { self.create_def( parent_def_id, node_id, diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 3af29838b72c4..72ad221b30dc9 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -456,7 +456,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let node_id = self.next_node_id(); // HACK(min_generic_const_args): see lower_anon_const - if !arg.is_potential_trivial_const_arg(true) { + if !arg.is_potential_trivial_const_arg() { // Add a definition for the in-band const def. self.create_def(parent_def_id, node_id, kw::Empty, DefKind::AnonConst, f.span); } diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 0b2969a49ba88..6e283a1fe5386 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -509,6 +509,23 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { def_id } + // HACK: See `DefCollector::handle_lazy_anon_const_def` for why this is necessary. + fn create_or_reuse_anon_const_def( + &mut self, + parent: LocalDefId, + node_id: ast::NodeId, + name: Symbol, + def_kind: DefKind, + span: Span, + ) -> LocalDefId { + debug_assert_eq!(def_kind, DefKind::AnonConst); + if let Some(def_id) = self.opt_local_def_id(node_id) { + def_id + } else { + self.create_def(parent, node_id, name, def_kind, span) + } + } + fn next_node_id(&mut self) -> NodeId { let start = self.resolver.next_node_id; let next = start.as_u32().checked_add(1).expect("input too large; ran out of NodeIds"); @@ -2181,19 +2198,13 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { /// See [`hir::ConstArg`] for when to use this function vs /// [`Self::lower_anon_const_to_const_arg`]. fn lower_anon_const_to_anon_const(&mut self, c: &AnonConst) -> &'hir hir::AnonConst { - if c.value.is_potential_trivial_const_arg(true) { - // HACK(min_generic_const_args): see DefCollector::visit_anon_const - // Over there, we guess if this is a bare param and only create a def if - // we think it's not. However we may can guess wrong (see there for example) - // in which case we have to create the def here. - self.create_def( - self.current_def_id_parent, - c.id, - kw::Empty, - DefKind::AnonConst, - c.value.span, - ); - } + self.create_or_reuse_anon_const_def( + self.current_def_id_parent, + c.id, + kw::Empty, + DefKind::AnonConst, + c.value.span, + ); self.arena.alloc(self.with_new_scopes(c.value.span, |this| { let def_id = this.local_def_id(c.id); diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index bf27b767a4972..24fca6ce78779 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -12,19 +12,19 @@ use rustc_span::hygiene::LocalExpnId; use rustc_span::symbol::{Symbol, kw, sym}; use tracing::debug; -use crate::{ImplTraitContext, InvocationParent, PendingAnonConstInfo, Resolver}; +use crate::{ImplTraitContext, InvocationParent, LazyAnonConstDefInfo, Resolver}; pub(crate) fn collect_definitions( resolver: &mut Resolver<'_, '_>, fragment: &AstFragment, expansion: LocalExpnId, ) { - let InvocationParent { parent_def, pending_anon_const_info, impl_trait_context, in_attr } = + let InvocationParent { parent_def, lazy_anon_const_def_info, impl_trait_context, in_attr } = resolver.invocation_parents[&expansion]; let mut visitor = DefCollector { resolver, parent_def, - pending_anon_const_info, + lazy_anon_const_def_info, expansion, impl_trait_context, in_attr, @@ -41,8 +41,8 @@ struct DefCollector<'a, 'ra, 'tcx> { /// the anon const. That's because we lower some anon consts into `hir::ConstArgKind::Path`, /// which don't have defs. /// - /// See `Self::visit_anon_const()`. - pending_anon_const_info: Option, + /// See `Self::handle_lazy_anon_const_def` for more details. + lazy_anon_const_def_info: Option, impl_trait_context: ImplTraitContext, in_attr: bool, expansion: LocalExpnId, @@ -57,6 +57,10 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> { span: Span, ) -> LocalDefId { let parent_def = self.parent_def; + // When recursion into anon-consts, we must only create nested definitions + // after creating the `DefId` for the anon-const. See `handle_lazy_anon_const_def` + // for more details. + debug_assert_eq!(self.lazy_anon_const_def_info, None); debug!( "create_def(node_id={:?}, def_kind={:?}, parent_def={:?})", node_id, def_kind, parent_def @@ -73,12 +77,88 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> { .def_id() } + /// When lowering anonymous constants, we only actually create a definition + /// for it if it is non-trivial. Correctly handling this when macro's are + /// involved is difficult, so when a macro may cause an anon const to be + /// non-trivial, we use this function as we'd otherwise have to track this + /// exact same information somewhere else. + fn create_or_reuse_anon_const_def( + &mut self, + node_id: NodeId, + name: Symbol, + def_kind: DefKind, + span: Span, + ) -> LocalDefId { + debug_assert_eq!(def_kind, DefKind::AnonConst); + if let Some(def_id) = self.resolver.opt_local_def_id(node_id) { + debug_assert_eq!( + self.resolver.tcx.parent(def_id.to_def_id()), + self.parent_def.to_def_id(), + "reusing incorrect anon const defintion: {node_id:?} {span:?}" + ); + def_id + } else { + self.create_def(node_id, name, def_kind, span) + } + } + fn with_parent(&mut self, parent_def: LocalDefId, f: F) { let orig_parent_def = mem::replace(&mut self.parent_def, parent_def); f(self); self.parent_def = orig_parent_def; } + /// Trivial const arguments get directly lowered to `hir::ConstArgKind::Path` instead + /// of an anon-const. Because of this, we do not create a `DefId` for the anonymous + /// constant. To support macros as const arguments and especially to support macros + /// expanding to nothing, e.g. `foo() -> [u8; { empty! {} N }]`, we + /// cannot eagerly know whether an AST-constant needs a `DefId`. + /// + /// See `tests/ui/const-generics/early/const_arg_trivial_macro_expansion-3-pass.rs` + /// for examples where this is relevant. + /// + /// We therefore only create `DefId` for the anon const lazily, either when we need + /// the `DefId` for a nested definition, or when we lower the AST-constant to a HIR + /// anon-const. We use [`DefCollector::create_or_reuse_anon_const_def`] to simplify + /// the tracking of whether a `DefId` has already been created. + /// + /// When recursing into anon-consts we set `lazy_anon_const_def_info` to `Some` and + /// use this function in all places could potentially create definitions. It then + /// lazily creates the `DefId` of the anon-const if it may be needed and the anon-const + /// is definitely non-trivial. The `lazy_anon_const_def_info` is also stored in the + /// [`InvocationParent`] when encountering any macros inside of the anon-const. + /// + /// There are two requirements here: + /// - the anon-const is definitely a trivial const-arg: we must not create a `DefId`. + /// - we encounter a nested definition inside of the anon-const: we must create a `DefId` + /// for the anon-const and provide it as a parent to the nested definition. + /// + /// The first requirement is handled by only creating the `DefId` for the anon-const + /// when encountering something that's definitely not a trivial const-arg. We make sure + /// the second requirement is satisfied by asserting that the `lazy_anon_const_def_info` + /// is `None` whenever we create a new definition. + fn handle_lazy_anon_const_def( + &mut self, + is_potential_trivial_const_arg: impl FnOnce() -> bool, + f: impl FnOnce(&mut Self), + ) { + if let Some(def_info) = self.lazy_anon_const_def_info + && !is_potential_trivial_const_arg() + { + self.lazy_anon_const_def_info = None; + let parent = self.create_or_reuse_anon_const_def( + def_info.id, + kw::Empty, + DefKind::AnonConst, + def_info.span, + ); + self.with_parent(parent, f); + self.lazy_anon_const_def_info = Some(def_info); + } else { + f(self) + } + } + fn with_impl_trait( &mut self, impl_trait_context: ImplTraitContext, @@ -110,61 +190,14 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> { fn visit_macro_invoc(&mut self, id: NodeId) { let id = id.placeholder_to_expn_id(); - let pending_anon_const_info = self.pending_anon_const_info.take(); let old_parent = self.resolver.invocation_parents.insert(id, InvocationParent { parent_def: self.parent_def, - pending_anon_const_info, + lazy_anon_const_def_info: self.lazy_anon_const_def_info, impl_trait_context: self.impl_trait_context, in_attr: self.in_attr, }); assert!(old_parent.is_none(), "parent `LocalDefId` is reset for an invocation"); } - - /// Determines whether the const argument `AnonConst` is a simple macro call, optionally - /// surrounded with braces. - /// - /// If this const argument *is* a trivial macro call then the id for the macro call is - /// returned along with the information required to build the anon const's def if - /// the macro call expands to a non-trivial expression. - fn is_const_arg_trivial_macro_expansion( - &self, - anon_const: &'a AnonConst, - ) -> Option<(PendingAnonConstInfo, NodeId)> { - anon_const.value.optionally_braced_mac_call(false).map(|(block_was_stripped, id)| { - ( - PendingAnonConstInfo { - id: anon_const.id, - span: anon_const.value.span, - block_was_stripped, - }, - id, - ) - }) - } - - /// Determines whether the expression `const_arg_sub_expr` is a simple macro call, sometimes - /// surrounded with braces if a set of braces has not already been entered. This is required - /// as `{ N }` is treated as equivalent to a bare parameter `N` whereas `{{ N }}` is treated as - /// a real block expression and is lowered to an anonymous constant which is not allowed to use - /// generic parameters. - /// - /// If this expression is a trivial macro call then the id for the macro call is - /// returned along with the information required to build the anon const's def if - /// the macro call expands to a non-trivial expression. - fn is_const_arg_sub_expr_trivial_macro_expansion( - &self, - const_arg_sub_expr: &'a Expr, - ) -> Option<(PendingAnonConstInfo, NodeId)> { - let pending_anon = self.pending_anon_const_info.unwrap_or_else(|| - panic!("Checking expr is trivial macro call without having entered anon const: `{const_arg_sub_expr:?}`"), - ); - - const_arg_sub_expr.optionally_braced_mac_call(pending_anon.block_was_stripped).map( - |(block_was_stripped, id)| { - (PendingAnonConstInfo { block_was_stripped, ..pending_anon }, id) - }, - ) - } } impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { @@ -375,81 +408,6 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { } } - fn visit_anon_const(&mut self, constant: &'a AnonConst) { - // HACK(min_generic_const_args): don't create defs for anon consts if we think they will - // later be turned into ConstArgKind::Path's. because this is before resolve is done, we - // may accidentally identify a construction of a unit struct as a param and not create a - // def. we'll then create a def later in ast lowering in this case. the parent of nested - // items will be messed up, but that's ok because there can't be any if we're just looking - // for bare idents. - - if let Some((pending_anon, macro_invoc)) = - self.is_const_arg_trivial_macro_expansion(constant) - { - self.pending_anon_const_info = Some(pending_anon); - return self.visit_macro_invoc(macro_invoc); - } else if constant.value.is_potential_trivial_const_arg(true) { - return visit::walk_anon_const(self, constant); - } - - let def = self.create_def(constant.id, kw::Empty, DefKind::AnonConst, constant.value.span); - self.with_parent(def, |this| visit::walk_anon_const(this, constant)); - } - - fn visit_expr(&mut self, expr: &'a Expr) { - // If we're visiting the expression of a const argument that was a macro call then - // check if it is *still* unknown whether it is a trivial const arg or not. If so - // recurse into the macro call and delay creating the anon const def until expansion. - if self.pending_anon_const_info.is_some() - && let Some((pending_anon, macro_invoc)) = - self.is_const_arg_sub_expr_trivial_macro_expansion(expr) - { - self.pending_anon_const_info = Some(pending_anon); - return self.visit_macro_invoc(macro_invoc); - } - - // See self.pending_anon_const_info for explanation - let parent_def = self - .pending_anon_const_info - .take() - // If we already stripped away a set of braces then do not do it again when determining - // if the macro expanded to a trivial const arg. This arises in cases such as: - // `Foo<{ bar!() }>` where `bar!()` expands to `{ N }`. This should not be considered a - // trivial const argument even though `{ N }` by itself *is*. - .filter(|pending_anon| { - !expr.is_potential_trivial_const_arg(!pending_anon.block_was_stripped) - }) - .map(|pending_anon| { - self.create_def(pending_anon.id, kw::Empty, DefKind::AnonConst, pending_anon.span) - }) - .unwrap_or(self.parent_def); - - self.with_parent(parent_def, |this| { - let parent_def = match expr.kind { - ExprKind::MacCall(..) => return this.visit_macro_invoc(expr.id), - ExprKind::Closure(..) | ExprKind::Gen(..) => { - this.create_def(expr.id, kw::Empty, DefKind::Closure, expr.span) - } - ExprKind::ConstBlock(ref constant) => { - for attr in &expr.attrs { - visit::walk_attribute(this, attr); - } - let def = this.create_def( - constant.id, - kw::Empty, - DefKind::InlineConst, - constant.value.span, - ); - this.with_parent(def, |this| visit::walk_anon_const(this, constant)); - return; - } - _ => this.parent_def, - }; - - this.with_parent(parent_def, |this| visit::walk_expr(this, expr)) - }) - } - fn visit_ty(&mut self, ty: &'a Ty) { match &ty.kind { TyKind::MacCall(..) => self.visit_macro_invoc(ty.id), @@ -477,11 +435,59 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { } } + fn visit_anon_const(&mut self, constant: &'a AnonConst) { + // Handling anon-consts is quite subtle, see `Self::handle_lazy_anon_const_def`. + self.lazy_anon_const_def_info = + Some(LazyAnonConstDefInfo { id: constant.id, span: constant.value.span }); + visit::walk_anon_const(self, constant); + self.lazy_anon_const_def_info = None; + } + + fn visit_expr(&mut self, expr: &'a Expr) { + self.handle_lazy_anon_const_def( + || match &expr.kind { + ExprKind::Block(_, None) => true, + _ => expr.is_potential_trivial_const_arg(), + }, + |this| { + let parent_def = match expr.kind { + ExprKind::MacCall(..) => return this.visit_macro_invoc(expr.id), + ExprKind::Closure(..) | ExprKind::Gen(..) => { + this.create_def(expr.id, kw::Empty, DefKind::Closure, expr.span) + } + ExprKind::ConstBlock(ref constant) => { + for attr in &expr.attrs { + visit::walk_attribute(this, attr); + } + let def = this.create_def( + constant.id, + kw::Empty, + DefKind::InlineConst, + constant.value.span, + ); + this.with_parent(def, |this| visit::walk_anon_const(this, constant)); + return; + } + _ => this.parent_def, + }; + + this.with_parent(parent_def, |this| visit::walk_expr(this, expr)) + }, + ) + } + fn visit_stmt(&mut self, stmt: &'a Stmt) { - match stmt.kind { - StmtKind::MacCall(..) => self.visit_macro_invoc(stmt.id), - _ => visit::walk_stmt(self, stmt), - } + self.handle_lazy_anon_const_def( + || match &stmt.kind { + StmtKind::Expr(expr) => expr.is_potential_trivial_const_arg(), + StmtKind::MacCall(_) => true, + _ => false, + }, + |this| match stmt.kind { + StmtKind::MacCall(..) => this.visit_macro_invoc(stmt.id), + _ => visit::walk_stmt(this, stmt), + }, + ) } fn visit_arm(&mut self, arm: &'a Arm) { diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 26b345f5941c4..170cc29e6d158 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -4522,7 +4522,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { ); self.resolve_anon_const_manual( - constant.value.is_potential_trivial_const_arg(true), + constant.value.is_potential_trivial_const_arg(), anon_const_kind, |this| this.resolve_expr(&constant.value, None), ) @@ -4686,7 +4686,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { // that is how they will be later lowered to HIR. if const_args.contains(&idx) { self.resolve_anon_const_manual( - argument.is_potential_trivial_const_arg(true), + argument.is_potential_trivial_const_arg(), AnonConstKind::ConstArg(IsRepeatExpr::No), |this| this.resolve_expr(argument, None), ); diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index e382295b8f6d8..63304f854766e 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -174,7 +174,7 @@ impl<'ra> ParentScope<'ra> { #[derive(Copy, Debug, Clone)] struct InvocationParent { parent_def: LocalDefId, - pending_anon_const_info: Option, + lazy_anon_const_def_info: Option, impl_trait_context: ImplTraitContext, in_attr: bool, } @@ -182,19 +182,16 @@ struct InvocationParent { impl InvocationParent { const ROOT: Self = Self { parent_def: CRATE_DEF_ID, - pending_anon_const_info: None, + lazy_anon_const_def_info: None, impl_trait_context: ImplTraitContext::Existential, in_attr: false, }; } -#[derive(Copy, Debug, Clone)] -struct PendingAnonConstInfo { - // A const arg is only a "trivial" const arg if it has at *most* one set of braces - // around the argument. We track whether we have stripped an outter brace so that - // if a macro expands to a braced expression *and* the macro was itself inside of - // some braces then we can consider it to be a non-trivial const argument. - block_was_stripped: bool, +/// The information necessary to lazily create the `DefId` for a non-trivial +/// anonymous constant. See `DefCollector::handle_lazy_anon_const_def` for more details. +#[derive(Copy, Debug, Clone, PartialEq, Eq)] +struct LazyAnonConstDefInfo { id: NodeId, span: Span, } diff --git a/tests/ui/const-generics/early/const_arg_trivial_macro_expansion.rs b/tests/ui/const-generics/early/const_arg_trivial_macro_expansion-1.rs similarity index 56% rename from tests/ui/const-generics/early/const_arg_trivial_macro_expansion.rs rename to tests/ui/const-generics/early/const_arg_trivial_macro_expansion-1.rs index 2fdd703ab6f32..5ea445520f1a5 100644 --- a/tests/ui/const-generics/early/const_arg_trivial_macro_expansion.rs +++ b/tests/ui/const-generics/early/const_arg_trivial_macro_expansion-1.rs @@ -85,6 +85,14 @@ macro_rules! braced_braced_expr { () => {{ braced_expr!() }}; } +macro_rules! closure { + () => { |()| () }; +} + +macro_rules! empty { + () => {}; +} + #[rustfmt::skip] mod array_paren_call { // Arrays where the expanded result is a `Res::Err` @@ -128,6 +136,14 @@ mod array_paren_call { fn array_33() -> [(); braced_expr!()] { loop {} } fn array_34() -> [(); { unbraced_expr!() }] { loop {} } fn array_35() -> [(); { braced_expr!() }] { loop {} } + + // Arrays whose expanded form contains a nested definition + fn array_36() -> [(); closure!()] { loop {} } + fn array_37() -> [(); { closure!() }] { loop {} } + + // Arrays whose macro expansion is empty + fn array_38() -> [(); empty!()] { loop {} } + fn array_39() -> [(); { empty!() }] { loop {} } } #[rustfmt::skip] @@ -173,6 +189,14 @@ mod array_brace_call { fn array_33() -> [(); braced_expr!{}] { loop {} } fn array_34() -> [(); { unbraced_expr!{} }] { loop {} } fn array_35() -> [(); { braced_expr!{} }] { loop {} } + + // Arrays whose expanded form contains a nested definition + fn array_36() -> [(); closure!{}] { loop {} } + fn array_37() -> [(); { closure!{} }] { loop {} } + + // Arrays whose macro expansion is empty + fn array_38() -> [(); empty!{}] { loop {} } + fn array_39() -> [(); { empty!{} }] { loop {} } } #[rustfmt::skip] @@ -218,6 +242,14 @@ mod array_square_call { fn array_33() -> [(); braced_expr![]] { loop {} } fn array_34() -> [(); { unbraced_expr![] }] { loop {} } fn array_35() -> [(); { braced_expr![] }] { loop {} } + + // Arrays whose expanded form contains a nested definition + fn array_36() -> [(); closure![]] { loop {} } + fn array_37() -> [(); { closure![] }] { loop {} } + + // Arrays whose macro expansion is empty + fn array_38() -> [(); empty![]] { loop {} } + fn array_39() -> [(); { empty![] }] { loop {} } } struct Foo; @@ -255,18 +287,26 @@ mod adt_paren_call { fn adt_23() -> Foo<{ braced_ident!() }> { loop {} } // An ADT where the expanded result is a complex expr - fn array_24() -> Foo { loop {} } - fn array_25() -> Foo { loop {} } - fn array_26() -> Foo { loop {} } - fn array_27() -> Foo { loop {} } - fn array_28() -> Foo<{ unbraced_unbraced_expr!() }> { loop {} } - fn array_29() -> Foo<{ braced_unbraced_expr!() }> { loop {} } - fn array_30() -> Foo<{ unbraced_braced_expr!() }> { loop {} } - fn array_31() -> Foo<{ braced_braced_expr!() }> { loop {} } - fn array_32() -> Foo { loop {} } - fn array_33() -> Foo { loop {} } - fn array_34() -> Foo<{ unbraced_expr!() }> { loop {} } - fn array_35() -> Foo<{ braced_expr!() }> { loop {} } + fn adt_24() -> Foo { loop {} } + fn adt_25() -> Foo { loop {} } + fn adt_26() -> Foo { loop {} } + fn adt_27() -> Foo { loop {} } + fn adt_28() -> Foo<{ unbraced_unbraced_expr!() }> { loop {} } + fn adt_29() -> Foo<{ braced_unbraced_expr!() }> { loop {} } + fn adt_30() -> Foo<{ unbraced_braced_expr!() }> { loop {} } + fn adt_31() -> Foo<{ braced_braced_expr!() }> { loop {} } + fn adt_32() -> Foo { loop {} } + fn adt_33() -> Foo { loop {} } + fn adt_34() -> Foo<{ unbraced_expr!() }> { loop {} } + fn adt_35() -> Foo<{ braced_expr!() }> { loop {} } + + // An ADT whose expanded form contains a nested definition + fn adt_36() -> Foo { loop {} } + fn adt_37() -> Foo<{ closure!() }> { loop {} } + + // An ADT whose macro expansion is empty + fn adt_38() -> Foo { loop {} } + fn adt_39() -> Foo<{ empty!() }> { loop {} } } #[rustfmt::skip] @@ -302,18 +342,26 @@ mod adt_brace_call { fn adt_23() -> Foo<{ braced_ident!{} }> { loop {} } // An ADT where the expanded result is a complex expr - fn array_24() -> Foo { loop {} } - fn array_25() -> Foo { loop {} } - fn array_26() -> Foo { loop {} } - fn array_27() -> Foo { loop {} } - fn array_28() -> Foo<{ unbraced_unbraced_expr!{} }> { loop {} } - fn array_29() -> Foo<{ braced_unbraced_expr!{} }> { loop {} } - fn array_30() -> Foo<{ unbraced_braced_expr!{} }> { loop {} } - fn array_31() -> Foo<{ braced_braced_expr!{} }> { loop {} } - fn array_32() -> Foo { loop {} } - fn array_33() -> Foo { loop {} } - fn array_34() -> Foo<{ unbraced_expr!{} }> { loop {} } - fn array_35() -> Foo<{ braced_expr!{} }> { loop {} } + fn adt_24() -> Foo { loop {} } + fn adt_25() -> Foo { loop {} } + fn adt_26() -> Foo { loop {} } + fn adt_27() -> Foo { loop {} } + fn adt_28() -> Foo<{ unbraced_unbraced_expr!{} }> { loop {} } + fn adt_29() -> Foo<{ braced_unbraced_expr!{} }> { loop {} } + fn adt_30() -> Foo<{ unbraced_braced_expr!{} }> { loop {} } + fn adt_31() -> Foo<{ braced_braced_expr!{} }> { loop {} } + fn adt_32() -> Foo { loop {} } + fn adt_33() -> Foo { loop {} } + fn adt_34() -> Foo<{ unbraced_expr!{} }> { loop {} } + fn adt_35() -> Foo<{ braced_expr!{} }> { loop {} } + + // An ADT whose expanded form contains a nested definition + fn adt_36() -> Foo { loop {} } + fn adt_37() -> Foo<{ closure!{} }> { loop {} } + + // An ADT whose macro expansion is empty + fn adt_38() -> Foo { loop {} } + fn adt_39() -> Foo<{ empty!{} }> { loop {} } } #[rustfmt::skip] @@ -349,18 +397,185 @@ mod adt_square_call { fn adt_23() -> Foo<{ braced_ident![] }> { loop {} } // An ADT where the expanded result is a complex expr - fn array_24() -> Foo { loop {} } - fn array_25() -> Foo { loop {} } - fn array_26() -> Foo { loop {} } - fn array_27() -> Foo { loop {} } - fn array_28() -> Foo<{ unbraced_unbraced_expr![] }> { loop {} } - fn array_29() -> Foo<{ braced_unbraced_expr![] }> { loop {} } - fn array_30() -> Foo<{ unbraced_braced_expr![] }> { loop {} } - fn array_31() -> Foo<{ braced_braced_expr![] }> { loop {} } - fn array_32() -> Foo { loop {} } - fn array_33() -> Foo { loop {} } - fn array_34() -> Foo<{ unbraced_expr![] }> { loop {} } - fn array_35() -> Foo<{ braced_expr![] }> { loop {} } + fn adt_24() -> Foo { loop {} } + fn adt_25() -> Foo { loop {} } + fn adt_26() -> Foo { loop {} } + fn adt_27() -> Foo { loop {} } + fn adt_28() -> Foo<{ unbraced_unbraced_expr![] }> { loop {} } + fn adt_29() -> Foo<{ braced_unbraced_expr![] }> { loop {} } + fn adt_30() -> Foo<{ unbraced_braced_expr![] }> { loop {} } + fn adt_31() -> Foo<{ braced_braced_expr![] }> { loop {} } + fn adt_32() -> Foo { loop {} } + fn adt_33() -> Foo { loop {} } + fn adt_34() -> Foo<{ unbraced_expr![] }> { loop {} } + fn adt_35() -> Foo<{ braced_expr![] }> { loop {} } + + // An ADT whose expanded form contains a nested definition + fn adt_36() -> Foo { loop {} } + fn adt_37() -> Foo<{ closure![] }> { loop {} } + + // An ADT whose macro expansion is empty + fn adt_38() -> Foo { loop {} } + fn adt_39() -> Foo<{ empty![] }> { loop {} } +} + +#[rustfmt::skip] +mod repeat_paren_call { + // A repeat expr where the expanded result is a `Res::Err` + fn repeat_0() { [(); unbraced_unbraced_ident!()]; } + fn repeat_1() { [(); braced_unbraced_ident!()]; } + fn repeat_2() { [(); unbraced_braced_ident!()]; } + fn repeat_3() { [(); braced_braced_ident!()]; } + fn repeat_4() { [(); { unbraced_unbraced_ident!() }]; } + fn repeat_5() { [(); { braced_unbraced_ident!() }]; } + fn repeat_6() { [(); { unbraced_braced_ident!() }]; } + fn repeat_7() { [(); { braced_braced_ident!() }]; } + fn repeat_8() { [(); unbraced_ident!()]; } + fn repeat_9() { [(); braced_ident!()]; } + fn repeat_10() { [(); { unbraced_ident!() }]; } + fn repeat_11() { [(); { braced_ident!() }]; } + + // A repeat expr where the expanded result is a `Res::ConstParam` + fn repeat_12() { [(); unbraced_unbraced_ident!()]; } + fn repeat_13() { [(); braced_unbraced_ident!()]; } + fn repeat_14() { [(); unbraced_braced_ident!()]; } + fn repeat_15() { [(); braced_braced_ident!()]; } + fn repeat_16() { [(); { unbraced_unbraced_ident!() }]; } + fn repeat_17() { [(); { braced_unbraced_ident!() }]; } + fn repeat_18() { [(); { unbraced_braced_ident!() }]; } + fn repeat_19() { [(); { braced_braced_ident!() }]; } + fn repeat_20() { [(); unbraced_ident!()]; } + fn repeat_21() { [(); braced_ident!()]; } + fn repeat_22() { [(); { unbraced_ident!() }]; } + fn repeat_23() { [(); { braced_ident!() }]; } + + // A repeat expr where the expanded result is a complex expr + fn repeat_24() { [(); unbraced_unbraced_expr!()]; } + fn repeat_25() { [(); braced_unbraced_expr!()]; } + fn repeat_26() { [(); unbraced_braced_expr!()]; } + fn repeat_27() { [(); braced_braced_expr!()]; } + fn repeat_28() { [(); { unbraced_unbraced_expr!() }]; } + fn repeat_29() { [(); { braced_unbraced_expr!() }]; } + fn repeat_30() { [(); { unbraced_braced_expr!() }]; } + fn repeat_31() { [(); { braced_braced_expr!() }]; } + fn repeat_32() { [(); unbraced_expr!()]; } + fn repeat_33() { [(); braced_expr!()]; } + fn repeat_34() { [(); { unbraced_expr!() }]; } + fn repeat_35() { [(); { braced_expr!() }]; } + + // A repeat expr whose expanded form contains a nested definition + fn repeat_36() { [(); closure!()] } + fn repeat_37() { [(); { closure!() }] } + + // A repeat expr whose macro expansion is empty + fn repeat_38() { [(); empty!()] } + fn repeat_39() { [(); { empty!() }] } +} + +#[rustfmt::skip] +mod repeat_brace_call { + // A repeat expr where the expanded result is a `Res::Err` + fn repeat_0() { [(); unbraced_unbraced_ident!{}]; } + fn repeat_1() { [(); braced_unbraced_ident!{}]; } + fn repeat_2() { [(); unbraced_braced_ident!{}]; } + fn repeat_3() { [(); braced_braced_ident!{}]; } + fn repeat_4() { [(); { unbraced_unbraced_ident!{} }]; } + fn repeat_5() { [(); { braced_unbraced_ident!{} }]; } + fn repeat_6() { [(); { unbraced_braced_ident!{} }]; } + fn repeat_7() { [(); { braced_braced_ident!{} }]; } + fn repeat_8() { [(); unbraced_ident!{}]; } + fn repeat_9() { [(); braced_ident!{}]; } + fn repeat_10() { [(); { unbraced_ident!{} }]; } + fn repeat_11() { [(); { braced_ident!{} }]; } + + // A repeat expr where the expanded result is a `Res::ConstParam` + fn repeat_12() { [(); unbraced_unbraced_ident!{}]; } + fn repeat_13() { [(); braced_unbraced_ident!{}]; } + fn repeat_14() { [(); unbraced_braced_ident!{}]; } + fn repeat_15() { [(); braced_braced_ident!{}]; } + fn repeat_16() { [(); { unbraced_unbraced_ident!{} }]; } + fn repeat_17() { [(); { braced_unbraced_ident!{} }]; } + fn repeat_18() { [(); { unbraced_braced_ident!{} }]; } + fn repeat_19() { [(); { braced_braced_ident!{} }]; } + fn repeat_20() { [(); unbraced_ident!{}]; } + fn repeat_21() { [(); braced_ident!{}]; } + fn repeat_22() { [(); { unbraced_ident!{} }]; } + fn repeat_23() { [(); { braced_ident!{} }]; } + + // A repeat expr where the expanded result is a complex expr + fn repeat_24() { [(); unbraced_unbraced_expr!{}]; } + fn repeat_25() { [(); braced_unbraced_expr!{}]; } + fn repeat_26() { [(); unbraced_braced_expr!{}]; } + fn repeat_27() { [(); braced_braced_expr!{}]; } + fn repeat_28() { [(); { unbraced_unbraced_expr!{} }]; } + fn repeat_29() { [(); { braced_unbraced_expr!{} }]; } + fn repeat_30() { [(); { unbraced_braced_expr!{} }]; } + fn repeat_31() { [(); { braced_braced_expr!{} }]; } + fn repeat_32() { [(); unbraced_expr!{}]; } + fn repeat_33() { [(); braced_expr!{}]; } + fn repeat_34() { [(); { unbraced_expr!{} }]; } + fn repeat_35() { [(); { braced_expr!{} }]; } + + // A repeat expr whose expanded form contains a nested definition + fn repeat_36() { [(); closure!{}] } + fn repeat_37() { [(); { closure!{} }] } + + // A repeat expr whose macro expansion is empty + fn repeat_38() { [(); empty!{}] } + fn repeat_39() { [(); { empty!{} }] } +} + +#[rustfmt::skip] +mod repeat_square_call { + // A repeat expr where the expanded result is a `Res::Err` + fn repeat_0() { [(); unbraced_unbraced_ident![]]; } + fn repeat_1() { [(); braced_unbraced_ident![]]; } + fn repeat_2() { [(); unbraced_braced_ident![]]; } + fn repeat_3() { [(); braced_braced_ident![]]; } + fn repeat_4() { [(); { unbraced_unbraced_ident![] }]; } + fn repeat_5() { [(); { braced_unbraced_ident![] }]; } + fn repeat_6() { [(); { unbraced_braced_ident![] }]; } + fn repeat_7() { [(); { braced_braced_ident![] }]; } + fn repeat_8() { [(); unbraced_ident![]]; } + fn repeat_9() { [(); braced_ident![]]; } + fn repeat_10() { [(); { unbraced_ident![] }]; } + fn repeat_11() { [(); { braced_ident![] }]; } + + // A repeat expr where the expanded result is a `Res::ConstParam` + fn repeat_12() { [(); unbraced_unbraced_ident![]]; } + fn repeat_13() { [(); braced_unbraced_ident![]]; } + fn repeat_14() { [(); unbraced_braced_ident![]]; } + fn repeat_15() { [(); braced_braced_ident![]]; } + fn repeat_16() { [(); { unbraced_unbraced_ident![] }]; } + fn repeat_17() { [(); { braced_unbraced_ident![] }]; } + fn repeat_18() { [(); { unbraced_braced_ident![] }]; } + fn repeat_19() { [(); { braced_braced_ident![] }]; } + fn repeat_20() { [(); unbraced_ident![]]; } + fn repeat_21() { [(); braced_ident![]]; } + fn repeat_22() { [(); { unbraced_ident![] }]; } + fn repeat_23() { [(); { braced_ident![] }]; } + + // A repeat expr where the expanded result is a complex expr + fn repeat_24() { [(); unbraced_unbraced_expr![]]; } + fn repeat_25() { [(); braced_unbraced_expr![]]; } + fn repeat_26() { [(); unbraced_braced_expr![]]; } + fn repeat_27() { [(); braced_braced_expr![]]; } + fn repeat_28() { [(); { unbraced_unbraced_expr![] }]; } + fn repeat_29() { [(); { braced_unbraced_expr![] }]; } + fn repeat_30() { [(); { unbraced_braced_expr![] }]; } + fn repeat_31() { [(); { braced_braced_expr![] }]; } + fn repeat_32() { [(); unbraced_expr![]]; } + fn repeat_33() { [(); braced_expr![]]; } + fn repeat_34() { [(); { unbraced_expr![] }]; } + fn repeat_35() { [(); { braced_expr![] }]; } + + // A repeat expr whose expanded form contains a nested definition + fn repeat_36() { [(); closure![]] } + fn repeat_37() { [(); { closure![] }] } + + // A repeat expr whose macro expansion is empty + fn repeat_38() { [(); empty![]] } + fn repeat_39() { [(); { empty![] }] } } fn main() {} diff --git a/tests/ui/const-generics/early/const_arg_trivial_macro_expansion-3-pass.rs b/tests/ui/const-generics/early/const_arg_trivial_macro_expansion-3-pass.rs new file mode 100644 index 0000000000000..fb6190324c6cf --- /dev/null +++ b/tests/ui/const-generics/early/const_arg_trivial_macro_expansion-3-pass.rs @@ -0,0 +1,46 @@ +// Additional checks for macro expansion in const args + +//@ check-pass + +macro_rules! closure { + () => { |()| () }; +} + +macro_rules! indir_semi { + ($nested:ident) => { $nested!(); }; +} + +macro_rules! indir { + ($nested:ident) => { $nested!() }; +} + +macro_rules! empty { + () => {}; +} + +macro_rules! arg { + () => { N }; +} + +struct Adt; + +fn array1() -> [(); { closure!(); 0 }] { loop {} } +fn array2() -> [(); { indir!(closure); 0}] { loop {} } +fn array3() -> [(); { indir_semi!{ closure } 0 }] { loop {} } +fn array4() -> [(); { indir!{ empty } arg!{} }] { loop {} } +fn array5() -> [(); { empty!{} arg!() }] { loop {} } +fn array6() -> [(); { empty!{} N }] { loop {} } +fn array7() -> [(); { arg!{} empty!{} }] { loop {} } +fn array8() -> [(); { empty!{} arg!{} empty!{} }] { loop {} } + +fn adt1() -> Adt<{ closure!(); 0 }> { loop {} } +fn adt2() -> Adt<{ indir!(closure); 0}> { loop {} } +fn adt3() -> Adt<{ indir_semi!{ closure } 0 }> { loop {} } +fn adt4() -> Adt<{ indir!{ empty } arg!{} }> { loop {} } +fn adt5() -> Adt<{ empty!{} arg!() }> { loop {} } +fn adt6() -> Adt<{ empty!{} N }> { loop {} } +fn adt7() -> Adt<{ arg!{} empty!{} }> { loop {} } +fn adt8() -> Adt<{ empty!{} arg!{} empty!{} }> { loop {} } + + +fn main() {} diff --git a/tests/ui/const-generics/early/const_arg_trivial_macro_expansion-4.rs b/tests/ui/const-generics/early/const_arg_trivial_macro_expansion-4.rs new file mode 100644 index 0000000000000..3353d6cf2da3d --- /dev/null +++ b/tests/ui/const-generics/early/const_arg_trivial_macro_expansion-4.rs @@ -0,0 +1,18 @@ +macro_rules! empty { + () => {}; +} + +macro_rules! arg { + () => { + N + //~^ ERROR generic parameters may not be used in const operations + //~| ERROR generic parameters may not be used in const operations + }; +} + +struct Foo; +fn foo() -> Foo<{ arg!{} arg!{} }> { loop {} } +fn bar() -> [(); { empty!{}; N }] { loop {} } +//~^ ERROR generic parameters may not be used in const operations + +fn main() {} diff --git a/tests/ui/const-generics/early/const_arg_trivial_macro_expansion-4.stderr b/tests/ui/const-generics/early/const_arg_trivial_macro_expansion-4.stderr new file mode 100644 index 0000000000000..4722968b20346 --- /dev/null +++ b/tests/ui/const-generics/early/const_arg_trivial_macro_expansion-4.stderr @@ -0,0 +1,37 @@ +error: generic parameters may not be used in const operations + --> $DIR/const_arg_trivial_macro_expansion-4.rs:7:9 + | +LL | N + | ^ cannot perform const operation using `N` +... +LL | fn foo() -> Foo<{ arg!{} arg!{} }> { loop {} } + | ------ in this macro invocation + | + = help: const parameters may only be used as standalone arguments, i.e. `N` + = help: add `#![feature(generic_const_exprs)]` to allow generic const expressions + = note: this error originates in the macro `arg` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: generic parameters may not be used in const operations + --> $DIR/const_arg_trivial_macro_expansion-4.rs:7:9 + | +LL | N + | ^ cannot perform const operation using `N` +... +LL | fn foo() -> Foo<{ arg!{} arg!{} }> { loop {} } + | ------ in this macro invocation + | + = help: const parameters may only be used as standalone arguments, i.e. `N` + = help: add `#![feature(generic_const_exprs)]` to allow generic const expressions + = note: this error originates in the macro `arg` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: generic parameters may not be used in const operations + --> $DIR/const_arg_trivial_macro_expansion-4.rs:15:46 + | +LL | fn bar() -> [(); { empty!{}; N }] { loop {} } + | ^ cannot perform const operation using `N` + | + = help: const parameters may only be used as standalone arguments, i.e. `N` + = help: add `#![feature(generic_const_exprs)]` to allow generic const expressions + +error: aborting due to 3 previous errors + From 47d266f30a89ddec90e8c5098a06ac9942aaddf0 Mon Sep 17 00:00:00 2001 From: lcnr Date: Mon, 25 Nov 2024 17:26:35 +0100 Subject: [PATCH 2/2] add comment to use of `invocation_parent` --- compiler/rustc_expand/src/expand.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 19c2d466f7ca6..413e5a7e3bfdd 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -608,6 +608,12 @@ impl<'a, 'b> MacroExpander<'a, 'b> { if self.cx.sess.opts.incremental.is_some() { for (invoc, _) in invocations.iter_mut() { let expn_id = invoc.expansion_data.id; + // When lowering anon-consts, we may end up with spans whose + // parent is the containing item instead of the anon-const itself. + // See `DefCollector::handle_lazy_anon_const_def` for more details. + // + // FIXME(lcnr): I believe that this shouldn't cause any issues wrt to + // incremental compilation, but am not 100% confident. let parent_def = self.cx.resolver.invocation_parent(expn_id); let span = invoc.span_mut(); *span = span.with_parent(Some(parent_def));