Skip to content

Commit f953178

Browse files
authored
Merge pull request #1567 from dtolnay/repralign
Improve repr(align) implementation
2 parents dd0c440 + b1f058f commit f953178

18 files changed

+169
-86
lines changed

book/src/shared.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -247,8 +247,13 @@ C++ data type:
247247

248248
## Alignment
249249

250-
Enforcing minimum alignment for structs using `repr(align(x))` is supported within the
251-
CXX bridge module. The alignment value must be a power of two from 1 up to 2<sup>29</sup>.
250+
The attribute `repr(align(…))` sets a minimum required alignment for a shared
251+
struct. The alignment value must be a power of two in the range 2<sup>0</sup> to
252+
2<sup>13</sup>.
253+
254+
This turns into an [`alignas`] specifier in C++.
255+
256+
[`alignas`]: https://en.cppreference.com/w/cpp/language/alignas.html
252257

253258
```rust,noplayground
254259
#[cxx::bridge]

gen/src/builtin.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ pub(crate) struct Builtins<'a> {
3232
pub is_complete: bool,
3333
pub destroy: bool,
3434
pub deleter_if: bool,
35+
pub alignmax: bool,
3536
pub content: Content<'a>,
3637
}
3738

@@ -228,6 +229,34 @@ pub(super) fn write(out: &mut OutFile) {
228229
writeln!(out, "}};");
229230
}
230231

232+
if builtin.alignmax {
233+
include.cstddef = true;
234+
out.next_section();
235+
writeln!(out, "#ifndef CXXBRIDGE_ALIGNMAX");
236+
writeln!(out, "#define CXXBRIDGE_ALIGNMAX");
237+
// This would be cleaner as the following, but GCC does not implement
238+
// that correctly. <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=64236>
239+
//
240+
// template <::std::size_t... N>
241+
// class alignas(N...) alignmax {};
242+
//
243+
// Next, it could be this, but MSVC does not implement this correctly.
244+
//
245+
// template <::std::size_t... N>
246+
// class alignmax { alignas(N...) union {} members; };
247+
//
248+
writeln!(out, "template <::std::size_t N>");
249+
writeln!(out, "class alignas(N) aligned {{}};");
250+
writeln!(out, "template <typename... T>");
251+
writeln!(
252+
out,
253+
"class alignmax_t {{ alignas(T...) union {{}} members; }};",
254+
);
255+
writeln!(out, "template <::std::size_t... N>");
256+
writeln!(out, "using alignmax = alignmax_t<aligned<N>...>;");
257+
writeln!(out, "#endif // CXXBRIDGE_ALIGNMAX");
258+
}
259+
231260
out.end_block(Block::Namespace("repr"));
232261

233262
out.begin_block(Block::Namespace("detail"));

gen/src/write.rs

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ use crate::syntax::set::UnorderedSet;
1111
use crate::syntax::symbol::{self, Symbol};
1212
use crate::syntax::trivial::{self, TrivialReason};
1313
use crate::syntax::{
14-
derive, mangle, Alignment, Api, Doc, Enum, ExternFn, ExternType, FnKind, Lang, Pair, Signature,
15-
Struct, Trait, Type, TypeAlias, Types, Var,
14+
derive, mangle, Api, Doc, Enum, ExternFn, ExternType, FnKind, Lang, Pair, Signature, Struct,
15+
Trait, Type, TypeAlias, Types, Var,
1616
};
1717
use proc_macro2::Ident;
1818

@@ -280,12 +280,23 @@ fn write_struct<'a>(out: &mut OutFile<'a>, strct: &'a Struct, methods: &[&Extern
280280
writeln!(out, "#ifndef {}", guard);
281281
writeln!(out, "#define {}", guard);
282282
write_doc(out, "", &strct.doc);
283-
let alignment = if let Some(Alignment::Align(x)) = strct.alignment {
284-
format!("alignas({}) ", x)
285-
} else {
286-
String::from("")
287-
};
288-
writeln!(out, "struct {}{} final {{", alignment, strct.name.cxx);
283+
write!(out, "struct");
284+
if let Some(align) = &strct.align {
285+
out.builtin.alignmax = true;
286+
writeln!(out, " alignas(::rust::repr::alignmax<");
287+
writeln!(out, " {},", align.base10_parse::<u32>().unwrap());
288+
for (i, field) in strct.fields.iter().enumerate() {
289+
write!(out, " alignof(");
290+
write_type(out, &field.ty);
291+
write!(out, ")");
292+
if i + 1 != strct.fields.len() {
293+
write!(out, ",");
294+
}
295+
writeln!(out);
296+
}
297+
write!(out, ">)");
298+
}
299+
writeln!(out, " {} final {{", strct.name.cxx);
289300

290301
for field in &strct.fields {
291302
write_doc(out, " ", &field.doc);

macro/src/expand.rs

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,12 @@ use crate::syntax::qualified::QualifiedName;
77
use crate::syntax::report::Errors;
88
use crate::syntax::symbol::Symbol;
99
use crate::syntax::{
10-
self, check, mangle, Alignment, Api, Doc, Enum, ExternFn, ExternType, FnKind, Impl, Lang,
11-
Lifetimes, Pair, Signature, Struct, Trait, Type, TypeAlias, Types,
10+
self, check, mangle, Api, Doc, Enum, ExternFn, ExternType, FnKind, Impl, Lang, Lifetimes, Pair,
11+
Signature, Struct, Trait, Type, TypeAlias, Types,
1212
};
1313
use crate::type_id::Crate;
1414
use crate::{derive, generics};
15-
use proc_macro2::{Ident, Literal, Span, TokenStream};
15+
use proc_macro2::{Ident, Span, TokenStream};
1616
use quote::{format_ident, quote, quote_spanned, ToTokens};
1717
use std::mem;
1818
use syn::{parse_quote, punctuated, Generics, Lifetime, Result, Token};
@@ -155,7 +155,6 @@ fn expand(ffi: Module, doc: Doc, attrs: OtherAttrs, apis: &[Api], types: &Types)
155155
fn expand_struct(strct: &Struct) -> TokenStream {
156156
let ident = &strct.name.rust;
157157
let doc = &strct.doc;
158-
let alignment = &strct.alignment;
159158
let attrs = &strct.attrs;
160159
let generics = &strct.generics;
161160
let type_id = type_id(&strct.name);
@@ -179,20 +178,13 @@ fn expand_struct(strct: &Struct) -> TokenStream {
179178
}
180179
};
181180

182-
let mut repr = quote! { #[repr(C)] };
183-
if let Some(Alignment::Align(x)) = alignment {
184-
// Suffix isn't allowed in repr(align)
185-
let x = Literal::u32_unsuffixed(*x);
186-
repr = quote! {
187-
#repr
188-
#[repr(align(#x))]
189-
}
190-
}
181+
let align = strct.align.as_ref().map(|align| quote!(, align(#align)));
182+
191183
quote! {
192184
#doc
193185
#derives
194186
#attrs
195-
#repr
187+
#[repr(C #align)]
196188
#struct_def
197189

198190
#[automatically_derived]

syntax/attrs.rs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ pub(crate) fn parse(cx: &mut Errors, attrs: Vec<Attribute>, mut parser: Parser)
7878
}
7979
}
8080
} else if attr_path.is_ident("repr") {
81-
match attr.parse_args_with(parse_repr_attribute) {
81+
match attr.parse_args::<Repr>() {
8282
Ok(attr) => {
8383
if let Some(repr) = &mut parser.repr {
8484
**repr = Some(attr);
@@ -230,10 +230,6 @@ fn parse_derive_attribute(cx: &mut Errors, input: ParseStream) -> Result<Vec<Der
230230
Ok(derives)
231231
}
232232

233-
fn parse_repr_attribute(input: ParseStream) -> Result<Repr> {
234-
input.parse::<Repr>()
235-
}
236-
237233
fn parse_cxx_name_attribute(meta: &Meta) -> Result<ForeignName> {
238234
if let Meta::NameValue(meta) = meta {
239235
match &meta.value {

syntax/mod.rs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,6 @@ pub(crate) use self::names::ForeignName;
5151
pub(crate) use self::parse::parse_items;
5252
pub(crate) use self::types::Types;
5353

54-
pub enum Alignment {
55-
Align(u32),
56-
}
57-
5854
pub(crate) enum Api {
5955
#[allow(dead_code)] // only used by cxx-build, not cxxbridge-macro
6056
Include(Include),
@@ -113,7 +109,7 @@ pub(crate) struct Struct {
113109
pub cfg: CfgExpr,
114110
pub doc: Doc,
115111
pub derives: Vec<Derive>,
116-
pub alignment: Option<Alignment>,
112+
pub align: Option<LitInt>,
117113
#[allow(dead_code)] // only used by cxxbridge-macro, not cxx-build
118114
pub attrs: OtherAttrs,
119115
#[allow(dead_code)] // only used by cxxbridge-macro, not cxx-build

syntax/parse.rs

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use crate::syntax::report::Errors;
66
use crate::syntax::repr::Repr;
77
use crate::syntax::Atom::*;
88
use crate::syntax::{
9-
attrs, error, Alignment, Api, Array, Derive, Doc, Enum, EnumRepr, ExternFn, ExternType, FnKind,
9+
attrs, error, Api, Array, Derive, Doc, Enum, EnumRepr, ExternFn, ExternType, FnKind,
1010
ForeignName, Impl, Include, IncludeKind, Lang, Lifetimes, NamedType, Namespace, Pair, Ptr,
1111
Receiver, Ref, Signature, SliceRef, Struct, Ty1, Type, TypeAlias, Var, Variant,
1212
};
@@ -79,10 +79,13 @@ fn parse_struct(cx: &mut Errors, mut item: ItemStruct, namespace: &Namespace) ->
7979
},
8080
);
8181

82-
let alignment = if let Some(Repr::Align(x)) = repr {
83-
Some(Alignment::Align(x))
84-
} else {
85-
None
82+
let align = match repr {
83+
Some(Repr::Align(align)) => Some(align),
84+
Some(Repr::Atom(_atom, span)) => {
85+
cx.push(Error::new(span, "unsupported alignment on a struct"));
86+
None
87+
}
88+
None => None,
8689
};
8790

8891
let named_fields = match item.fields {
@@ -186,7 +189,7 @@ fn parse_struct(cx: &mut Errors, mut item: ItemStruct, namespace: &Namespace) ->
186189
cfg,
187190
doc,
188191
derives,
189-
alignment,
192+
align,
190193
attrs,
191194
visibility,
192195
struct_token,
@@ -207,7 +210,7 @@ fn parse_enum(cx: &mut Errors, item: ItemEnum, namespace: &Namespace) -> Api {
207210
let mut rust_name = None;
208211
let attrs = attrs::parse(
209212
cx,
210-
item.attrs.clone(),
213+
item.attrs,
211214
attrs::Parser {
212215
cfg: Some(&mut cfg),
213216
doc: Some(&mut doc),
@@ -232,9 +235,9 @@ fn parse_enum(cx: &mut Errors, item: ItemEnum, namespace: &Namespace) -> Api {
232235
}
233236

234237
let repr = match repr {
235-
Some(Repr::Atom(atom)) => Some(atom),
236-
Some(Repr::Align(_)) => {
237-
cx.error(&item, "repr(align) on enums is not supported");
238+
Some(Repr::Atom(atom, _span)) => Some(atom),
239+
Some(Repr::Align(align)) => {
240+
cx.error(align, "C++ does not support custom alignment on an enum");
238241
None
239242
}
240243
None => None,

syntax/repr.rs

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
use crate::syntax::Atom::{self, *};
2+
use proc_macro2::{Ident, Span};
23
use syn::parse::{Error, Parse, ParseStream, Result};
3-
use syn::{Ident, LitInt};
4+
use syn::{parenthesized, Expr, LitInt};
45

5-
#[derive(Copy, Clone, PartialEq)]
66
pub(crate) enum Repr {
7-
Align(u32),
8-
Atom(Atom),
7+
Align(LitInt),
8+
Atom(Atom, Span),
99
}
1010

1111
impl Parse for Repr {
@@ -15,27 +15,35 @@ impl Parse for Repr {
1515
if let Some(atom) = Atom::from(&ident) {
1616
match atom {
1717
U8 | U16 | U32 | U64 | Usize | I8 | I16 | I32 | I64 | Isize if input.is_empty() => {
18-
return Ok(Repr::Atom(atom));
18+
return Ok(Repr::Atom(atom, ident.span()));
1919
}
2020
_ => {}
2121
}
2222
} else if ident == "align" {
2323
let content;
24-
syn::parenthesized!(content in input);
25-
let alignment: u32 = content.parse::<LitInt>()?.base10_parse()?;
26-
if !alignment.is_power_of_two() {
24+
parenthesized!(content in input);
25+
let align_expr: Expr = content.fork().parse()?;
26+
if !matches!(align_expr, Expr::Lit(_)) {
2727
return Err(Error::new_spanned(
28-
begin.token_stream(),
29-
"invalid `repr(align)` attribute: not a power of two",
28+
align_expr,
29+
"invalid repr(align) attribute: an arithmetic expression is not supported",
3030
));
3131
}
32-
if alignment > 2u32.pow(29) {
32+
let align_lit: LitInt = content.parse()?;
33+
let align: u32 = align_lit.base10_parse()?;
34+
if !align.is_power_of_two() {
3335
return Err(Error::new_spanned(
34-
begin.token_stream(),
35-
"invalid `repr(align)` attribute: larger than 2^29",
36+
align_lit,
37+
"invalid repr(align) attribute: not a power of two",
3638
));
3739
}
38-
return Ok(Repr::Align(alignment));
40+
if align > 2u32.pow(13) {
41+
return Err(Error::new_spanned(
42+
align_lit,
43+
"invalid repr(align) attribute: larger than 2^13",
44+
));
45+
}
46+
return Ok(Repr::Align(align_lit));
3947
}
4048
Err(Error::new_spanned(
4149
begin.token_stream(),

tests/ffi/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ pub mod ffi {
8888
}
8989

9090
#[repr(align(4))]
91-
pub struct StructWithAlignment4 {
91+
pub struct OveralignedStruct {
9292
b: [u8; 4],
9393
}
9494

tests/ffi/tests.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ extern "C" bool cxx_test_suite_r_is_correct(const tests::R *) noexcept;
1919

2020
namespace tests {
2121

22-
static_assert(4 == alignof(StructWithAlignment4), "expected 4 byte alignment");
22+
static_assert(4 == alignof(OveralignedStruct), "expected 4 byte alignment");
2323

2424
static constexpr char SLICE_DATA[] = "2020";
2525

0 commit comments

Comments
 (0)