-
Notifications
You must be signed in to change notification settings - Fork 57
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
Can layout change at run-time? #97
Comments
Swift has types where the layout is not known until runtime, as part of its ABI stability system, although it can't actually change during runtime. But |
Rust also has types whose layout is not known until runtime like, for example, slices, trait objects, etc. Since
What would that even mean? I don't think it means that the layout of a type can "spontaneously" change (e.g. across two volatile reads of the same type). So the question must imply that there is a certain action that one can take to change the layout of a type. The layout of One can only materialize references to these types, and these references can be modified (e.g. the metadata of a AFAICT none of the custom DST proposals would allow to, e.g., drop and deallocate the last element behind a |
The context here was about relative pointers, which (to my understanding) are basically member pointers. But I suggest you check the forum thread yourself; I linked it above. |
The general question, is Rust going to guarantee that the layouts of (I think when the discussion was on-going we didn't really think about how to word the question properly, as it was largely understood what we were talking about, so I reworded the question to be more clear about the specific issue) For some context on how this came up: Explanation of relative pointers for the unfamiliarsay I have some memory like so
where the byte In my library the relative pointer is defined as such: Context for the issueI implemented relative pointers my library on crates.io. I advertised that you could use relative pointers to build safe movable self-referential types. This depends on type layouts being only depending on the type itself, and not on the context of where it is stored. One example that came up was if you moved a value into an aggregate, that aggregate may change the layout of the struct for efficiency. A concrete example of this would be a Struct of Arrays, where we could convert an array of structs into a struct of arrays. This would be a space optimization, and it would break my model of self-referential types. Example of the problem(note this example is not attempt to be a real world example) struct Foo {
value: u32,
ptr: RelPtr<u32>
}
impl Foo {
pub fn new(value: u32) -> Self {
let mut this = Self { value, ptr: RelPtr::null() };
this.ptr.set(&mut this.value);
this
}
fn value(&self) -> u32 {
// this should be safe because Foo's layout doesn't depend on it's context
// only its definition
unsafe { *self.ptr.as_ref_unchecked() }
}
}
fn main() {
let foo_array = [Foo::new(10), Foo::new(20)];
println!("{}", foo_array[0].value());
println!("{}", foo_array[1].value());
} the two prints at the end would be UB if |
|
Yes, that is also what I thought, vorner brought up the issue, and that led to a minor debate which led to this issue. We weren't sure because this wasn't explicitly mentioned anywhere. |
The field access itself could check the pointer value and act differently based on where it's stored, as opposed to the normal behavior of adding a constant offset. However, this is limited by the need to make types movable via memcpy. |
I'm still having trouble understanding how we would implement this check. More concretely, suppose one has a How would the run-time look up the layout on field access ? The compiler could automatically put types with different layouts behind an I don't know if it would be enough for the run-time to have a map from addresses to layouts. Different types can live at the same address (e.g. None of this sounds very realistic. |
My point was, similar to how the compiler is allowed to create another instance of function with hard-coded specific value of one of the parameters or in C, if it does escape analysis and discovers nobody has a legal way to observe the actual order of fields, it can reorder the fields in the structure. In a similar way I was wondering if the compiler could eg. decide that there are two somehow distinct uses of a struct and generate two mostly independent types. For that it would have to first prove that the reference of It could be however allowed to assign T₁ into T₂ with compiler-generated conversion routine (memcpy with reordering). An example could be storing I'm not sure if there's anything preventing the compiler from doing such optimisation given it can prove there's no mixup and what it needs to check in the first place. Anyway, that's not to say I want to propose such optimisation. My point in the discussion was mostly „are you careful enough and do you have a good reason to believe the assumption not only look obvious, but are actually true?“ ‒ which seems to be the case. |
These optimizations are valid as long as they don't break legal Rust programs. I don't think this issue has anything to do with "layout changing at run-time" (these layout optimizations happen at compile-time), and if your program is legal, there is no way for you to tell that they happened (at least within Rust's abstract machine). The question appears to be whether the example you show in #97 (comment) is legal. Since the example is not self contained, it is impossible to tell with certainty. A naive implementation of The example materializes This has nothing to do with "layout changing at run-time". The unsafe code just assumes that after |
Maybe a better example is (playground): struct Foo {
value: u32,
offset: isize,
}
impl Foo {
pub fn new(value: u32) -> Self {
let mut this = Self { value, offset: 0 };
let this_ptr = &mut this as *mut _ as *mut u8;
let value_ptr = &mut this.value as *mut _ as *mut u8;
this.offset = unsafe { value_ptr.offset_from(this_ptr) };
this
}
pub fn value(&self) -> u32 {
let self_ptr = self as *const _ as *const u8;
let value_ptr = unsafe { self_ptr.offset(self.offset) };
unsafe { *(value_ptr as *const u32) }
}
} Given the offset Right now, this is something we guarantee (https://github.com/rust-lang/unsafe-code-guidelines/blob/master/reference/src/layout/structs-and-tuples.md#default-layout-repr-rust):
I read this as, on each compilation, there is only one layout for each struct type. This does not prevent the compiler from optimizing struct layouts in particular cases when it can prove the optimizations to be correct, but for that it would need to ensure that the semantics of examples like the above, which use relative struct offsets to access struct fields, do not change. Maybe we should add a note to that section about this? |
Layouts cannot change at runtime. For each type, the layout of that type is a value that is provided as a parameter to the AM and stays the same throughout its execution. For example, this means that the offset of a field within a struct is always going to be the same. However, general caveats:
@rfcbot close |
Is (3) a commitment to never have such restrictions, or just saying that we make no promises about it? |
I've clarified the text to indicate that it's not a commitment to never have them - that being said, I'm not sure that such a commitment would be meaningful, since we generally have no forwards compatibility promises around additional requirements placed on implementations |
Team member @JakobDegen has proposed to close this. The next step is review by the rest of the tagged team members: No concerns currently listed. Once a majority of reviewers approve (and at most 2 approvals are outstanding), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up! See this document for info about what commands tagged team members can give me. |
🔔 This is now entering its final comment period, as per the review above. 🔔 |
The final comment period, with a disposition to close, as per the review above, is now complete. As the automated representative of the governance process, I would like to thank the author for their work and everyone else who contributed. |
In this forum discussion, the question came up whether layout can change at run-time. I am pretty sure it cannot, but it seems that's something worth saying explicitly. ;)
The text was updated successfully, but these errors were encountered: