-
Notifications
You must be signed in to change notification settings - Fork 1.6k
core::marker::NoCell
in bounds (previously known an Freeze
)
#3633
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
base: master
Are you sure you want to change the base?
Conversation
IIRC @joshlf was also asking about exposing Freeze; maybe they can help provide some more motivating examples. Currently there's only really one. (I don't understand the "key of a map" note, and the RFC doesn't explain it in more than 5 words either.) |
The main reason for not stabilising I don't think that just stabilising it for bounds affects this concern. The issue isn't the trait itself being exposed in the library, but APIs having to worry about whether they are |
A bound is exactly what one is worried about here? If one writes a function This is exactly the same as Fundamentally there are some things the compiler only lets you do with (Or did I misunderstand what you mean? It sounded a bit like you're saying just stabilizing the bound means we don't have to worry about the concern. But upon re-reading I am less sure.)
Letting people write |
Sure! If you want to dive deeper, look at uses of On zerocopy stable, we provide traits like However, this is overly-restrictive. The "no Another restriction is that some authors want to use Our solution in the upcoming zerocopy 0.8 is to add a separate * Currently, |
I should clarify, what I meant here was the adding of stuff like Not the ability to make something |
For another "motivating example", One possible alternative that would "work around" the semver issue would be to have Footnotes
|
Yeah that could work -- as you say, the existing |
core::marker::Freeze
in boundscore::marker::Freeze
in bounds
Thanks for writing this up. I appreciate the effort that went into it and the desire to push things forward. But... I think this RFC needs a fair bit of work. After reading it, I'm still left with some very fundamental questions (and I expect these to be answered in the RFC):
|
IIUC, we couldn't do that today since If we decide to keep it a type property - ie, the absence of
|
So if we want to expose the |
@joshlf For the purpose of |
IMO the main advantage is that An additional advantage is that there would be only one trait to encode the invariant.
For my sketch of Footnotes
|
The most important reason is that the compiler can see into the internals of types in the standard library. For example, I filed this extremely silly issue because there's technically no guarantee provided to code outside of the standard library that Besides that, here are some other reasons:
|
Thanks to everyone taking an interest in this RFC. First, a quick apology for it starting up so half-assed, I clearly underestimated the task. I started this RFC as a knee-jerk reaction to 1.78 merging the breaking change the RFC mentions without providing a way for const references to generics to exist. I'm still committed to getting this to move forward, but I have limited availability for the next 2 weeks, so please bear with me as I rewrite this entire thing to the standard I should have started it with. I would really appreciate getting some early feedback on the direction people around here would like to see this RFC take regarding:
Sorry for my high latency for the beginnings of this RFC. I really appreciate all the feedback you can give, and would like to mention that PRs to this PR's branch are very welcome :) |
@p-avital happy to hear that you're not discouraged by all the extra work we're throwing your way. :)
Even if Freeze was recursive, types can use raw pointers or global |
@p-avital It's hard for me to give you good feedback here because I think I lack a fair bit of context. Most of the discussion (and the RFC itself) seems to be very high context here, and it's context that I don't have. With that said, I can say that adding a new auto/marker trait is a Very Big Deal. It's not just that "auto traits = bad," but that adding a new one comes with very significant trade offs. That doesn't mean that adding one is impossible, but it does mean, IMO, that adding one needs to come with significant benefits. Or that it's the only path forward for some feature that most everyone deems to be essential. From the discussion, it sounds like there are alternatives. So those alternatives, at minimum, should be explored. |
@BurntSushi note that the |
@RalfJung Yikes. OK. Thank you for pointing that out. That's exactly the kind of context I was missing. |
I am not a fan of this approach due to interactions with the aliasing model. This means either we need to eventually introduce a separate trait that controls whether and where shared references are exempt from immutability (since that needs to be properly and stably guaranteed at some point, this would have to be another stable trait), or we need to figure out how user-defined I don't think we should give users the power to have A
This is my preferred approach, except that I find the naming a bit unfortunate. FWIW I find the "embedded" term confusing here, it sounds like you are talking about embedded systems. |
I think this would be better expressed as |
These impls are very concerning as they violate the assumption that Users should not be able to violate such an assumption, even with unsafe code. |
|
Thank you @teor2345 Co-authored-by: teor <teor@riseup.net>
I'm not sure if this was already mentioned anywhere but that is an oversimplification for generics, since the compiler has no info about the type's contents or implemented traits. |
Generics are fully instantiated when compiling code, and at that point we rely on
I'm not denying there are uses for manual impls of this trait, but it's a highly non-trivial feature and at this point I do not think we have strong enough motivation for it. |
perfectly reasonable, and also we can get around this by having a second version of the type that has the extra bound :) |
I spawned a Zulip thread for bikeshedding of new possible |
Another option, maybe, would be to 1) fix pub struct Unique<T: ?Sized> {
pointer: NonNull<T>,
_marker: PhantomData<T>,
}
unsafe impl<T: ?Sized> NoCell for Unique<T> {} Then we could tell people to write |
Yeah I would be on board for this. In fact I brought it up in the lang team meeting the other day and someone said it was intentionally unstable, but didn't know the reason. If we can in fact stabilize |
Historically, Or we could decide that Would This would be yet another pointer type in |
Would be a shame to lose those optimizations wouldn't it?
It already is. |
More like, losing optimization potential -- currently, |
Also see: (With thanks to @RalfJung for the link.) |
@Nadrieril brings up an interesting data point: in this paper, they found a bug (Bug #30) where someone used a So, by using |
@rustbot labels -I-lang-nominated We discussed this today and proposed that this probably needs a design meeting: |
@RalfJung I don't quite follow this last example. I was thinking that there are kind of 3 levels here -- I'm wondering if you agree with this characterization. Something like
I understand you to be opposed to 'logical', I believe because of its interaction with opsem. I guess that you don't want to have to think about what traits are implemented to understand whether some memory is part of a cell and hence determine whether something is UB. Is that correct? (I guess in this case it would be library UB, though?) I don't know your take on what I called "phantom". I can't tell whether that last example is indicating a case where "phantom" would be helpful or not or if it's not relevant at all because of the pointer indirection ( |
I have an old internals thread about this missing raw pointer type. |
I think this is incompatible with If we said there would be a compiler lint ("using this type is immediate Undefined Behavior") against I think, no, that is a whole-program proof and a severe semver hazard. The safety proof of the unsafe impl would be that no values are ever constructed with the Cell-containing enum variants, which is a whole-program property. I am extremely doubtful of any valid uses for
|
No, it merely needs to be an underapproximation of what codegen cares about. This leaves crate authors freedom to evolve their crate by delaying a commitment to |
You mean this? Which part is confusing? All I am saying is: there are cases where
I can't follow what you mean by these levels. Are these different user-defined types, using different fields to encode different things they need from the language? Or are these different proposals for how the language works? The three items are too terse for me to understand what each case actually refers to.
Roughly, yes. What we currently do in the aliasing model to find out which parts of the data behind a shared reference allow mutation is the following:
If we allow
I find this a bit harder to think about, but maybe that's just a matter of habit. It also means this
Everything the aliasing model does must be language UB. Are we talking about the same thing?
I don't understand what you are proposing here so I can't answer this question.^^
No, the safety requirement would be that no mutation happens. Just constructing the Stacked Borrows is not happy with this, but that's a (hard to fix) Stacked Borrows bug which should not affect our decision here. Tree Borrows fixes this bug, so there are plausible models that do not have this problem.
Agreed. However. if If it does not have opsem consequences, we have the very strange situation where we'll put const promoted data into immutable memory for types where opsem may actually permit mutation through shared references. I think we should not do that -- it would basically be more of rust-lang/unsafe-code-guidelines#493. |
|
||
## `core::marker::PhantomCell` | ||
|
||
This ZST is proposed as a means for maintainers to reliably opt out of `NoCell` without constraining currently `!NoCell` ZSTs to remain so. |
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.
Is there any situation where PhantomCell
is a necessity, other than to prevent future API breakage beforehand? IIUC it's always required to use UnsafeCell
to create internal mutability, thus it's still unsound to add a _marker: PhantomCell
and expect to do anything creative with this struct (like mutating other fields behind &
), yes?
If so, NoCell
's behavior sounds like a diverge from soundness guarantees and optimization invariants (Freeze
). People who write PhantomCell
would be only to opt-out their structs from const promotions, and they won't expect a surprising de-optimization of all other code using &Struct
. Also in this way, user has no reason to override Freeze
impl, making it unnecessary to be exposed to users at all.
Another wrinkle with |
Rendered