Introduce min_generic_const_args and directly represent paths

Co-authored-by: Boxy UwU <rust@boxyuwu.dev>
Co-authored-by: León Orell Valerian Liehr <me@fmease.dev>
This commit is contained in:
Noah Lev 2024-11-19 05:01:59 +00:00 committed by Boxy
parent b71fb5edc0
commit 59e339f766
21 changed files with 361 additions and 195 deletions

View File

@ -2052,6 +2052,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
} }
} }
/// Used when lowering a type argument that turned out to actually be a const argument.
///
/// Only use for that purpose since otherwise it will create a duplicate def.
#[instrument(level = "debug", skip(self))] #[instrument(level = "debug", skip(self))]
fn lower_const_path_to_const_arg( fn lower_const_path_to_const_arg(
&mut self, &mut self,
@ -2060,51 +2063,58 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
ty_id: NodeId, ty_id: NodeId,
span: Span, span: Span,
) -> &'hir hir::ConstArg<'hir> { ) -> &'hir hir::ConstArg<'hir> {
let ct_kind = match res { let tcx = self.tcx;
Res::Def(DefKind::ConstParam, _) => {
let qpath = self.lower_qpath(
ty_id,
&None,
path,
ParamMode::Optional,
AllowReturnTypeNotation::No,
ImplTraitContext::Disallowed(ImplTraitPosition::Path),
None,
);
hir::ConstArgKind::Path(qpath)
}
_ => {
// Construct an AnonConst where the expr is the "ty"'s path.
let parent_def_id = self.current_def_id_parent; // FIXME(min_generic_const_args): we only allow one-segment const paths for now
let node_id = self.next_node_id(); let ct_kind = if path.is_potential_trivial_const_arg()
let span = self.lower_span(span); && (tcx.features().min_generic_const_args()
|| matches!(res, Res::Def(DefKind::ConstParam, _)))
{
let qpath = self.lower_qpath(
ty_id,
&None,
path,
ParamMode::Optional,
AllowReturnTypeNotation::No,
// FIXME(min_generic_const_args): update for `fn foo() -> Bar<FOO<impl Trait>>` support
ImplTraitContext::Disallowed(ImplTraitPosition::Path),
None,
);
hir::ConstArgKind::Path(qpath)
} else {
// Construct an AnonConst where the expr is the "ty"'s path.
// Add a definition for the in-band const def. let parent_def_id = self.current_def_id_parent;
let def_id = let node_id = self.next_node_id();
self.create_def(parent_def_id, node_id, kw::Empty, DefKind::AnonConst, span); let span = self.lower_span(span);
let hir_id = self.lower_node_id(node_id);
let path_expr = Expr { // Add a definition for the in-band const def.
id: ty_id, // We're lowering a const argument that was originally thought to be a type argument,
kind: ExprKind::Path(None, path.clone()), // so the def collector didn't create the def ahead of time. That's why we have to do
// it here.
let def_id =
self.create_def(parent_def_id, node_id, kw::Empty, DefKind::AnonConst, span);
let hir_id = self.lower_node_id(node_id);
let path_expr = Expr {
id: ty_id,
kind: ExprKind::Path(None, path.clone()),
span,
attrs: AttrVec::new(),
tokens: None,
};
let ct = self.with_new_scopes(span, |this| {
self.arena.alloc(hir::AnonConst {
def_id,
hir_id,
body: this.with_def_id_parent(def_id, |this| {
this.lower_const_body(path_expr.span, Some(&path_expr))
}),
span, span,
attrs: AttrVec::new(), })
tokens: None, });
}; hir::ConstArgKind::Anon(ct)
let ct = self.with_new_scopes(span, |this| {
self.arena.alloc(hir::AnonConst {
def_id,
hir_id,
body: this.with_def_id_parent(def_id, |this| {
this.lower_const_body(path_expr.span, Some(&path_expr))
}),
span,
})
});
hir::ConstArgKind::Anon(ct)
}
}; };
self.arena.alloc(hir::ConstArg { self.arena.alloc(hir::ConstArg {
@ -2122,6 +2132,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
#[instrument(level = "debug", skip(self))] #[instrument(level = "debug", skip(self))]
fn lower_anon_const_to_const_arg_direct(&mut self, anon: &AnonConst) -> hir::ConstArg<'hir> { fn lower_anon_const_to_const_arg_direct(&mut self, anon: &AnonConst) -> hir::ConstArg<'hir> {
let tcx = self.tcx;
// Unwrap a block, so that e.g. `{ P }` is recognised as a parameter. Const arguments // 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. // currently have to be wrapped in curly brackets, so it's necessary to special-case.
let expr = if let ExprKind::Block(block, _) = &anon.value.kind let expr = if let ExprKind::Block(block, _) = &anon.value.kind
@ -2135,18 +2146,19 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
}; };
let maybe_res = let maybe_res =
self.resolver.get_partial_res(expr.id).and_then(|partial_res| partial_res.full_res()); self.resolver.get_partial_res(expr.id).and_then(|partial_res| partial_res.full_res());
debug!("res={:?}", maybe_res); // FIXME(min_generic_const_args): we only allow one-segment const paths for now
// FIXME(min_generic_const_args): for now we only lower params to ConstArgKind::Path if let ExprKind::Path(None, path) = &expr.kind
if let Some(res) = maybe_res && path.is_potential_trivial_const_arg()
&& let Res::Def(DefKind::ConstParam, _) = res && (tcx.features().min_generic_const_args()
&& let ExprKind::Path(qself, path) = &expr.kind || matches!(maybe_res, Some(Res::Def(DefKind::ConstParam, _))))
{ {
let qpath = self.lower_qpath( let qpath = self.lower_qpath(
expr.id, expr.id,
qself, &None,
path, path,
ParamMode::Optional, ParamMode::Optional,
AllowReturnTypeNotation::No, AllowReturnTypeNotation::No,
// FIXME(min_generic_const_args): update for `fn foo() -> Bar<FOO<impl Trait>>` support
ImplTraitContext::Disallowed(ImplTraitPosition::Path), ImplTraitContext::Disallowed(ImplTraitPosition::Path),
None, None,
); );

View File

@ -529,6 +529,8 @@ declare_features! (
(unstable, macro_metavar_expr_concat, "1.81.0", Some(124225)), (unstable, macro_metavar_expr_concat, "1.81.0", Some(124225)),
/// Allows `#[marker]` on certain traits allowing overlapping implementations. /// Allows `#[marker]` on certain traits allowing overlapping implementations.
(unstable, marker_trait_attr, "1.30.0", Some(29864)), (unstable, marker_trait_attr, "1.30.0", Some(29864)),
/// Enables the generic const args MVP (only bare paths, not arbitrary computation).
(incomplete, min_generic_const_args, "CURRENT_RUSTC_VERSION", Some(132980)),
/// A minimal, sound subset of specialization intended to be used by the /// A minimal, sound subset of specialization intended to be used by the
/// standard library until the soundness issues with specialization /// standard library until the soundness issues with specialization
/// are fixed. /// are fixed.

View File

@ -46,7 +46,7 @@ use tracing::{debug, instrument};
use crate::check::intrinsic::intrinsic_operation_unsafety; use crate::check::intrinsic::intrinsic_operation_unsafety;
use crate::errors; use crate::errors;
use crate::hir_ty_lowering::{HirTyLowerer, RegionInferReason}; use crate::hir_ty_lowering::{FeedConstTy, HirTyLowerer, RegionInferReason};
pub(crate) mod dump; pub(crate) mod dump;
mod generics_of; mod generics_of;
@ -88,6 +88,7 @@ pub fn provide(providers: &mut Providers) {
coroutine_for_closure, coroutine_for_closure,
opaque_ty_origin, opaque_ty_origin,
rendered_precise_capturing_args, rendered_precise_capturing_args,
const_param_default,
..*providers ..*providers
}; };
} }
@ -1790,3 +1791,23 @@ fn rendered_precise_capturing_args<'tcx>(
_ => None, _ => None,
}) })
} }
fn const_param_default<'tcx>(
tcx: TyCtxt<'tcx>,
def_id: LocalDefId,
) -> ty::EarlyBinder<'tcx, Const<'tcx>> {
let default_ct = match tcx.hir_node_by_def_id(def_id) {
hir::Node::GenericParam(hir::GenericParam {
kind: hir::GenericParamKind::Const { default: Some(ct), .. },
..
}) => ct,
_ => span_bug!(
tcx.def_span(def_id),
"`const_param_default` expected a generic parameter with a constant"
),
};
let icx = ItemCtxt::new(tcx, def_id);
// FIXME(const_generics): investigate which places do and don't need const ty feeding
let ct = icx.lowerer().lower_const_arg(default_ct, FeedConstTy::No);
ty::EarlyBinder::bind(ct)
}

View File

@ -223,11 +223,12 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
trace!(?predicates); trace!(?predicates);
} }
hir::GenericParamKind::Const { .. } => { hir::GenericParamKind::Const { .. } => {
let param_def_id = param.def_id.to_def_id();
let ct_ty = tcx let ct_ty = tcx
.type_of(param.def_id.to_def_id()) .type_of(param_def_id)
.no_bound_vars() .no_bound_vars()
.expect("const parameters cannot be generic"); .expect("const parameters cannot be generic");
let ct = icx.lowerer().lower_const_param(param.hir_id); let ct = icx.lowerer().lower_const_param(param_def_id, param.hir_id);
predicates predicates
.insert((ty::ClauseKind::ConstArgHasType(ct, ct_ty).upcast(tcx), param.span)); .insert((ty::ClauseKind::ConstArgHasType(ct, ct_ty).upcast(tcx), param.span));
} }

View File

@ -19,7 +19,9 @@ use tracing::{debug, instrument};
use super::errors::GenericsArgsErrExtend; use super::errors::GenericsArgsErrExtend;
use crate::bounds::Bounds; use crate::bounds::Bounds;
use crate::errors; use crate::errors;
use crate::hir_ty_lowering::{AssocItemQSelf, HirTyLowerer, PredicateFilter, RegionInferReason}; use crate::hir_ty_lowering::{
AssocItemQSelf, FeedConstTy, HirTyLowerer, PredicateFilter, RegionInferReason,
};
impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
/// Add a `Sized` bound to the `bounds` if appropriate. /// Add a `Sized` bound to the `bounds` if appropriate.
@ -346,9 +348,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
hir::AssocItemConstraintKind::Equality { term } => { hir::AssocItemConstraintKind::Equality { term } => {
let term = match term { let term = match term {
hir::Term::Ty(ty) => self.lower_ty(ty).into(), hir::Term::Ty(ty) => self.lower_ty(ty).into(),
hir::Term::Const(ct) => { hir::Term::Const(ct) => self.lower_const_arg(ct, FeedConstTy::No).into(),
ty::Const::from_const_arg(tcx, ct, ty::FeedConstTy::No).into()
}
}; };
// Find any late-bound regions declared in `ty` that are not // Find any late-bound regions declared in `ty` that are not

View File

@ -115,17 +115,22 @@ fn generic_arg_mismatch_err(
} }
} }
(GenericArg::Const(cnst), GenericParamDefKind::Type { .. }) => { (GenericArg::Const(cnst), GenericParamDefKind::Type { .. }) => {
// FIXME(min_generic_const_args): once ConstArgKind::Path is used for non-params too, if let hir::ConstArgKind::Path(qpath) = cnst.kind
// this should match against that instead of ::Anon && let rustc_hir::QPath::Resolved(_, path) = qpath
if let hir::ConstArgKind::Anon(anon) = cnst.kind && let Res::Def(DefKind::Fn { .. }, id) = path.res
{
err.help(format!("`{}` is a function item, not a type", tcx.item_name(id)));
err.help("function item types cannot be named directly");
} else if let hir::ConstArgKind::Anon(anon) = cnst.kind
&& let body = tcx.hir().body(anon.body) && let body = tcx.hir().body(anon.body)
&& let rustc_hir::ExprKind::Path(rustc_hir::QPath::Resolved(_, path)) = && let rustc_hir::ExprKind::Path(rustc_hir::QPath::Resolved(_, path)) =
body.value.kind body.value.kind
&& let Res::Def(DefKind::Fn { .. }, id) = path.res
{ {
if let Res::Def(DefKind::Fn { .. }, id) = path.res { // FIXME(min_generic_const_args): this branch is dead once new const path lowering
err.help(format!("`{}` is a function item, not a type", tcx.item_name(id))); // (for single-segment paths) is no longer gated
err.help("function item types cannot be named directly"); err.help(format!("`{}` is a function item, not a type", tcx.item_name(id)));
} err.help("function item types cannot be named directly");
} }
} }
_ => {} _ => {}

View File

@ -30,7 +30,7 @@ use rustc_errors::{
Applicability, Diag, DiagCtxtHandle, ErrorGuaranteed, FatalError, struct_span_code_err, Applicability, Diag, DiagCtxtHandle, ErrorGuaranteed, FatalError, struct_span_code_err,
}; };
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::def::{CtorOf, DefKind, Namespace, Res}; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Namespace, Res};
use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::{GenericArg, GenericArgs, HirId}; use rustc_hir::{GenericArg, GenericArgs, HirId};
use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
@ -217,6 +217,23 @@ impl AssocItemQSelf {
} }
} }
/// 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,
}
/// New-typed boolean indicating whether explicit late-bound lifetimes /// New-typed boolean indicating whether explicit late-bound lifetimes
/// are present in a set of generic arguments. /// are present in a set of generic arguments.
/// ///
@ -500,8 +517,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
handle_ty_args(has_default, &inf.to_ty()) handle_ty_args(has_default, &inf.to_ty())
} }
(GenericParamDefKind::Const { .. }, GenericArg::Const(ct)) => { (GenericParamDefKind::Const { .. }, GenericArg::Const(ct)) => {
ty::Const::from_const_arg(tcx, ct, ty::FeedConstTy::Param(param.def_id)) self.lowerer.lower_const_arg(ct, FeedConstTy::Param(param.def_id)).into()
.into()
} }
(&GenericParamDefKind::Const { .. }, GenericArg::Infer(inf)) => { (&GenericParamDefKind::Const { .. }, GenericArg::Infer(inf)) => {
self.lowerer.ct_infer(Some(param), inf.span).into() self.lowerer.ct_infer(Some(param), inf.span).into()
@ -979,8 +995,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
let term: ty::Term<'_> = match term { let term: ty::Term<'_> = match term {
hir::Term::Ty(ty) => self.lower_ty(ty).into(), hir::Term::Ty(ty) => self.lower_ty(ty).into(),
hir::Term::Const(ct) => { hir::Term::Const(ct) => {
ty::Const::from_const_arg(tcx, ct, ty::FeedConstTy::No) self.lower_const_arg(ct, FeedConstTy::No).into()
.into()
} }
}; };
// FIXME(#97583): This isn't syntactically well-formed! // FIXME(#97583): This isn't syntactically well-formed!
@ -2025,23 +2040,138 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
/// ///
/// Early-bound const parameters get lowered to [`ty::ConstKind::Param`] /// Early-bound const parameters get lowered to [`ty::ConstKind::Param`]
/// and late-bound ones to [`ty::ConstKind::Bound`]. /// and late-bound ones to [`ty::ConstKind::Bound`].
pub(crate) fn lower_const_param(&self, hir_id: HirId) -> Const<'tcx> { pub(crate) fn lower_const_param(&self, param_def_id: DefId, path_hir_id: HirId) -> Const<'tcx> {
let tcx = self.tcx(); let tcx = self.tcx();
match tcx.named_bound_var(hir_id) {
Some(rbv::ResolvedArg::EarlyBound(def_id)) => { match tcx.named_bound_var(path_hir_id) {
Some(rbv::ResolvedArg::EarlyBound(_)) => {
// Find the name and index of the const parameter by indexing the generics of // Find the name and index of the const parameter by indexing the generics of
// the parent item and construct a `ParamConst`. // the parent item and construct a `ParamConst`.
let item_def_id = tcx.local_parent(def_id); let item_def_id = tcx.parent(param_def_id);
let generics = tcx.generics_of(item_def_id); let generics = tcx.generics_of(item_def_id);
let index = generics.param_def_id_to_index[&def_id.to_def_id()]; let index = generics.param_def_id_to_index[&param_def_id];
let name = tcx.item_name(def_id.to_def_id()); let name = tcx.item_name(param_def_id);
ty::Const::new_param(tcx, ty::ParamConst::new(index, name)) ty::Const::new_param(tcx, ty::ParamConst::new(index, name))
} }
Some(rbv::ResolvedArg::LateBound(debruijn, index, _)) => { Some(rbv::ResolvedArg::LateBound(debruijn, index, _)) => {
ty::Const::new_bound(tcx, debruijn, ty::BoundVar::from_u32(index)) ty::Const::new_bound(tcx, debruijn, ty::BoundVar::from_u32(index))
} }
Some(rbv::ResolvedArg::Error(guar)) => ty::Const::new_error(tcx, guar), Some(rbv::ResolvedArg::Error(guar)) => ty::Const::new_error(tcx, guar),
arg => bug!("unexpected bound var resolution for {:?}: {arg:?}", hir_id), arg => bug!("unexpected bound var resolution for {:?}: {arg:?}", path_hir_id),
}
}
/// Convert a [`hir::ConstArg`] to a [`ty::Const`](Const).
#[instrument(skip(self), level = "debug")]
pub fn lower_const_arg(
&self,
const_arg: &hir::ConstArg<'tcx>,
feed: FeedConstTy,
) -> Const<'tcx> {
let tcx = self.tcx();
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));
}
let hir_id = const_arg.hir_id;
match const_arg.kind {
hir::ConstArgKind::Path(hir::QPath::Resolved(maybe_qself, path)) => {
debug!(?maybe_qself, ?path);
let opt_self_ty = maybe_qself.as_ref().map(|qself| self.lower_ty(qself));
self.lower_const_path_resolved(opt_self_ty, path, hir_id)
}
hir::ConstArgKind::Path(qpath) => ty::Const::new_error_with_message(
tcx,
qpath.span(),
format!("Const::lower_const_arg: invalid qpath {qpath:?}"),
),
hir::ConstArgKind::Anon(anon) => Const::from_anon_const(tcx, anon.def_id),
}
}
fn lower_const_path_resolved(
&self,
opt_self_ty: Option<Ty<'tcx>>,
path: &hir::Path<'tcx>,
hir_id: HirId,
) -> Const<'tcx> {
let tcx = self.tcx();
let span = path.span;
match path.res {
Res::Def(DefKind::ConstParam, def_id) => {
assert_eq!(opt_self_ty, None);
let _ = self.prohibit_generic_args(
path.segments.iter(),
GenericsArgsErrExtend::Param(def_id),
);
self.lower_const_param(def_id, hir_id)
}
Res::Def(DefKind::Const | DefKind::Ctor(_, CtorKind::Const), did) => {
assert_eq!(opt_self_ty, None);
let _ = self.prohibit_generic_args(
path.segments.split_last().unwrap().1.iter(),
GenericsArgsErrExtend::None,
);
let args = self.lower_generic_args_of_path_segment(
span,
did,
path.segments.last().unwrap(),
);
ty::Const::new_unevaluated(tcx, ty::UnevaluatedConst::new(did, args))
}
Res::Def(DefKind::Static { .. }, _) => {
span_bug!(span, "use of bare `static` ConstArgKind::Path's not yet supported")
}
// FIXME(const_generics): create real const to allow fn items as const paths
Res::Def(DefKind::Fn | DefKind::AssocFn, _) => ty::Const::new_error_with_message(
tcx,
span,
"fn items cannot be used as const args",
),
// Exhaustive match to be clear about what exactly we're considering to be
// an invalid Res for a const path.
Res::Def(
DefKind::Mod
| DefKind::Enum
| DefKind::Variant
| DefKind::Ctor(CtorOf::Variant, CtorKind::Fn)
| DefKind::Struct
| DefKind::Ctor(CtorOf::Struct, CtorKind::Fn)
| DefKind::OpaqueTy
| DefKind::TyAlias
| DefKind::TraitAlias
| DefKind::AssocTy
| DefKind::Union
| DefKind::Trait
| DefKind::ForeignTy
| DefKind::AssocConst
| DefKind::TyParam
| DefKind::Macro(_)
| DefKind::LifetimeParam
| DefKind::Use
| DefKind::ForeignMod
| DefKind::AnonConst
| DefKind::InlineConst
| DefKind::Field
| DefKind::Impl { .. }
| DefKind::Closure
| DefKind::ExternCrate
| DefKind::GlobalAsm
| DefKind::SyntheticCoroutineBody,
_,
)
| Res::PrimTy(_)
| Res::SelfTyParam { .. }
| Res::SelfTyAlias { .. }
| Res::SelfCtor(_)
| Res::Local(_)
| Res::ToolMod
| Res::NonMacroAttr(_)
| Res::Err => Const::new_error_with_message(tcx, span, "invalid Res for const path"),
} }
} }
@ -2053,14 +2183,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
} }
} }
/// Lower a type from the HIR to our internal notion of a type given some extra data for diagnostics. /// Lower a type from the HIR to our internal notion of a type.
///
/// Extra diagnostic data:
///
/// 1. `borrowed`: Whether trait object types are borrowed like in `&dyn Trait`.
/// Used to avoid emitting redundant errors.
/// 2. `in_path`: Whether the type appears inside of a path.
/// Used to provide correct diagnostics for bare trait object types.
#[instrument(level = "debug", skip(self), ret)] #[instrument(level = "debug", skip(self), ret)]
pub fn lower_ty(&self, hir_ty: &hir::Ty<'tcx>) -> Ty<'tcx> { pub fn lower_ty(&self, hir_ty: &hir::Ty<'tcx>) -> Ty<'tcx> {
let tcx = self.tcx(); let tcx = self.tcx();
@ -2189,7 +2312,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
let length = match length { let length = match length {
hir::ArrayLen::Infer(inf) => self.ct_infer(None, inf.span), hir::ArrayLen::Infer(inf) => self.ct_infer(None, inf.span),
hir::ArrayLen::Body(constant) => { hir::ArrayLen::Body(constant) => {
ty::Const::from_const_arg(tcx, constant, ty::FeedConstTy::No) self.lower_const_arg(constant, FeedConstTy::No)
} }
}; };
@ -2247,7 +2370,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
.type_of(def_id) .type_of(def_id)
.no_bound_vars() .no_bound_vars()
.expect("const parameter types cannot be generic"); .expect("const parameter types cannot be generic");
let ct = self.lower_const_param(expr.hir_id); let ct = self.lower_const_param(def_id, expr.hir_id);
(ct, ty) (ct, ty)
} }

View File

@ -97,12 +97,14 @@ use rustc_hir::def::DefKind;
use rustc_middle::middle; use rustc_middle::middle;
use rustc_middle::mir::interpret::GlobalId; use rustc_middle::mir::interpret::GlobalId;
use rustc_middle::query::Providers; use rustc_middle::query::Providers;
use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::ty::{self, Const, Ty, TyCtxt};
use rustc_session::parse::feature_err; use rustc_session::parse::feature_err;
use rustc_span::Span; use rustc_span::Span;
use rustc_span::symbol::sym; use rustc_span::symbol::sym;
use rustc_trait_selection::traits; use rustc_trait_selection::traits;
use self::hir_ty_lowering::{FeedConstTy, HirTyLowerer};
rustc_fluent_macro::fluent_messages! { "../messages.ftl" } rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
fn require_c_abi_if_c_variadic( fn require_c_abi_if_c_variadic(
@ -226,3 +228,14 @@ pub fn lower_ty<'tcx>(tcx: TyCtxt<'tcx>, hir_ty: &hir::Ty<'tcx>) -> Ty<'tcx> {
let env_def_id = tcx.hir().get_parent_item(hir_ty.hir_id); let env_def_id = tcx.hir().get_parent_item(hir_ty.hir_id);
collect::ItemCtxt::new(tcx, env_def_id.def_id).lower_ty(hir_ty) collect::ItemCtxt::new(tcx, env_def_id.def_id).lower_ty(hir_ty)
} }
/// This is for rustdoc.
// FIXME(const_generics): having special methods for rustdoc in `rustc_hir_analysis` is cursed
pub fn lower_const_arg_for_rustdoc<'tcx>(
tcx: TyCtxt<'tcx>,
hir_ct: &hir::ConstArg<'tcx>,
feed: FeedConstTy,
) -> Const<'tcx> {
let env_def_id = tcx.hir().get_parent_item(hir_ct.hir_id);
collect::ItemCtxt::new(tcx, env_def_id.def_id).lowerer().lower_const_arg(hir_ct, feed)
}

View File

@ -14,8 +14,8 @@ use rustc_hir_analysis::hir_ty_lowering::generics::{
check_generic_arg_count_for_call, lower_generic_args, check_generic_arg_count_for_call, lower_generic_args,
}; };
use rustc_hir_analysis::hir_ty_lowering::{ use rustc_hir_analysis::hir_ty_lowering::{
ExplicitLateBound, GenericArgCountMismatch, GenericArgCountResult, GenericArgsLowerer, ExplicitLateBound, FeedConstTy, GenericArgCountMismatch, GenericArgCountResult,
GenericPathSegment, HirTyLowerer, IsMethodCall, RegionInferReason, GenericArgsLowerer, GenericPathSegment, HirTyLowerer, IsMethodCall, RegionInferReason,
}; };
use rustc_infer::infer::canonical::{Canonical, OriginalQueryValues, QueryResponse}; use rustc_infer::infer::canonical::{Canonical, OriginalQueryValues, QueryResponse};
use rustc_infer::infer::{DefineOpaqueTypes, InferResult}; use rustc_infer::infer::{DefineOpaqueTypes, InferResult};
@ -491,7 +491,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
hir::ArrayLen::Infer(inf) => self.ct_infer(None, inf.span), hir::ArrayLen::Infer(inf) => self.ct_infer(None, inf.span),
hir::ArrayLen::Body(const_arg) => { hir::ArrayLen::Body(const_arg) => {
let span = const_arg.span(); let span = const_arg.span();
let c = ty::Const::from_const_arg(self.tcx, const_arg, ty::FeedConstTy::No); let c = self.lowerer().lower_const_arg(const_arg, FeedConstTy::No);
self.register_wf_obligation(c.into(), span, ObligationCauseCode::WellFormed(None)); self.register_wf_obligation(c.into(), span, ObligationCauseCode::WellFormed(None));
self.normalize(span, c) self.normalize(span, c)
} }
@ -503,8 +503,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
const_arg: &'tcx hir::ConstArg<'tcx>, const_arg: &'tcx hir::ConstArg<'tcx>,
param_def_id: DefId, param_def_id: DefId,
) -> ty::Const<'tcx> { ) -> ty::Const<'tcx> {
let ct = let ct = self.lowerer().lower_const_arg(const_arg, FeedConstTy::Param(param_def_id));
ty::Const::from_const_arg(self.tcx, const_arg, ty::FeedConstTy::Param(param_def_id));
self.register_wf_obligation( self.register_wf_obligation(
ct.into(), ct.into(),
self.tcx.hir().span(const_arg.hir_id), self.tcx.hir().span(const_arg.hir_id),

View File

@ -1,13 +1,14 @@
use std::borrow::Cow;
use rustc_data_structures::intern::Interned; use rustc_data_structures::intern::Interned;
use rustc_error_messages::MultiSpan; use rustc_error_messages::MultiSpan;
use rustc_hir::def::{DefKind, Res}; use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::def_id::LocalDefId;
use rustc_hir::{self as hir, HirId}; use rustc_hir::{self as hir};
use rustc_macros::HashStable; use rustc_macros::HashStable;
use rustc_type_ir::{self as ir, TypeFlags, WithCachedTypeInfo}; use rustc_type_ir::{self as ir, TypeFlags, WithCachedTypeInfo};
use tracing::{debug, instrument}; use tracing::{debug, instrument};
use crate::middle::resolve_bound_vars as rbv;
use crate::mir::interpret::{LitToConstInput, Scalar}; use crate::mir::interpret::{LitToConstInput, Scalar};
use crate::ty::{self, GenericArgs, Ty, TyCtxt, TypeVisitableExt}; use crate::ty::{self, GenericArgs, Ty, TyCtxt, TypeVisitableExt};
@ -142,7 +143,7 @@ impl<'tcx> Const<'tcx> {
pub fn new_error_with_message<S: Into<MultiSpan>>( pub fn new_error_with_message<S: Into<MultiSpan>>(
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
span: S, span: S,
msg: &'static str, msg: impl Into<Cow<'static, str>>,
) -> Const<'tcx> { ) -> Const<'tcx> {
let reported = tcx.dcx().span_delayed_bug(span, msg); let reported = tcx.dcx().span_delayed_bug(span, msg);
Const::new_error(tcx, reported) Const::new_error(tcx, reported)
@ -183,46 +184,8 @@ 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> { impl<'tcx> Const<'tcx> {
/// Convert a [`hir::ConstArg`] to a [`ty::Const`](Self). // FIXME: move this and try_from_lit to hir_ty_lowering like lower_const_arg/from_const_arg
#[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 /// Literals and const generic parameters are eagerly converted to a constant, everything else
/// becomes `Unevaluated`. /// becomes `Unevaluated`.
#[instrument(skip(tcx), level = "debug")] #[instrument(skip(tcx), level = "debug")]
@ -240,7 +203,7 @@ impl<'tcx> Const<'tcx> {
let ty = tcx.type_of(def).no_bound_vars().expect("const parameter types cannot be generic"); 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, Some(v) => v,
None => ty::Const::new_unevaluated(tcx, ty::UnevaluatedConst { None => ty::Const::new_unevaluated(tcx, ty::UnevaluatedConst {
def: def.to_def_id(), def: def.to_def_id(),
@ -249,40 +212,8 @@ 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")] #[instrument(skip(tcx), level = "debug")]
fn try_from_lit_or_param( fn try_from_lit(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, expr: &'tcx hir::Expr<'tcx>) -> Option<Self> {
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 // 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. // currently have to be wrapped in curly brackets, so it's necessary to special-case.
let expr = match &expr.kind { let expr = match &expr.kind {
@ -321,7 +252,7 @@ impl<'tcx> Const<'tcx> {
Err(e) => { Err(e) => {
tcx.dcx().span_delayed_bug( tcx.dcx().span_delayed_bug(
expr.span, expr.span,
format!("Const::from_anon_const: couldn't lit_to_const {e:?}"), format!("Const::try_from_lit: couldn't lit_to_const {e:?}"),
); );
} }
} }
@ -414,20 +345,3 @@ impl<'tcx> Const<'tcx> {
matches!(self.kind(), ty::ConstKind::Infer(_)) matches!(self.kind(), ty::ConstKind::Infer(_))
} }
} }
pub fn const_param_default<'tcx>(
tcx: TyCtxt<'tcx>,
def_id: LocalDefId,
) -> ty::EarlyBinder<'tcx, Const<'tcx>> {
let default_ct = match tcx.hir_node_by_def_id(def_id) {
hir::Node::GenericParam(hir::GenericParam {
kind: hir::GenericParamKind::Const { default: Some(ct), .. },
..
}) => ct,
_ => span_bug!(
tcx.def_span(def_id),
"`const_param_default` expected a generic parameter with a constant"
),
};
ty::EarlyBinder::bind(Const::from_const_arg(tcx, default_ct, FeedConstTy::No))
}

View File

@ -30,7 +30,7 @@ use rustc_errors::{
Applicability, Diag, DiagCtxtHandle, ErrorGuaranteed, LintDiagnostic, MultiSpan, Applicability, Diag, DiagCtxtHandle, ErrorGuaranteed, LintDiagnostic, MultiSpan,
}; };
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::def::DefKind; use rustc_hir::def::{CtorKind, DefKind};
use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE, LocalDefId}; use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE, LocalDefId};
use rustc_hir::definitions::Definitions; use rustc_hir::definitions::Definitions;
use rustc_hir::intravisit::Visitor; use rustc_hir::intravisit::Visitor;
@ -230,7 +230,9 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
DefKind::OpaqueTy => ty::AliasTermKind::OpaqueTy, DefKind::OpaqueTy => ty::AliasTermKind::OpaqueTy,
DefKind::TyAlias => ty::AliasTermKind::WeakTy, DefKind::TyAlias => ty::AliasTermKind::WeakTy,
DefKind::AssocConst => ty::AliasTermKind::ProjectionConst, DefKind::AssocConst => ty::AliasTermKind::ProjectionConst,
DefKind::AnonConst => ty::AliasTermKind::UnevaluatedConst, DefKind::AnonConst | DefKind::Const | DefKind::Ctor(_, CtorKind::Const) => {
ty::AliasTermKind::UnevaluatedConst
}
kind => bug!("unexpected DefKind in AliasTy: {kind:?}"), kind => bug!("unexpected DefKind in AliasTy: {kind:?}"),
} }
} }

View File

@ -62,7 +62,7 @@ pub use self::closure::{
place_to_string_for_capture, place_to_string_for_capture,
}; };
pub use self::consts::{ pub use self::consts::{
Const, ConstInt, ConstKind, Expr, ExprKind, FeedConstTy, ScalarInt, UnevaluatedConst, ValTree, Const, ConstInt, ConstKind, Expr, ExprKind, ScalarInt, UnevaluatedConst, ValTree,
}; };
pub use self::context::{ pub use self::context::{
CtxtInterners, CurrentGcx, DeducedParamAttrs, Feed, FreeRegionInfo, GlobalCtxt, Lift, TyCtxt, CtxtInterners, CurrentGcx, DeducedParamAttrs, Feed, FreeRegionInfo, GlobalCtxt, Lift, TyCtxt,
@ -2249,7 +2249,6 @@ pub fn provide(providers: &mut Providers) {
incoherent_impls: trait_def::incoherent_impls_provider, incoherent_impls: trait_def::incoherent_impls_provider,
trait_impls_in_crate: trait_def::trait_impls_in_crate_provider, trait_impls_in_crate: trait_def::trait_impls_in_crate_provider,
traits: trait_def::traits_provider, traits: trait_def::traits_provider,
const_param_default: consts::const_param_default,
vtable_allocation: vtable::vtable_allocation_provider, vtable_allocation: vtable::vtable_allocation_provider,
..*providers ..*providers
}; };

View File

@ -1226,6 +1226,7 @@ symbols! {
min_const_generics, min_const_generics,
min_const_unsafe_fn, min_const_unsafe_fn,
min_exhaustive_patterns, min_exhaustive_patterns,
min_generic_const_args,
min_specialization, min_specialization,
min_type_alias_impl_trait, min_type_alias_impl_trait,
minnumf128, minnumf128,

View File

@ -42,7 +42,8 @@ use rustc_errors::{FatalError, struct_span_code_err};
use rustc_hir::PredicateOrigin; use rustc_hir::PredicateOrigin;
use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def::{CtorKind, DefKind, Res};
use rustc_hir::def_id::{DefId, DefIdMap, DefIdSet, LOCAL_CRATE, LocalDefId}; use rustc_hir::def_id::{DefId, DefIdMap, DefIdSet, LOCAL_CRATE, LocalDefId};
use rustc_hir_analysis::lower_ty; use rustc_hir_analysis::hir_ty_lowering::FeedConstTy;
use rustc_hir_analysis::{lower_const_arg_for_rustdoc, lower_ty};
use rustc_middle::metadata::Reexport; use rustc_middle::metadata::Reexport;
use rustc_middle::middle::resolve_bound_vars as rbv; use rustc_middle::middle::resolve_bound_vars as rbv;
use rustc_middle::ty::{self, AdtKind, GenericArgsRef, Ty, TyCtxt, TypeVisitableExt, TypingMode}; use rustc_middle::ty::{self, AdtKind, GenericArgsRef, Ty, TyCtxt, TypeVisitableExt, TypingMode};
@ -435,10 +436,10 @@ fn clean_middle_term<'tcx>(
fn clean_hir_term<'tcx>(term: &hir::Term<'tcx>, cx: &mut DocContext<'tcx>) -> Term { fn clean_hir_term<'tcx>(term: &hir::Term<'tcx>, cx: &mut DocContext<'tcx>) -> Term {
match term { match term {
hir::Term::Ty(ty) => Term::Type(clean_ty(ty, cx)), hir::Term::Ty(ty) => Term::Type(clean_ty(ty, cx)),
hir::Term::Const(c) => Term::Constant(clean_middle_const( hir::Term::Const(c) => {
ty::Binder::dummy(ty::Const::from_const_arg(cx.tcx, c, ty::FeedConstTy::No)), let ct = lower_const_arg_for_rustdoc(cx.tcx, c, FeedConstTy::No);
cx, Term::Constant(clean_middle_const(ty::Binder::dummy(ct), cx))
)), }
} }
} }
@ -625,7 +626,7 @@ fn clean_generic_param<'tcx>(
(param.name.ident().name, GenericParamDefKind::Const { (param.name.ident().name, GenericParamDefKind::Const {
ty: Box::new(clean_ty(ty, cx)), ty: Box::new(clean_ty(ty, cx)),
default: default.map(|ct| { default: default.map(|ct| {
Box::new(ty::Const::from_const_arg(cx.tcx, ct, ty::FeedConstTy::No).to_string()) Box::new(lower_const_arg_for_rustdoc(cx.tcx, ct, FeedConstTy::No).to_string())
}), }),
synthetic, synthetic,
}) })
@ -1813,7 +1814,7 @@ pub(crate) fn clean_ty<'tcx>(ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> T
// `const_eval_poly` tries to first substitute generic parameters which // `const_eval_poly` tries to first substitute generic parameters which
// results in an ICE while manually constructing the constant and using `eval` // results in an ICE while manually constructing the constant and using `eval`
// does nothing for `ConstKind::Param`. // does nothing for `ConstKind::Param`.
let ct = ty::Const::from_const_arg(cx.tcx, const_arg, ty::FeedConstTy::No); let ct = lower_const_arg_for_rustdoc(cx.tcx, const_arg, FeedConstTy::No);
let ct = if let hir::ConstArgKind::Anon(hir::AnonConst { def_id, .. }) = let ct = if let hir::ConstArgKind::Anon(hir::AnonConst { def_id, .. }) =
const_arg.kind const_arg.kind
{ {

17
tests/crashes/132985.rs Normal file
View File

@ -0,0 +1,17 @@
//@ known-bug: #132985
//@ aux-build:aux132985.rs
#![allow(incomplete_features)]
#![feature(min_generic_const_args)]
#![feature(adt_const_params)]
extern crate aux132985;
use aux132985::Foo;
fn bar<const N: Foo>() {}
fn baz() {
bar::<{ Foo }>();
}
fn main() {}

View File

@ -0,0 +1,6 @@
#![feature(adt_const_params)]
use std::marker::ConstParamTy;
#[derive(Eq, PartialEq, ConstParamTy)]
pub struct Foo;

View File

@ -0,0 +1,6 @@
#![feature(adt_const_params)]
use std::marker::ConstParamTy;
#[derive(Eq, PartialEq, ConstParamTy)]
pub struct Foo;

View File

@ -0,0 +1,7 @@
//@ check-pass
pub static STATIC: u32 = 0;
pub struct Foo<const N: u32>;
pub const FOO: Foo<{STATIC}> = Foo;
fn main() {}

View File

@ -0,0 +1,15 @@
//@ check-pass
//@ aux-build:xcrate-const-ctor-a.rs
#![feature(adt_const_params)]
extern crate xcrate_const_ctor_a;
use xcrate_const_ctor_a::Foo;
fn bar<const N: Foo>() {}
fn baz() {
bar::<{ Foo }>();
}
fn main() {}

View File

@ -0,0 +1,11 @@
trait Trait {
const ASSOC: usize;
}
// FIXME(min_generic_const_args): implement support for this, behind the feature gate
fn foo<T: Trait>() -> [u8; <T as Trait>::ASSOC] {
//~^ ERROR generic parameters may not be used in const operations
loop {}
}
fn main() {}

View File

@ -0,0 +1,11 @@
error: generic parameters may not be used in const operations
--> $DIR/feature-gate-min-generic-const-args.rs:6:29
|
LL | fn foo<T: Trait>() -> [u8; <T as Trait>::ASSOC] {
| ^ cannot perform const operation using `T`
|
= note: type parameters may not be used in const expressions
= help: add `#![feature(generic_const_exprs)]` to allow generic const expressions
error: aborting due to 1 previous error