-
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
Replace @T in the AST with a smart pointer. #13316
Conversation
Why is "sharing AST nodes is bad for the various analysis passes"? The justification for a smart pointer here seems pretty weak to me. The biggest advantage, that it's immutable, is easily solved by immutably rooting the entire AST. |
That said, I don't strongly oppose |
@brson I need to expand on that - if you have sharing, you can trivially bypass, say, borrock, and create an unsafe program without the |
OK, it took me a while to understand your point about sharing, but I think @eddyb you are saying: if the AST is not a tree but a DAG, that'd be bad, and hence |
If that is what you are saying, I certainly agree, we want the AST to be a tree, and I can see that it's beneficial to choose types that enforce that. |
(Though I think the current pass which deduplicates and assigns indices is not a terrible thing.) |
All that said, I think that overall I still prefer the (more radical) idea of changing the AST into an array and making pointers into newtype'd indices into the array. This does not address your concern about enforcing the tree requirements, nor does it necessarily provide the ability for the folder to reuse memory, but it does address a lot of other nice things:
Do you have any kind of measurements suggesting that folding is a significant part of our execution time? I don't recall ever getting that impression from profiles. (Under your scheme here, one solution to the AST map would be to allocate the |
Folding doesn't take that much time, maybe a couple seconds here (the simplistic in-place implementation halves that time). Yes, an arena would work for the AST map, though I would've preferred a "stable vector" of inlined items, alongside the crate. Those form the roots of the AST forest that we map, anything they point to will have the same lifetime. |
Closing due to inactivity, but feel free to reopen with a rebase! This looks like it may have significant wins, and I'd love to see those wins in rustc! (trying to keep the queue size down for now). |
Reopening at @eddyb's request. |
I've canceled the build for this. There appears to not be a lot of discussion on this PR about this, and I'm unaware of other discussion that has happened through other channels. This is a massive change to the AST, and I want to make sure that this it the right direction we want to go in before committing to this. I don't want to block this longer, I just want to make sure that there's enough consensus that we want to go in this direction. I like the idea here, but I don't see any data supporting this change and why it should be made (although it sounds like in-place folding is more efficient). |
Additionally, this seems pretty radical from what we're already doing, and perhaps different from what one may expect. Is there documentation in the code about the rationale for a design such as this? I looked in |
To me, this is a simple refactor from |
And this doesn't seem like that big of a change, mostly just fallout. Until we move to a different AST represention entirely (#7929 has some ideas), this seems pretty benign. |
let map = self.map.borrow(); | ||
if map.len() > id as uint { | ||
Some(*map.get(id as uint)) | ||
Some(unsafe{::std::mem::transmute(map.get(id as uint))}) |
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 this just transmuting lifetimes? If so, could you use copy_lifetime(self, map.get(id as uint)
, and it would be good to have a comment about why this is safe.
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.
Thanks, I don't remember seeing copy_lifetime
before. It is safe because we never remove elements from the map nor do we mutate existing elements, i.e. the "stable vector" abstraction (I will put this in a comment).
Few visitors used the context passing feature and it can be easily emulated. The added lifetime threading allows a visitor to keep safe references to AST nodes it visits, making a non-owning ast_map design possible, for #13316.
83403cb
to
b3f62ee
Compare
Replaces Gc<T> in the AST with a custom owned smart pointer, P<T>. Fixes #7929. ## Benefits * **Identity** (affinity?): sharing AST nodes is bad for the various analysis passes (e.g. one could bypass borrowck with a shared `ExprAddrOf` node taking a mutable borrow), the only reason we haven't hit any serious issues with it is because of inefficient folding passes which will always deduplicate any such shared nodes. Even if we were to switch to an arena, this would still hold, i.e. we wouldn't just use `&'a T` in the AST, but rather an wrapper (`P<'a, T>`?). * **Immutability**: `P<T>` disallows mutating its inner `T` (unless that contains an `Unsafe` interior, which won't happen in the AST), unlike `~T`. * **Efficiency**: folding can reuse allocation space for `P<T>` and `Vec<T>`, the latter even when the input and output types differ (as it would be the case with arenas or an AST with type parameters to toggle macro support). Also, various algorithms have been changed from copying `Gc<T>` to using `&T` and iterators. * **Maintainability**: there is another reason I didn't just replace `Gc<T>` with `~T`: `P<T>` provides a fixed interface (`Deref`, `and_then` and `map`) which can remain fully functional even if the implementation changes (using a special thread-local heap, for example). Moreover, switching to, e.g. `P<'a, T>` (for a contextual arena) is easy and mostly automated.
There are only two uses of Thanks everyone for your help in making this happen, and @nikomatsakis for #16453 (which arrived just when I finally had time to work on Rust again). |
Replaces Gc in the AST with a custom owned smart pointer, P. Fixes #7929.
Benefits
ExprAddrOf
node taking a mutable borrow), the only reason we haven't hit any serious issues with it is because of inefficient folding passes which will always deduplicate any such shared nodes. Even if we were to switch to an arena, this would still hold, i.e. we wouldn't just use&'a T
in the AST, but rather an wrapper (P<'a, T>
?).P<T>
disallows mutating its innerT
(unless that contains anUnsafe
interior, which won't happen in the AST), unlike~T
.P<T>
andVec<T>
, the latter even when the input and output types differ (as it would be the case with arenas or an AST with type parameters to toggle macro support). Also, various algorithms have been changed from copyingGc<T>
to using&T
and iterators.Gc<T>
with~T
:P<T>
provides a fixed interface (Deref
,and_then
andmap
) which can remain fully functional even if the implementation changes (using a special thread-local heap, for example). Moreover, switching to, e.g.P<'a, T>
(for a contextual arena) is easy and mostly automated.