From 59e339f76658bd8bd55f7514c95ffb5f39c94227 Mon Sep 17 00:00:00 2001 From: Noah Lev Date: Tue, 19 Nov 2024 05:01:59 +0000 Subject: [PATCH] Introduce `min_generic_const_args` and directly represent paths MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Boxy UwU Co-authored-by: León Orell Valerian Liehr --- compiler/rustc_ast_lowering/src/lib.rs | 106 ++++++----- compiler/rustc_feature/src/unstable.rs | 2 + compiler/rustc_hir_analysis/src/collect.rs | 23 ++- .../src/collect/predicates_of.rs | 5 +- .../src/hir_ty_lowering/bounds.rs | 8 +- .../src/hir_ty_lowering/generics.rs | 19 +- .../src/hir_ty_lowering/mod.rs | 167 +++++++++++++++--- compiler/rustc_hir_analysis/src/lib.rs | 15 +- .../rustc_hir_typeck/src/fn_ctxt/_impl.rs | 9 +- compiler/rustc_middle/src/ty/consts.rs | 104 +---------- compiler/rustc_middle/src/ty/context.rs | 6 +- compiler/rustc_middle/src/ty/mod.rs | 3 +- compiler/rustc_span/src/symbol.rs | 1 + src/librustdoc/clean/mod.rs | 15 +- tests/crashes/132985.rs | 17 ++ tests/crashes/auxiliary/aux132985.rs | 6 + .../auxiliary/xcrate-const-ctor-a.rs | 6 + .../using-static-as-const-arg.rs | 7 + .../ui/const-generics/xcrate-const-ctor-b.rs | 15 ++ .../feature-gate-min-generic-const-args.rs | 11 ++ ...feature-gate-min-generic-const-args.stderr | 11 ++ 21 files changed, 361 insertions(+), 195 deletions(-) create mode 100644 tests/crashes/132985.rs create mode 100644 tests/crashes/auxiliary/aux132985.rs create mode 100644 tests/ui/const-generics/auxiliary/xcrate-const-ctor-a.rs create mode 100644 tests/ui/const-generics/using-static-as-const-arg.rs create mode 100644 tests/ui/const-generics/xcrate-const-ctor-b.rs create mode 100644 tests/ui/feature-gates/feature-gate-min-generic-const-args.rs create mode 100644 tests/ui/feature-gates/feature-gate-min-generic-const-args.stderr diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index d53280751fc..0b2969a49ba 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -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))] fn lower_const_path_to_const_arg( &mut self, @@ -2060,51 +2063,58 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ty_id: NodeId, span: Span, ) -> &'hir hir::ConstArg<'hir> { - let ct_kind = match res { - 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 tcx = self.tcx; - let parent_def_id = self.current_def_id_parent; - let node_id = self.next_node_id(); - let span = self.lower_span(span); + // FIXME(min_generic_const_args): we only allow one-segment const paths for now + let ct_kind = if path.is_potential_trivial_const_arg() + && (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>` 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 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 parent_def_id = self.current_def_id_parent; + let node_id = self.next_node_id(); + let span = self.lower_span(span); - let path_expr = Expr { - id: ty_id, - kind: ExprKind::Path(None, path.clone()), + // Add a definition for the in-band const def. + // We're lowering a const argument that was originally thought to be a type argument, + // 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, - 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, - }) - }); - hir::ConstArgKind::Anon(ct) - } + }) + }); + hir::ConstArgKind::Anon(ct) }; self.arena.alloc(hir::ConstArg { @@ -2122,6 +2132,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { #[instrument(level = "debug", skip(self))] 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 // 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 @@ -2135,18 +2146,19 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { }; let maybe_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): for now we only lower params to ConstArgKind::Path - if let Some(res) = maybe_res - && let Res::Def(DefKind::ConstParam, _) = res - && let ExprKind::Path(qself, path) = &expr.kind + // FIXME(min_generic_const_args): we only allow one-segment const paths for now + if let ExprKind::Path(None, path) = &expr.kind + && path.is_potential_trivial_const_arg() + && (tcx.features().min_generic_const_args() + || matches!(maybe_res, Some(Res::Def(DefKind::ConstParam, _)))) { let qpath = self.lower_qpath( expr.id, - qself, + &None, path, ParamMode::Optional, AllowReturnTypeNotation::No, + // FIXME(min_generic_const_args): update for `fn foo() -> Bar>` support ImplTraitContext::Disallowed(ImplTraitPosition::Path), None, ); diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index a67a5776449..2acebebb419 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -529,6 +529,8 @@ declare_features! ( (unstable, macro_metavar_expr_concat, "1.81.0", Some(124225)), /// Allows `#[marker]` on certain traits allowing overlapping implementations. (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 /// standard library until the soundness issues with specialization /// are fixed. diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index 63a0e7d31c3..1a925597c6c 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -46,7 +46,7 @@ use tracing::{debug, instrument}; use crate::check::intrinsic::intrinsic_operation_unsafety; use crate::errors; -use crate::hir_ty_lowering::{HirTyLowerer, RegionInferReason}; +use crate::hir_ty_lowering::{FeedConstTy, HirTyLowerer, RegionInferReason}; pub(crate) mod dump; mod generics_of; @@ -88,6 +88,7 @@ pub fn provide(providers: &mut Providers) { coroutine_for_closure, opaque_ty_origin, rendered_precise_capturing_args, + const_param_default, ..*providers }; } @@ -1790,3 +1791,23 @@ fn rendered_precise_capturing_args<'tcx>( _ => 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) +} diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs index 7ce12d48160..0f37d61beb0 100644 --- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs @@ -223,11 +223,12 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen trace!(?predicates); } hir::GenericParamKind::Const { .. } => { + let param_def_id = param.def_id.to_def_id(); let ct_ty = tcx - .type_of(param.def_id.to_def_id()) + .type_of(param_def_id) .no_bound_vars() .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 .insert((ty::ClauseKind::ConstArgHasType(ct, ct_ty).upcast(tcx), param.span)); } diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs index 1cade402c54..6ebe1cedcaf 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs @@ -19,7 +19,9 @@ use tracing::{debug, instrument}; use super::errors::GenericsArgsErrExtend; use crate::bounds::Bounds; 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> + '_ { /// Add a `Sized` bound to the `bounds` if appropriate. @@ -346,9 +348,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { hir::AssocItemConstraintKind::Equality { term } => { let term = match term { hir::Term::Ty(ty) => self.lower_ty(ty).into(), - hir::Term::Const(ct) => { - ty::Const::from_const_arg(tcx, ct, ty::FeedConstTy::No).into() - } + hir::Term::Const(ct) => self.lower_const_arg(ct, FeedConstTy::No).into(), }; // Find any late-bound regions declared in `ty` that are not diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs index 6e8a9ded4f3..ae1279d428c 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs @@ -115,17 +115,22 @@ fn generic_arg_mismatch_err( } } (GenericArg::Const(cnst), GenericParamDefKind::Type { .. }) => { - // FIXME(min_generic_const_args): once ConstArgKind::Path is used for non-params too, - // this should match against that instead of ::Anon - if let hir::ConstArgKind::Anon(anon) = cnst.kind + if let hir::ConstArgKind::Path(qpath) = cnst.kind + && let rustc_hir::QPath::Resolved(_, path) = qpath + && 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 rustc_hir::ExprKind::Path(rustc_hir::QPath::Resolved(_, path)) = body.value.kind + && let Res::Def(DefKind::Fn { .. }, id) = path.res { - if 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"); - } + // FIXME(min_generic_const_args): this branch is dead once new const path lowering + // (for single-segment paths) is no longer gated + err.help(format!("`{}` is a function item, not a type", tcx.item_name(id))); + err.help("function item types cannot be named directly"); } } _ => {} diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index ed39708981b..f7035531e88 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -30,7 +30,7 @@ use rustc_errors::{ Applicability, Diag, DiagCtxtHandle, ErrorGuaranteed, FatalError, struct_span_code_err, }; 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::{GenericArg, GenericArgs, HirId}; 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 /// 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()) } (GenericParamDefKind::Const { .. }, GenericArg::Const(ct)) => { - ty::Const::from_const_arg(tcx, ct, ty::FeedConstTy::Param(param.def_id)) - .into() + self.lowerer.lower_const_arg(ct, FeedConstTy::Param(param.def_id)).into() } (&GenericParamDefKind::Const { .. }, GenericArg::Infer(inf)) => { self.lowerer.ct_infer(Some(param), inf.span).into() @@ -979,8 +995,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let term: ty::Term<'_> = match term { hir::Term::Ty(ty) => self.lower_ty(ty).into(), hir::Term::Const(ct) => { - ty::Const::from_const_arg(tcx, ct, ty::FeedConstTy::No) - .into() + self.lower_const_arg(ct, FeedConstTy::No).into() } }; // 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`] /// 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(); - 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 // 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 index = generics.param_def_id_to_index[&def_id.to_def_id()]; - let name = tcx.item_name(def_id.to_def_id()); + let index = generics.param_def_id_to_index[¶m_def_id]; + let name = tcx.item_name(param_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), + 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>, + 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. - /// - /// 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. + /// Lower a type from the HIR to our internal notion of a type. #[instrument(level = "debug", skip(self), ret)] pub fn lower_ty(&self, hir_ty: &hir::Ty<'tcx>) -> Ty<'tcx> { let tcx = self.tcx(); @@ -2189,7 +2312,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let length = match length { hir::ArrayLen::Infer(inf) => self.ct_infer(None, inf.span), 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) .no_bound_vars() .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) } diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index 5830636c6e8..564e45c677d 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -97,12 +97,14 @@ use rustc_hir::def::DefKind; use rustc_middle::middle; use rustc_middle::mir::interpret::GlobalId; 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_span::Span; use rustc_span::symbol::sym; use rustc_trait_selection::traits; +use self::hir_ty_lowering::{FeedConstTy, HirTyLowerer}; + rustc_fluent_macro::fluent_messages! { "../messages.ftl" } 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); 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) +} diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index f0738491609..ce6ce0381a9 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -14,8 +14,8 @@ use rustc_hir_analysis::hir_ty_lowering::generics::{ check_generic_arg_count_for_call, lower_generic_args, }; use rustc_hir_analysis::hir_ty_lowering::{ - ExplicitLateBound, GenericArgCountMismatch, GenericArgCountResult, GenericArgsLowerer, - GenericPathSegment, HirTyLowerer, IsMethodCall, RegionInferReason, + ExplicitLateBound, FeedConstTy, GenericArgCountMismatch, GenericArgCountResult, + GenericArgsLowerer, GenericPathSegment, HirTyLowerer, IsMethodCall, RegionInferReason, }; use rustc_infer::infer::canonical::{Canonical, OriginalQueryValues, QueryResponse}; 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::Body(const_arg) => { 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.normalize(span, c) } @@ -503,8 +503,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { const_arg: &'tcx hir::ConstArg<'tcx>, param_def_id: DefId, ) -> ty::Const<'tcx> { - let ct = - ty::Const::from_const_arg(self.tcx, const_arg, ty::FeedConstTy::Param(param_def_id)); + let ct = self.lowerer().lower_const_arg(const_arg, FeedConstTy::Param(param_def_id)); self.register_wf_obligation( ct.into(), self.tcx.hir().span(const_arg.hir_id), diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs index 3bd09fc91c6..d853edb34c9 100644 --- a/compiler/rustc_middle/src/ty/consts.rs +++ b/compiler/rustc_middle/src/ty/consts.rs @@ -1,13 +1,14 @@ +use std::borrow::Cow; + use rustc_data_structures::intern::Interned; use rustc_error_messages::MultiSpan; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir::{self as hir, HirId}; +use rustc_hir::def_id::LocalDefId; +use rustc_hir::{self as hir}; use rustc_macros::HashStable; use rustc_type_ir::{self as ir, TypeFlags, WithCachedTypeInfo}; use tracing::{debug, instrument}; -use crate::middle::resolve_bound_vars as rbv; use crate::mir::interpret::{LitToConstInput, Scalar}; use crate::ty::{self, GenericArgs, Ty, TyCtxt, TypeVisitableExt}; @@ -142,7 +143,7 @@ impl<'tcx> Const<'tcx> { pub fn new_error_with_message>( tcx: TyCtxt<'tcx>, span: S, - msg: &'static str, + msg: impl Into>, ) -> Const<'tcx> { let reported = tcx.dcx().span_delayed_bug(span, msg); Const::new_error(tcx, reported) @@ -183,46 +184,8 @@ impl<'tcx> rustc_type_ir::inherent::Const> 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), - } - } - + // FIXME: move this and try_from_lit to hir_ty_lowering like lower_const_arg/from_const_arg /// Literals and const generic parameters are eagerly converted to a constant, everything else /// becomes `Unevaluated`. #[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"); - 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, ty::UnevaluatedConst { 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")] - fn try_from_lit_or_param( - tcx: TyCtxt<'tcx>, - ty: Ty<'tcx>, - expr: &'tcx hir::Expr<'tcx>, - ) -> Option { + fn try_from_lit(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, expr: &'tcx hir::Expr<'tcx>) -> Option { // 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 { @@ -321,7 +252,7 @@ impl<'tcx> Const<'tcx> { Err(e) => { tcx.dcx().span_delayed_bug( 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(_)) } } - -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)) -} diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 2ba1bf2822f..68c3b064eee 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -30,7 +30,7 @@ use rustc_errors::{ Applicability, Diag, DiagCtxtHandle, ErrorGuaranteed, LintDiagnostic, MultiSpan, }; 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::definitions::Definitions; use rustc_hir::intravisit::Visitor; @@ -230,7 +230,9 @@ impl<'tcx> Interner for TyCtxt<'tcx> { DefKind::OpaqueTy => ty::AliasTermKind::OpaqueTy, DefKind::TyAlias => ty::AliasTermKind::WeakTy, 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:?}"), } } diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index cddd6110c23..965a8c8c95e 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -62,7 +62,7 @@ pub use self::closure::{ place_to_string_for_capture, }; 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::{ 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, trait_impls_in_crate: trait_def::trait_impls_in_crate_provider, traits: trait_def::traits_provider, - const_param_default: consts::const_param_default, vtable_allocation: vtable::vtable_allocation_provider, ..*providers }; diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index a562d34abde..e4261822040 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1226,6 +1226,7 @@ symbols! { min_const_generics, min_const_unsafe_fn, min_exhaustive_patterns, + min_generic_const_args, min_specialization, min_type_alias_impl_trait, minnumf128, diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index f0787d286fd..e42350dc5e6 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -42,7 +42,8 @@ use rustc_errors::{FatalError, struct_span_code_err}; use rustc_hir::PredicateOrigin; use rustc_hir::def::{CtorKind, DefKind, Res}; 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::middle::resolve_bound_vars as rbv; 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 { match term { hir::Term::Ty(ty) => Term::Type(clean_ty(ty, cx)), - hir::Term::Const(c) => Term::Constant(clean_middle_const( - ty::Binder::dummy(ty::Const::from_const_arg(cx.tcx, c, ty::FeedConstTy::No)), - cx, - )), + hir::Term::Const(c) => { + let ct = lower_const_arg_for_rustdoc(cx.tcx, c, FeedConstTy::No); + 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 { ty: Box::new(clean_ty(ty, cx)), 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, }) @@ -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 // results in an ICE while manually constructing the constant and using `eval` // 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, .. }) = const_arg.kind { diff --git a/tests/crashes/132985.rs b/tests/crashes/132985.rs new file mode 100644 index 00000000000..2735074f44d --- /dev/null +++ b/tests/crashes/132985.rs @@ -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() {} + +fn baz() { + bar::<{ Foo }>(); +} + +fn main() {} diff --git a/tests/crashes/auxiliary/aux132985.rs b/tests/crashes/auxiliary/aux132985.rs new file mode 100644 index 00000000000..7ae5567bdc5 --- /dev/null +++ b/tests/crashes/auxiliary/aux132985.rs @@ -0,0 +1,6 @@ +#![feature(adt_const_params)] + +use std::marker::ConstParamTy; + +#[derive(Eq, PartialEq, ConstParamTy)] +pub struct Foo; diff --git a/tests/ui/const-generics/auxiliary/xcrate-const-ctor-a.rs b/tests/ui/const-generics/auxiliary/xcrate-const-ctor-a.rs new file mode 100644 index 00000000000..7ae5567bdc5 --- /dev/null +++ b/tests/ui/const-generics/auxiliary/xcrate-const-ctor-a.rs @@ -0,0 +1,6 @@ +#![feature(adt_const_params)] + +use std::marker::ConstParamTy; + +#[derive(Eq, PartialEq, ConstParamTy)] +pub struct Foo; diff --git a/tests/ui/const-generics/using-static-as-const-arg.rs b/tests/ui/const-generics/using-static-as-const-arg.rs new file mode 100644 index 00000000000..2e8a2a14484 --- /dev/null +++ b/tests/ui/const-generics/using-static-as-const-arg.rs @@ -0,0 +1,7 @@ +//@ check-pass + +pub static STATIC: u32 = 0; +pub struct Foo; +pub const FOO: Foo<{STATIC}> = Foo; + +fn main() {} diff --git a/tests/ui/const-generics/xcrate-const-ctor-b.rs b/tests/ui/const-generics/xcrate-const-ctor-b.rs new file mode 100644 index 00000000000..dce2e43b316 --- /dev/null +++ b/tests/ui/const-generics/xcrate-const-ctor-b.rs @@ -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() {} + +fn baz() { + bar::<{ Foo }>(); +} + +fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-min-generic-const-args.rs b/tests/ui/feature-gates/feature-gate-min-generic-const-args.rs new file mode 100644 index 00000000000..171509876d1 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-min-generic-const-args.rs @@ -0,0 +1,11 @@ +trait Trait { + const ASSOC: usize; +} + +// FIXME(min_generic_const_args): implement support for this, behind the feature gate +fn foo() -> [u8; ::ASSOC] { + //~^ ERROR generic parameters may not be used in const operations + loop {} +} + +fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-min-generic-const-args.stderr b/tests/ui/feature-gates/feature-gate-min-generic-const-args.stderr new file mode 100644 index 00000000000..04d96b4c11e --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-min-generic-const-args.stderr @@ -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() -> [u8; ::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 +