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`
This commit is contained in:
commit
98f8cce6db
16 changed files with 429 additions and 4 deletions
|
@ -9,7 +9,9 @@ use rustc_data_structures::thin_vec::ThinVec;
|
||||||
use rustc_errors::struct_span_err;
|
use rustc_errors::struct_span_err;
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_hir::def::Res;
|
use rustc_hir::def::Res;
|
||||||
|
use rustc_hir::definitions::DefPathData;
|
||||||
use rustc_session::parse::feature_err;
|
use rustc_session::parse::feature_err;
|
||||||
|
use rustc_span::hygiene::ExpnId;
|
||||||
use rustc_span::source_map::{respan, DesugaringKind, Span, Spanned};
|
use rustc_span::source_map::{respan, DesugaringKind, Span, Spanned};
|
||||||
use rustc_span::symbol::{sym, Ident, Symbol};
|
use rustc_span::symbol::{sym, Ident, Symbol};
|
||||||
use rustc_span::{hygiene::ForLoopLoc, DUMMY_SP};
|
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::Tup(ref elts) => hir::ExprKind::Tup(self.lower_exprs(elts)),
|
||||||
ExprKind::Call(ref f, ref args) => {
|
ExprKind::Call(ref f, ref args) => {
|
||||||
let f = self.lower_expr(f);
|
if let Some(legacy_args) = self.resolver.legacy_const_generic_args(f) {
|
||||||
hir::ExprKind::Call(f, self.lower_exprs(args))
|
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) => {
|
ExprKind::MethodCall(ref seg, ref args, span) => {
|
||||||
let hir_seg = self.arena.alloc(self.lower_path_segment(
|
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:
|
/// Emit an error and lower `ast::ExprKind::Let(pat, scrutinee)` into:
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// match scrutinee { pats => true, _ => false }
|
/// match scrutinee { pats => true, _ => false }
|
||||||
|
|
|
@ -175,6 +175,8 @@ pub trait ResolverAstLowering {
|
||||||
|
|
||||||
fn item_generics_num_lifetimes(&self, def: DefId, sess: &Session) -> usize;
|
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.
|
/// Obtains resolution for a `NodeId` with a single resolution.
|
||||||
fn get_partial_res(&mut self, id: NodeId) -> Option<PartialRes>;
|
fn get_partial_res(&mut self, id: NodeId) -> Option<PartialRes>;
|
||||||
|
|
||||||
|
|
|
@ -470,6 +470,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
|
||||||
|
|
||||||
rustc_attr!(rustc_promotable, AssumedUsed, template!(Word), IMPL_DETAIL),
|
rustc_attr!(rustc_promotable, AssumedUsed, template!(Word), IMPL_DETAIL),
|
||||||
rustc_attr!(rustc_args_required_const, AssumedUsed, template!(List: "N"), INTERNAL_UNSTABLE),
|
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:
|
// Internal attributes, Layout related:
|
||||||
|
|
|
@ -468,6 +468,10 @@ impl CStore {
|
||||||
pub fn num_def_ids(&self, cnum: CrateNum) -> usize {
|
pub fn num_def_ids(&self, cnum: CrateNum) -> usize {
|
||||||
self.get_crate_data(cnum).num_def_ids()
|
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 {
|
impl CrateStore for CStore {
|
||||||
|
|
|
@ -91,6 +91,8 @@ impl CheckAttrVisitor<'tcx> {
|
||||||
self.check_rustc_allow_const_fn_unstable(hir_id, &attr, span, target)
|
self.check_rustc_allow_const_fn_unstable(hir_id, &attr, span, target)
|
||||||
} else if self.tcx.sess.check_name(attr, sym::naked) {
|
} else if self.tcx.sess.check_name(attr, sym::naked) {
|
||||||
self.check_naked(hir_id, attr, span, target)
|
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 {
|
} else {
|
||||||
// lint-only checks
|
// lint-only checks
|
||||||
if self.tcx.sess.check_name(attr, sym::cold) {
|
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.
|
/// 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) {
|
fn check_link_section(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) {
|
||||||
match target {
|
match target {
|
||||||
|
|
|
@ -2326,8 +2326,22 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|
||||||
|
|
||||||
ExprKind::Call(ref callee, ref arguments) => {
|
ExprKind::Call(ref callee, ref arguments) => {
|
||||||
self.resolve_expr(callee, Some(expr));
|
self.resolve_expr(callee, Some(expr));
|
||||||
for argument in arguments {
|
let const_args = self.r.legacy_const_generic_args(callee).unwrap_or(Vec::new());
|
||||||
self.resolve_expr(argument, None);
|
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) => {
|
ExprKind::Type(ref type_expr, ref ty) => {
|
||||||
|
|
|
@ -29,6 +29,7 @@ use rustc_ast::unwrap_or;
|
||||||
use rustc_ast::visit::{self, Visitor};
|
use rustc_ast::visit::{self, Visitor};
|
||||||
use rustc_ast::{self as ast, NodeId};
|
use rustc_ast::{self as ast, NodeId};
|
||||||
use rustc_ast::{Crate, CRATE_NODE_ID};
|
use rustc_ast::{Crate, CRATE_NODE_ID};
|
||||||
|
use rustc_ast::{Expr, ExprKind, LitKind};
|
||||||
use rustc_ast::{ItemKind, ModKind, Path};
|
use rustc_ast::{ItemKind, ModKind, Path};
|
||||||
use rustc_ast_lowering::ResolverAstLowering;
|
use rustc_ast_lowering::ResolverAstLowering;
|
||||||
use rustc_ast_pretty::pprust;
|
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`.
|
/// 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).
|
/// FIXME: Replace with a more general AST map (together with some other fields).
|
||||||
trait_impl_items: FxHashSet<LocalDefId>,
|
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.
|
/// 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)
|
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> {
|
fn get_partial_res(&mut self, id: NodeId) -> Option<PartialRes> {
|
||||||
self.partial_res_map.get(&id).cloned()
|
self.partial_res_map.get(&id).cloned()
|
||||||
}
|
}
|
||||||
|
@ -1316,6 +1323,7 @@ impl<'a> Resolver<'a> {
|
||||||
invocation_parents,
|
invocation_parents,
|
||||||
next_disambiguator: Default::default(),
|
next_disambiguator: Default::default(),
|
||||||
trait_impl_items: Default::default(),
|
trait_impl_items: Default::default(),
|
||||||
|
legacy_const_generic_args: Default::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let root_parent_scope = ParentScope::module(graph_root, &resolver);
|
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> {
|
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 }
|
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 {
|
fn names_to_string(names: &[Symbol]) -> String {
|
||||||
|
|
|
@ -981,6 +981,7 @@ symbols! {
|
||||||
rustc_layout,
|
rustc_layout,
|
||||||
rustc_layout_scalar_valid_range_end,
|
rustc_layout_scalar_valid_range_end,
|
||||||
rustc_layout_scalar_valid_range_start,
|
rustc_layout_scalar_valid_range_start,
|
||||||
|
rustc_legacy_const_generics,
|
||||||
rustc_macro_transparency,
|
rustc_macro_transparency,
|
||||||
rustc_mir,
|
rustc_mir,
|
||||||
rustc_nonnull_optimization_guaranteed,
|
rustc_nonnull_optimization_guaranteed,
|
||||||
|
|
6
src/test/ui/auxiliary/legacy-const-generics.rs
Normal file
6
src/test/ui/auxiliary/legacy-const-generics.rs
Normal file
|
@ -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]
|
||||||
|
}
|
|
@ -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() {}
|
|
@ -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`.
|
16
src/test/ui/legacy-const-generics-bad.rs
Normal file
16
src/test/ui/legacy-const-generics-bad.rs
Normal file
|
@ -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() {}
|
20
src/test/ui/legacy-const-generics-bad.stderr
Normal file
20
src/test/ui/legacy-const-generics-bad.stderr
Normal file
|
@ -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`.
|
18
src/test/ui/legacy-const-generics.rs
Normal file
18
src/test/ui/legacy-const-generics.rs
Normal file
|
@ -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]);
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue