From d87eec1bf67959977c8ebaadc2ddcf4bdc14658a Mon Sep 17 00:00:00 2001 From: Amanieu d'Antras Date: Tue, 23 Feb 2021 15:12:28 +0000 Subject: [PATCH] Add #[rustc_legacy_const_generics] --- compiler/rustc_ast_lowering/src/expr.rs | 90 ++++++++++++++++++- compiler/rustc_ast_lowering/src/lib.rs | 12 +++ compiler/rustc_feature/src/builtin_attrs.rs | 1 + .../src/rmeta/decoder/cstore_impl.rs | 4 + compiler/rustc_passes/src/check_attr.rs | 75 ++++++++++++++++ compiler/rustc_resolve/src/lib.rs | 4 + compiler/rustc_span/src/symbol.rs | 1 + .../ui/auxiliary/legacy-const-generics.rs | 6 ++ ...d-rustc_legacy_const_generics-arguments.rs | 35 ++++++++ ...stc_legacy_const_generics-arguments.stderr | 66 ++++++++++++++ src/test/ui/legacy-const-generics.rs | 18 ++++ 11 files changed, 310 insertions(+), 2 deletions(-) create mode 100644 src/test/ui/auxiliary/legacy-const-generics.rs create mode 100644 src/test/ui/invalid-rustc_legacy_const_generics-arguments.rs create mode 100644 src/test/ui/invalid-rustc_legacy_const_generics-arguments.stderr create mode 100644 src/test/ui/legacy-const-generics.rs diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index b118c0eaed4..145a9784010 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -9,7 +9,9 @@ use rustc_data_structures::thin_vec::ThinVec; use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_hir::def::Res; +use rustc_hir::definitions::DefPathData; use rustc_session::parse::feature_err; +use rustc_span::hygiene::ExpnId; use rustc_span::source_map::{respan, DesugaringKind, Span, Spanned}; use rustc_span::symbol::{sym, Ident, Symbol}; use rustc_span::{hygiene::ForLoopLoc, DUMMY_SP}; @@ -42,8 +44,12 @@ impl<'hir> LoweringContext<'_, 'hir> { } ExprKind::Tup(ref elts) => hir::ExprKind::Tup(self.lower_exprs(elts)), ExprKind::Call(ref f, ref args) => { - let f = self.lower_expr(f); - hir::ExprKind::Call(f, self.lower_exprs(args)) + if let Some(legacy_args) = self.legacy_const_generic_args(f) { + self.lower_legacy_const_generics((**f).clone(), args.clone(), &legacy_args) + } else { + let f = self.lower_expr(f); + hir::ExprKind::Call(f, self.lower_exprs(args)) + } } ExprKind::MethodCall(ref seg, ref args, span) => { let hir_seg = self.arena.alloc(self.lower_path_segment( @@ -292,6 +298,86 @@ impl<'hir> LoweringContext<'_, 'hir> { } } + /// Checks if an expression refers to a function marked with + /// `#[rustc_legacy_const_generics]` and returns the argument index list + /// from the attribute. + fn legacy_const_generic_args(&mut self, expr: &Expr) -> Option> { + if let ExprKind::Path(None, path) = &expr.kind { + if path.segments.last().unwrap().args.is_some() { + return None; + } + if let Some(partial_res) = self.resolver.get_partial_res(expr.id) { + if partial_res.unresolved_segments() != 0 { + return None; + } + if let Res::Def(hir::def::DefKind::Fn, def_id) = partial_res.base_res() { + let attrs = self.item_attrs(def_id); + let attr = attrs + .iter() + .find(|a| self.sess.check_name(a, sym::rustc_legacy_const_generics))?; + let mut ret = vec![]; + for meta in attr.meta_item_list()? { + match meta.literal()?.kind { + LitKind::Int(a, _) => { + ret.push(a as usize); + } + _ => panic!("invalid arg index"), + } + } + return Some(ret); + } + } + } + None + } + + fn lower_legacy_const_generics( + &mut self, + mut f: Expr, + args: Vec>, + legacy_args_idx: &[usize], + ) -> hir::ExprKind<'hir> { + let path = match f.kind { + ExprKind::Path(None, ref mut path) => path, + _ => unreachable!(), + }; + + // Split the arguments into const generics and normal arguments + let mut real_args = vec![]; + let mut generic_args = vec![]; + for (idx, arg) in args.into_iter().enumerate() { + if legacy_args_idx.contains(&idx) { + let parent_def_id = self.current_hir_id_owner.last().unwrap().0; + let node_id = self.resolver.next_node_id(); + + // Add a definition for the in-band const def. + self.resolver.create_def( + parent_def_id, + node_id, + DefPathData::AnonConst, + ExpnId::root(), + arg.span, + ); + + let anon_const = AnonConst { id: node_id, value: arg }; + generic_args.push(AngleBracketedArg::Arg(GenericArg::Const(anon_const))); + } else { + real_args.push(arg); + } + } + + // Add generic args to the last element of the path + path.segments.last_mut().unwrap().args = + Some(AstP(GenericArgs::AngleBracketed(AngleBracketedArgs { + span: DUMMY_SP, + args: generic_args, + }))); + + // Now lower everything as normal. + let f = self.lower_expr(&f); + hir::ExprKind::Call(f, self.lower_exprs(&real_args)) + } + /// Emit an error and lower `ast::ExprKind::Let(pat, scrutinee)` into: /// ```rust /// match scrutinee { pats => true, _ => false } diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 05b417effd4..4a438541211 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -175,6 +175,8 @@ pub trait ResolverAstLowering { fn item_generics_num_lifetimes(&self, def: DefId, sess: &Session) -> usize; + fn item_attrs(&self, def_id: DefId, sess: &Session) -> Vec; + /// Obtains resolution for a `NodeId` with a single resolution. fn get_partial_res(&mut self, id: NodeId) -> Option; @@ -2826,6 +2828,16 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ) } } + + fn item_attrs(&self, def_id: DefId) -> Vec { + if let Some(_local_def_id) = def_id.as_local() { + // TODO: This doesn't actually work, items doesn't include everything? + //self.items[&hir::ItemId { def_id: local_def_id }].attrs.into() + Vec::new() + } else { + self.resolver.item_attrs(def_id, self.sess) + } + } } fn body_ids(bodies: &BTreeMap>) -> Vec { diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index ac50703b544..072062dd615 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -470,6 +470,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ rustc_attr!(rustc_promotable, AssumedUsed, template!(Word), IMPL_DETAIL), rustc_attr!(rustc_args_required_const, AssumedUsed, template!(List: "N"), INTERNAL_UNSTABLE), + rustc_attr!(rustc_legacy_const_generics, AssumedUsed, template!(List: "N"), INTERNAL_UNSTABLE), // ========================================================================== // Internal attributes, Layout related: diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index 828c025d38d..0f860d11dc2 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -468,6 +468,10 @@ impl CStore { pub fn num_def_ids(&self, cnum: CrateNum) -> usize { self.get_crate_data(cnum).num_def_ids() } + + pub fn item_attrs(&self, def_id: DefId, sess: &Session) -> Vec { + self.get_crate_data(def_id.krate).get_item_attrs(def_id.index, sess).collect() + } } impl CrateStore for CStore { diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index bf9b7e588bd..947cca3c725 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -91,6 +91,8 @@ impl CheckAttrVisitor<'tcx> { self.check_rustc_allow_const_fn_unstable(hir_id, &attr, span, target) } else if self.tcx.sess.check_name(attr, sym::naked) { self.check_naked(hir_id, attr, span, target) + } else if self.tcx.sess.check_name(attr, sym::rustc_legacy_const_generics) { + self.check_rustc_legacy_const_generics(&attr, span, target, item) } else { // lint-only checks if self.tcx.sess.check_name(attr, sym::cold) { @@ -750,6 +752,79 @@ impl CheckAttrVisitor<'tcx> { } } + /// Checks if `#[rustc_legacy_const_generics]` is applied to a function and has a valid argument. + fn check_rustc_legacy_const_generics( + &self, + attr: &Attribute, + span: &Span, + target: Target, + item: Option>, + ) -> bool { + let is_function = matches!(target, Target::Fn | Target::Method(..) | Target::ForeignFn); + if !is_function { + self.tcx + .sess + .struct_span_err(attr.span, "attribute should be applied to a function") + .span_label(*span, "not a function") + .emit(); + return false; + } + + let list = match attr.meta_item_list() { + // The attribute form is validated on AST. + None => return false, + Some(it) => it, + }; + + let mut invalid_args = vec![]; + for meta in list { + if let Some(LitKind::Int(val, _)) = meta.literal().map(|lit| &lit.kind) { + if let Some(ItemLike::Item(Item { + kind: ItemKind::Fn(FnSig { decl, .. }, generics, _), + .. + })) + | Some(ItemLike::ForeignItem(ForeignItem { + kind: ForeignItemKind::Fn(decl, _, generics), + .. + })) = item + { + let arg_count = decl.inputs.len() as u128 + generics.params.len() as u128; + if *val >= arg_count { + let span = meta.span(); + self.tcx + .sess + .struct_span_err(span, "index exceeds number of arguments") + .span_label( + span, + format!( + "there {} only {} argument{}", + if arg_count != 1 { "are" } else { "is" }, + arg_count, + pluralize!(arg_count) + ), + ) + .emit(); + return false; + } + } else { + bug!("should be a function item"); + } + } else { + invalid_args.push(meta.span()); + } + } + + if !invalid_args.is_empty() { + self.tcx + .sess + .struct_span_err(invalid_args, "arguments should be non-negative integers") + .emit(); + false + } else { + true + } + } + /// Checks if `#[link_section]` is applied to a function or static. fn check_link_section(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) { match target { diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 77fbbaa1532..7e663444595 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -1076,6 +1076,10 @@ impl ResolverAstLowering for Resolver<'_> { self.cstore().item_generics_num_lifetimes(def_id, sess) } + fn item_attrs(&self, def_id: DefId, sess: &Session) -> Vec { + self.cstore().item_attrs(def_id, sess) + } + fn get_partial_res(&mut self, id: NodeId) -> Option { self.partial_res_map.get(&id).cloned() } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 653d70b6cf2..a3dadc372a9 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -973,6 +973,7 @@ symbols! { rustc_layout, rustc_layout_scalar_valid_range_end, rustc_layout_scalar_valid_range_start, + rustc_legacy_const_generics, rustc_macro_transparency, rustc_mir, rustc_nonnull_optimization_guaranteed, diff --git a/src/test/ui/auxiliary/legacy-const-generics.rs b/src/test/ui/auxiliary/legacy-const-generics.rs new file mode 100644 index 00000000000..67352a2fbbb --- /dev/null +++ b/src/test/ui/auxiliary/legacy-const-generics.rs @@ -0,0 +1,6 @@ +#![feature(rustc_attrs)] + +#[rustc_legacy_const_generics(1)] +pub fn foo(x: usize, z: usize) -> [usize; 3] { + [x, Y, z] +} diff --git a/src/test/ui/invalid-rustc_legacy_const_generics-arguments.rs b/src/test/ui/invalid-rustc_legacy_const_generics-arguments.rs new file mode 100644 index 00000000000..e044cd7a1f5 --- /dev/null +++ b/src/test/ui/invalid-rustc_legacy_const_generics-arguments.rs @@ -0,0 +1,35 @@ +#![feature(rustc_attrs)] + +#[rustc_legacy_const_generics(0)] //~ ERROR index exceeds number of arguments +fn foo1() {} + +#[rustc_legacy_const_generics(1)] //~ ERROR index exceeds number of arguments +fn foo2(_: u8) {} + +#[rustc_legacy_const_generics(2)] //~ ERROR index exceeds number of arguments +fn foo3(_: u8) {} + +#[rustc_legacy_const_generics(a)] //~ ERROR arguments should be non-negative integers +fn foo4() {} + +#[rustc_legacy_const_generics(1, a, 2, b)] //~ ERROR arguments should be non-negative integers +fn foo5(_: u8, _: u8, _: u8) {} + +#[rustc_legacy_const_generics(0)] //~ ERROR attribute should be applied to a function +struct S; + +#[rustc_legacy_const_generics(0usize)] //~ ERROR suffixed literals are not allowed in attributes +fn foo6(_: u8) {} + +extern { + #[rustc_legacy_const_generics(1)] //~ ERROR index exceeds number of arguments + fn foo7(_: u8); +} + +#[rustc_legacy_const_generics] //~ ERROR malformed `rustc_legacy_const_generics` attribute +fn bar1() {} + +#[rustc_legacy_const_generics = 1] //~ ERROR malformed `rustc_legacy_const_generics` attribute +fn bar2() {} + +fn main() {} diff --git a/src/test/ui/invalid-rustc_legacy_const_generics-arguments.stderr b/src/test/ui/invalid-rustc_legacy_const_generics-arguments.stderr new file mode 100644 index 00000000000..92fad679257 --- /dev/null +++ b/src/test/ui/invalid-rustc_legacy_const_generics-arguments.stderr @@ -0,0 +1,66 @@ +error: suffixed literals are not allowed in attributes + --> $DIR/invalid-rustc_legacy_const_generics-arguments.rs:21:31 + | +LL | #[rustc_legacy_const_generics(0usize)] + | ^^^^^^ + | + = help: instead of using a suffixed literal (`1u8`, `1.0f32`, etc.), use an unsuffixed version (`1`, `1.0`, etc.) + +error: malformed `rustc_legacy_const_generics` attribute input + --> $DIR/invalid-rustc_legacy_const_generics-arguments.rs:29:1 + | +LL | #[rustc_legacy_const_generics] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[rustc_legacy_const_generics(N)]` + +error: malformed `rustc_legacy_const_generics` attribute input + --> $DIR/invalid-rustc_legacy_const_generics-arguments.rs:32:1 + | +LL | #[rustc_legacy_const_generics = 1] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[rustc_legacy_const_generics(N)]` + +error: index exceeds number of arguments + --> $DIR/invalid-rustc_legacy_const_generics-arguments.rs:3:31 + | +LL | #[rustc_legacy_const_generics(0)] + | ^ there are only 0 arguments + +error: index exceeds number of arguments + --> $DIR/invalid-rustc_legacy_const_generics-arguments.rs:6:31 + | +LL | #[rustc_legacy_const_generics(1)] + | ^ there is only 1 argument + +error: index exceeds number of arguments + --> $DIR/invalid-rustc_legacy_const_generics-arguments.rs:9:31 + | +LL | #[rustc_legacy_const_generics(2)] + | ^ there are only 2 arguments + +error: arguments should be non-negative integers + --> $DIR/invalid-rustc_legacy_const_generics-arguments.rs:12:31 + | +LL | #[rustc_legacy_const_generics(a)] + | ^ + +error: arguments should be non-negative integers + --> $DIR/invalid-rustc_legacy_const_generics-arguments.rs:15:34 + | +LL | #[rustc_legacy_const_generics(1, a, 2, b)] + | ^ ^ + +error: attribute should be applied to a function + --> $DIR/invalid-rustc_legacy_const_generics-arguments.rs:18:1 + | +LL | #[rustc_legacy_const_generics(0)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | struct S; + | --------- not a function + +error: index exceeds number of arguments + --> $DIR/invalid-rustc_legacy_const_generics-arguments.rs:25:35 + | +LL | #[rustc_legacy_const_generics(1)] + | ^ there is only 1 argument + +error: aborting due to 10 previous errors + diff --git a/src/test/ui/legacy-const-generics.rs b/src/test/ui/legacy-const-generics.rs new file mode 100644 index 00000000000..00ab4b6cccf --- /dev/null +++ b/src/test/ui/legacy-const-generics.rs @@ -0,0 +1,18 @@ +// aux-build:legacy-const-generics.rs +// run-pass + +#![feature(rustc_attrs)] + +extern crate legacy_const_generics; + +#[rustc_legacy_const_generics(1)] +pub fn bar(x: usize, z: usize) -> [usize; 3] { + [x, Y, z] +} + +fn main() { + assert_eq!(legacy_const_generics::foo(0 + 0, 1 + 1, 2 + 2), [0, 2, 4]); + assert_eq!(legacy_const_generics::foo::<{1 + 1}>(0 + 0, 2 + 2), [0, 2, 4]); + // TODO: Only works cross-crate + //assert_eq!(bar(0, 1, 2), [0, 1, 2]); +}