mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-22 14:55:26 +00:00
Auto merge of #130337 - BoxyUwU:anon_const_macro_call, r=camelid
Fix anon const def-creation when macros are involved take 2 Fixes #130321 There were two cases that #129137 did not handle correctly: - Given a const argument `Foo<{ bar!() }>` in which `bar!()` expands to `N`, we would visit the anon const and then visit the `{ bar() }` expression instead of visiting the macro call. This meant that we would build a def for the anon const as `{ bar!() }` is not a trivial const argument as `bar!()` is not a path. - Given a const argument `Foo<{ bar!() }>` is which `bar!()` expands to `{ qux!() }` in which `qux!()` expands to `N`, it should not be considered a trivial const argument as `{{ N }}` has two pairs of braces. If we only looked at `qux`'s expansion it would *look* like a trivial const argument even though it is not. We have to track whether we have "unwrapped" a brace already when recursing into the expansions of `bar`/`qux`/any macro r? `@camelid`
This commit is contained in:
commit
55043f067d
@ -1187,8 +1187,8 @@ impl Expr {
|
|||||||
/// `min_const_generics` as more complex expressions are not supported.
|
/// `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.
|
/// Does not ensure that the path resolves to a const param, the caller should check this.
|
||||||
pub fn is_potential_trivial_const_arg(&self) -> bool {
|
pub fn is_potential_trivial_const_arg(&self, strip_identity_block: bool) -> bool {
|
||||||
let this = self.maybe_unwrap_block();
|
let this = if strip_identity_block { self.maybe_unwrap_block().1 } else { self };
|
||||||
|
|
||||||
if let ExprKind::Path(None, path) = &this.kind
|
if let ExprKind::Path(None, path) = &this.kind
|
||||||
&& path.is_potential_trivial_const_arg()
|
&& path.is_potential_trivial_const_arg()
|
||||||
@ -1199,14 +1199,15 @@ impl Expr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn maybe_unwrap_block(&self) -> &Expr {
|
/// Returns an expression with (when possible) *one* outter brace removed
|
||||||
|
pub fn maybe_unwrap_block(&self) -> (bool, &Expr) {
|
||||||
if let ExprKind::Block(block, None) = &self.kind
|
if let ExprKind::Block(block, None) = &self.kind
|
||||||
&& let [stmt] = block.stmts.as_slice()
|
&& let [stmt] = block.stmts.as_slice()
|
||||||
&& let StmtKind::Expr(expr) = &stmt.kind
|
&& let StmtKind::Expr(expr) = &stmt.kind
|
||||||
{
|
{
|
||||||
expr
|
(true, expr)
|
||||||
} else {
|
} else {
|
||||||
self
|
(false, self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -221,7 +221,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||||||
let parent_def_id = self.current_def_id_parent;
|
let parent_def_id = self.current_def_id_parent;
|
||||||
let node_id = self.next_node_id();
|
let node_id = self.next_node_id();
|
||||||
// HACK(min_generic_const_args): see lower_anon_const
|
// HACK(min_generic_const_args): see lower_anon_const
|
||||||
if !expr.is_potential_trivial_const_arg() {
|
if !expr.is_potential_trivial_const_arg(true) {
|
||||||
self.create_def(
|
self.create_def(
|
||||||
parent_def_id,
|
parent_def_id,
|
||||||
node_id,
|
node_id,
|
||||||
|
@ -389,7 +389,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||||||
let node_id = self.next_node_id();
|
let node_id = self.next_node_id();
|
||||||
|
|
||||||
// HACK(min_generic_const_args): see lower_anon_const
|
// HACK(min_generic_const_args): see lower_anon_const
|
||||||
if !arg.is_potential_trivial_const_arg() {
|
if !arg.is_potential_trivial_const_arg(true) {
|
||||||
// Add a definition for the in-band const def.
|
// Add a definition for the in-band const def.
|
||||||
self.create_def(parent_def_id, node_id, kw::Empty, DefKind::AnonConst, f.span);
|
self.create_def(parent_def_id, node_id, kw::Empty, DefKind::AnonConst, f.span);
|
||||||
}
|
}
|
||||||
|
@ -2466,7 +2466,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||||||
/// See [`hir::ConstArg`] for when to use this function vs
|
/// See [`hir::ConstArg`] for when to use this function vs
|
||||||
/// [`Self::lower_anon_const_to_const_arg`].
|
/// [`Self::lower_anon_const_to_const_arg`].
|
||||||
fn lower_anon_const_to_anon_const(&mut self, c: &AnonConst) -> &'hir hir::AnonConst {
|
fn lower_anon_const_to_anon_const(&mut self, c: &AnonConst) -> &'hir hir::AnonConst {
|
||||||
if c.value.is_potential_trivial_const_arg() {
|
if c.value.is_potential_trivial_const_arg(true) {
|
||||||
// HACK(min_generic_const_args): see DefCollector::visit_anon_const
|
// 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
|
// 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)
|
// we think it's not. However we may can guess wrong (see there for example)
|
||||||
|
@ -138,6 +138,61 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> {
|
|||||||
);
|
);
|
||||||
assert!(old_parent.is_none(), "parent `LocalDefId` is reset for an invocation");
|
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)> {
|
||||||
|
let (block_was_stripped, expr) = anon_const.value.maybe_unwrap_block();
|
||||||
|
match expr {
|
||||||
|
Expr { kind: ExprKind::MacCall(..), id, .. } => Some((
|
||||||
|
PendingAnonConstInfo {
|
||||||
|
id: anon_const.id,
|
||||||
|
span: anon_const.value.span,
|
||||||
|
block_was_stripped,
|
||||||
|
},
|
||||||
|
*id,
|
||||||
|
)),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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:?}`"),
|
||||||
|
);
|
||||||
|
|
||||||
|
let (block_was_stripped, expr) = if pending_anon.block_was_stripped {
|
||||||
|
(true, const_arg_sub_expr)
|
||||||
|
} else {
|
||||||
|
const_arg_sub_expr.maybe_unwrap_block()
|
||||||
|
};
|
||||||
|
|
||||||
|
match expr {
|
||||||
|
Expr { kind: ExprKind::MacCall(..), id, .. } => {
|
||||||
|
Some((PendingAnonConstInfo { block_was_stripped, ..pending_anon }, *id))
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> {
|
impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> {
|
||||||
@ -354,12 +409,12 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> {
|
|||||||
// items will be messed up, but that's ok because there can't be any if we're just looking
|
// items will be messed up, but that's ok because there can't be any if we're just looking
|
||||||
// for bare idents.
|
// for bare idents.
|
||||||
|
|
||||||
if matches!(constant.value.maybe_unwrap_block().kind, ExprKind::MacCall(..)) {
|
if let Some((pending_anon, macro_invoc)) =
|
||||||
// See self.pending_anon_const_info for explanation
|
self.is_const_arg_trivial_macro_expansion(constant)
|
||||||
self.pending_anon_const_info =
|
{
|
||||||
Some(PendingAnonConstInfo { id: constant.id, span: constant.value.span });
|
self.pending_anon_const_info = Some(pending_anon);
|
||||||
return visit::walk_anon_const(self, constant);
|
return self.visit_macro_invoc(macro_invoc);
|
||||||
} else if constant.value.is_potential_trivial_const_arg() {
|
} else if constant.value.is_potential_trivial_const_arg(true) {
|
||||||
return visit::walk_anon_const(self, constant);
|
return visit::walk_anon_const(self, constant);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -368,23 +423,36 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn visit_expr(&mut self, expr: &'a Expr) {
|
fn visit_expr(&mut self, expr: &'a Expr) {
|
||||||
if matches!(expr.kind, ExprKind::MacCall(..)) {
|
// If we're visiting the expression of a const argument that was a macro call then
|
||||||
return self.visit_macro_invoc(expr.id);
|
// 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
let grandparent_def = if let Some(pending_anon) = self.pending_anon_const_info.take() {
|
|
||||||
// See self.pending_anon_const_info for explanation
|
// See self.pending_anon_const_info for explanation
|
||||||
if !expr.is_potential_trivial_const_arg() {
|
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)
|
self.create_def(pending_anon.id, kw::Empty, DefKind::AnonConst, pending_anon.span)
|
||||||
} else {
|
})
|
||||||
self.parent_def
|
.unwrap_or(self.parent_def);
|
||||||
}
|
|
||||||
} else {
|
|
||||||
self.parent_def
|
|
||||||
};
|
|
||||||
|
|
||||||
self.with_parent(grandparent_def, |this| {
|
self.with_parent(parent_def, |this| {
|
||||||
let parent_def = match expr.kind {
|
let parent_def = match expr.kind {
|
||||||
|
ExprKind::MacCall(..) => return this.visit_macro_invoc(expr.id),
|
||||||
ExprKind::Closure(..) | ExprKind::Gen(..) => {
|
ExprKind::Closure(..) | ExprKind::Gen(..) => {
|
||||||
this.create_def(expr.id, kw::Empty, DefKind::Closure, expr.span)
|
this.create_def(expr.id, kw::Empty, DefKind::Closure, expr.span)
|
||||||
}
|
}
|
||||||
|
@ -4546,7 +4546,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
self.resolve_anon_const_manual(
|
self.resolve_anon_const_manual(
|
||||||
constant.value.is_potential_trivial_const_arg(),
|
constant.value.is_potential_trivial_const_arg(true),
|
||||||
anon_const_kind,
|
anon_const_kind,
|
||||||
|this| this.resolve_expr(&constant.value, None),
|
|this| this.resolve_expr(&constant.value, None),
|
||||||
)
|
)
|
||||||
@ -4710,7 +4710,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
|
|||||||
// that is how they will be later lowered to HIR.
|
// that is how they will be later lowered to HIR.
|
||||||
if const_args.contains(&idx) {
|
if const_args.contains(&idx) {
|
||||||
self.resolve_anon_const_manual(
|
self.resolve_anon_const_manual(
|
||||||
argument.is_potential_trivial_const_arg(),
|
argument.is_potential_trivial_const_arg(true),
|
||||||
AnonConstKind::ConstArg(IsRepeatExpr::No),
|
AnonConstKind::ConstArg(IsRepeatExpr::No),
|
||||||
|this| this.resolve_expr(argument, None),
|
|this| this.resolve_expr(argument, None),
|
||||||
);
|
);
|
||||||
|
@ -190,6 +190,11 @@ impl InvocationParent {
|
|||||||
|
|
||||||
#[derive(Copy, Debug, Clone)]
|
#[derive(Copy, Debug, Clone)]
|
||||||
struct PendingAnonConstInfo {
|
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,
|
||||||
id: NodeId,
|
id: NodeId,
|
||||||
span: Span,
|
span: Span,
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,14 @@
|
|||||||
|
macro_rules! y {
|
||||||
|
() => {
|
||||||
|
N
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
struct A<const N: usize>;
|
||||||
|
|
||||||
|
fn foo<const N: usize>() -> A<{ y!() }> {
|
||||||
|
A::<1>
|
||||||
|
//~^ ERROR: mismatched types
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
@ -0,0 +1,14 @@
|
|||||||
|
error[E0308]: mismatched types
|
||||||
|
--> $DIR/trivial-const-arg-macro-braced-expansion.rs:10:5
|
||||||
|
|
|
||||||
|
LL | fn foo<const N: usize>() -> A<{ y!() }> {
|
||||||
|
| ----------- expected `A<N>` because of return type
|
||||||
|
LL | A::<1>
|
||||||
|
| ^^^^^^ expected `N`, found `1`
|
||||||
|
|
|
||||||
|
= note: expected struct `A<N>`
|
||||||
|
found struct `A<1>`
|
||||||
|
|
||||||
|
error: aborting due to 1 previous error
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0308`.
|
@ -0,0 +1,15 @@
|
|||||||
|
macro_rules! y {
|
||||||
|
() => {
|
||||||
|
N
|
||||||
|
//~^ ERROR: generic parameters may not be used in const operations
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
struct A<const N: usize>;
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
|
fn foo<const N: usize>() -> A<{{ y!() }}> {
|
||||||
|
A::<1>
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
@ -0,0 +1,15 @@
|
|||||||
|
error: generic parameters may not be used in const operations
|
||||||
|
--> $DIR/trivial-const-arg-macro-nested-braces-2.rs:3:9
|
||||||
|
|
|
||||||
|
LL | N
|
||||||
|
| ^ cannot perform const operation using `N`
|
||||||
|
...
|
||||||
|
LL | fn foo<const N: usize>() -> A<{{ y!() }}> {
|
||||||
|
| ---- 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 `y` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
|
error: aborting due to 1 previous error
|
||||||
|
|
@ -0,0 +1,15 @@
|
|||||||
|
#[rustfmt::skip]
|
||||||
|
macro_rules! y {
|
||||||
|
() => {
|
||||||
|
{ N }
|
||||||
|
//~^ ERROR: generic parameters may not be used in const operations
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
struct A<const N: usize>;
|
||||||
|
|
||||||
|
fn foo<const N: usize>() -> A<{ y!() }> {
|
||||||
|
A::<1>
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
@ -0,0 +1,15 @@
|
|||||||
|
error: generic parameters may not be used in const operations
|
||||||
|
--> $DIR/trivial-const-arg-macro-nested-braces.rs:4:11
|
||||||
|
|
|
||||||
|
LL | { N }
|
||||||
|
| ^ cannot perform const operation using `N`
|
||||||
|
...
|
||||||
|
LL | fn foo<const N: usize>() -> A<{ y!() }> {
|
||||||
|
| ---- 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 `y` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
|
error: aborting due to 1 previous error
|
||||||
|
|
@ -0,0 +1,9 @@
|
|||||||
|
struct A<const N: usize>;
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
|
fn foo<const N: usize>() -> A<{ { N } }> {
|
||||||
|
//~^ ERROR: generic parameters may not be used in const operations
|
||||||
|
A::<1>
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
@ -0,0 +1,11 @@
|
|||||||
|
error: generic parameters may not be used in const operations
|
||||||
|
--> $DIR/trivial-const-arg-nested-braces.rs:4:35
|
||||||
|
|
|
||||||
|
LL | fn foo<const N: usize>() -> A<{ { N } }> {
|
||||||
|
| ^ 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 1 previous error
|
||||||
|
|
Loading…
Reference in New Issue
Block a user