Skip to content

Commit

Permalink
Introduce feature(generic_const_parameter_types)
Browse files Browse the repository at this point in the history
  • Loading branch information
BoxyUwU committed Feb 26, 2025
1 parent ad27045 commit f47c2d5
Show file tree
Hide file tree
Showing 57 changed files with 423 additions and 154 deletions.
2 changes: 2 additions & 0 deletions compiler/rustc_feature/src/unstable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,8 @@ declare_features! (
(incomplete, generic_const_exprs, "1.56.0", Some(76560)),
/// Allows generic parameters and where-clauses on free & associated const items.
(incomplete, generic_const_items, "1.73.0", Some(113521)),
/// Allows the type of const generics to depend on generic parameters
(incomplete, generic_const_parameter_types, "CURRENT_RUSTC_VERSION", Some(137626)),
/// Allows any generic constants being used as pattern type range ends
(incomplete, generic_pattern_types, "1.86.0", Some(136574)),
/// Allows registering static items globally, possibly across crates, to iterate over at runtime.
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_hir_analysis/src/check/wfcheck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -966,7 +966,7 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) -> Result<(),
let ty = tcx.type_of(param.def_id).instantiate_identity();

if tcx.features().unsized_const_params() {
enter_wf_checking_ctxt(tcx, hir_ty.span, param.def_id, |wfcx| {
enter_wf_checking_ctxt(tcx, hir_ty.span, tcx.local_parent(param.def_id), |wfcx| {
wfcx.register_bound(
ObligationCause::new(
hir_ty.span,
Expand All @@ -980,7 +980,7 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) -> Result<(),
Ok(())
})
} else if tcx.features().adt_const_params() {
enter_wf_checking_ctxt(tcx, hir_ty.span, param.def_id, |wfcx| {
enter_wf_checking_ctxt(tcx, hir_ty.span, tcx.local_parent(param.def_id), |wfcx| {
wfcx.register_bound(
ObligationCause::new(
hir_ty.span,
Expand Down
5 changes: 4 additions & 1 deletion compiler/rustc_hir_analysis/src/collect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1823,6 +1823,9 @@ fn const_param_default<'tcx>(
),
};
let icx = ItemCtxt::new(tcx, def_id);
let ct = icx.lowerer().lower_const_arg(default_ct, FeedConstTy::Param(def_id.to_def_id()));
let identity_args = ty::GenericArgs::identity_for_item(tcx, def_id);
let ct = icx
.lowerer()
.lower_const_arg(default_ct, FeedConstTy::Param(def_id.to_def_id(), identity_args));
ty::EarlyBinder::bind(ct)
}
5 changes: 1 addition & 4 deletions compiler/rustc_hir_analysis/src/collect/predicates_of.rs
Original file line number Diff line number Diff line change
Expand Up @@ -223,10 +223,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
}
hir::GenericParamKind::Const { .. } => {
let param_def_id = param.def_id.to_def_id();
let ct_ty = tcx
.type_of(param_def_id)
.no_bound_vars()
.expect("const parameters cannot be generic");
let ct_ty = tcx.type_of(param_def_id).instantiate_identity();
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));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ pub fn lower_generic_args<'tcx: 'a, 'a>(

// We lower to an infer even when the feature gate is not enabled
// as it is useful for diagnostics to be able to see a `ConstKind::Infer`
args.push(ctx.provided_kind(param, arg));
args.push(ctx.provided_kind(&args, param, arg));
args_iter.next();
params.next();
}
Expand Down
65 changes: 53 additions & 12 deletions compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -232,12 +232,15 @@ impl AssocItemQSelf {
/// Use this enum with `<dyn HirTyLowerer>::lower_const_arg` to instruct it with the
/// desired behavior.
#[derive(Debug, Clone, Copy)]
pub enum FeedConstTy {
pub enum FeedConstTy<'a, 'tcx> {
/// Feed the type.
///
/// The `DefId` belongs to the const param that we are supplying
/// this (anon) const arg to.
Param(DefId),
///
/// The list of generic args is used to instantiate the parameters
/// used by the type of the const param specified by `DefId`.
Param(DefId, &'a [ty::GenericArg<'tcx>]),
/// Don't feed the type.
No,
}
Expand Down Expand Up @@ -298,6 +301,7 @@ pub trait GenericArgsLowerer<'a, 'tcx> {

fn provided_kind(
&mut self,
preceding_args: &[ty::GenericArg<'tcx>],
param: &ty::GenericParamDef,
arg: &GenericArg<'tcx>,
) -> ty::GenericArg<'tcx>;
Expand Down Expand Up @@ -481,6 +485,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {

fn provided_kind(
&mut self,
preceding_args: &[ty::GenericArg<'tcx>],
param: &ty::GenericParamDef,
arg: &GenericArg<'tcx>,
) -> ty::GenericArg<'tcx> {
Expand Down Expand Up @@ -526,7 +531,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
(GenericParamDefKind::Const { .. }, GenericArg::Const(ct)) => self
.lowerer
// Ambig portions of `ConstArg` are handled in the match arm below
.lower_const_arg(ct.as_unambig_ct(), FeedConstTy::Param(param.def_id))
.lower_const_arg(
ct.as_unambig_ct(),
FeedConstTy::Param(param.def_id, preceding_args),
)
.into(),
(&GenericParamDefKind::Const { .. }, GenericArg::Infer(inf)) => {
self.lowerer.ct_infer(Some(param), inf.span).into()
Expand Down Expand Up @@ -582,8 +590,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
let ty = tcx
.at(self.span)
.type_of(param.def_id)
.no_bound_vars()
.expect("const parameter types cannot be generic");
.instantiate(tcx, preceding_args);
if let Err(guar) = ty.error_reported() {
return ty::Const::new_error(tcx, guar).into();
}
Expand Down Expand Up @@ -2107,14 +2114,48 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
pub fn lower_const_arg(
&self,
const_arg: &hir::ConstArg<'tcx>,
feed: FeedConstTy,
feed: FeedConstTy<'_, 'tcx>,
) -> Const<'tcx> {
let tcx = self.tcx();

if let FeedConstTy::Param(param_def_id) = feed
if let FeedConstTy::Param(param_def_id, args) = feed
&& let hir::ConstArgKind::Anon(anon) = &const_arg.kind
{
tcx.feed_anon_const_type(anon.def_id, tcx.type_of(param_def_id));
let anon_const_type = tcx.type_of(param_def_id).instantiate(tcx, args);

// We must error if the instantiated type has any inference variables as we will
// use this type to feed the `type_of` and query results must not contain inference
// variables otherwise we will ICE.
//
// We also error if the type contains any regions as effectively any region will wind
// up as a region variable in mir borrowck. It would also be somewhat concerning if
// hir typeck was using equality but mir borrowck wound up using subtyping as that could
// result in a non-infer in hir typeck but a region variable in borrowck.
//
// FIXME(generic_const_parameter_types): Ideally we remove these errors one day when
// we have the ability to intermix typeck of anon const const args with the parent
// bodies typeck.
if tcx.features().generic_const_parameter_types() && anon_const_type.has_regions() {
let e = tcx.dcx().span_err(
const_arg.span(),
"anonymous constants with lifetimes in their type are not yet supported",
);
tcx.feed_anon_const_type(anon.def_id, ty::EarlyBinder::bind(Ty::new_error(tcx, e)));
return ty::Const::new_error(tcx, e);
}
if anon_const_type.has_non_region_infer() {
let e = tcx.dcx().span_err(
const_arg.span(),
"anonymous constants with inferred types are not yet supported",
);
tcx.feed_anon_const_type(anon.def_id, ty::EarlyBinder::bind(Ty::new_error(tcx, e)));
return ty::Const::new_error(tcx, e);
}

tcx.feed_anon_const_type(
anon.def_id,
ty::EarlyBinder::bind(tcx.type_of(param_def_id).instantiate(tcx, args)),
);
}

let hir_id = const_arg.hir_id;
Expand Down Expand Up @@ -2230,10 +2271,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
let expr = &tcx.hir_body(anon.body).value;
debug!(?expr);

let ty = tcx
.type_of(anon.def_id)
.no_bound_vars()
.expect("const parameter types cannot be generic");
// FIXME(generic_const_parameter_types): We should use the proper generic args
// here. It's only used as a hint for literals so doesn't matter too much to use the right
// generic arguments, just weaker type inference.
let ty = tcx.type_of(anon.def_id).instantiate_identity();

match self.try_lower_anon_const_lit(ty, expr) {
Some(v) => v,
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_hir_analysis/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ pub fn lower_ty<'tcx>(tcx: TyCtxt<'tcx>, hir_ty: &hir::Ty<'tcx>) -> Ty<'tcx> {
pub fn lower_const_arg_for_rustdoc<'tcx>(
tcx: TyCtxt<'tcx>,
hir_ct: &hir::ConstArg<'tcx>,
feed: FeedConstTy,
feed: FeedConstTy<'_, 'tcx>,
) -> 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)
Expand Down
23 changes: 19 additions & 4 deletions compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -519,7 +519,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
pub(crate) fn lower_const_arg(
&self,
const_arg: &'tcx hir::ConstArg<'tcx>,
feed: FeedConstTy,
feed: FeedConstTy<'_, 'tcx>,
) -> ty::Const<'tcx> {
let ct = self.lowerer().lower_const_arg(const_arg, feed);
self.register_wf_obligation(
Expand Down Expand Up @@ -1135,7 +1135,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
.last()
.is_some_and(|GenericPathSegment(def_id, _)| tcx.generics_of(*def_id).has_self);

let (res, self_ctor_args) = if let Res::SelfCtor(impl_def_id) = res {
let (res, implicit_args) = if let Res::Def(DefKind::ConstParam, def) = res {
// types of const parameters are somewhat special as they are part of
// the same environment as the const parameter itself. this means that
// unlike most paths `type-of(N)` can return a type naming parameters
// introduced by the containing item, rather than provided through `N`.
//
// for example given `<T, const M: usize, const N: [T; M]>` and some
// `let a = N;` expression. The path to `N` would wind up with no args
// (as it has no args), but instantiating the early binder on `typeof(N)`
// requires providing generic arguments for `[T, M, N]`.
(res, Some(ty::GenericArgs::identity_for_item(tcx, tcx.parent(def))))
} else if let Res::SelfCtor(impl_def_id) = res {
let ty = LoweredTy::from_raw(
self,
span,
Expand Down Expand Up @@ -1261,6 +1272,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {

fn provided_kind(
&mut self,
preceding_args: &[ty::GenericArg<'tcx>],
param: &ty::GenericParamDef,
arg: &GenericArg<'tcx>,
) -> ty::GenericArg<'tcx> {
Expand All @@ -1280,7 +1292,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
(GenericParamDefKind::Const { .. }, GenericArg::Const(ct)) => self
.fcx
// Ambiguous parts of `ConstArg` are handled in the match arms below
.lower_const_arg(ct.as_unambig_ct(), FeedConstTy::Param(param.def_id))
.lower_const_arg(
ct.as_unambig_ct(),
FeedConstTy::Param(param.def_id, preceding_args),
)
.into(),
(&GenericParamDefKind::Const { .. }, GenericArg::Infer(inf)) => {
self.fcx.ct_infer(Some(param), inf.span).into()
Expand Down Expand Up @@ -1320,7 +1335,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}

let args_raw = self_ctor_args.unwrap_or_else(|| {
let args_raw = implicit_args.unwrap_or_else(|| {
lower_generic_args(
self,
def_id,
Expand Down
6 changes: 5 additions & 1 deletion compiler/rustc_hir_typeck/src/method/confirm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {

fn provided_kind(
&mut self,
preceding_args: &[ty::GenericArg<'tcx>],
param: &ty::GenericParamDef,
arg: &GenericArg<'tcx>,
) -> ty::GenericArg<'tcx> {
Expand All @@ -446,7 +447,10 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
(GenericParamDefKind::Const { .. }, GenericArg::Const(ct)) => self
.cfcx
// We handle the ambig portions of `ConstArg` in the match arms below
.lower_const_arg(ct.as_unambig_ct(), FeedConstTy::Param(param.def_id))
.lower_const_arg(
ct.as_unambig_ct(),
FeedConstTy::Param(param.def_id, preceding_args),
)
.into(),
(GenericParamDefKind::Const { .. }, GenericArg::Infer(inf)) => {
self.cfcx.ct_infer(Some(param), inf.span).into()
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_middle/src/ty/sty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,7 @@ impl ParamConst {
ParamConst::new(def.index, def.name)
}

#[instrument(level = "debug")]
pub fn find_ty_from_env<'tcx>(self, env: ParamEnv<'tcx>) -> Ty<'tcx> {
let mut candidates = env.caller_bounds().iter().filter_map(|clause| {
// `ConstArgHasType` are never desugared to be higher ranked.
Expand Down
9 changes: 0 additions & 9 deletions compiler/rustc_resolve/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -125,9 +125,6 @@ resolve_const_param_in_enum_discriminant =
resolve_const_param_in_non_trivial_anon_const =
const parameters may only be used as standalone arguments, i.e. `{$name}`
resolve_const_param_in_ty_of_const_param =
const parameters may not be used in the type of const parameters
resolve_constructor_private_if_any_field_private =
a constructor is private if any of the fields is private
Expand Down Expand Up @@ -250,9 +247,6 @@ resolve_lifetime_param_in_enum_discriminant =
resolve_lifetime_param_in_non_trivial_anon_const =
lifetime parameters may not be used in const expressions
resolve_lifetime_param_in_ty_of_const_param =
lifetime parameters may not be used in the type of const parameters
resolve_lowercase_self =
attempt to use a non-constant value in a constant
.suggestion = try using `Self`
Expand Down Expand Up @@ -437,9 +431,6 @@ resolve_type_param_in_enum_discriminant =
resolve_type_param_in_non_trivial_anon_const =
type parameters may not be used in const expressions
resolve_type_param_in_ty_of_const_param =
type parameters may not be used in the type of const parameters
resolve_undeclared_label =
use of undeclared label `{$name}`
.label = undeclared label `{$name}`
Expand Down
6 changes: 3 additions & 3 deletions compiler/rustc_resolve/src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -890,9 +890,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
ResolutionError::ForwardDeclaredGenericParam => {
self.dcx().create_err(errs::ForwardDeclaredGenericParam { span })
}
ResolutionError::ParamInTyOfConstParam { name, param_kind: is_type } => self
.dcx()
.create_err(errs::ParamInTyOfConstParam { span, name, param_kind: is_type }),
ResolutionError::ParamInTyOfConstParam { name } => {
self.dcx().create_err(errs::ParamInTyOfConstParam { span, name })
}
ResolutionError::ParamInNonTrivialAnonConst { name, param_kind: is_type } => {
self.dcx().create_err(errs::ParamInNonTrivialAnonConst {
span,
Expand Down
13 changes: 0 additions & 13 deletions compiler/rustc_resolve/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -347,19 +347,6 @@ pub(crate) struct ParamInTyOfConstParam {
#[label]
pub(crate) span: Span,
pub(crate) name: Symbol,
#[subdiagnostic]
pub(crate) param_kind: Option<ParamKindInTyOfConstParam>,
}

#[derive(Debug)]
#[derive(Subdiagnostic)]
pub(crate) enum ParamKindInTyOfConstParam {
#[note(resolve_type_param_in_ty_of_const_param)]
Type,
#[note(resolve_const_param_in_ty_of_const_param)]
Const,
#[note(resolve_lifetime_param_in_ty_of_const_param)]
Lifetime,
}

#[derive(Diagnostic)]
Expand Down
Loading

0 comments on commit f47c2d5

Please # to comment.