Skip to content
4 changes: 3 additions & 1 deletion compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,8 @@ pub enum GenericParamKind {
span: Span,
/// Optional default value for the const generic param.
default: Option<AnonConst>,
#[visitable(ignore)]
arg_pos: Option<u32>,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Potentially a better name, but I think especially a doc comment, would go a long way here. It's rather opaque/confusing what this is right now.

},
}

Expand Down Expand Up @@ -4489,7 +4491,7 @@ mod size_asserts {
static_assert_size!(GenericArg, 24);
static_assert_size!(GenericArgs, 40);
static_assert_size!(GenericBound, 80);
static_assert_size!(GenericParam, 80);
static_assert_size!(GenericParam, 88);
static_assert_size!(Generics, 40);
static_assert_size!(Impl, 80);
static_assert_size!(Item, 144);
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_ast_lowering/src/delegation/generics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -641,6 +641,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
hir::GenericParamKind::Const {
ty: self.arena.alloc(hir::Ty { kind, hir_id, span }),
default: None,
arg_pos: None,
}
}
};
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_ast_lowering/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2373,7 +2373,7 @@ impl<'hir> LoweringContext<'_, 'hir> {

(hir::ParamName::Plain(self.lower_ident(param.ident)), kind)
}
GenericParamKind::Const { ty, span: _, default } => {
GenericParamKind::Const { ty, span: _, default, arg_pos } => {
let ty = self.lower_ty_alloc(
ty,
ImplTraitContext::Disallowed(ImplTraitPosition::GenericDefault),
Expand Down Expand Up @@ -2406,7 +2406,7 @@ impl<'hir> LoweringContext<'_, 'hir> {

(
hir::ParamName::Plain(self.lower_ident(param.ident)),
hir::GenericParamKind::Const { ty, default },
hir::GenericParamKind::Const { ty, default, arg_pos: *arg_pos },
)
}
}
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_ast_passes/src/ast_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1383,11 +1383,11 @@ fn validate_generic_param_order(dcx: DiagCtxtHandle<'_>, generics: &[GenericPara
}
GenericParamKind::Type { default: None } => (),
GenericParamKind::Lifetime => (),
GenericParamKind::Const { ty: _, span: _, default: Some(default) } => {
GenericParamKind::Const { ty: _, span: _, default: Some(default), arg_pos: _ } => {
ordered_params += " = ";
ordered_params += &pprust::expr_to_string(&default.value);
}
GenericParamKind::Const { ty: _, span: _, default: None } => (),
GenericParamKind::Const { ty: _, span: _, default: None, arg_pos: _ } => (),
}
first = false;
}
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_ast_passes/src/feature_gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
gate_all!(final_associated_functions, "`final` on trait functions is experimental");
gate_all!(fn_delegation, "functions delegation is not yet fully implemented");
gate_all!(frontmatter, "frontmatters are experimental");
gate_all!(function_arg_const_generics, "function arg const generics are experimental");
gate_all!(gen_blocks, "gen blocks are experimental");
gate_all!(generic_const_items, "generic const items are experimental");
gate_all!(global_registration, "global registration is experimental");
Expand Down
7 changes: 4 additions & 3 deletions compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,14 +117,15 @@ pub(crate) fn expand_deriving_coerce_pointee(
GenericParamKind::Type { default: _ } => {
cx.typaram(p.span(), p.ident, p.bounds.clone(), None)
}
GenericParamKind::Const { ty, span: _, default: _ } => cx
.const_param(
GenericParamKind::Const { ty, span: _, default: _, arg_pos: _ } => {
cx.const_param(
p.span(),
p.ident,
p.bounds.clone(),
ty.clone(),
None,
),
)
}
})
.collect(),
where_clause: generics.where_clause.clone(),
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -635,6 +635,7 @@ impl<'a> TraitDef<'a> {

// We can't have default values inside impl block
default: None,
arg_pos: None,
};
let mut param_clone = param.clone();
param_clone.kind = const_nodefault_kind;
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_builtin_macros/src/deriving/reborrow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ fn impl_generics(cx: &ExtCtxt<'_>, generics: &Generics) -> Generics {
GenericParamKind::Type { default: _ } => {
cx.typaram(param.span(), param.ident, param.bounds.clone(), None)
}
GenericParamKind::Const { ty, span: _, default: _ } => cx.const_param(
GenericParamKind::Const { ty, span: _, default: _, arg_pos: _ } => cx.const_param(
param.span(),
param.ident,
param.bounds.clone(),
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_expand/src/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ impl<'a> ExtCtxt<'a> {
attrs: AttrVec::new(),
bounds,
is_placeholder: false,
kind: ast::GenericParamKind::Const { ty, span: DUMMY_SP, default },
kind: ast::GenericParamKind::Const { ty, span: DUMMY_SP, default, arg_pos: None },
colon_span: None,
}
}
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_feature/src/unstable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -546,6 +546,8 @@ declare_features! (
(internal, freeze_impls, "1.78.0", Some(121675)),
/// Frontmatter `---` blocks for use by external tools.
(unstable, frontmatter, "1.88.0", Some(136889)),
/// Allow function args to be const generics
(incomplete, function_arg_const_generics, "CURRENT_RUSTC_VERSION", Some(163261)),
/// Allows defining gen blocks and `gen fn`.
(unstable, gen_blocks, "1.75.0", Some(117078)),
/// Allows using generics in more complex const expressions, based on definitional equality.
Expand Down Expand Up @@ -859,6 +861,7 @@ pub const INCOMPATIBLE_FEATURES: &[(Symbol, Symbol)] = &[

/// Some features require one or more other features to be enabled.
pub const DEPENDENT_FEATURES: &[(Symbol, &[Symbol])] = &[
(sym::function_arg_const_generics, &[sym::min_generic_const_args]),

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technically, this is not strictly necessary - it is possible to use direct args right now on stable, plain simple paths to generic parameters are direct args on stable. Maaaybe still want to include it though, probably as being an Or(gca_min_const_items, gca_adts), unsure.

(sym::generic_const_args, &[sym::min_generic_const_args]),
(sym::macroless_generic_const_args, &[sym::min_generic_const_args]),
(sym::macroless_const_item_generic_const_args, &[sym::min_generic_const_args]),
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_hir/src/hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -848,6 +848,7 @@ pub enum GenericParamKind<'hir> {
ty: &'hir Ty<'hir>,
/// Optional default value for the const generic param
default: Option<&'hir ConstArg<'hir>>,
arg_pos: Option<u32>,
},
}

Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_hir/src/intravisit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1169,7 +1169,7 @@ pub fn walk_generic_param<'v, V: Visitor<'v>>(
GenericParamKind::Type { ref default, .. } => {
visit_opt!(visitor, visit_ty_unambig, default)
}
GenericParamKind::Const { ref ty, ref default } => {
GenericParamKind::Const { ref ty, ref default, arg_pos: _ } => {
try_visit!(visitor.visit_ty_unambig(ty));
if let Some(default) = default {
try_visit!(visitor.visit_const_param_default(*hir_id, default));
Expand Down
6 changes: 3 additions & 3 deletions compiler/rustc_hir_analysis/src/collect/generics_of.rs
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {

ty::GenericParamDefKind::Type { has_default: default.is_some(), synthetic }
}
GenericParamKind::Const { ty: _, default } => {
GenericParamKind::Const { ty: _, default, arg_pos } => {
if default.is_some() {
match param_default_policy.expect("no policy for generic param default") {
ParamDefaultPolicy::Allowed => {}
Expand All @@ -321,7 +321,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
}
}

ty::GenericParamDefKind::Const { has_default: default.is_some() }
ty::GenericParamDefKind::Const { has_default: default.is_some(), arg_pos }
}
};
Some(ty::GenericParamDef {
Expand Down Expand Up @@ -524,7 +524,7 @@ impl<'v> Visitor<'v> for AnonConstInParamTyDetector {
type Result = ControlFlow<()>;

fn visit_generic_param(&mut self, p: &'v hir::GenericParam<'v>) -> Self::Result {
if let GenericParamKind::Const { ty, default: _ } = p.kind {
if let GenericParamKind::Const { ty, default: _, arg_pos: _ } = p.kind {
let prev = self.in_param_ty;
self.in_param_ty = true;
let res = self.visit_ty_unambig(ty);
Expand Down
5 changes: 3 additions & 2 deletions compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -451,8 +451,9 @@ pub(crate) fn check_generic_arg_count(
.iter()
.filter(|param| matches!(param.kind, ty::GenericParamDefKind::Type { synthetic: true, .. }))
.count();
let arg_const_param_count = gen_params.own_arg_pos_consts().count();
let named_type_param_count = param_counts.types - has_self as usize - synth_type_param_count;
let named_const_param_count = param_counts.consts;
let named_const_param_count = param_counts.consts - arg_const_param_count;
let infer_lifetimes =
(gen_pos != GenericArgPosition::Type || seg.infer_args) && !gen_args.has_lifetime_args();

Expand Down Expand Up @@ -628,7 +629,7 @@ pub(crate) fn check_generic_arg_count(
let expected_min = if seg.infer_args {
0
} else {
param_counts.consts + named_type_param_count
named_const_param_count + named_type_param_count
- default_counts.types
- default_counts.consts
};
Expand Down
35 changes: 35 additions & 0 deletions compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2385,6 +2385,41 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
self.check_param_uses_if_mcg(ct, tcx.hir_span(path_hir_id), false)
}

pub fn lower_const_arg_expr(&self, expr: &hir::Expr<'_>, ty: Ty<'tcx>) -> Const<'tcx> {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would strongly prefer this to not be duplicated with the lower_expr_to_const_arg_direct machinery. I'm unsure of how to do so, and is my main bit of feedback in this PR... perhaps it's best to chat about it in Zulip or something. I haven't fully thought this through yet.

let tcx = self.tcx();
match expr.kind {
hir::ExprKind::Lit(lit) => {
self.lower_const_arg_literal(&lit.node, false, ty, expr.span)
}
hir::ExprKind::Unary(
hir::UnOp::Neg,
hir::Expr { kind: hir::ExprKind::Lit(lit), .. },
) => self.lower_const_arg_literal(&lit.node, true, ty, expr.span),
hir::ExprKind::Path(hir::QPath::Resolved(_, hir::Path { res: Res::Local(_), .. })) => {
Const::new_error(tcx, self.dcx().span_err(expr.span, "Path error"))
}
hir::ExprKind::Path(hir::QPath::Resolved(maybe_qself, path)) => {
let opt_self_ty = maybe_qself.as_ref().map(|qself| self.lower_ty(qself));
self.lower_resolved_const_path(opt_self_ty, path, expr.hir_id)
}
hir::ExprKind::Path(hir::QPath::TypeRelative(hir_self_ty, segment)) => {
let self_ty = self.lower_ty(hir_self_ty);
self.lower_type_relative_const_path(
self_ty,
hir_self_ty,
segment,
expr.hir_id,
expr.span,
)
.unwrap_or_else(|guard| Const::new_error(tcx, guard))
}
_ => Const::new_error(
tcx,
self.dcx().span_err(expr.span, "Some other variant we dont support"),
),
}
}

/// Lower a [`hir::ConstArg`] to a (type-level) [`ty::Const`].
#[instrument(skip(self), level = "debug")]
pub fn lower_const_arg(&self, const_arg: &hir::ConstArg<'_>, ty: Ty<'tcx>) -> Const<'tcx> {
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_hir_pretty/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2475,7 +2475,7 @@ impl<'a> State<'a> {
self.print_type(default);
}
}
GenericParamKind::Const { ty, ref default } => {
GenericParamKind::Const { ty, ref default, arg_pos: _ } => {
self.word_space(":");
self.print_type(ty);
if let Some(default) = default {
Expand Down
12 changes: 11 additions & 1 deletion compiler/rustc_hir_typeck/src/callee.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use std::borrow::Cow;
use std::iter;

use rustc_abi::{CanonAbi, ExternAbi};
Expand Down Expand Up @@ -665,11 +666,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
fn_id: SplatLoweringInfo<'tcx>,
callee_generic_args: Option<GenericArgsRef<'tcx>>,
) {
let mut formal_inputs = Cow::Borrowed(fn_sig.inputs());
if let (SplatLoweringInfo::FnDef(def_id), Some(args)) = (fn_id, callee_generic_args) {
for (param, pos) in self.tcx.generics_of(def_id).own_arg_pos_consts() {
let ty = self.tcx.type_of(param.def_id).instantiate(self.tcx, args).skip_norm_wip();
let ty = self.normalize(call_expr.span, Unnormalized::new_wip(ty));
formal_inputs.to_mut().insert(pos as usize, ty);
}
}

let do_check = || {
self.check_argument_types(
call_expr.span,
call_expr,
fn_sig.inputs(),
&formal_inputs,
fn_sig.output(),
expected,
arg_exprs,
Expand Down
17 changes: 14 additions & 3 deletions compiler/rustc_hir_typeck/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
//!
//! See [`rustc_hir_analysis::check`] for more context on type checking in general.

use std::borrow::Cow;

use rustc_abi::{FIRST_VARIANT, FieldIdx};
use rustc_ast as ast;
use rustc_ast::util::parser::ExprPrecedence;
Expand Down Expand Up @@ -618,7 +620,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
call_expr_and_args.map_or(expr.span, |(e, _)| e.span),
expr.span,
expr.hir_id,
call_expr_and_args.is_some(),
call_expr_and_args.map(|(_, args)| args),
)
.0
}
Expand Down Expand Up @@ -1489,14 +1491,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {

// Handle splatted method arguments
// self is already handled as `rcvr`, so it's never splatted here
let method_inputs = &method.sig.inputs()[1..];
let mut method_inputs = Cow::Borrowed(&method.sig.inputs()[1..]);
for (param, pos) in self.tcx.generics_of(method.def_id).own_arg_pos_consts() {
let ty = self
.tcx
.type_of(param.def_id)
.instantiate(self.tcx, method.args)
.skip_norm_wip();
method_inputs.to_mut().insert(pos as usize - 1, ty);
}

let method_tuple_args_flag =
TupleArgumentsFlag::with_fn_sig_kind(method.sig.fn_sig_kind, true);

self.check_argument_types(
segment.ident.span,
expr,
method_inputs,
&method_inputs,
method.sig.output(),
expected,
args,
Expand Down
33 changes: 32 additions & 1 deletion compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -613,6 +613,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
ct
}

pub(crate) fn lower_const_arg_expr(
&self,
expr: &hir::Expr<'_>,
ty: Ty<'tcx>,
) -> ty::Const<'tcx> {
let ct = self.lowerer().lower_const_arg_expr(expr, ty);
self.register_wf_obligation(ct.into(), expr.span, ObligationCauseCode::WellFormed(None));
ct
}

// If the type given by the user has free regions, save it for later, since
// NLL would like to enforce those. Also pass in types that involve
// projections, since those can resolve to `'static` bounds (modulo #54940,
Expand Down Expand Up @@ -1006,8 +1016,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
span: Span,
path_span: Span,
hir_id: HirId,
has_args: bool,
call_args: Option<&'tcx [hir::Expr<'tcx>]>,
) -> (Ty<'tcx>, Res) {
let has_args = call_args.is_some();
let tcx = self.tcx;

let generic_segments = match res {
Expand Down Expand Up @@ -1325,6 +1336,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
generic_segments: &'a [GenericPathSegment],
infer_args_for_err: &'a FxHashSet<usize>,
segments: &'tcx [hir::PathSegment<'tcx>],
call_args: Option<&'tcx [hir::Expr<'tcx>]>,
}
impl<'a, 'tcx> GenericArgsLowerer<'a, 'tcx> for CtorGenericArgsCtxt<'a, 'tcx> {
fn args_for_def_id(
Expand Down Expand Up @@ -1393,6 +1405,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
infer_args: bool,
) -> ty::GenericArg<'tcx> {
let tcx = self.fcx.tcx();
if let Some(pos) = param.kind.arg_pos() {
let Some(args) = self.call_args else {
let guard =
self.fcx.dcx().span_err(self.span, "argument should be provided");
return ty::Const::new_error(tcx, guard).into();
};

let Some(arg) = args.get(pos as usize) else {
let guard = self
.fcx
.dcx()
.span_delayed_bug(self.span, "missing argument for const param");
return ty::Const::new_error(tcx, guard).into();
};
let ty =
tcx.type_of(param.def_id).instantiate(tcx, preceding_args).skip_norm_wip();
return self.fcx.lower_const_arg_expr(arg, ty).into();
}
if !infer_args && let Some(default) = param.default_value(tcx) {
// If we have a default, then it doesn't matter that we're not inferring
// the type/const arguments: We provide the default where any is missing.
Expand Down Expand Up @@ -1420,6 +1450,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
generic_segments: &generic_segments,
infer_args_for_err: &infer_args_for_err,
segments,
call_args,
},
)
});
Expand Down
Loading
Loading