Skip to content

Commit

Permalink
Add #[rustc_legacy_const_generics]
Browse files Browse the repository at this point in the history
  • Loading branch information
Amanieu committed Feb 23, 2021
1 parent 9b471a3 commit d87eec1
Show file tree
Hide file tree
Showing 11 changed files with 310 additions and 2 deletions.
90 changes: 88 additions & 2 deletions compiler/rustc_ast_lowering/src/expr.rs
Expand Up @@ -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};
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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<Vec<usize>> {
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<AstP<Expr>>,
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 }
Expand Down
12 changes: 12 additions & 0 deletions compiler/rustc_ast_lowering/src/lib.rs
Expand Up @@ -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<ast::Attribute>;

/// Obtains resolution for a `NodeId` with a single resolution.
fn get_partial_res(&mut self, id: NodeId) -> Option<PartialRes>;

Expand Down Expand Up @@ -2826,6 +2828,16 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
)
}
}

fn item_attrs(&self, def_id: DefId) -> Vec<ast::Attribute> {
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<hir::BodyId, hir::Body<'_>>) -> Vec<hir::BodyId> {
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_feature/src/builtin_attrs.rs
Expand Up @@ -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:
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
Expand Up @@ -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<ast::Attribute> {
self.get_crate_data(def_id.krate).get_item_attrs(def_id.index, sess).collect()
}
}

impl CrateStore for CStore {
Expand Down
75 changes: 75 additions & 0 deletions compiler/rustc_passes/src/check_attr.rs
Expand Up @@ -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) {
Expand Down Expand Up @@ -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<ItemLike<'_>>,
) -> 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 {
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_resolve/src/lib.rs
Expand Up @@ -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<ast::Attribute> {
self.cstore().item_attrs(def_id, sess)
}

fn get_partial_res(&mut self, id: NodeId) -> Option<PartialRes> {
self.partial_res_map.get(&id).cloned()
}
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_span/src/symbol.rs
Expand Up @@ -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,
Expand Down
6 changes: 6 additions & 0 deletions src/test/ui/auxiliary/legacy-const-generics.rs
@@ -0,0 +1,6 @@
#![feature(rustc_attrs)]

#[rustc_legacy_const_generics(1)]
pub fn foo<const Y: usize>(x: usize, z: usize) -> [usize; 3] {
[x, Y, z]
}
35 changes: 35 additions & 0 deletions 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<const X: usize>(_: 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() {}
66 changes: 66 additions & 0 deletions 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

18 changes: 18 additions & 0 deletions 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<const Y: usize>(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]);
}

0 comments on commit d87eec1

Please sign in to comment.