diff options
| author | bors <bors@rust-lang.org> | 2021-02-25 18:14:50 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2021-02-25 18:14:50 +0000 |
| commit | 98f8cce6db6c6c6660eeffee2b3903104e547ecf (patch) | |
| tree | 0b7593868337147ef3b8b0fe6ca4125f8b28aa42 /compiler/rustc_passes/src | |
| parent | 0ab7c1d56f92ebc3c456a0c7c502ba1593e76f8c (diff) | |
| parent | 00afbe70f2f5cb0b6455036f4b97537ff77f2052 (diff) | |
| download | rust-98f8cce6db6c6c6660eeffee2b3903104e547ecf.tar.gz rust-98f8cce6db6c6c6660eeffee2b3903104e547ecf.zip | |
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`
Diffstat (limited to 'compiler/rustc_passes/src')
| -rw-r--r-- | compiler/rustc_passes/src/check_attr.rs | 101 |
1 files changed, 101 insertions, 0 deletions
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index bf9b7e588bd..cf08881cd47 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,105 @@ 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(..)); + 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 (decl, generics) = match item { + Some(ItemLike::Item(Item { + kind: ItemKind::Fn(FnSig { decl, .. }, generics, _), + .. + })) => (decl, generics), + _ => bug!("should be a function item"), + }; + + for param in generics.params { + match param.kind { + hir::GenericParamKind::Const { .. } => {} + _ => { + self.tcx + .sess + .struct_span_err( + attr.span, + "#[rustc_legacy_const_generics] functions must \ + only have const generics", + ) + .span_label(param.span, "non-const generic parameter") + .emit(); + return false; + } + } + } + + if list.len() != generics.params.len() { + self.tcx + .sess + .struct_span_err( + attr.span, + "#[rustc_legacy_const_generics] must have one index for each generic parameter", + ) + .span_label(generics.span, "generic parameters") + .emit(); + return false; + } + + let arg_count = decl.inputs.len() as u128 + generics.params.len() as u128; + let mut invalid_args = vec![]; + for meta in list { + if let Some(LitKind::Int(val, _)) = meta.literal().map(|lit| &lit.kind) { + 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 { + 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 { |
