Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2935,6 +2935,9 @@ pub struct Param {
pub id: NodeId,
pub span: Span,
pub is_placeholder: bool,
/// Records whether `const` was written before the parameter.
/// This only tracks syntax, not whether it's a valid const generic param.
pub has_const_keyword: bool,
}

/// Alternative representation for `Arg`s describing `self` parameter of methods.
Expand Down Expand Up @@ -3032,8 +3035,28 @@ impl Param {
ty,
id: DUMMY_NODE_ID,
is_placeholder: false,
has_const_keyword: false,
}
}

/// Returns the ident if this param is a valid arg position const param.
/// Unlike has_const_keyword, this also checks the pats
pub fn const_param_ident(&self) -> Option<Ident> {
if !self.has_const_keyword {
return None;
}

match self.pat.kind {
PatKind::Ident(BindingMode::NONE, ident, None) => Some(ident),
_ => None,
}
}

/// Checks if this is an arg position const param.
/// Use this instead of has_const_keyword, which only checks for the keyword.
pub fn is_const_param(&self) -> bool {
self.const_param_ident().is_some()
}
}

/// A signature (not the body) of a function declaration.
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 @@ -671,6 +671,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
16 changes: 13 additions & 3 deletions compiler/rustc_ast_lowering/src/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1254,8 +1254,12 @@ impl<'hir> LoweringContext<'_, 'hir> {
body: impl FnOnce(&mut Self) -> hir::Expr<'hir>,
) -> hir::BodyId {
self.lower_body(|this| {
let params =
this.arena.alloc_from_iter(decl.inputs.iter().map(|x| this.lower_param(x)));
let params = this.arena.alloc_from_iter(
decl.inputs
.iter()
.filter(|param| !param.is_const_param())
.map(|x| this.lower_param(x)),
);

// Optionally lower the fn contract
if let Some(contract) = contract {
Expand Down Expand Up @@ -1398,7 +1402,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
// let-bound variables and temporaries created in the body
// (and its tail expression!) before we drop the
// parameters (c.f. rust-lang/rust#64512).
for (index, parameter) in decl.inputs.iter().enumerate() {
for (index, parameter) in
decl.inputs.iter().filter(|param| !param.is_const_param()).enumerate()
{
let parameter = self.lower_param(parameter);
let span = parameter.pat.span;

Expand Down Expand Up @@ -1771,6 +1777,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
) -> (&'hir hir::Generics<'hir>, T) {
assert!(self.curr_owner.impl_trait_defs.is_empty());
assert!(self.curr_owner.impl_trait_bounds.is_empty());
assert!(self.curr_owner.const_defs.is_empty());

let mut predicates: SmallVec<[hir::WherePredicate<'hir>; 4]> = SmallVec::new();
// We need to make sure that generic params don't have multiple relaxed bounds for the same trait
Expand Down Expand Up @@ -1815,6 +1822,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
let span = self.lower_span(generics.span);
let res = f(self);

let const_defs = std::mem::take(&mut self.curr_owner.const_defs);
params.extend(const_defs.into_iter());

let impl_trait_defs = std::mem::take(&mut self.curr_owner.impl_trait_defs);
params.extend(impl_trait_defs.into_iter());

Expand Down
83 changes: 64 additions & 19 deletions compiler/rustc_ast_lowering/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ struct PerOwnerLoweringState<'a, 'hir> {
// -- Transient --
impl_trait_defs: Vec<hir::GenericParam<'hir>>,
impl_trait_bounds: Vec<hir::WherePredicate<'hir>>,
const_defs: Vec<hir::GenericParam<'hir>>,
}

impl<'a, 'hir> PerOwnerLoweringState<'a, 'hir> {
Expand Down Expand Up @@ -211,6 +212,7 @@ impl<'a, 'hir> PerOwnerLoweringState<'a, 'hir> {
children: LocalDefIdMap::default(),
impl_trait_defs: Vec::new(),
impl_trait_bounds: Vec::new(),
const_defs: Vec::new(),
}
}

Expand All @@ -226,6 +228,7 @@ impl<'a, 'hir> PerOwnerLoweringState<'a, 'hir> {
assert_eq!(self.owner_id(), node.def_id());
assert!(self.impl_trait_defs.is_empty());
assert!(self.impl_trait_bounds.is_empty());
assert!(self.const_defs.is_empty());

let attrs = self.attrs;
let mut bodies = self.bodies;
Expand Down Expand Up @@ -1900,18 +1903,20 @@ impl<'hir> LoweringContext<'_, 'hir> {
}

fn lower_fn_params_to_idents(&mut self, decl: &FnDecl) -> &'hir [Option<Ident>] {
self.arena.alloc_from_iter(decl.inputs.iter().map(|param| match param.pat.kind {
PatKind::Missing => None,
PatKind::Ident(_, ident, _) => Some(self.lower_ident(ident)),
PatKind::Wild => Some(Ident::new(kw::Underscore, self.lower_span(param.pat.span))),
_ => {
self.dcx().span_delayed_bug(
param.pat.span,
"non-missing/ident/wild param pat must trigger an error",
);
None
}
}))
self.arena.alloc_from_iter(decl.inputs.iter().filter(|param| !param.is_const_param()).map(
|param| match param.pat.kind {
PatKind::Missing => None,
PatKind::Ident(_, ident, _) => Some(self.lower_ident(ident)),
PatKind::Wild => Some(Ident::new(kw::Underscore, self.lower_span(param.pat.span))),
_ => {
self.dcx().span_delayed_bug(
param.pat.span,
"non-missing/ident/wild param pat must trigger an error",
);
None
}
},
))
}

/// Lowers a function declaration.
Expand Down Expand Up @@ -1945,13 +1950,53 @@ impl<'hir> LoweringContext<'_, 'hir> {
// Skip the `...` (`CVarArgs`) trailing arguments from the AST,
// as they are not explicit in HIR/Ty function signatures.
// (instead, the `c_variadic` flag is set to `true`)
let mut inputs = &decl.inputs[..];
if decl.c_variadic() {
let input_count = if decl.c_variadic() {
// Splat + variadic errors in AST validation, so just ignore one of them here.
splatted = None;
inputs = &inputs[..inputs.len() - 1];
}
let inputs = self.arena.alloc_from_iter(inputs.iter().map(|param| {
decl.inputs.len() - 1
} else {
decl.inputs.len()
};

let inputs = decl.inputs.iter().enumerate().take(input_count);
// Function argument const params are moved from `inputs` into the function's
// generics. Their position is taken from the original `decl.inputs` (before
// removing `...`), so it matches the argument position at the call site.
let inputs = self.arena.alloc_from_iter(inputs.filter_map(|(param_idx, param)| {
if let Some(ident) = param.const_param_ident() {
if !matches!(
kind,
FnDeclKind::Fn | FnDeclKind::Inherent | FnDeclKind::Impl | FnDeclKind::Trait
) {
self.dcx().span_delayed_bug(
param.span,
"Const args only allowed for Free/Inherent/Impl/Trait methods",
);
return None;
}
let ty = self.lower_ty_alloc(
&param.ty,
ImplTraitContext::Disallowed(ImplTraitPosition::GenericDefault),
);
let hir_id = self.lower_node_id(param.id);
let generic_param = hir::GenericParam {
hir_id,
def_id: self.local_def_id(param.id),
name: ParamName::Plain(self.lower_ident(ident)),
span: self.lower_span(param.span),
pure_wrt_drop: false,
kind: hir::GenericParamKind::Const {
ty,
default: None,
arg_pos: Some(param_idx as u16),
},
colon_span: None,
source: hir::GenericParamSource::Generics,
};
self.lower_attrs(hir_id, &param.attrs, param.span, Target::from(&generic_param));
self.curr_owner.const_defs.push(generic_param);
return None;
}
if let DiscardParams::Yes = discard_params {
// FIXME This uses `fn_hir_id`, which is not correct, it should use the parameter hir id instead
// The parameter is currently not lowered for functions without bodies, so there is no place to store the lowered hir id
Expand All @@ -1972,7 +2017,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
ImplTraitContext::Disallowed(ImplTraitPosition::PointerParam)
}
};
self.lower_ty(&param.ty, itctx)
Some(self.lower_ty(&param.ty, itctx))
}));

let output = match coro {
Expand Down Expand Up @@ -2368,7 +2413,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: None },
)
}
}
Expand Down
15 changes: 15 additions & 0 deletions compiler/rustc_ast_passes/src/ast_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,7 @@ impl<'a> AstValidator<'a> {
self.check_decl_splatting(fn_decl, c_variadic_span, splat_semantic);
self.check_decl_attrs(fn_decl);
self.check_decl_self_param(&fn_decl.inputs, self_semantic);
self.check_decl_const_param(&fn_decl.inputs);
}

/// Emits fatal error if function declaration has more than `u16::MAX` arguments
Expand Down Expand Up @@ -555,6 +556,20 @@ impl<'a> AstValidator<'a> {
}
}

fn check_decl_const_param(&self, fn_inputs: &[Param]) {
for param in fn_inputs {
if param.has_const_keyword && !param.is_const_param() {
let span = if matches!(param.pat.kind, PatKind::Missing) {
param.span
} else {
param.pat.span
};

self.dcx().emit_err(diagnostics::InvalidConstParam { span });
}
}
}

/// Check that the signature of this function does not violate the constraints of its ABI.
fn check_extern_fn_signature(
&self,
Expand Down
8 changes: 8 additions & 0 deletions compiler/rustc_ast_passes/src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,14 @@ pub(crate) struct FnParamForbiddenSelf {
pub span: Span,
}

#[derive(Diagnostic)]
#[diag("`const` parameter is only allowed in free/trait/impl functions")]
pub(crate) struct InvalidConstParam {
#[primary_span]
#[label("not semantically valid as function parameter")]
pub span: Span,
}

#[derive(Diagnostic)]
#[diag("`default` is only allowed on items in trait impls")]
pub(crate) struct ForbiddenDefault {
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 @@ -445,6 +445,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
gate_all!(fn_delegation, "functions delegation is not yet fully implemented");
gate_all!(forced_keywords, "forced keywords are experimental");
gate_all!(frontmatter, "frontmatters are experimental");
gate_all!(function_arg_const_generics, "function arg const generics are experimental");
gate_all!(gca_min_const_items, "unbraced const blocks as const args are experimental");
gate_all!(gen_blocks, "gen blocks are experimental");
gate_all!(generic_const_items, "generic const items are experimental");
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_ast_pretty/src/pprust/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2244,6 +2244,9 @@ impl<'a> State<'a> {
self.print_explicit_self(&eself);
} else {
if !matches!(input.pat.kind, PatKind::Missing) {
if input.has_const_keyword {
self.word_nbsp("const");
}
self.print_pat(&input.pat);
self.word(":");
self.space();
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_builtin_macros/src/autodiff.rs
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,7 @@ mod llvm_enzyme {
id: ast::DUMMY_NODE_ID,
span,
is_placeholder: false,
has_const_keyword: false,
}
})
.collect();
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_expand/src/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -600,6 +600,7 @@ impl<'a> ExtCtxt<'a> {
span,
ty,
is_placeholder: false,
has_const_keyword: false,
}
}

Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_expand/src/placeholders.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ pub(crate) fn placeholder(
span,
ty: ty(),
is_placeholder: true,
has_const_keyword: false,
}]),
AstFragmentKind::FieldDefs => AstFragment::FieldDefs(smallvec![ast::FieldDef {
attrs: Default::default(),
Expand Down
9 changes: 9 additions & 0 deletions compiler/rustc_feature/src/unstable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -555,6 +555,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 using ADTs in directly represented generic const args.
(incomplete, gca_adts, "CURRENT_RUSTC_VERSION", Some(163420)),
/// Allows using generics in more complex const expressions, based on definitional equality.
Expand Down Expand Up @@ -879,6 +881,13 @@ pub enum DependentFeature {
/// Some features require one or more other features to be enabled.
pub const DEPENDENT_FEATURES: &[(Symbol, DependentFeature)] = &[
// tidy-alphabetical-start
(
sym::function_arg_const_generics,
DependentFeature::Or(&[
DependentFeature::Leaf(sym::gca_min_const_items),
DependentFeature::Leaf(sym::gca_adts),
]),
),
(
sym::gca_adts,
DependentFeature::Or(&[
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_hir/src/hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -848,6 +848,9 @@ pub enum GenericParamKind<'hir> {
ty: &'hir Ty<'hir>,
/// Optional default value for the const generic param
default: Option<&'hir ConstArg<'hir>>,
/// Position of the const parameter in the function's argument list,
/// used by `function_arg_const_generics`.
arg_pos: Option<u16>,
},
}

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 @@ -1170,7 +1170,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
Loading
Loading