Skip to content

Commit

Permalink
Auto merge of rust-lang#81234 - repnop:fn-alignment, r=lcnr
Browse files Browse the repository at this point in the history
Allow specifying alignment for functions

Fixes rust-lang#75072

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`.
  • Loading branch information
bors committed Apr 6, 2021
2 parents 0c7d4ef + 448d076 commit a6e7a5a
Show file tree
Hide file tree
Showing 18 changed files with 137 additions and 83 deletions.
50 changes: 14 additions & 36 deletions compiler/rustc_attr/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand All @@ -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;
Expand Down Expand Up @@ -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");
}
}
}
Expand Down Expand Up @@ -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")
}
}
3 changes: 3 additions & 0 deletions compiler/rustc_codegen_llvm/src/attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_feature/src/active.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
// -------------------------------------------------------------------------
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_middle/src/middle/codegen_fn_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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! {
Expand Down Expand Up @@ -103,6 +106,7 @@ impl CodegenFnAttrs {
link_section: None,
no_sanitize: SanitizerSet::empty(),
instruction_set: None,
alignment: None,
}
}

Expand Down
46 changes: 40 additions & 6 deletions compiler/rustc_passes/src/check_attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down Expand Up @@ -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!(
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -561,6 +561,7 @@ symbols! {
fmt,
fmt_internals,
fmul_fast,
fn_align,
fn_must_use,
fn_mut,
fn_once,
Expand Down
32 changes: 32 additions & 0 deletions compiler/rustc_typeck/src/collect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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,
};
}
}

Expand Down
9 changes: 9 additions & 0 deletions src/test/codegen/align-fn.rs
Original file line number Diff line number Diff line change
@@ -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() {}
1 change: 0 additions & 1 deletion src/test/ui/attributes/nonterminal-expansion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};
}
Expand Down
14 changes: 1 addition & 13 deletions src/test/ui/attributes/nonterminal-expansion.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
5 changes: 2 additions & 3 deletions src/test/ui/error-codes/E0565.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// repr currently doesn't support literals
#[repr("C")] //~ ERROR E0565
//~| ERROR E0565
struct A { }
struct A {}

fn main() { }
fn main() {}
8 changes: 1 addition & 7 deletions src/test/ui/error-codes/E0565.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
4 changes: 4 additions & 0 deletions src/test/ui/feature-gates/feature-gate-fn_align.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#![crate_type = "lib"]

#[repr(align(16))] //~ ERROR `repr(align)` attributes on functions are unstable
fn requires_alignment() {}
12 changes: 12 additions & 0 deletions src/test/ui/feature-gates/feature-gate-fn_align.stderr
Original file line number Diff line number Diff line change
@@ -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`.
4 changes: 2 additions & 2 deletions src/test/ui/issues/issue-43988.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
18 changes: 6 additions & 12 deletions src/test/ui/issues/issue-43988.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 changes: 1 addition & 1 deletion src/test/ui/repr/repr-disallow-on-variant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}

Expand Down
4 changes: 2 additions & 2 deletions src/test/ui/repr/repr-disallow-on-variant.stderr
Original file line number Diff line number Diff line change
@@ -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

Expand Down

0 comments on commit a6e7a5a

Please sign in to comment.