Skip to content

Commit d87eec1

Browse files
committed
Add #[rustc_legacy_const_generics]
1 parent 9b471a3 commit d87eec1

File tree

11 files changed

+310
-2
lines changed

11 files changed

+310
-2
lines changed

compiler/rustc_ast_lowering/src/expr.rs

+88-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.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,86 @@ impl<'hir> LoweringContext<'_, 'hir> {
292298
}
293299
}
294300

301+
/// Checks if an expression refers to a function marked with
302+
/// `#[rustc_legacy_const_generics]` and returns the argument index list
303+
/// from the attribute.
304+
fn legacy_const_generic_args(&mut self, expr: &Expr) -> Option<Vec<usize>> {
305+
if let ExprKind::Path(None, path) = &expr.kind {
306+
if path.segments.last().unwrap().args.is_some() {
307+
return None;
308+
}
309+
if let Some(partial_res) = self.resolver.get_partial_res(expr.id) {
310+
if partial_res.unresolved_segments() != 0 {
311+
return None;
312+
}
313+
if let Res::Def(hir::def::DefKind::Fn, def_id) = partial_res.base_res() {
314+
let attrs = self.item_attrs(def_id);
315+
let attr = attrs
316+
.iter()
317+
.find(|a| self.sess.check_name(a, sym::rustc_legacy_const_generics))?;
318+
let mut ret = vec![];
319+
for meta in attr.meta_item_list()? {
320+
match meta.literal()?.kind {
321+
LitKind::Int(a, _) => {
322+
ret.push(a as usize);
323+
}
324+
_ => panic!("invalid arg index"),
325+
}
326+
}
327+
return Some(ret);
328+
}
329+
}
330+
}
331+
None
332+
}
333+
334+
fn lower_legacy_const_generics(
335+
&mut self,
336+
mut f: Expr,
337+
args: Vec<AstP<Expr>>,
338+
legacy_args_idx: &[usize],
339+
) -> hir::ExprKind<'hir> {
340+
let path = match f.kind {
341+
ExprKind::Path(None, ref mut path) => path,
342+
_ => unreachable!(),
343+
};
344+
345+
// Split the arguments into const generics and normal arguments
346+
let mut real_args = vec![];
347+
let mut generic_args = vec![];
348+
for (idx, arg) in args.into_iter().enumerate() {
349+
if legacy_args_idx.contains(&idx) {
350+
let parent_def_id = self.current_hir_id_owner.last().unwrap().0;
351+
let node_id = self.resolver.next_node_id();
352+
353+
// Add a definition for the in-band const def.
354+
self.resolver.create_def(
355+
parent_def_id,
356+
node_id,
357+
DefPathData::AnonConst,
358+
ExpnId::root(),
359+
arg.span,
360+
);
361+
362+
let anon_const = AnonConst { id: node_id, value: arg };
363+
generic_args.push(AngleBracketedArg::Arg(GenericArg::Const(anon_const)));
364+
} else {
365+
real_args.push(arg);
366+
}
367+
}
368+
369+
// Add generic args to the last element of the path
370+
path.segments.last_mut().unwrap().args =
371+
Some(AstP(GenericArgs::AngleBracketed(AngleBracketedArgs {
372+
span: DUMMY_SP,
373+
args: generic_args,
374+
})));
375+
376+
// Now lower everything as normal.
377+
let f = self.lower_expr(&f);
378+
hir::ExprKind::Call(f, self.lower_exprs(&real_args))
379+
}
380+
295381
/// Emit an error and lower `ast::ExprKind::Let(pat, scrutinee)` into:
296382
/// ```rust
297383
/// match scrutinee { pats => true, _ => false }

compiler/rustc_ast_lowering/src/lib.rs

+12
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 item_attrs(&self, def_id: DefId, sess: &Session) -> Vec<ast::Attribute>;
179+
178180
/// Obtains resolution for a `NodeId` with a single resolution.
179181
fn get_partial_res(&mut self, id: NodeId) -> Option<PartialRes>;
180182

@@ -2826,6 +2828,16 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
28262828
)
28272829
}
28282830
}
2831+
2832+
fn item_attrs(&self, def_id: DefId) -> Vec<ast::Attribute> {
2833+
if let Some(_local_def_id) = def_id.as_local() {
2834+
// TODO: This doesn't actually work, items doesn't include everything?
2835+
//self.items[&hir::ItemId { def_id: local_def_id }].attrs.into()
2836+
Vec::new()
2837+
} else {
2838+
self.resolver.item_attrs(def_id, self.sess)
2839+
}
2840+
}
28292841
}
28302842

28312843
fn body_ids(bodies: &BTreeMap<hir::BodyId, hir::Body<'_>>) -> Vec<hir::BodyId> {

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

+75
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,79 @@ 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(..) | Target::ForeignFn);
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 mut invalid_args = vec![];
780+
for meta in list {
781+
if let Some(LitKind::Int(val, _)) = meta.literal().map(|lit| &lit.kind) {
782+
if let Some(ItemLike::Item(Item {
783+
kind: ItemKind::Fn(FnSig { decl, .. }, generics, _),
784+
..
785+
}))
786+
| Some(ItemLike::ForeignItem(ForeignItem {
787+
kind: ForeignItemKind::Fn(decl, _, generics),
788+
..
789+
})) = item
790+
{
791+
let arg_count = decl.inputs.len() as u128 + generics.params.len() as u128;
792+
if *val >= arg_count {
793+
let span = meta.span();
794+
self.tcx
795+
.sess
796+
.struct_span_err(span, "index exceeds number of arguments")
797+
.span_label(
798+
span,
799+
format!(
800+
"there {} only {} argument{}",
801+
if arg_count != 1 { "are" } else { "is" },
802+
arg_count,
803+
pluralize!(arg_count)
804+
),
805+
)
806+
.emit();
807+
return false;
808+
}
809+
} else {
810+
bug!("should be a function item");
811+
}
812+
} else {
813+
invalid_args.push(meta.span());
814+
}
815+
}
816+
817+
if !invalid_args.is_empty() {
818+
self.tcx
819+
.sess
820+
.struct_span_err(invalid_args, "arguments should be non-negative integers")
821+
.emit();
822+
false
823+
} else {
824+
true
825+
}
826+
}
827+
753828
/// Checks if `#[link_section]` is applied to a function or static.
754829
fn check_link_section(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) {
755830
match target {

compiler/rustc_resolve/src/lib.rs

+4
Original file line numberDiff line numberDiff line change
@@ -1076,6 +1076,10 @@ impl ResolverAstLowering for Resolver<'_> {
10761076
self.cstore().item_generics_num_lifetimes(def_id, sess)
10771077
}
10781078

1079+
fn item_attrs(&self, def_id: DefId, sess: &Session) -> Vec<ast::Attribute> {
1080+
self.cstore().item_attrs(def_id, sess)
1081+
}
1082+
10791083
fn get_partial_res(&mut self, id: NodeId) -> Option<PartialRes> {
10801084
self.partial_res_map.get(&id).cloned()
10811085
}

compiler/rustc_span/src/symbol.rs

+1
Original file line numberDiff line numberDiff line change
@@ -973,6 +973,7 @@ symbols! {
973973
rustc_layout,
974974
rustc_layout_scalar_valid_range_end,
975975
rustc_layout_scalar_valid_range_start,
976+
rustc_legacy_const_generics,
976977
rustc_macro_transparency,
977978
rustc_mir,
978979
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,35 @@
1+
#![feature(rustc_attrs)]
2+
3+
#[rustc_legacy_const_generics(0)] //~ ERROR index exceeds number of arguments
4+
fn foo1() {}
5+
6+
#[rustc_legacy_const_generics(1)] //~ ERROR index exceeds number of arguments
7+
fn foo2(_: u8) {}
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() {}
14+
15+
#[rustc_legacy_const_generics(1, a, 2, b)] //~ ERROR arguments should be non-negative integers
16+
fn foo5(_: u8, _: u8, _: u8) {}
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(_: u8) {}
23+
24+
extern {
25+
#[rustc_legacy_const_generics(1)] //~ ERROR index exceeds number of arguments
26+
fn foo7(_: u8);
27+
}
28+
29+
#[rustc_legacy_const_generics] //~ ERROR malformed `rustc_legacy_const_generics` attribute
30+
fn bar1() {}
31+
32+
#[rustc_legacy_const_generics = 1] //~ ERROR malformed `rustc_legacy_const_generics` attribute
33+
fn bar2() {}
34+
35+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
error: suffixed literals are not allowed in attributes
2+
--> $DIR/invalid-rustc_legacy_const_generics-arguments.rs:21:31
3+
|
4+
LL | #[rustc_legacy_const_generics(0usize)]
5+
| ^^^^^^
6+
|
7+
= help: instead of using a suffixed literal (`1u8`, `1.0f32`, etc.), use an unsuffixed version (`1`, `1.0`, etc.)
8+
9+
error: malformed `rustc_legacy_const_generics` attribute input
10+
--> $DIR/invalid-rustc_legacy_const_generics-arguments.rs:29:1
11+
|
12+
LL | #[rustc_legacy_const_generics]
13+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[rustc_legacy_const_generics(N)]`
14+
15+
error: malformed `rustc_legacy_const_generics` attribute input
16+
--> $DIR/invalid-rustc_legacy_const_generics-arguments.rs:32:1
17+
|
18+
LL | #[rustc_legacy_const_generics = 1]
19+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[rustc_legacy_const_generics(N)]`
20+
21+
error: index exceeds number of arguments
22+
--> $DIR/invalid-rustc_legacy_const_generics-arguments.rs:3:31
23+
|
24+
LL | #[rustc_legacy_const_generics(0)]
25+
| ^ there are only 0 arguments
26+
27+
error: index exceeds number of arguments
28+
--> $DIR/invalid-rustc_legacy_const_generics-arguments.rs:6:31
29+
|
30+
LL | #[rustc_legacy_const_generics(1)]
31+
| ^ there is only 1 argument
32+
33+
error: index exceeds number of arguments
34+
--> $DIR/invalid-rustc_legacy_const_generics-arguments.rs:9:31
35+
|
36+
LL | #[rustc_legacy_const_generics(2)]
37+
| ^ there are only 2 arguments
38+
39+
error: arguments should be non-negative integers
40+
--> $DIR/invalid-rustc_legacy_const_generics-arguments.rs:12:31
41+
|
42+
LL | #[rustc_legacy_const_generics(a)]
43+
| ^
44+
45+
error: arguments should be non-negative integers
46+
--> $DIR/invalid-rustc_legacy_const_generics-arguments.rs:15:34
47+
|
48+
LL | #[rustc_legacy_const_generics(1, a, 2, b)]
49+
| ^ ^
50+
51+
error: attribute should be applied to a function
52+
--> $DIR/invalid-rustc_legacy_const_generics-arguments.rs:18:1
53+
|
54+
LL | #[rustc_legacy_const_generics(0)]
55+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
56+
LL | struct S;
57+
| --------- not a function
58+
59+
error: index exceeds number of arguments
60+
--> $DIR/invalid-rustc_legacy_const_generics-arguments.rs:25:35
61+
|
62+
LL | #[rustc_legacy_const_generics(1)]
63+
| ^ there is only 1 argument
64+
65+
error: aborting due to 10 previous errors
66+

src/test/ui/legacy-const-generics.rs

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// aux-build:legacy-const-generics.rs
2+
// run-pass
3+
4+
#![feature(rustc_attrs)]
5+
6+
extern crate legacy_const_generics;
7+
8+
#[rustc_legacy_const_generics(1)]
9+
pub fn bar<const Y: usize>(x: usize, z: usize) -> [usize; 3] {
10+
[x, Y, z]
11+
}
12+
13+
fn main() {
14+
assert_eq!(legacy_const_generics::foo(0 + 0, 1 + 1, 2 + 2), [0, 2, 4]);
15+
assert_eq!(legacy_const_generics::foo::<{1 + 1}>(0 + 0, 2 + 2), [0, 2, 4]);
16+
// TODO: Only works cross-crate
17+
//assert_eq!(bar(0, 1, 2), [0, 1, 2]);
18+
}

0 commit comments

Comments
 (0)