-
Notifications
You must be signed in to change notification settings - Fork 501
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
Fix an error in repr(C) struct offset pseudocode #559
Conversation
Thanks! The original is clearly very wrong, but I think the updated version is still not correct. I think the algorithm is something closer to this: struct.alignment = struct.fields().map(|field| field.alignment).max();
let align_to = |offset: u64, alignment: u64| -> u64 {
let mask = alignment - 1;
(offset + mask) & !mask
};
let mut current_offset = 0;
for field in struct.fields_in_declaration_order() {
// Increase the current offset so that it's a multiple of the alignment
// of this field. For the first field, this will always be zero.
// The skipped bytes are called padding bytes.
current_offset = align_to(current_offset, field.alignment);
struct[field].offset = current_offset;
current_offset += field.size;
}
struct.size = align_to(current_offset, struct.alignment); Maybe someone more knowledgeable than me could verify that? |
@ehuss unless I need more coffee, that's equivalent for powers of two, and wrong otherwise. And it is, in any case, pseudocode, and modulo communicates the idea better than a mask. Edit: actually you're half right, I had a brain fart. Fixing now. |
This seems correct to me, however I'm not confident enough to definitively say whether it is correct. Or maybe it should be closer to what I wrote in the comment above? Or maybe something like the suggestion in #632? I'm going to randomly ping @RalfJung and @gnzlbg who seem to be knowledgeable about type layout. Can one of you review this and see if it is correct, or find someone who can? |
@rkanati Didn't notice you spotted this already 👍, forgot to check PRs before the bug report. You might be interested in this branch-free formulation of
The current patch seems to fix the offset of fields, but there is another alignment offsetting in the last line of the algorithm that also needs fixing. The whole struct should be aligned to |
The point of the pseudocode is to explain the layout of EDIT: If you want to, you could provide an implementation of |
Shouldn't Or is that too high-level, not showing enough of the details? |
|
It has methods for building up a layout of a struct from the layout of its fields, though. Check https://doc.rust-lang.org/nightly/std/alloc/struct.Layout.html#method.extend. So basically |
Which kind of method would you like When concatenating layouts you don't need offsets, and something like "align to" would probably belong in |
What I meant is that the pseudo-code in the reference, instead of manipulating integers directly, could manipulate |
Can you show an example ? When one concatenates layouts, one doesn't get any offsets to any fields. You only get a new layout with the size required for the struct, but the offset of the last field isn't necessarily at |
I mean something like this: /// Returns the layout of a repr(C) struct with the given field layouts in the given order,
/// and the individual field's offsets.
pub fn repr_c(fields: &[Layout]) -> (Layout, Vec<usize>) {
let mut offsets = Vec::new();
let mut layout = Layout::from_size_align(0, 0).unwrap();
for field in fields {
let (new_layout, offset) = layout.extend(*field).unwrap();
offsets.push(offset);
layout = new_layout;
}
// Don't forget trailing padding!
(layout.pad_to_align().unwrap(), offsets)
} Could probably be improved to rely less on mutability. |
Ah cool, |
I just noticed this issue too when I looked a bit more carefully at the pseudocode. This seems pretty trivial for a merge, @ehuss @RalfJung @steveklabnik any idea why it hasn't been approved? |
@@ -184,7 +184,10 @@ for field in struct.fields_in_declaration_order() { | |||
// Increase the current offset so that it's a multiple of the alignment | |||
// of this field. For the first field, this will always be zero. | |||
// The skipped bytes are called padding bytes. | |||
current_offset += field.alignment % current_offset; |
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.
There's another "current_offset + current_offset % struct.alignment" further down, why doesn't that need fixing, too?
I indicated above how I think this should be written. Also I have no approval powers here. Cc @Centril |
Lol, I had no idea. Also, I didn't say I wanted approval powers here. ;) Anyway, in terms of the code here, there might be good reasons not to use Also Cc @gnzlbg and @hanna-kruppe who are much more familiar with data layout matters than I am. |
Heh, maybe it's not so trivial then? But regardless, as @ehuss said in the first reply, the original is clearly very wrong, so this PR deserves to finish being considered. (And if it needs changes and @rkanati ends up being AWOL I might open my own, but hopefully that won't be necessary!) Also @RalfJung I pinged you specifically because you'd commented on this PR and you're a member of rust-lang, which if I understand correctly means you should have write access to this repo? Anyway, just explaining my logic there XP |
Okay, took some time to actually read more carefully through the comments and understand the arguments and the stuff referencing |
Sorry to keep you all waiting - it's been the better part of a year since I submitted this, and I barely remember what's going on. If the consensus is that this isn't as "trivial" as I thought last April, then I'll close this. Otherwise, I can copy-paste @RalfJung 's suggestion if that's somehow easier for you. Let me know. |
There's an obvious error in the code in question. This fixes that.