-
Notifications
You must be signed in to change notification settings - Fork 12.9k
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
rustc: replace TypeContents with two independent properties (is_freeze / needs_drop). #41349
Conversation
|
||
unsafe impl Freeze for .. {} | ||
|
||
impl<T: ?Sized> !Freeze for UnsafeCell<T> {} |
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 a Box<T>
freeze?
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.
Yes, as it only contains pointers and PhantomData
.
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'm concerned about the caching strategy, see comments below. =)
src/librustc/ty/util.rs
Outdated
// | ||
// Consider the type as not needing drop in the meanwhile to avoid | ||
// further errors. | ||
if visited.replace(self).is_some() { |
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.
not that I care very much...but why replace(self).is_some()
and not
insert(self)`?
Also, I think it's worth expanding the comment to clarify that this condition indicates a cycle -- specifically because there is no CACHED
flag set. At first I was confused because a "cross-edge" in the graph (i.e., visiting something you already completely visited before) seemed like it would also be true here.
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 cargo-culted it from @arielb1, actually.
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.
The cargo cult is if let Some(_) = visited.replace(self)
. The reason is because I don't remember the sense of HashSet::insert
.
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.
Makes sense. I find the sense easy enough to remember, but it is somewhat arbitrary. (I think of it as "return true if you inserted, false if you did a no-op").
src/librustc/ty/util.rs
Outdated
pub fn needs_drop(&'tcx self, tcx: TyCtxt<'a, 'tcx, 'tcx>, | ||
param_env: &ty::ParameterEnvironment<'tcx>) -> bool { | ||
if self.flags.get().intersects(TypeFlags::NEEDS_DROP_CACHED) { | ||
return self.flags.get().intersects(TypeFlags::NEEDS_DROP); |
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 seems wrong to me to cache the NEEDS_DROP
flag on the type when the property depends on the environment. After all, the same type might be "drop or not drop" depending on the current environment. Consider:
impl<T> Foo<T> {
fn method1(&self, t: T) where T: Copy { .. }
fn method2(&self, t: T) { .. }
}
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.
We do this for the other properties, there's a condition that the type has no type parameters or Self
- I didn't invent this scheme.
} | ||
}; | ||
|
||
if !self.has_param_types() && !self.has_self_ty() { |
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.
OK, I see that we are not caching in the case where it uses parameters. I take back my assertion that the code is wrong, though I definitely think some comments are in order (I'd like to revamp how we do caching of these sorts of properties, but this is ok for now).
However, I think then that the visited set is insufficient. Consider a case like this:
struct Foo<T> { b: Bar<T>, c: Bar<T> }
struct Bar<T> { x: T }
Now Bar<T>
will not be cached, but it will be considered "visited". So then when we visit the next field, instead of computing true for "needs-drop", we get back false. Maybe it doesn't matter because we are always taking the union of all results, so if we get back true, we'll always get true back for the overall structure?
I'd prefer to see either (a) the entry in the set removed when we return (or else changed to a map that remembers the final result) or (b) some comments written around that check above that document the rather subtle reasons why a set is sufficient (it doesn't matter if we spuriously return false so long as we returned true at some point in the past).
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.
The result is completely irrelevant as no structural cycle is valid Rust code, as mentioned by:
// This should be reported as an error by `check_representable`
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.
Oh, thinking about this some more: we can actually cache a lot of cases, even if they have parameters (for example, Box<T>
needs drop). So we could store a result that is true/false along with a separate boolean for whether the result is cachable (did we use the environment to derive it?). We'd have to propagate that value back up, though, and it may not be worth it. =)
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.
That sounds like a very good reason for having everything expressed in terms of traits and doing that tracking automatically ;).
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.
The result is completely irrelevant as no structural cycle is valid Rust code, as mentioned by:
My example did not have a structural cycle. Repeated:
struct Foo<T> { b: Bar<T>, c: Bar<T> }
struct Bar<T> { x: T }
Note that Bar<T>
does not reach Foo<T>
, just T
.
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.
Uh oh I can't read. Yeah that logic was only valid before I expanded it to not always cache.
I'll turn that detection into a "set-backed stack" then.
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.
The latest diffs look reasonable to me.
@bors r+ |
📌 Commit 99c3579 has been approved by |
⌛ Testing commit 99c3579 with merge 4d9385f... |
💔 Test failed - status-appveyor |
@bors r=nikomatsakis |
📌 Commit 6528f11 has been approved by |
rustc: replace TypeContents with two independent properties (is_freeze / needs_drop). `InteriorUnsafe` / `interior_unsafe` was replaced with a private lang-item `Freeze` auto trait in libcore. `OwnsDtor` / `needs_drop` was replaced with a specialized traversal that *doesn't* avoid caching results in case of a cycle, as the only cycles left can only occur in erroneous "types with infinite sizes", references and raw pointers not having destructors. Also, `Copy` is now checked at every step of the recursion. r? @nikomatsakis
rustc: replace TypeContents with two independent properties (is_freeze / needs_drop). `InteriorUnsafe` / `interior_unsafe` was replaced with a private lang-item `Freeze` auto trait in libcore. `OwnsDtor` / `needs_drop` was replaced with a specialized traversal that *doesn't* avoid caching results in case of a cycle, as the only cycles left can only occur in erroneous "types with infinite sizes", references and raw pointers not having destructors. Also, `Copy` is now checked at every step of the recursion. r? @nikomatsakis
rustc: replace TypeContents with two independent properties (is_freeze / needs_drop). `InteriorUnsafe` / `interior_unsafe` was replaced with a private lang-item `Freeze` auto trait in libcore. `OwnsDtor` / `needs_drop` was replaced with a specialized traversal that *doesn't* avoid caching results in case of a cycle, as the only cycles left can only occur in erroneous "types with infinite sizes", references and raw pointers not having destructors. Also, `Copy` is now checked at every step of the recursion. r? @nikomatsakis
⌛ Testing commit 6528f11 with merge 69b19db... |
@bors r- retry |
I think you need to add |
@bors r=nikomatsakis |
📌 Commit 89bd3f3 has been approved by |
rustc: replace TypeContents with two independent properties (is_freeze / needs_drop). `InteriorUnsafe` / `interior_unsafe` was replaced with a private lang-item `Freeze` auto trait in libcore. `OwnsDtor` / `needs_drop` was replaced with a specialized traversal that *doesn't* avoid caching results in case of a cycle, as the only cycles left can only occur in erroneous "types with infinite sizes", references and raw pointers not having destructors. Also, `Copy` is now checked at every step of the recursion. r? @nikomatsakis
☀️ Test successful - status-appveyor, status-travis |
InteriorUnsafe
/interior_unsafe
was replaced with a private lang-itemFreeze
auto trait in libcore.OwnsDtor
/needs_drop
was replaced with a specialized traversal that doesn't avoid caching results in case of a cycle, as the only cycles left can only occur in erroneous "types with infinite sizes", references and raw pointers not having destructors. Also,Copy
is now checked at every step of the recursion.r? @nikomatsakis