Skip to content

Commit 781ec11

Browse files
committed
Handle macro calls in anon const def creation take 2
1 parent 1d68e6d commit 781ec11

15 files changed

+210
-28
lines changed

compiler/rustc_ast/src/ast.rs

+6-5
Original file line numberDiff line numberDiff line change
@@ -1187,8 +1187,8 @@ impl Expr {
11871187
/// `min_const_generics` as more complex expressions are not supported.
11881188
///
11891189
/// Does not ensure that the path resolves to a const param, the caller should check this.
1190-
pub fn is_potential_trivial_const_arg(&self) -> bool {
1191-
let this = self.maybe_unwrap_block();
1190+
pub fn is_potential_trivial_const_arg(&self, strip_identity_block: bool) -> bool {
1191+
let this = if strip_identity_block { self.maybe_unwrap_block().1 } else { self };
11921192

11931193
if let ExprKind::Path(None, path) = &this.kind
11941194
&& path.is_potential_trivial_const_arg()
@@ -1199,14 +1199,15 @@ impl Expr {
11991199
}
12001200
}
12011201

1202-
pub fn maybe_unwrap_block(&self) -> &Expr {
1202+
/// Returns an expression with (when possible) *one* outter brace removed
1203+
pub fn maybe_unwrap_block(&self) -> (bool, &Expr) {
12031204
if let ExprKind::Block(block, None) = &self.kind
12041205
&& let [stmt] = block.stmts.as_slice()
12051206
&& let StmtKind::Expr(expr) = &stmt.kind
12061207
{
1207-
expr
1208+
(true, expr)
12081209
} else {
1209-
self
1210+
(false, self)
12101211
}
12111212
}
12121213

compiler/rustc_ast_lowering/src/asm.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
220220
let parent_def_id = self.current_def_id_parent;
221221
let node_id = self.next_node_id();
222222
// HACK(min_generic_const_args): see lower_anon_const
223-
if !expr.is_potential_trivial_const_arg() {
223+
if !expr.is_potential_trivial_const_arg(true) {
224224
self.create_def(
225225
parent_def_id,
226226
node_id,

compiler/rustc_ast_lowering/src/expr.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -387,7 +387,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
387387
let node_id = self.next_node_id();
388388

389389
// HACK(min_generic_const_args): see lower_anon_const
390-
if !arg.is_potential_trivial_const_arg() {
390+
if !arg.is_potential_trivial_const_arg(true) {
391391
// Add a definition for the in-band const def.
392392
self.create_def(parent_def_id, node_id, kw::Empty, DefKind::AnonConst, f.span);
393393
}

compiler/rustc_ast_lowering/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -2441,7 +2441,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
24412441
/// See [`hir::ConstArg`] for when to use this function vs
24422442
/// [`Self::lower_anon_const_to_const_arg`].
24432443
fn lower_anon_const_to_anon_const(&mut self, c: &AnonConst) -> &'hir hir::AnonConst {
2444-
if c.value.is_potential_trivial_const_arg() {
2444+
if c.value.is_potential_trivial_const_arg(true) {
24452445
// HACK(min_generic_const_args): see DefCollector::visit_anon_const
24462446
// Over there, we guess if this is a bare param and only create a def if
24472447
// we think it's not. However we may can guess wrong (see there for example)

compiler/rustc_resolve/src/def_collector.rs

+86-18
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,61 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> {
138138
);
139139
assert!(old_parent.is_none(), "parent `LocalDefId` is reset for an invocation");
140140
}
141+
142+
/// Determines whether the const argument `AnonConst` is a simple macro call, optionally
143+
/// surrounded with braces.
144+
///
145+
/// If this const argument *is* a trivial macro call then the id for the macro call is
146+
/// returned along with the information required to build the anon const's def if
147+
/// the macro call expands to a non-trivial expression.
148+
fn is_const_arg_trivial_macro_expansion(
149+
&self,
150+
anon_const: &'a AnonConst,
151+
) -> Option<(PendingAnonConstInfo, NodeId)> {
152+
let (block_was_stripped, expr) = anon_const.value.maybe_unwrap_block();
153+
match expr {
154+
Expr { kind: ExprKind::MacCall(..), id, .. } => Some((
155+
PendingAnonConstInfo {
156+
id: anon_const.id,
157+
span: anon_const.value.span,
158+
block_was_stripped,
159+
},
160+
*id,
161+
)),
162+
_ => None,
163+
}
164+
}
165+
166+
/// Determines whether the expression `const_arg_sub_expr` is a simple macro call, sometimes
167+
/// surrounded with braces if a set of braces has not already been entered. This is required
168+
/// as `{ N }` is treated as equivalent to a bare parameter `N` whereas `{{ N }}` is treated as
169+
/// a real block expression and is lowered to an anonymous constant which is not allowed to use
170+
/// generic parameters.
171+
///
172+
/// If this expression is a trivial macro call then the id for the macro call is
173+
/// returned along with the information required to build the anon const's def if
174+
/// the macro call expands to a non-trivial expression.
175+
fn is_const_arg_sub_expr_trivial_macro_expansion(
176+
&self,
177+
const_arg_sub_expr: &'a Expr,
178+
) -> Option<(PendingAnonConstInfo, NodeId)> {
179+
let pending_anon = self.pending_anon_const_info.unwrap_or_else(||
180+
panic!("Checking expr is trivial macro call without having entered anon const: `{const_arg_sub_expr:?}`"),
181+
);
182+
183+
let (block_was_stripped, expr) = if pending_anon.block_was_stripped {
184+
(true, const_arg_sub_expr)
185+
} else {
186+
const_arg_sub_expr.maybe_unwrap_block()
187+
};
188+
189+
match expr {
190+
Expr { kind: ExprKind::MacCall(..), id, .. } => {
191+
Some((PendingAnonConstInfo { block_was_stripped, ..pending_anon }, *id))
192+
}
193+
_ => None,
194+
}
195+
}
141196
}
142197

143198
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> {
354409
// items will be messed up, but that's ok because there can't be any if we're just looking
355410
// for bare idents.
356411

357-
if matches!(constant.value.maybe_unwrap_block().kind, ExprKind::MacCall(..)) {
358-
// See self.pending_anon_const_info for explanation
359-
self.pending_anon_const_info =
360-
Some(PendingAnonConstInfo { id: constant.id, span: constant.value.span });
361-
return visit::walk_anon_const(self, constant);
362-
} else if constant.value.is_potential_trivial_const_arg() {
412+
if let Some((pending_anon, macro_invoc)) =
413+
self.is_const_arg_trivial_macro_expansion(constant)
414+
{
415+
self.pending_anon_const_info = Some(pending_anon);
416+
return self.visit_macro_invoc(macro_invoc);
417+
} else if constant.value.is_potential_trivial_const_arg(true) {
363418
return visit::walk_anon_const(self, constant);
364419
}
365420

@@ -368,23 +423,36 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> {
368423
}
369424

370425
fn visit_expr(&mut self, expr: &'a Expr) {
371-
if matches!(expr.kind, ExprKind::MacCall(..)) {
372-
return self.visit_macro_invoc(expr.id);
426+
// If we're visiting the expression of a const argument that was a macro call then
427+
// check if it is *still* unknown whether it is a trivial const arg or not. If so
428+
// recurse into the macro call and delay creating the anon const def until expansion.
429+
if self.pending_anon_const_info.is_some()
430+
&& let Some((pending_anon, macro_invoc)) =
431+
self.is_const_arg_sub_expr_trivial_macro_expansion(expr)
432+
{
433+
self.pending_anon_const_info = Some(pending_anon);
434+
return self.visit_macro_invoc(macro_invoc);
373435
}
374436

375-
let grandparent_def = if let Some(pending_anon) = self.pending_anon_const_info.take() {
376-
// See self.pending_anon_const_info for explanation
377-
if !expr.is_potential_trivial_const_arg() {
437+
// See self.pending_anon_const_info for explanation
438+
let parent_def = self
439+
.pending_anon_const_info
440+
.take()
441+
// If we already stripped away a set of braces then do not do it again when determining
442+
// if the macro expanded to a trivial const arg. This arises in cases such as:
443+
// `Foo<{ bar!() }>` where `bar!()` expands to `{ N }`. This should not be considered a
444+
// trivial const argument even though `{ N }` by itself *is*.
445+
.filter(|pending_anon| {
446+
!expr.is_potential_trivial_const_arg(!pending_anon.block_was_stripped)
447+
})
448+
.map(|pending_anon| {
378449
self.create_def(pending_anon.id, kw::Empty, DefKind::AnonConst, pending_anon.span)
379-
} else {
380-
self.parent_def
381-
}
382-
} else {
383-
self.parent_def
384-
};
450+
})
451+
.unwrap_or(self.parent_def);
385452

386-
self.with_parent(grandparent_def, |this| {
453+
self.with_parent(parent_def, |this| {
387454
let parent_def = match expr.kind {
455+
ExprKind::MacCall(..) => return this.visit_macro_invoc(expr.id),
388456
ExprKind::Closure(..) | ExprKind::Gen(..) => {
389457
this.create_def(expr.id, kw::Empty, DefKind::Closure, expr.span)
390458
}

compiler/rustc_resolve/src/late.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -4524,7 +4524,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
45244524
);
45254525

45264526
self.resolve_anon_const_manual(
4527-
constant.value.is_potential_trivial_const_arg(),
4527+
constant.value.is_potential_trivial_const_arg(true),
45284528
anon_const_kind,
45294529
|this| this.resolve_expr(&constant.value, None),
45304530
)
@@ -4688,7 +4688,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
46884688
// that is how they will be later lowered to HIR.
46894689
if const_args.contains(&idx) {
46904690
self.resolve_anon_const_manual(
4691-
argument.is_potential_trivial_const_arg(),
4691+
argument.is_potential_trivial_const_arg(true),
46924692
AnonConstKind::ConstArg(IsRepeatExpr::No),
46934693
|this| this.resolve_expr(argument, None),
46944694
);

compiler/rustc_resolve/src/lib.rs

+5
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,11 @@ impl InvocationParent {
190190

191191
#[derive(Copy, Debug, Clone)]
192192
struct PendingAnonConstInfo {
193+
// A const arg is only a "trivial" const arg if it has at *most* one set of braces
194+
// around the argument. We track whether we have stripped an outter brace so that
195+
// if a macro expands to a braced expression *and* the macro was itself inside of
196+
// some braces then we can consider it to be a non-trivial const argument.
197+
block_was_stripped: bool,
193198
id: NodeId,
194199
span: Span,
195200
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
macro_rules! y {
2+
() => {
3+
N
4+
};
5+
}
6+
7+
struct A<const N: usize>;
8+
9+
fn foo<const N: usize>() -> A<{ y!() }> {
10+
A::<1>
11+
//~^ ERROR: mismatched types
12+
}
13+
14+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/trivial-const-arg-macro-braced-expansion.rs:10:5
3+
|
4+
LL | fn foo<const N: usize>() -> A<{ y!() }> {
5+
| ----------- expected `A<N>` because of return type
6+
LL | A::<1>
7+
| ^^^^^^ expected `N`, found `1`
8+
|
9+
= note: expected struct `A<N>`
10+
found struct `A<1>`
11+
12+
error: aborting due to 1 previous error
13+
14+
For more information about this error, try `rustc --explain E0308`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
macro_rules! y {
2+
() => {
3+
N
4+
//~^ ERROR: generic parameters may not be used in const operations
5+
};
6+
}
7+
8+
struct A<const N: usize>;
9+
10+
#[rustfmt::skip]
11+
fn foo<const N: usize>() -> A<{{ y!() }}> {
12+
A::<1>
13+
}
14+
15+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
error: generic parameters may not be used in const operations
2+
--> $DIR/trivial-const-arg-macro-nested-braces-2.rs:3:9
3+
|
4+
LL | N
5+
| ^ cannot perform const operation using `N`
6+
...
7+
LL | fn foo<const N: usize>() -> A<{{ y!() }}> {
8+
| ---- in this macro invocation
9+
|
10+
= help: const parameters may only be used as standalone arguments, i.e. `N`
11+
= help: add `#![feature(generic_const_exprs)]` to allow generic const expressions
12+
= note: this error originates in the macro `y` (in Nightly builds, run with -Z macro-backtrace for more info)
13+
14+
error: aborting due to 1 previous error
15+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#[rustfmt::skip]
2+
macro_rules! y {
3+
() => {
4+
{ N }
5+
//~^ ERROR: generic parameters may not be used in const operations
6+
};
7+
}
8+
9+
struct A<const N: usize>;
10+
11+
fn foo<const N: usize>() -> A<{ y!() }> {
12+
A::<1>
13+
}
14+
15+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
error: generic parameters may not be used in const operations
2+
--> $DIR/trivial-const-arg-macro-nested-braces.rs:4:11
3+
|
4+
LL | { N }
5+
| ^ cannot perform const operation using `N`
6+
...
7+
LL | fn foo<const N: usize>() -> A<{ y!() }> {
8+
| ---- in this macro invocation
9+
|
10+
= help: const parameters may only be used as standalone arguments, i.e. `N`
11+
= help: add `#![feature(generic_const_exprs)]` to allow generic const expressions
12+
= note: this error originates in the macro `y` (in Nightly builds, run with -Z macro-backtrace for more info)
13+
14+
error: aborting due to 1 previous error
15+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
struct A<const N: usize>;
2+
3+
#[rustfmt::skip]
4+
fn foo<const N: usize>() -> A<{ { N } }> {
5+
//~^ ERROR: generic parameters may not be used in const operations
6+
A::<1>
7+
}
8+
9+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
error: generic parameters may not be used in const operations
2+
--> $DIR/trivial-const-arg-nested-braces.rs:4:35
3+
|
4+
LL | fn foo<const N: usize>() -> A<{ { N } }> {
5+
| ^ cannot perform const operation using `N`
6+
|
7+
= help: const parameters may only be used as standalone arguments, i.e. `N`
8+
= help: add `#![feature(generic_const_exprs)]` to allow generic const expressions
9+
10+
error: aborting due to 1 previous error
11+

0 commit comments

Comments
 (0)