Introduce hir::ConstArgKind::Struct
This commit is contained in:
parent
8f3a76bd38
commit
c65551e835
12 changed files with 205 additions and 19 deletions
|
|
@ -281,6 +281,13 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> {
|
|||
});
|
||||
}
|
||||
|
||||
fn visit_const_arg_expr_field(&mut self, field: &'hir ConstArgExprField<'hir>) {
|
||||
self.insert(field.span, field.hir_id, Node::ConstArgExprField(field));
|
||||
self.with_parent(field.hir_id, |this| {
|
||||
intravisit::walk_const_arg_expr_field(this, field);
|
||||
})
|
||||
}
|
||||
|
||||
fn visit_stmt(&mut self, stmt: &'hir Stmt<'hir>) {
|
||||
self.insert(stmt.span, stmt.hir_id, Node::Stmt(stmt));
|
||||
|
||||
|
|
|
|||
|
|
@ -2410,6 +2410,47 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
|
||||
ConstArg { hir_id: self.next_id(), kind: hir::ConstArgKind::Path(qpath) }
|
||||
}
|
||||
ExprKind::Struct(se) => {
|
||||
let path = self.lower_qpath(
|
||||
expr.id,
|
||||
&se.qself,
|
||||
&se.path,
|
||||
// FIXME(mgca): we may want this to be `Optional` instead, but
|
||||
// we would also need to make sure that HIR ty lowering errors
|
||||
// when these paths wind up in signatures.
|
||||
ParamMode::Explicit,
|
||||
AllowReturnTypeNotation::No,
|
||||
ImplTraitContext::Disallowed(ImplTraitPosition::Path),
|
||||
None,
|
||||
);
|
||||
|
||||
let fields = self.arena.alloc_from_iter(se.fields.iter().map(|f| {
|
||||
let hir_id = self.lower_node_id(f.id);
|
||||
// FIXME(mgca): This might result in lowering attributes that
|
||||
// then go unused as the `Target::ExprField` is not actually
|
||||
// corresponding to `Node::ExprField`.
|
||||
self.lower_attrs(hir_id, &f.attrs, f.span, Target::ExprField);
|
||||
|
||||
let expr = if let ExprKind::ConstBlock(anon_const) = &f.expr.kind {
|
||||
let def_id = self.local_def_id(anon_const.id);
|
||||
let def_kind = self.tcx.def_kind(def_id);
|
||||
assert_eq!(DefKind::AnonConst, def_kind);
|
||||
|
||||
self.lower_anon_const_to_const_arg_direct(anon_const)
|
||||
} else {
|
||||
self.lower_expr_to_const_arg_direct(&f.expr)
|
||||
};
|
||||
|
||||
&*self.arena.alloc(hir::ConstArgExprField {
|
||||
hir_id,
|
||||
field: self.lower_ident(f.ident),
|
||||
expr: self.arena.alloc(expr),
|
||||
span: self.lower_span(f.span),
|
||||
})
|
||||
}));
|
||||
|
||||
ConstArg { hir_id: self.next_id(), kind: hir::ConstArgKind::Struct(path, fields) }
|
||||
}
|
||||
ExprKind::Underscore => ConstArg {
|
||||
hir_id: self.lower_node_id(expr.id),
|
||||
kind: hir::ConstArgKind::Infer(expr.span, ()),
|
||||
|
|
|
|||
|
|
@ -494,6 +494,7 @@ impl<'hir, Unambig> ConstArg<'hir, Unambig> {
|
|||
|
||||
pub fn span(&self) -> Span {
|
||||
match self.kind {
|
||||
ConstArgKind::Struct(path, _) => path.span(),
|
||||
ConstArgKind::Path(path) => path.span(),
|
||||
ConstArgKind::Anon(anon) => anon.span,
|
||||
ConstArgKind::Error(span, _) => span,
|
||||
|
|
@ -513,6 +514,8 @@ pub enum ConstArgKind<'hir, Unambig = ()> {
|
|||
/// However, in the future, we'll be using it for all of those.
|
||||
Path(QPath<'hir>),
|
||||
Anon(&'hir AnonConst),
|
||||
/// Represents construction of struct/struct variants
|
||||
Struct(QPath<'hir>, &'hir [&'hir ConstArgExprField<'hir>]),
|
||||
/// Error const
|
||||
Error(Span, ErrorGuaranteed),
|
||||
/// This variant is not always used to represent inference consts, sometimes
|
||||
|
|
@ -520,6 +523,14 @@ pub enum ConstArgKind<'hir, Unambig = ()> {
|
|||
Infer(Span, Unambig),
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, HashStable_Generic)]
|
||||
pub struct ConstArgExprField<'hir> {
|
||||
pub hir_id: HirId,
|
||||
pub span: Span,
|
||||
pub field: Ident,
|
||||
pub expr: &'hir ConstArg<'hir>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, HashStable_Generic)]
|
||||
pub struct InferArg {
|
||||
#[stable_hasher(ignore)]
|
||||
|
|
@ -4714,6 +4725,7 @@ pub enum Node<'hir> {
|
|||
ConstArg(&'hir ConstArg<'hir>),
|
||||
Expr(&'hir Expr<'hir>),
|
||||
ExprField(&'hir ExprField<'hir>),
|
||||
ConstArgExprField(&'hir ConstArgExprField<'hir>),
|
||||
Stmt(&'hir Stmt<'hir>),
|
||||
PathSegment(&'hir PathSegment<'hir>),
|
||||
Ty(&'hir Ty<'hir>),
|
||||
|
|
@ -4773,6 +4785,7 @@ impl<'hir> Node<'hir> {
|
|||
Node::AssocItemConstraint(c) => Some(c.ident),
|
||||
Node::PatField(f) => Some(f.ident),
|
||||
Node::ExprField(f) => Some(f.ident),
|
||||
Node::ConstArgExprField(f) => Some(f.field),
|
||||
Node::PreciseCapturingNonLifetimeArg(a) => Some(a.ident),
|
||||
Node::Param(..)
|
||||
| Node::AnonConst(..)
|
||||
|
|
|
|||
|
|
@ -396,6 +396,9 @@ pub trait Visitor<'v>: Sized {
|
|||
fn visit_expr_field(&mut self, field: &'v ExprField<'v>) -> Self::Result {
|
||||
walk_expr_field(self, field)
|
||||
}
|
||||
fn visit_const_arg_expr_field(&mut self, field: &'v ConstArgExprField<'v>) -> Self::Result {
|
||||
walk_const_arg_expr_field(self, field)
|
||||
}
|
||||
fn visit_pattern_type_pattern(&mut self, p: &'v TyPat<'v>) -> Self::Result {
|
||||
walk_ty_pat(self, p)
|
||||
}
|
||||
|
|
@ -954,6 +957,17 @@ pub fn walk_expr_field<'v, V: Visitor<'v>>(visitor: &mut V, field: &'v ExprField
|
|||
try_visit!(visitor.visit_ident(*ident));
|
||||
visitor.visit_expr(*expr)
|
||||
}
|
||||
|
||||
pub fn walk_const_arg_expr_field<'v, V: Visitor<'v>>(
|
||||
visitor: &mut V,
|
||||
field: &'v ConstArgExprField<'v>,
|
||||
) -> V::Result {
|
||||
let ConstArgExprField { hir_id, field, expr, span: _ } = field;
|
||||
try_visit!(visitor.visit_id(*hir_id));
|
||||
try_visit!(visitor.visit_ident(*field));
|
||||
visitor.visit_const_arg_unambig(*expr)
|
||||
}
|
||||
|
||||
/// We track whether an infer var is from a [`Ty`], [`ConstArg`], or [`GenericArg`] so that
|
||||
/// HIR visitors overriding [`Visitor::visit_infer`] can determine what kind of infer is being visited
|
||||
pub enum InferKind<'hir> {
|
||||
|
|
@ -1068,6 +1082,15 @@ pub fn walk_const_arg<'v, V: Visitor<'v>>(
|
|||
let ConstArg { hir_id, kind } = const_arg;
|
||||
try_visit!(visitor.visit_id(*hir_id));
|
||||
match kind {
|
||||
ConstArgKind::Struct(qpath, field_exprs) => {
|
||||
try_visit!(visitor.visit_qpath(qpath, *hir_id, qpath.span()));
|
||||
|
||||
for field_expr in *field_exprs {
|
||||
try_visit!(visitor.visit_const_arg_expr_field(field_expr));
|
||||
}
|
||||
|
||||
V::Result::output()
|
||||
}
|
||||
ConstArgKind::Path(qpath) => visitor.visit_qpath(qpath, *hir_id, qpath.span()),
|
||||
ConstArgKind::Anon(anon) => visitor.visit_anon_const(*anon),
|
||||
ConstArgKind::Error(_, _) => V::Result::output(), // errors and spans are not important
|
||||
|
|
|
|||
|
|
@ -2263,6 +2263,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
)
|
||||
.unwrap_or_else(|guar| Const::new_error(tcx, guar))
|
||||
}
|
||||
hir::ConstArgKind::Struct(..) => {
|
||||
span_bug!(const_arg.span(), "lowering `{:?}` is not yet implemented", const_arg)
|
||||
}
|
||||
hir::ConstArgKind::Anon(anon) => self.lower_anon_const(anon),
|
||||
hir::ConstArgKind::Infer(span, ()) => self.ct_infer(None, span),
|
||||
hir::ConstArgKind::Error(_, e) => ty::Const::new_error(tcx, e),
|
||||
|
|
|
|||
|
|
@ -180,6 +180,8 @@ impl<'a> State<'a> {
|
|||
Node::ConstArg(a) => self.print_const_arg(a),
|
||||
Node::Expr(a) => self.print_expr(a),
|
||||
Node::ExprField(a) => self.print_expr_field(a),
|
||||
// FIXME(mgca): proper printing for struct exprs
|
||||
Node::ConstArgExprField(_) => self.word("/* STRUCT EXPR */"),
|
||||
Node::Stmt(a) => self.print_stmt(a),
|
||||
Node::PathSegment(a) => self.print_path_segment(a),
|
||||
Node::Ty(a) => self.print_type(a),
|
||||
|
|
@ -1135,6 +1137,8 @@ impl<'a> State<'a> {
|
|||
|
||||
fn print_const_arg(&mut self, const_arg: &hir::ConstArg<'_>) {
|
||||
match &const_arg.kind {
|
||||
// FIXME(mgca): proper printing for struct exprs
|
||||
ConstArgKind::Struct(..) => self.word("/* STRUCT EXPR */"),
|
||||
ConstArgKind::Path(qpath) => self.print_qpath(qpath, true),
|
||||
ConstArgKind::Anon(anon) => self.print_anon_const(anon),
|
||||
ConstArgKind::Error(_, _) => self.word("/*ERROR*/"),
|
||||
|
|
|
|||
|
|
@ -1440,6 +1440,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
|
|||
hir::Node::ConstArg(hir::ConstArg { kind, .. }) => match kind {
|
||||
// Skip encoding defs for these as they should not have had a `DefId` created
|
||||
hir::ConstArgKind::Error(..)
|
||||
| hir::ConstArgKind::Struct(..)
|
||||
| hir::ConstArgKind::Path(..)
|
||||
| hir::ConstArgKind::Infer(..) => true,
|
||||
hir::ConstArgKind::Anon(..) => false,
|
||||
|
|
|
|||
|
|
@ -739,6 +739,7 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
Node::ConstArg(_) => node_str("const"),
|
||||
Node::Expr(_) => node_str("expr"),
|
||||
Node::ExprField(_) => node_str("expr field"),
|
||||
Node::ConstArgExprField(_) => node_str("const arg expr field"),
|
||||
Node::Stmt(_) => node_str("stmt"),
|
||||
Node::PathSegment(_) => node_str("path segment"),
|
||||
Node::Ty(_) => node_str("type"),
|
||||
|
|
@ -1007,6 +1008,7 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
Node::ConstArg(const_arg) => const_arg.span(),
|
||||
Node::Expr(expr) => expr.span,
|
||||
Node::ExprField(field) => field.span,
|
||||
Node::ConstArgExprField(field) => field.span,
|
||||
Node::Stmt(stmt) => stmt.span,
|
||||
Node::PathSegment(seg) => {
|
||||
let ident_span = seg.ident.span;
|
||||
|
|
|
|||
|
|
@ -359,6 +359,7 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
| Node::Infer(_)
|
||||
| Node::WherePredicate(_)
|
||||
| Node::PreciseCapturingNonLifetimeArg(_)
|
||||
| Node::ConstArgExprField(_)
|
||||
| Node::OpaqueTy(_) => {
|
||||
unreachable!("no sub-expr expected for {parent_node:?}")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ use rustc_hir::def_id::LocalDefId;
|
|||
use rustc_middle::span_bug;
|
||||
use rustc_span::hygiene::LocalExpnId;
|
||||
use rustc_span::{Span, Symbol, sym};
|
||||
use tracing::debug;
|
||||
use tracing::{debug, instrument};
|
||||
|
||||
use crate::{ImplTraitContext, InvocationParent, Resolver};
|
||||
use crate::{ConstArgContext, ImplTraitContext, InvocationParent, Resolver};
|
||||
|
||||
pub(crate) fn collect_definitions(
|
||||
resolver: &mut Resolver<'_, '_>,
|
||||
|
|
@ -21,6 +21,7 @@ pub(crate) fn collect_definitions(
|
|||
expansion: LocalExpnId,
|
||||
) {
|
||||
let invocation_parent = resolver.invocation_parents[&expansion];
|
||||
debug!("new fragment to visit with invocation_parent: {invocation_parent:?}");
|
||||
let mut visitor = DefCollector { resolver, expansion, invocation_parent };
|
||||
fragment.visit_with(&mut visitor);
|
||||
}
|
||||
|
|
@ -74,6 +75,12 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> {
|
|||
self.invocation_parent.impl_trait_context = orig_itc;
|
||||
}
|
||||
|
||||
fn with_const_arg<F: FnOnce(&mut Self)>(&mut self, ctxt: ConstArgContext, f: F) {
|
||||
let orig = mem::replace(&mut self.invocation_parent.const_arg_context, ctxt);
|
||||
f(self);
|
||||
self.invocation_parent.const_arg_context = orig;
|
||||
}
|
||||
|
||||
fn collect_field(&mut self, field: &'a FieldDef, index: Option<usize>) {
|
||||
let index = |this: &Self| {
|
||||
index.unwrap_or_else(|| {
|
||||
|
|
@ -93,7 +100,10 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
fn visit_macro_invoc(&mut self, id: NodeId) {
|
||||
debug!(?self.invocation_parent);
|
||||
|
||||
let id = id.placeholder_to_expn_id();
|
||||
let old_parent = self.resolver.invocation_parents.insert(id, self.invocation_parent);
|
||||
assert!(old_parent.is_none(), "parent `LocalDefId` is reset for an invocation");
|
||||
|
|
@ -360,36 +370,77 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> {
|
|||
// `MgcaDisambiguation::Direct` is set even when MGCA is disabled, so
|
||||
// to avoid affecting stable we have to feature gate the not creating
|
||||
// anon consts
|
||||
if let MgcaDisambiguation::Direct = constant.mgca_disambiguation
|
||||
&& self.resolver.tcx.features().min_generic_const_args()
|
||||
{
|
||||
visit::walk_anon_const(self, constant);
|
||||
return;
|
||||
if !self.resolver.tcx.features().min_generic_const_args() {
|
||||
let parent =
|
||||
self.create_def(constant.id, None, DefKind::AnonConst, constant.value.span);
|
||||
return self.with_parent(parent, |this| visit::walk_anon_const(this, constant));
|
||||
}
|
||||
|
||||
let parent = self.create_def(constant.id, None, DefKind::AnonConst, constant.value.span);
|
||||
self.with_parent(parent, |this| visit::walk_anon_const(this, constant));
|
||||
match constant.mgca_disambiguation {
|
||||
MgcaDisambiguation::Direct => self.with_const_arg(ConstArgContext::Direct, |this| {
|
||||
visit::walk_anon_const(this, constant);
|
||||
}),
|
||||
MgcaDisambiguation::AnonConst => {
|
||||
self.with_const_arg(ConstArgContext::NonDirect, |this| {
|
||||
let parent =
|
||||
this.create_def(constant.id, None, DefKind::AnonConst, constant.value.span);
|
||||
this.with_parent(parent, |this| visit::walk_anon_const(this, constant));
|
||||
})
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
fn visit_expr(&mut self, expr: &'a Expr) {
|
||||
let parent_def = match expr.kind {
|
||||
debug!(?self.invocation_parent);
|
||||
|
||||
let parent_def = match &expr.kind {
|
||||
ExprKind::MacCall(..) => return self.visit_macro_invoc(expr.id),
|
||||
ExprKind::Closure(..) | ExprKind::Gen(..) => {
|
||||
self.create_def(expr.id, None, DefKind::Closure, expr.span)
|
||||
}
|
||||
ExprKind::ConstBlock(ref constant) => {
|
||||
for attr in &expr.attrs {
|
||||
visit::walk_attribute(self, attr);
|
||||
}
|
||||
let def =
|
||||
self.create_def(constant.id, None, DefKind::InlineConst, constant.value.span);
|
||||
self.with_parent(def, |this| visit::walk_anon_const(this, constant));
|
||||
return;
|
||||
ExprKind::ConstBlock(constant) => {
|
||||
// Under `min_generic_const_args` a `const { }` block sometimes
|
||||
// corresponds to an anon const rather than an inline const.
|
||||
let def_kind = match self.invocation_parent.const_arg_context {
|
||||
ConstArgContext::Direct => DefKind::AnonConst,
|
||||
ConstArgContext::NonDirect => DefKind::InlineConst,
|
||||
};
|
||||
|
||||
return self.with_const_arg(ConstArgContext::NonDirect, |this| {
|
||||
for attr in &expr.attrs {
|
||||
visit::walk_attribute(this, attr);
|
||||
}
|
||||
|
||||
let def = this.create_def(constant.id, None, def_kind, constant.value.span);
|
||||
this.with_parent(def, |this| visit::walk_anon_const(this, constant));
|
||||
});
|
||||
}
|
||||
|
||||
// Avoid overwriting `const_arg_context` as we may want to treat const blocks
|
||||
// as being anon consts if we are inside a const argument.
|
||||
ExprKind::Struct(_) => return visit::walk_expr(self, expr),
|
||||
// FIXME(mgca): we may want to handle block labels in some manner
|
||||
ExprKind::Block(block, _) if let [stmt] = block.stmts.as_slice() => match stmt.kind {
|
||||
// FIXME(mgca): this probably means that mac calls that expand
|
||||
// to semi'd const blocks are handled differently to just writing
|
||||
// out a semi'd const block.
|
||||
StmtKind::Expr(..) | StmtKind::MacCall(..) => return visit::walk_expr(self, expr),
|
||||
|
||||
// Fallback to normal behaviour
|
||||
StmtKind::Let(..) | StmtKind::Item(..) | StmtKind::Semi(..) | StmtKind::Empty => {
|
||||
self.invocation_parent.parent_def
|
||||
}
|
||||
},
|
||||
|
||||
_ => self.invocation_parent.parent_def,
|
||||
};
|
||||
|
||||
self.with_parent(parent_def, |this| visit::walk_expr(this, expr))
|
||||
self.with_const_arg(ConstArgContext::NonDirect, |this| {
|
||||
// Note in some cases the `parent_def` here may be the existing parent
|
||||
// and this is actually a no-op `with_parent` call.
|
||||
this.with_parent(parent_def, |this| visit::walk_expr(this, expr))
|
||||
})
|
||||
}
|
||||
|
||||
fn visit_ty(&mut self, ty: &'a Ty) {
|
||||
|
|
|
|||
|
|
@ -187,6 +187,7 @@ struct InvocationParent {
|
|||
parent_def: LocalDefId,
|
||||
impl_trait_context: ImplTraitContext,
|
||||
in_attr: bool,
|
||||
const_arg_context: ConstArgContext,
|
||||
}
|
||||
|
||||
impl InvocationParent {
|
||||
|
|
@ -194,6 +195,7 @@ impl InvocationParent {
|
|||
parent_def: CRATE_DEF_ID,
|
||||
impl_trait_context: ImplTraitContext::Existential,
|
||||
in_attr: false,
|
||||
const_arg_context: ConstArgContext::NonDirect,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -204,6 +206,13 @@ enum ImplTraitContext {
|
|||
InBinding,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
enum ConstArgContext {
|
||||
Direct,
|
||||
/// Either inside of an `AnonConst` or not inside a const argument at all.
|
||||
NonDirect,
|
||||
}
|
||||
|
||||
/// Used for tracking import use types which will be used for redundant import checking.
|
||||
///
|
||||
/// ### Used::Scope Example
|
||||
|
|
|
|||
31
tests/ui/const-generics/mgca/struct_expr_with_macros.rs
Normal file
31
tests/ui/const-generics/mgca/struct_expr_with_macros.rs
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
//@ check-pass
|
||||
|
||||
// Test that the def collector makes `AnonConst`s not `InlineConst`s even
|
||||
// when the const block is obscured via macros.
|
||||
|
||||
#![feature(min_generic_const_args, adt_const_params)]
|
||||
#![expect(incomplete_features)]
|
||||
|
||||
macro_rules! const_block {
|
||||
($e:expr) => { const {
|
||||
$e
|
||||
} }
|
||||
}
|
||||
|
||||
macro_rules! foo_expr {
|
||||
($e:expr) => { Foo {
|
||||
field: $e,
|
||||
} }
|
||||
}
|
||||
|
||||
use std::marker::ConstParamTy;
|
||||
|
||||
#[derive(PartialEq, Eq, ConstParamTy)]
|
||||
struct Foo { field: u32 }
|
||||
|
||||
fn foo<const N: Foo>() {}
|
||||
|
||||
fn main() {
|
||||
foo::<{ Foo { field: const_block!{ 1 + 1 }} }>();
|
||||
foo::<{ foo_expr! { const_block! { 1 + 1 }} }>();
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue