Skip to content

Commit 98f8cce

Browse files
committed
Auto merge of #82447 - Amanieu:legacy_const_generics, r=oli-obk
Add #[rustc_legacy_const_generics] This is the first step towards removing `#[rustc_args_required_const]`: a new attribute is added which rewrites function calls of the form `func(a, b, c)` to `func::<{b}>(a, c)`. This allows previously stabilized functions in `stdarch` which use `rustc_args_required_const` to use const generics instead. This new attribute is not intended to ever be stabilized, it is only intended for use in `stdarch` as a replacement for `#[rustc_args_required_const]`. ```rust #[rustc_legacy_const_generics(1)] pub fn foo<const Y: usize>(x: usize, z: usize) -> [usize; 3] { [x, Y, z] } fn main() { assert_eq!(foo(0 + 0, 1 + 1, 2 + 2), [0, 2, 4]); assert_eq!(foo::<{1 + 1}>(0 + 0, 2 + 2), [0, 2, 4]); } ``` r? `@oli-obk`
2 parents 0ab7c1d + 00afbe7 commit 98f8cce

16 files changed

+429
-4
lines changed

compiler/rustc_ast_lowering/src/expr.rs

+56-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ use rustc_data_structures::thin_vec::ThinVec;
99
use rustc_errors::struct_span_err;
1010
use rustc_hir as hir;
1111
use rustc_hir::def::Res;
12+
use rustc_hir::definitions::DefPathData;
1213
use rustc_session::parse::feature_err;
14+
use rustc_span::hygiene::ExpnId;
1315
use rustc_span::source_map::{respan, DesugaringKind, Span, Spanned};
1416
use rustc_span::symbol::{sym, Ident, Symbol};
1517
use rustc_span::{hygiene::ForLoopLoc, DUMMY_SP};
@@ -42,8 +44,12 @@ impl<'hir> LoweringContext<'_, 'hir> {
4244
}
4345
ExprKind::Tup(ref elts) => hir::ExprKind::Tup(self.lower_exprs(elts)),
4446
ExprKind::Call(ref f, ref args) => {
45-
let f = self.lower_expr(f);
46-
hir::ExprKind::Call(f, self.lower_exprs(args))
47+
if let Some(legacy_args) = self.resolver.legacy_const_generic_args(f) {
48+
self.lower_legacy_const_generics((**f).clone(), args.clone(), &legacy_args)
49+
} else {
50+
let f = self.lower_expr(f);
51+
hir::ExprKind::Call(f, self.lower_exprs(args))
52+
}
4753
}
4854
ExprKind::MethodCall(ref seg, ref args, span) => {
4955
let hir_seg = self.arena.alloc(self.lower_path_segment(
@@ -292,6 +298,54 @@ impl<'hir> LoweringContext<'_, 'hir> {
292298
}
293299
}
294300

301+
fn lower_legacy_const_generics(
302+
&mut self,
303+
mut f: Expr,
304+
args: Vec<AstP<Expr>>,
305+
legacy_args_idx: &[usize],
306+
) -> hir::ExprKind<'hir> {
307+
let path = match f.kind {
308+
ExprKind::Path(None, ref mut path) => path,
309+
_ => unreachable!(),
310+
};
311+
312+
// Split the arguments into const generics and normal arguments
313+
let mut real_args = vec![];
314+
let mut generic_args = vec![];
315+
for (idx, arg) in args.into_iter().enumerate() {
316+
if legacy_args_idx.contains(&idx) {
317+
let parent_def_id = self.current_hir_id_owner.last().unwrap().0;
318+
let node_id = self.resolver.next_node_id();
319+
320+
// Add a definition for the in-band const def.
321+
self.resolver.create_def(
322+
parent_def_id,
323+
node_id,
324+
DefPathData::AnonConst,
325+
ExpnId::root(),
326+
arg.span,
327+
);
328+
329+
let anon_const = AnonConst { id: node_id, value: arg };
330+
generic_args.push(AngleBracketedArg::Arg(GenericArg::Const(anon_const)));
331+
} else {
332+
real_args.push(arg);
333+
}
334+
}
335+
336+
// Add generic args to the last element of the path.
337+
let last_segment = path.segments.last_mut().unwrap();
338+
assert!(last_segment.args.is_none());
339+
last_segment.args = Some(AstP(GenericArgs::AngleBracketed(AngleBracketedArgs {
340+
span: DUMMY_SP,
341+
args: generic_args,
342+
})));
343+
344+
// Now lower everything as normal.
345+
let f = self.lower_expr(&f);
346+
hir::ExprKind::Call(f, self.lower_exprs(&real_args))
347+
}
348+
295349
/// Emit an error and lower `ast::ExprKind::Let(pat, scrutinee)` into:
296350
/// ```rust
297351
/// match scrutinee { pats => true, _ => false }

compiler/rustc_ast_lowering/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,8 @@ pub trait ResolverAstLowering {
175175

176176
fn item_generics_num_lifetimes(&self, def: DefId, sess: &Session) -> usize;
177177

178+
fn legacy_const_generic_args(&mut self, expr: &Expr) -> Option<Vec<usize>>;
179+
178180
/// Obtains resolution for a `NodeId` with a single resolution.
179181
fn get_partial_res(&mut self, id: NodeId) -> Option<PartialRes>;
180182

compiler/rustc_feature/src/builtin_attrs.rs

+1
Original file line numberDiff line numberDiff line change
@@ -470,6 +470,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
470470

471471
rustc_attr!(rustc_promotable, AssumedUsed, template!(Word), IMPL_DETAIL),
472472
rustc_attr!(rustc_args_required_const, AssumedUsed, template!(List: "N"), INTERNAL_UNSTABLE),
473+
rustc_attr!(rustc_legacy_const_generics, AssumedUsed, template!(List: "N"), INTERNAL_UNSTABLE),
473474

474475
// ==========================================================================
475476
// Internal attributes, Layout related:

compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs

+4
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,10 @@ impl CStore {
468468
pub fn num_def_ids(&self, cnum: CrateNum) -> usize {
469469
self.get_crate_data(cnum).num_def_ids()
470470
}
471+
472+
pub fn item_attrs(&self, def_id: DefId, sess: &Session) -> Vec<ast::Attribute> {
473+
self.get_crate_data(def_id.krate).get_item_attrs(def_id.index, sess).collect()
474+
}
471475
}
472476

473477
impl CrateStore for CStore {

compiler/rustc_passes/src/check_attr.rs

+101
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@ impl CheckAttrVisitor<'tcx> {
9191
self.check_rustc_allow_const_fn_unstable(hir_id, &attr, span, target)
9292
} else if self.tcx.sess.check_name(attr, sym::naked) {
9393
self.check_naked(hir_id, attr, span, target)
94+
} else if self.tcx.sess.check_name(attr, sym::rustc_legacy_const_generics) {
95+
self.check_rustc_legacy_const_generics(&attr, span, target, item)
9496
} else {
9597
// lint-only checks
9698
if self.tcx.sess.check_name(attr, sym::cold) {
@@ -750,6 +752,105 @@ impl CheckAttrVisitor<'tcx> {
750752
}
751753
}
752754

755+
/// Checks if `#[rustc_legacy_const_generics]` is applied to a function and has a valid argument.
756+
fn check_rustc_legacy_const_generics(
757+
&self,
758+
attr: &Attribute,
759+
span: &Span,
760+
target: Target,
761+
item: Option<ItemLike<'_>>,
762+
) -> bool {
763+
let is_function = matches!(target, Target::Fn | Target::Method(..));
764+
if !is_function {
765+
self.tcx
766+
.sess
767+
.struct_span_err(attr.span, "attribute should be applied to a function")
768+
.span_label(*span, "not a function")
769+
.emit();
770+
return false;
771+
}
772+
773+
let list = match attr.meta_item_list() {
774+
// The attribute form is validated on AST.
775+
None => return false,
776+
Some(it) => it,
777+
};
778+
779+
let (decl, generics) = match item {
780+
Some(ItemLike::Item(Item {
781+
kind: ItemKind::Fn(FnSig { decl, .. }, generics, _),
782+
..
783+
})) => (decl, generics),
784+
_ => bug!("should be a function item"),
785+
};
786+
787+
for param in generics.params {
788+
match param.kind {
789+
hir::GenericParamKind::Const { .. } => {}
790+
_ => {
791+
self.tcx
792+
.sess
793+
.struct_span_err(
794+
attr.span,
795+
"#[rustc_legacy_const_generics] functions must \
796+
only have const generics",
797+
)
798+
.span_label(param.span, "non-const generic parameter")
799+
.emit();
800+
return false;
801+
}
802+
}
803+
}
804+
805+
if list.len() != generics.params.len() {
806+
self.tcx
807+
.sess
808+
.struct_span_err(
809+
attr.span,
810+
"#[rustc_legacy_const_generics] must have one index for each generic parameter",
811+
)
812+
.span_label(generics.span, "generic parameters")
813+
.emit();
814+
return false;
815+
}
816+
817+
let arg_count = decl.inputs.len() as u128 + generics.params.len() as u128;
818+
let mut invalid_args = vec![];
819+
for meta in list {
820+
if let Some(LitKind::Int(val, _)) = meta.literal().map(|lit| &lit.kind) {
821+
if *val >= arg_count {
822+
let span = meta.span();
823+
self.tcx
824+
.sess
825+
.struct_span_err(span, "index exceeds number of arguments")
826+
.span_label(
827+
span,
828+
format!(
829+
"there {} only {} argument{}",
830+
if arg_count != 1 { "are" } else { "is" },
831+
arg_count,
832+
pluralize!(arg_count)
833+
),
834+
)
835+
.emit();
836+
return false;
837+
}
838+
} else {
839+
invalid_args.push(meta.span());
840+
}
841+
}
842+
843+
if !invalid_args.is_empty() {
844+
self.tcx
845+
.sess
846+
.struct_span_err(invalid_args, "arguments should be non-negative integers")
847+
.emit();
848+
false
849+
} else {
850+
true
851+
}
852+
}
853+
753854
/// Checks if `#[link_section]` is applied to a function or static.
754855
fn check_link_section(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) {
755856
match target {

compiler/rustc_resolve/src/late.rs

+16-2
Original file line numberDiff line numberDiff line change
@@ -2326,8 +2326,22 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
23262326

23272327
ExprKind::Call(ref callee, ref arguments) => {
23282328
self.resolve_expr(callee, Some(expr));
2329-
for argument in arguments {
2330-
self.resolve_expr(argument, None);
2329+
let const_args = self.r.legacy_const_generic_args(callee).unwrap_or(Vec::new());
2330+
for (idx, argument) in arguments.iter().enumerate() {
2331+
// Constant arguments need to be treated as AnonConst since
2332+
// that is how they will be later lowered to HIR.
2333+
if const_args.contains(&idx) {
2334+
self.with_constant_rib(
2335+
IsRepeatExpr::No,
2336+
argument.is_potential_trivial_const_param(),
2337+
None,
2338+
|this| {
2339+
this.resolve_expr(argument, None);
2340+
},
2341+
);
2342+
} else {
2343+
self.resolve_expr(argument, None);
2344+
}
23312345
}
23322346
}
23332347
ExprKind::Type(ref type_expr, ref ty) => {

compiler/rustc_resolve/src/lib.rs

+63
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ use rustc_ast::unwrap_or;
2929
use rustc_ast::visit::{self, Visitor};
3030
use rustc_ast::{self as ast, NodeId};
3131
use rustc_ast::{Crate, CRATE_NODE_ID};
32+
use rustc_ast::{Expr, ExprKind, LitKind};
3233
use rustc_ast::{ItemKind, ModKind, Path};
3334
use rustc_ast_lowering::ResolverAstLowering;
3435
use rustc_ast_pretty::pprust;
@@ -995,6 +996,8 @@ pub struct Resolver<'a> {
995996
/// Some way to know that we are in a *trait* impl in `visit_assoc_item`.
996997
/// FIXME: Replace with a more general AST map (together with some other fields).
997998
trait_impl_items: FxHashSet<LocalDefId>,
999+
1000+
legacy_const_generic_args: FxHashMap<DefId, Option<Vec<usize>>>,
9981001
}
9991002

10001003
/// Nothing really interesting here; it just provides memory for the rest of the crate.
@@ -1076,6 +1079,10 @@ impl ResolverAstLowering for Resolver<'_> {
10761079
self.cstore().item_generics_num_lifetimes(def_id, sess)
10771080
}
10781081

1082+
fn legacy_const_generic_args(&mut self, expr: &Expr) -> Option<Vec<usize>> {
1083+
self.legacy_const_generic_args(expr)
1084+
}
1085+
10791086
fn get_partial_res(&mut self, id: NodeId) -> Option<PartialRes> {
10801087
self.partial_res_map.get(&id).cloned()
10811088
}
@@ -1316,6 +1323,7 @@ impl<'a> Resolver<'a> {
13161323
invocation_parents,
13171324
next_disambiguator: Default::default(),
13181325
trait_impl_items: Default::default(),
1326+
legacy_const_generic_args: Default::default(),
13191327
};
13201328

13211329
let root_parent_scope = ParentScope::module(graph_root, &resolver);
@@ -3308,6 +3316,61 @@ impl<'a> Resolver<'a> {
33083316
pub fn opt_span(&self, def_id: DefId) -> Option<Span> {
33093317
if let Some(def_id) = def_id.as_local() { Some(self.def_id_to_span[def_id]) } else { None }
33103318
}
3319+
3320+
/// Checks if an expression refers to a function marked with
3321+
/// `#[rustc_legacy_const_generics]` and returns the argument index list
3322+
/// from the attribute.
3323+
pub fn legacy_const_generic_args(&mut self, expr: &Expr) -> Option<Vec<usize>> {
3324+
if let ExprKind::Path(None, path) = &expr.kind {
3325+
// Don't perform legacy const generics rewriting if the path already
3326+
// has generic arguments.
3327+
if path.segments.last().unwrap().args.is_some() {
3328+
return None;
3329+
}
3330+
3331+
let partial_res = self.partial_res_map.get(&expr.id)?;
3332+
if partial_res.unresolved_segments() != 0 {
3333+
return None;
3334+
}
3335+
3336+
if let Res::Def(def::DefKind::Fn, def_id) = partial_res.base_res() {
3337+
// We only support cross-crate argument rewriting. Uses
3338+
// within the same crate should be updated to use the new
3339+
// const generics style.
3340+
if def_id.is_local() {
3341+
return None;
3342+
}
3343+
3344+
if let Some(v) = self.legacy_const_generic_args.get(&def_id) {
3345+
return v.clone();
3346+
}
3347+
3348+
let parse_attrs = || {
3349+
let attrs = self.cstore().item_attrs(def_id, self.session);
3350+
let attr = attrs
3351+
.iter()
3352+
.find(|a| self.session.check_name(a, sym::rustc_legacy_const_generics))?;
3353+
let mut ret = vec![];
3354+
for meta in attr.meta_item_list()? {
3355+
match meta.literal()?.kind {
3356+
LitKind::Int(a, _) => {
3357+
ret.push(a as usize);
3358+
}
3359+
_ => panic!("invalid arg index"),
3360+
}
3361+
}
3362+
Some(ret)
3363+
};
3364+
3365+
// Cache the lookup to avoid parsing attributes for an iterm
3366+
// multiple times.
3367+
let ret = parse_attrs();
3368+
self.legacy_const_generic_args.insert(def_id, ret.clone());
3369+
return ret;
3370+
}
3371+
}
3372+
None
3373+
}
33113374
}
33123375

33133376
fn names_to_string(names: &[Symbol]) -> String {

compiler/rustc_span/src/symbol.rs

+1
Original file line numberDiff line numberDiff line change
@@ -981,6 +981,7 @@ symbols! {
981981
rustc_layout,
982982
rustc_layout_scalar_valid_range_end,
983983
rustc_layout_scalar_valid_range_start,
984+
rustc_legacy_const_generics,
984985
rustc_macro_transparency,
985986
rustc_mir,
986987
rustc_nonnull_optimization_guaranteed,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#![feature(rustc_attrs)]
2+
3+
#[rustc_legacy_const_generics(1)]
4+
pub fn foo<const Y: usize>(x: usize, z: usize) -> [usize; 3] {
5+
[x, Y, z]
6+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#![feature(rustc_attrs)]
2+
3+
#[rustc_legacy_const_generics(0)] //~ ERROR #[rustc_legacy_const_generics] must have one index for
4+
fn foo1() {}
5+
6+
#[rustc_legacy_const_generics(1)] //~ ERROR index exceeds number of arguments
7+
fn foo2<const X: usize>() {}
8+
9+
#[rustc_legacy_const_generics(2)] //~ ERROR index exceeds number of arguments
10+
fn foo3<const X: usize>(_: u8) {}
11+
12+
#[rustc_legacy_const_generics(a)] //~ ERROR arguments should be non-negative integers
13+
fn foo4<const X: usize>() {}
14+
15+
#[rustc_legacy_const_generics(1, a, 2, b)] //~ ERROR arguments should be non-negative integers
16+
fn foo5<const X: usize, const Y: usize, const Z: usize, const W: usize>() {}
17+
18+
#[rustc_legacy_const_generics(0)] //~ ERROR attribute should be applied to a function
19+
struct S;
20+
21+
#[rustc_legacy_const_generics(0usize)] //~ ERROR suffixed literals are not allowed in attributes
22+
fn foo6<const X: usize>() {}
23+
24+
extern {
25+
#[rustc_legacy_const_generics(1)] //~ ERROR attribute should be applied to a function
26+
fn foo7<const X: usize>(); //~ ERROR foreign items may not have const parameters
27+
}
28+
29+
#[rustc_legacy_const_generics(0)] //~ ERROR #[rustc_legacy_const_generics] functions must only have
30+
fn foo8<X>() {}
31+
32+
#[rustc_legacy_const_generics] //~ ERROR malformed `rustc_legacy_const_generics` attribute
33+
fn bar1() {}
34+
35+
#[rustc_legacy_const_generics = 1] //~ ERROR malformed `rustc_legacy_const_generics` attribute
36+
fn bar2() {}
37+
38+
fn main() {}

0 commit comments

Comments
 (0)