about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_ast_lowering/src/expr.rs58
-rw-r--r--compiler/rustc_ast_lowering/src/lib.rs2
-rw-r--r--compiler/rustc_feature/src/builtin_attrs.rs1
-rw-r--r--compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs4
-rw-r--r--compiler/rustc_passes/src/check_attr.rs101
-rw-r--r--compiler/rustc_resolve/src/late.rs18
-rw-r--r--compiler/rustc_resolve/src/lib.rs63
-rw-r--r--compiler/rustc_span/src/symbol.rs1
-rw-r--r--src/test/ui/auxiliary/legacy-const-generics.rs6
-rw-r--r--src/test/ui/invalid/invalid-rustc_args_required_const-arguments.rs (renamed from src/test/ui/invalid-rustc_args_required_const-arguments.rs)0
-rw-r--r--src/test/ui/invalid/invalid-rustc_args_required_const-arguments.stderr (renamed from src/test/ui/invalid-rustc_args_required_const-arguments.stderr)0
-rw-r--r--src/test/ui/invalid/invalid-rustc_legacy_const_generics-arguments.rs38
-rw-r--r--src/test/ui/invalid/invalid-rustc_legacy_const_generics-arguments.stderr87
-rw-r--r--src/test/ui/legacy-const-generics-bad.rs16
-rw-r--r--src/test/ui/legacy-const-generics-bad.stderr20
-rw-r--r--src/test/ui/legacy-const-generics.rs18
16 files changed, 429 insertions, 4 deletions
diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs
index 6d44feec2c4..82b41e13ccc 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.resolver.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,54 @@ impl<'hir> LoweringContext<'_, 'hir> {
         }
     }
 
+    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.
+        let last_segment = path.segments.last_mut().unwrap();
+        assert!(last_segment.args.is_none());
+        last_segment.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..c564ada0395 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 legacy_const_generic_args(&mut self, expr: &Expr) -> Option<Vec<usize>>;
+
     /// Obtains resolution for a `NodeId` with a single resolution.
     fn get_partial_res(&mut self, id: NodeId) -> Option<PartialRes>;
 
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<ast::Attribute> {
+        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..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 {
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs
index 701d48a982d..6f24d7e9413 100644
--- a/compiler/rustc_resolve/src/late.rs
+++ b/compiler/rustc_resolve/src/late.rs
@@ -2326,8 +2326,22 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
 
             ExprKind::Call(ref callee, ref arguments) => {
                 self.resolve_expr(callee, Some(expr));
-                for argument in arguments {
-                    self.resolve_expr(argument, None);
+                let const_args = self.r.legacy_const_generic_args(callee).unwrap_or(Vec::new());
+                for (idx, argument) in arguments.iter().enumerate() {
+                    // Constant arguments need to be treated as AnonConst since
+                    // that is how they will be later lowered to HIR.
+                    if const_args.contains(&idx) {
+                        self.with_constant_rib(
+                            IsRepeatExpr::No,
+                            argument.is_potential_trivial_const_param(),
+                            None,
+                            |this| {
+                                this.resolve_expr(argument, None);
+                            },
+                        );
+                    } else {
+                        self.resolve_expr(argument, None);
+                    }
                 }
             }
             ExprKind::Type(ref type_expr, ref ty) => {
diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs
index 77fbbaa1532..e63fd4ce635 100644
--- a/compiler/rustc_resolve/src/lib.rs
+++ b/compiler/rustc_resolve/src/lib.rs
@@ -29,6 +29,7 @@ use rustc_ast::unwrap_or;
 use rustc_ast::visit::{self, Visitor};
 use rustc_ast::{self as ast, NodeId};
 use rustc_ast::{Crate, CRATE_NODE_ID};
+use rustc_ast::{Expr, ExprKind, LitKind};
 use rustc_ast::{ItemKind, ModKind, Path};
 use rustc_ast_lowering::ResolverAstLowering;
 use rustc_ast_pretty::pprust;
@@ -995,6 +996,8 @@ pub struct Resolver<'a> {
     /// Some way to know that we are in a *trait* impl in `visit_assoc_item`.
     /// FIXME: Replace with a more general AST map (together with some other fields).
     trait_impl_items: FxHashSet<LocalDefId>,
+
+    legacy_const_generic_args: FxHashMap<DefId, Option<Vec<usize>>>,
 }
 
 /// Nothing really interesting here; it just provides memory for the rest of the crate.
@@ -1076,6 +1079,10 @@ impl ResolverAstLowering for Resolver<'_> {
         self.cstore().item_generics_num_lifetimes(def_id, sess)
     }
 
+    fn legacy_const_generic_args(&mut self, expr: &Expr) -> Option<Vec<usize>> {
+        self.legacy_const_generic_args(expr)
+    }
+
     fn get_partial_res(&mut self, id: NodeId) -> Option<PartialRes> {
         self.partial_res_map.get(&id).cloned()
     }
@@ -1316,6 +1323,7 @@ impl<'a> Resolver<'a> {
             invocation_parents,
             next_disambiguator: Default::default(),
             trait_impl_items: Default::default(),
+            legacy_const_generic_args: Default::default(),
         };
 
         let root_parent_scope = ParentScope::module(graph_root, &resolver);
@@ -3308,6 +3316,61 @@ impl<'a> Resolver<'a> {
     pub fn opt_span(&self, def_id: DefId) -> Option<Span> {
         if let Some(def_id) = def_id.as_local() { Some(self.def_id_to_span[def_id]) } else { None }
     }
+
+    /// Checks if an expression refers to a function marked with
+    /// `#[rustc_legacy_const_generics]` and returns the argument index list
+    /// from the attribute.
+    pub fn legacy_const_generic_args(&mut self, expr: &Expr) -> Option<Vec<usize>> {
+        if let ExprKind::Path(None, path) = &expr.kind {
+            // Don't perform legacy const generics rewriting if the path already
+            // has generic arguments.
+            if path.segments.last().unwrap().args.is_some() {
+                return None;
+            }
+
+            let partial_res = self.partial_res_map.get(&expr.id)?;
+            if partial_res.unresolved_segments() != 0 {
+                return None;
+            }
+
+            if let Res::Def(def::DefKind::Fn, def_id) = partial_res.base_res() {
+                // We only support cross-crate argument rewriting. Uses
+                // within the same crate should be updated to use the new
+                // const generics style.
+                if def_id.is_local() {
+                    return None;
+                }
+
+                if let Some(v) = self.legacy_const_generic_args.get(&def_id) {
+                    return v.clone();
+                }
+
+                let parse_attrs = || {
+                    let attrs = self.cstore().item_attrs(def_id, self.session);
+                    let attr = attrs
+                        .iter()
+                        .find(|a| self.session.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"),
+                        }
+                    }
+                    Some(ret)
+                };
+
+                // Cache the lookup to avoid parsing attributes for an iterm
+                // multiple times.
+                let ret = parse_attrs();
+                self.legacy_const_generic_args.insert(def_id, ret.clone());
+                return ret;
+            }
+        }
+        None
+    }
 }
 
 fn names_to_string(names: &[Symbol]) -> String {
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index b112402ffe3..6fe65a8a1cd 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -981,6 +981,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<const Y: usize>(x: usize, z: usize) -> [usize; 3] {
+    [x, Y, z]
+}
diff --git a/src/test/ui/invalid-rustc_args_required_const-arguments.rs b/src/test/ui/invalid/invalid-rustc_args_required_const-arguments.rs
index 99508baeb00..99508baeb00 100644
--- a/src/test/ui/invalid-rustc_args_required_const-arguments.rs
+++ b/src/test/ui/invalid/invalid-rustc_args_required_const-arguments.rs
diff --git a/src/test/ui/invalid-rustc_args_required_const-arguments.stderr b/src/test/ui/invalid/invalid-rustc_args_required_const-arguments.stderr
index 932344f0a33..932344f0a33 100644
--- a/src/test/ui/invalid-rustc_args_required_const-arguments.stderr
+++ b/src/test/ui/invalid/invalid-rustc_args_required_const-arguments.stderr
diff --git a/src/test/ui/invalid/invalid-rustc_legacy_const_generics-arguments.rs b/src/test/ui/invalid/invalid-rustc_legacy_const_generics-arguments.rs
new file mode 100644
index 00000000000..3d8478f06db
--- /dev/null
+++ b/src/test/ui/invalid/invalid-rustc_legacy_const_generics-arguments.rs
@@ -0,0 +1,38 @@
+#![feature(rustc_attrs)]
+
+#[rustc_legacy_const_generics(0)] //~ ERROR #[rustc_legacy_const_generics] must have one index for
+fn foo1() {}
+
+#[rustc_legacy_const_generics(1)] //~ ERROR index exceeds number of arguments
+fn foo2<const X: usize>() {}
+
+#[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<const X: usize>() {}
+
+#[rustc_legacy_const_generics(1, a, 2, b)] //~ ERROR arguments should be non-negative integers
+fn foo5<const X: usize, const Y: usize, const Z: usize, const W: usize>() {}
+
+#[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<const X: usize>() {}
+
+extern {
+    #[rustc_legacy_const_generics(1)] //~ ERROR attribute should be applied to a function
+    fn foo7<const X: usize>(); //~ ERROR foreign items may not have const parameters
+}
+
+#[rustc_legacy_const_generics(0)] //~ ERROR #[rustc_legacy_const_generics] functions must only have
+fn foo8<X>() {}
+
+#[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/invalid-rustc_legacy_const_generics-arguments.stderr b/src/test/ui/invalid/invalid-rustc_legacy_const_generics-arguments.stderr
new file mode 100644
index 00000000000..1f55a8e72d2
--- /dev/null
+++ b/src/test/ui/invalid/invalid-rustc_legacy_const_generics-arguments.stderr
@@ -0,0 +1,87 @@
+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:32: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:35:1
+   |
+LL | #[rustc_legacy_const_generics = 1]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[rustc_legacy_const_generics(N)]`
+
+error: #[rustc_legacy_const_generics] must have one index for each generic parameter
+  --> $DIR/invalid-rustc_legacy_const_generics-arguments.rs:3:1
+   |
+LL | #[rustc_legacy_const_generics(0)]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | fn foo1() {}
+   |        - generic parameters
+
+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: #[rustc_legacy_const_generics] functions must only have const generics
+  --> $DIR/invalid-rustc_legacy_const_generics-arguments.rs:29:1
+   |
+LL | #[rustc_legacy_const_generics(0)]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | fn foo8<X>() {}
+   |         - non-const generic parameter
+
+error: attribute should be applied to a function
+  --> $DIR/invalid-rustc_legacy_const_generics-arguments.rs:25:5
+   |
+LL |     #[rustc_legacy_const_generics(1)]
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |     fn foo7<const X: usize>();
+   |     -------------------------- not a function
+
+error[E0044]: foreign items may not have const parameters
+  --> $DIR/invalid-rustc_legacy_const_generics-arguments.rs:26:5
+   |
+LL |     fn foo7<const X: usize>();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^ can't have const parameters
+   |
+   = help: replace the const parameters with concrete consts
+
+error: aborting due to 12 previous errors
+
+For more information about this error, try `rustc --explain E0044`.
diff --git a/src/test/ui/legacy-const-generics-bad.rs b/src/test/ui/legacy-const-generics-bad.rs
new file mode 100644
index 00000000000..538eee337cc
--- /dev/null
+++ b/src/test/ui/legacy-const-generics-bad.rs
@@ -0,0 +1,16 @@
+// aux-build:legacy-const-generics.rs
+
+extern crate legacy_const_generics;
+
+fn foo<const N: usize>() {
+    let a = 1;
+    legacy_const_generics::foo(0, a, 2);
+    //~^ ERROR attempt to use a non-constant value in a constant
+
+    legacy_const_generics::foo(0, N, 2);
+
+    legacy_const_generics::foo(0, N + 1, 2);
+    //~^ ERROR generic parameters may not be used in const operations
+}
+
+fn main() {}
diff --git a/src/test/ui/legacy-const-generics-bad.stderr b/src/test/ui/legacy-const-generics-bad.stderr
new file mode 100644
index 00000000000..5a44b8e7065
--- /dev/null
+++ b/src/test/ui/legacy-const-generics-bad.stderr
@@ -0,0 +1,20 @@
+error[E0435]: attempt to use a non-constant value in a constant
+  --> $DIR/legacy-const-generics-bad.rs:7:35
+   |
+LL |     let a = 1;
+   |     ----- help: consider using `const` instead of `let`: `const a`
+LL |     legacy_const_generics::foo(0, a, 2);
+   |                                   ^ non-constant value
+
+error: generic parameters may not be used in const operations
+  --> $DIR/legacy-const-generics-bad.rs:12:35
+   |
+LL |     legacy_const_generics::foo(0, N + 1, 2);
+   |                                   ^ cannot perform const operation using `N`
+   |
+   = help: const parameters may only be used as standalone arguments, i.e. `N`
+   = help: use `#![feature(const_generics)]` and `#![feature(const_evaluatable_checked)]` to allow generic const expressions
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0435`.
diff --git a/src/test/ui/legacy-const-generics.rs b/src/test/ui/legacy-const-generics.rs
new file mode 100644
index 00000000000..9abc72d98e6
--- /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<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]);
+    // FIXME: Only works cross-crate
+    //assert_eq!(bar(0, 1, 2), [0, 1, 2]);
+}