-
Notifications
You must be signed in to change notification settings - Fork 380
Add support for #[repr(align(x))]
on bridge structs
#902
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
@dtolnay is there any feedback on this change? What is the path to getting this merged? I’m contemplating closing the last mile or putting up a bounty for this specific feature. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks!
@@ -238,7 +238,12 @@ fn write_struct<'a>(out: &mut OutFile<'a>, strct: &'a Struct, methods: &[&Extern | |||
for line in strct.doc.to_string().lines() { | |||
writeln!(out, "//{}", line); | |||
} | |||
writeln!(out, "struct {} final {{", strct.name.cxx); | |||
let alignment = if let Some(Alignment::Align(x)) = strct.alignment { | |||
format!("alignas({}) ", x) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not sufficient for alignment values that are smaller than the alignment of one of the fields, which Rust allows and C++ does not: "If the strictest (largest) alignas
on a declaration is weaker than the alignment it would have without any alignas
specifiers (that is, weaker than its natural alignment or weaker than alignas
on another declaration of the same object or type), the program is ill-formed."
It needs to expand to something like:
struct alignas(max(
N,
alignof(Field1),
alignof(Field2),
...
)) Struct final {
let alignment = if let Some(Alignment::Align(x)) = strct.alignment { | ||
format!("alignas({}) ", x) | ||
} else { | ||
String::from("") | ||
}; | ||
writeln!(out, "struct {}{} final {{", alignment, strct.name.cxx); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let alignment = if let Some(Alignment::Align(x)) = strct.alignment { | |
format!("alignas({}) ", x) | |
} else { | |
String::from("") | |
}; | |
writeln!(out, "struct {}{} final {{", alignment, strct.name.cxx); | |
write!(out, "struct"); | |
if let Some(Alignment::Align(align)) = strct.alignment { | |
write!(out, " alignas({})", align); | |
} | |
writeln!(out, " {} final {{", strct.name.cxx); |
fn parse_repr_attribute(input: ParseStream) -> Result<Repr> { | ||
input.parse::<Repr>() | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unnecessary function, it can use attr.parse_args::<Repr>()
above instead of attr.parse_args_with(parse_repr_attribute)
.
pub enum Alignment { | ||
Align(u32), | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This enum is not serving any purpose. An alignment can be stored directly as u32 or LitInt below.
@@ -92,6 +97,7 @@ pub struct ExternType { | |||
pub struct Struct { | |||
pub doc: Doc, | |||
pub derives: Vec<Derive>, | |||
pub alignment: Option<Alignment>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pub alignment: Option<Alignment>, | |
pub alignment: Option<LitInt>, |
let repr = match repr { | ||
Some(Repr::Atom(atom)) => Some(atom), | ||
Some(Repr::Align(_)) => { | ||
cx.error(&item, "repr(align) on enums is not supported"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It should be clarified that this is a limitation of C++ enums and not cxx.
cx.error(&item, "repr(align) on enums is not supported"); | |
cx.error(&item, "C++ does not support custom alignment on an enum"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also the error should point to just the align attribute, not the entire item.
use syn::parse::{Error, Parse, ParseStream, Result}; | ||
use syn::{Ident, LitInt}; | ||
|
||
#[derive(Copy, Clone, PartialEq)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
None of these trait impls are used anywhere.
#[derive(Copy, Clone, PartialEq)] |
} else if ident == "align" { | ||
let content; | ||
syn::parenthesized!(content in input); | ||
let alignment: u32 = content.parse::<LitInt>()?.base10_parse()?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would accept a suffixed integer literal such as #[repr(align(2int))]
. That should not be allowed.
"invalid `repr(align)` attribute: not a power of two", | ||
)); | ||
} | ||
if alignment > 2u32.pow(29) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think there is a guarantee that C++ compilers support alignments in this whole range. For example Clang:
struct alignas(1<<19) A {};
struct alignas(1<<29) B {};
auto a() { return alignof(A); } // 524288
auto b() { return alignof(B); } // 1
This needs to use a smaller limit until somebody needs otherwise. As is, this could lead to C++ passing an insufficiently aligned reference to Rust which would be UB.
} | ||
_ => {} | ||
} | ||
} else if ident == "align" { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be helpful to give a better error message if a repr attribute is written with a non-literal expression, since C++ allows this but Rust does not. #[repr(align(1 << 6))]
Add support for
#[repr(align(x))]
on bridge structs, closing #835. For the example code:the following output was obtained using the cxx code generators:
I believe it would be straightforward to extend this implementation for
#[repr(packed)]
as well.I was wondering whether to add support for enums but decided against it for now and made it an "unsupported" error for the time being.
enum alignas(X)
is accepted by all the C++ compilers I tried, but I also found a defect report from 2017 (2534) that removes the ability to applyalignas
to enums: https://wg21.cmeerw.net/cwg/issue2354.