Skip to content
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

Add binders validator #694

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

flodiebold
Copy link
Member

This is a visitor which checks that all bound vars refer to an existing binder of the right type. This has been quite useful for me: we somewhat regularly have bugs in the Chalk transformation code (or now with the chalk_ir move earlier) where we don't shift the bound variables correctly, which then causes a crash some time down the line when something tries to substitute them. At that point it's usually harder to find the actual place the error comes from; also you don't always hit the error. For the latest of these bugs, I added this validator, and immediately had failing tests that pointed me to the right location.

The disadvantage is that I needed to add some rather specific methods to Visitor. I didn't find a better way to model this (and this is also the reason I couldn't just implement it in rust-analyzer).

This is a visitor which checks that all bound vars refer to an existing
binder of the right type. This has been quite useful for me: we somewhat
regularly have errors in the binders which then cause a crash some time
down the line when something tries to substitute them. At that point
it's usually harder to find the actual place the error comes from; also
you don't always hit the error. For the latest of these bugs, I added
this validator, and immediately had failing tests that pointed me to the
right location.

The disadvantage is that I needed to add some rather specific methods to
`Visitor`. I didn't find a better way to model this (and this is also
the reason I couldn't just implement it in rust-analyzer).
Copy link
Contributor

@nikomatsakis nikomatsakis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No objection, if it's useful.

Copy link
Member

@jackh726 jackh726 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only other thought I have is how useful it is to validate all bound vars in a "type" (or whatever we're checking), versus just checking for bound vars at the current level?

Comment on lines +231 to +239
/// Invoked for each occurence of a `Binders`, before visiting them.
fn before_binders(&mut self, _kinds: &crate::VariableKinds<I>) {}
/// Invoked for each occurence of a `Canonical`, before visiting them.
fn before_canonical(&mut self, _kinds: &crate::CanonicalVarKinds<I>) {}
/// Invoked for each occurence of a `FnPointer`, before visiting them.
fn before_fn_pointer_substs(&mut self, _number: usize) {}
/// Invoked for each occurence of a type introducing binders, after visiting them.
fn after_any_binders(&mut self) {}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if it's worth just adding a visit_binders function like in rustc? (Probably) Is it all helpful to have these split out? I don't know if rustc calls visit_binder for fn pointer substs or canonical items, but we should.

Unfortunately, I think that our visit_binders would have to look like fn visit_binder<T: SuperVisit<I>>(&mut self, binders: crate::VariableKinds<I>, value: T) -> ControlFlow<Self::BreakTy>);, but that's probably fine.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alternatively, we could just change this to only have before_binders and do the "into VariableKinds conversion" happen before calling it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried something like that first, the problem is that it makes the Visitor trait not object safe.

We could do the before_binders, but it'd mean doing the conversion in every visitor instead of just the one where it's necessary.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, I mean, you only need to do the conversion in the Visit impls, right?

@jackh726
Copy link
Member

I'd like to see the change/cleanup from my comment, but if that doesn't work out for whatever reason, I'm fine with the current state if things can't be cleaned up.

So, r=me preferring the requested changes but either way I'm fine.

@flodiebold
Copy link
Member Author

The only other thought I have is how useful it is to validate all bound vars in a "type" (or whatever we're checking), versus just checking for bound vars at the current level?

Hmm. I guess it would actually be possible to validate bound vars in each Binders/Canonical/FnPointer as we're creating them...

@bors
Copy link
Contributor

bors commented Dec 9, 2021

☔ The latest upstream changes (presumably #735) made this pull request unmergeable. Please resolve the merge conflicts.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants