Auto merge of #125915 - camelid:const-arg-refactor, r=BoxyUwU
Represent type-level consts with new-and-improved `hir::ConstArg` ### Summary This is a step toward `min_generic_const_exprs`. We now represent all const generic arguments using an enum that differentiates between const *paths* (temporarily just bare const params) and arbitrary anon consts that may perform computations. This will enable us to cleanly implement the `min_generic_const_args` plan of allowing the use of generics in paths used as const args, while disallowing their use in arbitrary anon consts. Here is a summary of the salient aspects of this change: - Add `current_def_id_parent` to `LoweringContext` This is needed to track anon const parents properly once we implement `ConstArgKind::Path` (which requires moving anon const def-creation outside of `DefCollector`). - Create `hir::ConstArgKind` enum with `Path` and `Anon` variants. Use it in the existing `hir::ConstArg` struct, replacing the previous `hir::AnonConst` field. - Use `ConstArg` for all instances of const args. Specifically, use it instead of `AnonConst` for assoc item constraints, array lengths, and const param defaults. - Some `ast::AnonConst`s now have their `DefId`s created in rustc_ast_lowering rather than `DefCollector`. This is because in some cases they will end up becoming a `ConstArgKind::Path` instead, which has no `DefId`. We have to solve this in a hacky way where we guess whether the `AnonConst` could end up as a path const since we can't know for sure until after name resolution (`N` could refer to a free const or a nullary struct). If it has no chance as being a const param, then we create a `DefId` in `DefCollector` -- otherwise we decide during ast_lowering. This will have to be updated once all path consts use `ConstArgKind::Path`. - We explicitly use `ConstArgHasType` for array lengths, rather than implicitly relying on anon const type feeding -- this is due to the addition of `ConstArgKind::Path`. - Some tests have their outputs changed, but the changes are for the most part minor (including removing duplicate or almost-duplicate errors). One test now ICEs, but it is for an incomplete, unstable feature and is now tracked at https://github.com/rust-lang/rust/issues/127009. ### Followup items post-merge - Use `ConstArgKind::Path` for all const paths, not just const params. - Fix (no github dont close this issue) #127009 - If a path in generic args doesn't resolve as a type, try to resolve as a const instead (do this in rustc_resolve). Then remove the special-casing from `rustc_ast_lowering`, so that all params will automatically be lowered as `ConstArgKind::Path`. - (?) Consider making `const_evaluatable_unchecked` a hard error, or at least trying it in crater r? `@BoxyUwU`
This commit is contained in:
commit
8c3a94a1c7
72 changed files with 853 additions and 543 deletions
|
@ -912,6 +912,7 @@ impl<'hir> Map<'hir> {
|
|||
Node::Field(field) => field.span,
|
||||
Node::AnonConst(constant) => constant.span,
|
||||
Node::ConstBlock(constant) => self.body(constant.body).value.span,
|
||||
Node::ConstArg(const_arg) => const_arg.span(),
|
||||
Node::Expr(expr) => expr.span,
|
||||
Node::ExprField(field) => field.span,
|
||||
Node::Stmt(stmt) => stmt.span,
|
||||
|
@ -962,7 +963,8 @@ impl<'hir> Map<'hir> {
|
|||
/// Returns the HirId of `N` in `struct Foo<const N: usize = { ... }>` when
|
||||
/// called with the HirId for the `{ ... }` anon const
|
||||
pub fn opt_const_param_default_param_def_id(self, anon_const: HirId) -> Option<LocalDefId> {
|
||||
match self.tcx.parent_hir_node(anon_const) {
|
||||
let const_arg = self.tcx.parent_hir_id(anon_const);
|
||||
match self.tcx.parent_hir_node(const_arg) {
|
||||
Node::GenericParam(GenericParam {
|
||||
def_id: param_id,
|
||||
kind: GenericParamKind::Const { .. },
|
||||
|
@ -1182,6 +1184,7 @@ fn hir_id_to_string(map: Map<'_>, id: HirId) -> String {
|
|||
}
|
||||
Node::AnonConst(_) => node_str("const"),
|
||||
Node::ConstBlock(_) => node_str("const"),
|
||||
Node::ConstArg(_) => node_str("const"),
|
||||
Node::Expr(_) => node_str("expr"),
|
||||
Node::ExprField(_) => node_str("expr field"),
|
||||
Node::Stmt(_) => node_str("stmt"),
|
||||
|
|
|
@ -4,9 +4,9 @@ use crate::ty::{self, GenericArgs, ParamEnv, ParamEnvAnd, Ty, TyCtxt, TypeVisita
|
|||
use either::Either;
|
||||
use rustc_data_structures::intern::Interned;
|
||||
use rustc_error_messages::MultiSpan;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::def_id::LocalDefId;
|
||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_hir::{self as hir, HirId};
|
||||
use rustc_macros::HashStable;
|
||||
use rustc_type_ir::{self as ir, TypeFlags, WithCachedTypeInfo};
|
||||
use tracing::{debug, instrument};
|
||||
|
@ -183,16 +183,55 @@ impl<'tcx> rustc_type_ir::inherent::Const<TyCtxt<'tcx>> for Const<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
/// In some cases, [`hir::ConstArg`]s that are being used in the type system
|
||||
/// through const generics need to have their type "fed" to them
|
||||
/// using the query system.
|
||||
///
|
||||
/// Use this enum with [`Const::from_const_arg`] to instruct it with the
|
||||
/// desired behavior.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum FeedConstTy {
|
||||
/// Feed the type.
|
||||
///
|
||||
/// The `DefId` belongs to the const param that we are supplying
|
||||
/// this (anon) const arg to.
|
||||
Param(DefId),
|
||||
/// Don't feed the type.
|
||||
No,
|
||||
}
|
||||
|
||||
impl<'tcx> Const<'tcx> {
|
||||
/// Convert a [`hir::ConstArg`] to a [`ty::Const`](Self).
|
||||
#[instrument(skip(tcx), level = "debug")]
|
||||
pub fn from_const_arg(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
const_arg: &'tcx hir::ConstArg<'tcx>,
|
||||
feed: FeedConstTy,
|
||||
) -> Self {
|
||||
if let FeedConstTy::Param(param_def_id) = feed
|
||||
&& let hir::ConstArgKind::Anon(anon) = &const_arg.kind
|
||||
{
|
||||
tcx.feed_anon_const_type(anon.def_id, tcx.type_of(param_def_id));
|
||||
}
|
||||
|
||||
match const_arg.kind {
|
||||
hir::ConstArgKind::Path(qpath) => {
|
||||
// FIXME(min_generic_const_args): for now only params are lowered to ConstArgKind::Path
|
||||
Self::from_param(tcx, qpath, const_arg.hir_id)
|
||||
}
|
||||
hir::ConstArgKind::Anon(anon) => Self::from_anon_const(tcx, anon.def_id),
|
||||
}
|
||||
}
|
||||
|
||||
/// Literals and const generic parameters are eagerly converted to a constant, everything else
|
||||
/// becomes `Unevaluated`.
|
||||
#[instrument(skip(tcx), level = "debug")]
|
||||
pub fn from_anon_const(tcx: TyCtxt<'tcx>, def: LocalDefId) -> Self {
|
||||
let body_id = match tcx.hir_node_by_def_id(def) {
|
||||
hir::Node::AnonConst(ac) => ac.body,
|
||||
_ => span_bug!(
|
||||
node => span_bug!(
|
||||
tcx.def_span(def.to_def_id()),
|
||||
"from_anon_const can only process anonymous constants"
|
||||
"from_anon_const can only process anonymous constants, not {node:?}"
|
||||
),
|
||||
};
|
||||
|
||||
|
@ -201,7 +240,7 @@ impl<'tcx> Const<'tcx> {
|
|||
|
||||
let ty = tcx.type_of(def).no_bound_vars().expect("const parameter types cannot be generic");
|
||||
|
||||
match Self::try_from_lit_or_param(tcx, ty, expr) {
|
||||
match Self::try_from_lit(tcx, ty, expr) {
|
||||
Some(v) => v,
|
||||
None => ty::Const::new_unevaluated(
|
||||
tcx,
|
||||
|
@ -213,12 +252,36 @@ impl<'tcx> Const<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Lower a const param to a [`Const`].
|
||||
///
|
||||
/// IMPORTANT: `qpath` must be a const param, otherwise this will panic
|
||||
fn from_param(tcx: TyCtxt<'tcx>, qpath: hir::QPath<'tcx>, hir_id: HirId) -> Self {
|
||||
let hir::QPath::Resolved(_, &hir::Path { res: Res::Def(DefKind::ConstParam, def_id), .. }) =
|
||||
qpath
|
||||
else {
|
||||
span_bug!(qpath.span(), "non-param {qpath:?} passed to Const::from_param")
|
||||
};
|
||||
|
||||
match tcx.named_bound_var(hir_id) {
|
||||
Some(rbv::ResolvedArg::EarlyBound(_)) => {
|
||||
// Find the name and index of the const parameter by indexing the generics of
|
||||
// the parent item and construct a `ParamConst`.
|
||||
let item_def_id = tcx.parent(def_id);
|
||||
let generics = tcx.generics_of(item_def_id);
|
||||
let index = generics.param_def_id_to_index[&def_id];
|
||||
let name = tcx.item_name(def_id);
|
||||
ty::Const::new_param(tcx, ty::ParamConst::new(index, name))
|
||||
}
|
||||
Some(rbv::ResolvedArg::LateBound(debruijn, index, _)) => {
|
||||
ty::Const::new_bound(tcx, debruijn, ty::BoundVar::from_u32(index))
|
||||
}
|
||||
Some(rbv::ResolvedArg::Error(guar)) => ty::Const::new_error(tcx, guar),
|
||||
arg => bug!("unexpected bound var resolution for {:?}: {arg:?}", hir_id),
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(skip(tcx), level = "debug")]
|
||||
fn try_from_lit_or_param(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
ty: Ty<'tcx>,
|
||||
expr: &'tcx hir::Expr<'tcx>,
|
||||
) -> Option<Self> {
|
||||
fn try_from_lit(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, expr: &'tcx hir::Expr<'tcx>) -> Option<Self> {
|
||||
// Unwrap a block, so that e.g. `{ P }` is recognised as a parameter. Const arguments
|
||||
// currently have to be wrapped in curly brackets, so it's necessary to special-case.
|
||||
let expr = match &expr.kind {
|
||||
|
@ -251,34 +314,15 @@ impl<'tcx> Const<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
// FIXME(const_generics): We currently have to special case parameters because `min_const_generics`
|
||||
// does not provide the parents generics to anonymous constants. We still allow generic const
|
||||
// parameters by themselves however, e.g. `N`. These constants would cause an ICE if we were to
|
||||
// ever try to instantiate the generic parameters in their bodies.
|
||||
match expr.kind {
|
||||
hir::ExprKind::Path(hir::QPath::Resolved(
|
||||
_,
|
||||
&hir::Path { res: Res::Def(DefKind::ConstParam, def_id), .. },
|
||||
)) => {
|
||||
match tcx.named_bound_var(expr.hir_id) {
|
||||
Some(rbv::ResolvedArg::EarlyBound(_)) => {
|
||||
// Find the name and index of the const parameter by indexing the generics of
|
||||
// the parent item and construct a `ParamConst`.
|
||||
let item_def_id = tcx.parent(def_id);
|
||||
let generics = tcx.generics_of(item_def_id);
|
||||
let index = generics.param_def_id_to_index[&def_id];
|
||||
let name = tcx.item_name(def_id);
|
||||
Some(ty::Const::new_param(tcx, ty::ParamConst::new(index, name)))
|
||||
}
|
||||
Some(rbv::ResolvedArg::LateBound(debruijn, index, _)) => {
|
||||
Some(ty::Const::new_bound(tcx, debruijn, ty::BoundVar::from_u32(index)))
|
||||
}
|
||||
Some(rbv::ResolvedArg::Error(guar)) => Some(ty::Const::new_error(tcx, guar)),
|
||||
arg => bug!("unexpected bound var resolution for {:?}: {arg:?}", expr.hir_id),
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
if let hir::ExprKind::Path(hir::QPath::Resolved(
|
||||
_,
|
||||
&hir::Path { res: Res::Def(DefKind::ConstParam, _), .. },
|
||||
)) = expr.kind
|
||||
{
|
||||
span_bug!(expr.span, "try_from_lit: received const param which shouldn't be possible")
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -483,15 +527,15 @@ pub fn const_param_default<'tcx>(
|
|||
tcx: TyCtxt<'tcx>,
|
||||
def_id: LocalDefId,
|
||||
) -> ty::EarlyBinder<'tcx, Const<'tcx>> {
|
||||
let default_def_id = match tcx.hir_node_by_def_id(def_id) {
|
||||
let default_ct = match tcx.hir_node_by_def_id(def_id) {
|
||||
hir::Node::GenericParam(hir::GenericParam {
|
||||
kind: hir::GenericParamKind::Const { default: Some(ac), .. },
|
||||
kind: hir::GenericParamKind::Const { default: Some(ct), .. },
|
||||
..
|
||||
}) => ac.def_id,
|
||||
}) => ct,
|
||||
_ => span_bug!(
|
||||
tcx.def_span(def_id),
|
||||
"`const_param_default` expected a generic parameter with a constant"
|
||||
),
|
||||
};
|
||||
ty::EarlyBinder::bind(Const::from_anon_const(tcx, default_def_id))
|
||||
ty::EarlyBinder::bind(Const::from_const_arg(tcx, default_ct, FeedConstTy::No))
|
||||
}
|
||||
|
|
|
@ -86,7 +86,7 @@ pub use self::closure::{
|
|||
CAPTURE_STRUCT_LOCAL,
|
||||
};
|
||||
pub use self::consts::{
|
||||
Const, ConstInt, ConstKind, Expr, ExprKind, ScalarInt, UnevaluatedConst, ValTree,
|
||||
Const, ConstInt, ConstKind, Expr, ExprKind, FeedConstTy, ScalarInt, UnevaluatedConst, ValTree,
|
||||
};
|
||||
pub use self::context::{
|
||||
tls, CtxtInterners, CurrentGcx, DeducedParamAttrs, Feed, FreeRegionInfo, GlobalCtxt, Lift,
|
||||
|
|
|
@ -607,7 +607,9 @@ impl<'a, V> ::std::ops::Index<HirId> for LocalTableInContext<'a, V> {
|
|||
type Output = V;
|
||||
|
||||
fn index(&self, key: HirId) -> &V {
|
||||
self.get(key).expect("LocalTableInContext: key not found")
|
||||
self.get(key).unwrap_or_else(|| {
|
||||
bug!("LocalTableInContext({:?}): key {:?} not found", self.hir_owner, key)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue