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>,
},
}

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]),
(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> {
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