Skip to content

Add binders validator #694

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

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.

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

Successfully merging this pull request may close these issues.

4 participants