Auto merge of - repnop:fn-alignment, r=lcnr

Allow specifying alignment for functions

Fixes 

This allows the user to specify alignment for functions, which can be useful for low level work where functions need to necessarily be aligned to a specific value.

I believe the error cases not covered in the match are caught earlier based on my testing so I had them just return `None`.
This commit is contained in:
bors 2021-04-06 04:35:26 +00:00
commit a6e7a5aa5d
18 changed files with 137 additions and 83 deletions

@ -862,18 +862,6 @@ pub fn find_repr_attrs(sess: &Session, attr: &Attribute) -> Vec<ReprAttr> {
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<ReprAttr> {
acc.push(h);
}
} else if let Some((name, value)) = item.name_value_literal() {
let parse_alignment = |node: &ast::LitKind| -> Result<u32, &'static str> {
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<ReprAttr> {
}
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<u32, &'static str> {
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")
}
}

@ -280,6 +280,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.

@ -642,6 +642,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
// -------------------------------------------------------------------------

@ -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<InstructionSetAttr>,
/// The `#[repr(align(...))]` attribute. Indicates the value of which the function should be
/// aligned to.
pub alignment: Option<u32>,
}
bitflags! {
@ -103,6 +106,7 @@ impl CodegenFnAttrs {
link_section: None,
no_sanitize: SanitizerSet::empty(),
instruction_set: None,
alignment: None,
}
}

@ -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!(

@ -561,6 +561,7 @@ symbols! {
fmt,
fmt_internals,
fmul_fast,
fn_align,
fn_must_use,
fn_mut,
fn_once,

@ -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;
@ -2889,6 +2891,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,
};
}
}

@ -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() {}

@ -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;
};
}

@ -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 previous error
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0552`.

@ -1,6 +1,5 @@
// repr currently doesn't support literals
#[repr("C")] //~ ERROR E0565
//~| ERROR E0565
struct A { }
struct A {}
fn main() { }
fn main() {}

@ -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`.

@ -0,0 +1,4 @@
#![crate_type = "lib"]
#[repr(align(16))] //~ ERROR `repr(align)` attributes on functions are unstable
fn requires_alignment() {}

@ -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 <https://github.com/rust-lang/rust/issues/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`.

@ -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";

@ -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`.

@ -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,
}

@ -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