diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs index e58b266fdb9e1..20971ebb95748 100644 --- a/compiler/rustc_attr/src/builtin.rs +++ b/compiler/rustc_attr/src/builtin.rs @@ -862,18 +862,6 @@ pub fn find_repr_attrs(sess: &Session, attr: &Attribute) -> Vec { if let Some(items) = attr.meta_item_list() { sess.mark_attr_used(attr); for item in items { - if !item.is_meta_item() { - handle_errors( - &sess.parse_sess, - item.span(), - AttrError::UnsupportedLiteral( - "meta item in `repr` must be an identifier", - false, - ), - ); - continue; - } - let mut recognised = false; if item.is_word() { let hint = match item.name_or_empty() { @@ -890,23 +878,6 @@ pub fn find_repr_attrs(sess: &Session, attr: &Attribute) -> Vec { acc.push(h); } } else if let Some((name, value)) = item.name_value_literal() { - let parse_alignment = |node: &ast::LitKind| -> Result { - if let ast::LitKind::Int(literal, ast::LitIntType::Unsuffixed) = node { - if literal.is_power_of_two() { - // rustc_middle::ty::layout::Align restricts align to <= 2^29 - if *literal <= 1 << 29 { - Ok(*literal as u32) - } else { - Err("larger than 2^29") - } - } else { - Err("not a power of two") - } - } else { - Err("not an unsuffixed integer") - } - }; - let mut literal_error = None; if name == sym::align { recognised = true; @@ -966,13 +937,7 @@ pub fn find_repr_attrs(sess: &Session, attr: &Attribute) -> Vec { } if !recognised { // Not a word we recognize - struct_span_err!( - diagnostic, - item.span(), - E0552, - "unrecognized representation hint" - ) - .emit(); + diagnostic.delay_span_bug(item.span(), "unrecognized representation hint"); } } } @@ -1080,3 +1045,16 @@ fn allow_unstable<'a>( name }) } + +pub fn parse_alignment(node: &ast::LitKind) -> Result { + if let ast::LitKind::Int(literal, ast::LitIntType::Unsuffixed) = node { + if literal.is_power_of_two() { + // rustc_middle::ty::layout::Align restricts align to <= 2^29 + if *literal <= 1 << 29 { Ok(*literal as u32) } else { Err("larger than 2^29") } + } else { + Err("not a power of two") + } + } else { + Err("not an unsuffixed integer") + } +} diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index 64ebe585dd837..e50a9d16ca4bf 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -279,6 +279,9 @@ pub fn from_fn_attrs(cx: &CodegenCx<'ll, 'tcx>, llfn: &'ll Value, instance: ty:: if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::CMSE_NONSECURE_ENTRY) { llvm::AddFunctionAttrString(llfn, Function, cstr!("cmse_nonsecure_entry")); } + if let Some(align) = codegen_fn_attrs.alignment { + llvm::set_alignment(llfn, align as usize); + } sanitize(cx, codegen_fn_attrs.no_sanitize, llfn); // Always annotate functions with the target-cpu they are compiled for. diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index 040260f5cf5a4..b2d891b967495 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -645,6 +645,9 @@ declare_features! ( /// Allows `extern "C-unwind" fn` to enable unwinding across ABI boundaries. (active, c_unwind, "1.52.0", Some(74990), None), + /// Allows using `#[repr(align(...))]` on function items + (active, fn_align, "1.53.0", Some(82232), None), + // ------------------------------------------------------------------------- // feature-group-end: actual feature gates // ------------------------------------------------------------------------- diff --git a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs index 5f2ffda642cee..8f83589753d32 100644 --- a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs +++ b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs @@ -38,6 +38,9 @@ pub struct CodegenFnAttrs { /// be generated against a specific instruction set. Only usable on architectures which allow /// switching between multiple instruction sets. pub instruction_set: Option, + /// The `#[repr(align(...))]` attribute. Indicates the value of which the function should be + /// aligned to. + pub alignment: Option, } bitflags! { @@ -103,6 +106,7 @@ impl CodegenFnAttrs { link_section: None, no_sanitize: SanitizerSet::empty(), instruction_set: None, + alignment: None, } } diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index d1a3971f5695b..df292b141760d 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -1127,17 +1127,41 @@ impl CheckAttrVisitor<'tcx> { let mut is_transparent = false; for hint in &hints { + if !hint.is_meta_item() { + struct_span_err!( + self.tcx.sess, + hint.span(), + E0565, + "meta item in `repr` must be an identifier" + ) + .emit(); + continue; + } + let (article, allowed_targets) = match hint.name_or_empty() { - _ if !matches!(target, Target::Struct | Target::Enum | Target::Union) => { - ("a", "struct, enum, or union") - } - name @ sym::C | name @ sym::align => { - is_c |= name == sym::C; + sym::C => { + is_c = true; match target { Target::Struct | Target::Union | Target::Enum => continue, _ => ("a", "struct, enum, or union"), } } + sym::align => { + if let (Target::Fn, true) = (target, !self.tcx.features().fn_align) { + feature_err( + &self.tcx.sess.parse_sess, + sym::fn_align, + hint.span(), + "`repr(align)` attributes on functions are unstable", + ) + .emit(); + } + + match target { + Target::Struct | Target::Union | Target::Enum | Target::Fn => continue, + _ => ("a", "struct, enum, function, or union"), + } + } sym::packed => { if target != Target::Struct && target != Target::Union { ("a", "struct or union") @@ -1194,7 +1218,17 @@ impl CheckAttrVisitor<'tcx> { continue; } } - _ => continue, + _ => { + struct_span_err!( + self.tcx.sess, + hint.span(), + E0552, + "unrecognized representation hint" + ) + .emit(); + + continue; + } }; struct_span_err!( diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index c5f929ab183a5..03fe5bcd2971b 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -561,6 +561,7 @@ symbols! { fmt, fmt_internals, fmul_fast, + fn_align, fn_must_use, fn_mut, fn_once, diff --git a/compiler/rustc_typeck/src/collect.rs b/compiler/rustc_typeck/src/collect.rs index afe52c977334d..067b0e8cff814 100644 --- a/compiler/rustc_typeck/src/collect.rs +++ b/compiler/rustc_typeck/src/collect.rs @@ -15,6 +15,8 @@ //! At present, however, we do run collection across all items in the //! crate as a kind of pass. This should eventually be factored away. +// ignore-tidy-filelength + use crate::astconv::{AstConv, SizedByDefault}; use crate::bounds::Bounds; use crate::check::intrinsic::intrinsic_operation_unsafety; @@ -2884,6 +2886,36 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs { None } }; + } else if tcx.sess.check_name(attr, sym::repr) { + codegen_fn_attrs.alignment = match attr.meta_item_list() { + Some(items) => match items.as_slice() { + [item] => match item.name_value_literal() { + Some((sym::align, literal)) => { + let alignment = rustc_attr::parse_alignment(&literal.kind); + + match alignment { + Ok(align) => Some(align), + Err(msg) => { + struct_span_err!( + tcx.sess.diagnostic(), + attr.span, + E0589, + "invalid `repr(align)` attribute: {}", + msg + ) + .emit(); + + None + } + } + } + _ => None, + }, + [] => None, + _ => None, + }, + None => None, + }; } } diff --git a/src/test/codegen/align-fn.rs b/src/test/codegen/align-fn.rs new file mode 100644 index 0000000000000..c5886cf28081a --- /dev/null +++ b/src/test/codegen/align-fn.rs @@ -0,0 +1,9 @@ +// compile-flags: -C no-prepopulate-passes -Z mir-opt-level=0 + +#![crate_type = "lib"] +#![feature(fn_align)] + +// CHECK: align 16 +#[no_mangle] +#[repr(align(16))] +pub fn fn_align() {} diff --git a/src/test/ui/attributes/nonterminal-expansion.rs b/src/test/ui/attributes/nonterminal-expansion.rs index b4f523936a0b0..d663431d6836f 100644 --- a/src/test/ui/attributes/nonterminal-expansion.rs +++ b/src/test/ui/attributes/nonterminal-expansion.rs @@ -3,7 +3,6 @@ macro_rules! pass_nonterminal { ($n:expr) => { #[repr(align($n))] //~ ERROR expected unsuffixed literal or identifier, found `n!()` - //~| ERROR unrecognized representation hint struct S; }; } diff --git a/src/test/ui/attributes/nonterminal-expansion.stderr b/src/test/ui/attributes/nonterminal-expansion.stderr index 9f7f185f94752..75663a666a56d 100644 --- a/src/test/ui/attributes/nonterminal-expansion.stderr +++ b/src/test/ui/attributes/nonterminal-expansion.stderr @@ -9,17 +9,5 @@ LL | pass_nonterminal!(n!()); | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0552]: unrecognized representation hint - --> $DIR/nonterminal-expansion.rs:5:16 - | -LL | #[repr(align($n))] - | ^^^^^^^^^ -... -LL | pass_nonterminal!(n!()); - | ------------------------ in this macro invocation - | - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) - -error: aborting due to 2 previous errors +error: aborting due to previous error -For more information about this error, try `rustc --explain E0552`. diff --git a/src/test/ui/error-codes/E0565.rs b/src/test/ui/error-codes/E0565.rs index 3bf428676105d..df76f6b13af9b 100644 --- a/src/test/ui/error-codes/E0565.rs +++ b/src/test/ui/error-codes/E0565.rs @@ -1,6 +1,5 @@ // repr currently doesn't support literals #[repr("C")] //~ ERROR E0565 - //~| ERROR E0565 -struct A { } +struct A {} -fn main() { } +fn main() {} diff --git a/src/test/ui/error-codes/E0565.stderr b/src/test/ui/error-codes/E0565.stderr index aa0951528e1ed..6ed90c0ae4ffe 100644 --- a/src/test/ui/error-codes/E0565.stderr +++ b/src/test/ui/error-codes/E0565.stderr @@ -4,12 +4,6 @@ error[E0565]: meta item in `repr` must be an identifier LL | #[repr("C")] | ^^^ -error[E0565]: meta item in `repr` must be an identifier - --> $DIR/E0565.rs:2:8 - | -LL | #[repr("C")] - | ^^^ - -error: aborting due to 2 previous errors +error: aborting due to previous error For more information about this error, try `rustc --explain E0565`. diff --git a/src/test/ui/feature-gates/feature-gate-fn_align.rs b/src/test/ui/feature-gates/feature-gate-fn_align.rs new file mode 100644 index 0000000000000..ea873dba269c4 --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-fn_align.rs @@ -0,0 +1,4 @@ +#![crate_type = "lib"] + +#[repr(align(16))] //~ ERROR `repr(align)` attributes on functions are unstable +fn requires_alignment() {} diff --git a/src/test/ui/feature-gates/feature-gate-fn_align.stderr b/src/test/ui/feature-gates/feature-gate-fn_align.stderr new file mode 100644 index 0000000000000..5ff124e48dca9 --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-fn_align.stderr @@ -0,0 +1,12 @@ +error[E0658]: `repr(align)` attributes on functions are unstable + --> $DIR/feature-gate-fn_align.rs:3:8 + | +LL | #[repr(align(16))] + | ^^^^^^^^^ + | + = note: see issue #82232 for more information + = help: add `#![feature(fn_align)]` to the crate attributes to enable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/issues/issue-43988.rs b/src/test/ui/issues/issue-43988.rs index 4b3a0269baea2..b114e8e03333d 100644 --- a/src/test/ui/issues/issue-43988.rs +++ b/src/test/ui/issues/issue-43988.rs @@ -13,13 +13,13 @@ fn main() { #[repr(nothing)] let _x = 0; - //~^^ ERROR attribute should be applied to a struct, enum, or union + //~^^ ERROR E0552 #[repr(something_not_real)] loop { () }; - //~^^^^ ERROR attribute should be applied to a struct, enum, or union + //~^^^^ ERROR E0552 #[repr] let _y = "123"; diff --git a/src/test/ui/issues/issue-43988.stderr b/src/test/ui/issues/issue-43988.stderr index f1205d447e4ba..03aa37f52075f 100644 --- a/src/test/ui/issues/issue-43988.stderr +++ b/src/test/ui/issues/issue-43988.stderr @@ -26,23 +26,17 @@ LL | #[inline(XYZ)] LL | let _b = 4; | ----------- not a function or closure -error[E0517]: attribute should be applied to a struct, enum, or union +error[E0552]: unrecognized representation hint --> $DIR/issue-43988.rs:14:12 | LL | #[repr(nothing)] | ^^^^^^^ -LL | let _x = 0; - | ----------- not a struct, enum, or union -error[E0517]: attribute should be applied to a struct, enum, or union +error[E0552]: unrecognized representation hint --> $DIR/issue-43988.rs:18:12 | -LL | #[repr(something_not_real)] - | ^^^^^^^^^^^^^^^^^^ -LL | / loop { -LL | | () -LL | | }; - | |_____- not a struct, enum, or union +LL | #[repr(something_not_real)] + | ^^^^^^^^^^^^^^^^^^ error[E0518]: attribute should be applied to function or closure --> $DIR/issue-43988.rs:30:5 @@ -54,5 +48,5 @@ LL | foo(); error: aborting due to 7 previous errors -Some errors have detailed explanations: E0517, E0518. -For more information about an error, try `rustc --explain E0517`. +Some errors have detailed explanations: E0518, E0552. +For more information about an error, try `rustc --explain E0518`. diff --git a/src/test/ui/repr/repr-disallow-on-variant.rs b/src/test/ui/repr/repr-disallow-on-variant.rs index 90cad7e647b05..d9bd0b0e38a69 100644 --- a/src/test/ui/repr/repr-disallow-on-variant.rs +++ b/src/test/ui/repr/repr-disallow-on-variant.rs @@ -2,7 +2,7 @@ struct Test; enum Foo { #[repr(u8)] - //~^ ERROR attribute should be applied to a struct, enum, or union + //~^ ERROR attribute should be applied to an enum Variant, } diff --git a/src/test/ui/repr/repr-disallow-on-variant.stderr b/src/test/ui/repr/repr-disallow-on-variant.stderr index 70b45e393fcf1..f7e4dcc9d81be 100644 --- a/src/test/ui/repr/repr-disallow-on-variant.stderr +++ b/src/test/ui/repr/repr-disallow-on-variant.stderr @@ -1,11 +1,11 @@ -error[E0517]: attribute should be applied to a struct, enum, or union +error[E0517]: attribute should be applied to an enum --> $DIR/repr-disallow-on-variant.rs:4:12 | LL | #[repr(u8)] | ^^ LL | LL | Variant, - | ------- not a struct, enum, or union + | ------- not an enum error: aborting due to previous error