-
Notifications
You must be signed in to change notification settings - Fork 12.7k
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
Value visitors for miri #55549
Value visitors for miri #55549
Conversation
r? @oli-obk (rust_highfive has picked a reviewer for you, use r? to override) |
The job Click to expand the log.
I'm a bot! I can only do what humans tell me to, so if this was not helpful or you have suggestions for improvements, please ping or otherwise contact |
Wtf, this compiled fine in stage 2... NLL bug or 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.
Preliminary review. I think reviewing the rest only makes sense with the review questions addressed
// Downcast functions. These change the value as a side-effect. | ||
fn downcast_enum(&mut self, ectx: &EvalContext<'a, 'mir, 'tcx, M>) | ||
-> EvalResult<'tcx>; | ||
fn downcast_dyn_trait(&mut self, ectx: &EvalContext<'a, 'mir, 'tcx, M>) |
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 dislike the self
mutation. It does not feel right. I think these downcast functions should be recursing via visit_value
.
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.
downcast_dyn_trait
is gone in a later commit.
For downcast_enum
I will do this, but it actually makes the interface larger overall (i.e., one more method to implement).
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 and then there is an extra hook I need to keep the nice error reporting... of the validity check :/
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.
Okay I did something. Thanks for making me do it, it is now better than it was before. :) Let me know what you think.
--> $DIR/ub-enum.rs:42:1 | ||
| | ||
LL | const BAD_ENUM3 : [Enum2; 2] = [unsafe { TransmuteEnum2 { c: () }.b }; 2]; | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ attempted to read undefined bytes |
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 am aware this is a bad error message. Fixing this will be fairly easy once #55293 lands: Use ScalarMaybeUndef
in EvalErrorKind::InvalidDiscriminant
.
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 damn, I never pressed the submit review button :/
Anyway, yes, this is getting closer to what I'm imagining.
Take the comments with a grain of salt, because I've not checked how obsolete they have become with your changes
self.value().layout() | ||
} | ||
|
||
// Replace the value by `val`, which must be the `field`th field of `self`, then call |
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.
What do you mean by replace
? changing a function argument does not persist outside the function call.
I would have assumed this to just forward to visit_value
by default, ignoring all the other arguments.
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 visitor has in its internal state the current value we are visiting. This function makes it change that state to another value.
I know this is a somewhat strange architecture for a visitor, but it's the best I could come up with so far in terms of code reuse. But the original design also did not have the Value
trait; now that I got that maybe I can separate the value from the rest of the visitor state. However, I actually found it quite nice that they are connected because the ValidationVisitor
uses that to keep track of its stack.
|
||
// Execute visitor on the current value. Used for recursing. | ||
#[inline] | ||
fn visit(&mut self, ectx: &mut EvalContext<'a, 'mir, 'tcx, M>) |
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.
So I think I fundamentally misunderstood the nature of this visitor. I thought it was like the visitor for Mir
, which just gives you mutable references to rustc::mir::*
types and by default calls walk_*
, which destructures the value and calls visit_*
on all the values obtainable.
I am not sure what this "visitor" is doing anymore. Would it be possible to write a visitor of the following form, still support your use cases and still copy/allocate as little intermediate state as possible?
trait ValueVisitor<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>>: fmt::Debug + Sized {
type V: Value<'tcx, M>;
fn ecx(&mut self) -> &mut EvalContext<'a, 'mir, 'tcx, M>)
fn visit(&mut self, value: &mut V) -> EvalResult<'tcx> {
walk_value(self, value)
}
fn visit_array(&mut self, elements: impl Iterator<Item = &mut V>) -> EvalResult<'tcx> {
walk_array(self, elements)
}
fn visit_enum(&mut self, value: &mut Value) -> EvalResutl<'tcx> {
walk_enum(self, value)
}
// leafs
fn visit_scalar(&mut self, _scalar: &mut Scalar, _layout: &layout::Scalar) -> EvalResult<'tcx> { Ok(()) }
// ...
}
fn walk_value....
fn walk_array<VIS: ValueVisitor>(visitor: &mut VIS, elements: impl Iterator<Item = &mut VIS::V>) -> EvalResult<'tcx> {
for element in elements {
visitor.visit(element)?;
}
Ok(())
}
fn walk_enum<VIS: ValueVisitor>(visitor: &mut VIS, value: &mut VIS::V) -> EvalResult<'tcx> {
let variant = value.read_discriminant(self.ecx())?.1;
visitor.visit(value.downcast(self.ecx(), variant)?)
}
This will increase the amount of recursion happening, but it has a much nicer feel to me, because fewer things are entangled with each other.
☔ The latest upstream changes (presumably #55579) made this pull request unmergeable. Please resolve the merge conflicts. |
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 visitor as it is (except for the visit_scalar
method as mentioned in the comments) looks good to me now. I'll review the rest of the code independently now
{ | ||
self.walk_value(v) | ||
} | ||
/// Visit the given value as a union. |
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.
/// Visit the given value as a union. | |
/// Visit the given value as a union. Does not recurse unless overwritten to do so. |
|
||
// Actions on the leaves, ready to be overloaded. | ||
/// Called whenever we reach a value with uninhabited layout. | ||
/// Recursing to fields will continue after this! |
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.
how can an uninhabited type have fields?
fn visit_uninhabited(&mut self, _v: Self::V) -> EvalResult<'tcx> | ||
{ Ok(()) } | ||
/// Called whenever we reach a value with scalar layout. | ||
/// Recursing to fields will continue after this! |
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 think the default impl should be calling a walk_scalar
method then. Pass all arguments needed to recurse. Implementing a visit_*
function as Ok(())
should stop recursion right there.
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 does not match the structure we used for the validation visitor, and I think it is the wrong structure. The point is that being a scalar and being an aggregate are orthogonal properties, a value can be neither or both or any combination. Some scalars are primitives, some are not - -we'd have to duplicate all the primitive-vs-aggregate handling. I don't think that would be a good idea.
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 right. Add a comment that it's not really recursing into the scalar, but the value, so one should overwrite visit_value
if they want to not recurse into this case
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.
Like so?
e27a04e
to
bebf782
Compare
I added the iterator-based |
My plan is to ask for perf once #55316 lands, so that we can see just the impact of this refactor. |
09e4551
to
7f93557
Compare
Rebased onto master; now this PR can stand for itself. |
@bors try |
Value visitors for miri Generalize the traversal part of validation to a `ValueVisitor`. ~~This includes #55316, [click here](RalfJung/rust@retagging...RalfJung:miri-visitor) for just the new commits.~~ This PR does not change the interface to miri, so I use the opportunity to update miri.
☀️ Test successful - status-travis |
@rust-timer build 5f8bd0a |
Success: Queued 5f8bd0a with parent e53a5ff, comparison URL. |
ScalarMaybeUndef::Scalar(Scalar::Bits { bits, .. }) => | ||
bits.to_string(), | ||
struct ValidityVisitor<'rt, 'a: 'rt, 'mir: 'rt, 'tcx: 'a+'rt+'mir, M: Machine<'a, 'mir, 'tcx>+'rt> { | ||
/// The `path` may be pushed to, but the part that is present when a function |
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 really need to start using the im
crate in the compiler
This comment has been minimized.
This comment has been minimized.
Finished benchmarking try commit 5f8bd0a |
Seems like something went wrong? There is no benchmark data. Cc @rust-lang/infra Maybe we should just try again. |
⌛ Trying commit 3538532cb617bcb6c97e304459c2135a5e2240b9 with merge ad5b1b390d95a7a68786df120aefae5118043810... |
@RalfJung the parent commit e53a5ff is still being benchmarked (see https://perf.rust-lang.org/status.html). There's no need to retry. |
Oh, so the bot was just wrong telling me that everything is done? Curious. |
☀️ Test successful - status-travis |
Some small losses, some small gains... @oli-obk seems acceptable? |
3538532
to
7b7c6ce
Compare
@bors r+ |
📌 Commit 7b7c6ce has been approved by |
Value visitors for miri Generalize the traversal part of validation to a `ValueVisitor`. ~~This includes #55316, [click here](RalfJung/rust@retagging...RalfJung:miri-visitor) for just the new commits.~~
☀️ Test successful - status-appveyor, status-travis |
Add escape-to-raw MIR statement Add a new MIR "ghost state statement": Escaping a ptr to permit raw accesses. ~~This includes #55549, [click here](RalfJung/rust@miri-visitor...RalfJung:escape-to-raw) for just the new commits.~~
Generalize the traversal part of validation to a
ValueVisitor
.This includes #55316, click here for just the new commits.