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

switch to EarlyBinder as default #105779

Closed
lcnr opened this issue Dec 16, 2022 · 12 comments · Fixed by #106696
Closed

switch to EarlyBinder as default #105779

lcnr opened this issue Dec 16, 2022 · 12 comments · Fixed by #106696
Assignees
Labels
A-type-system Area: Type system E-medium Call for participation: Medium difficulty. Experience needed to fix: Intermediate. E-mentor Call for participation: This issue has a mentor. Use #t-compiler/help on Zulip for discussion. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-types Relevant to the types team, which will review and decide on the PR/issue.

Comments

@lcnr
Copy link
Contributor

lcnr commented Dec 16, 2022

implement rust-lang/types-team#78

Change all queries which currently have a bound_X variant to return EarlyBinder<T> by default and remove the bound_X version. Add a method fn EarlyBinder::<T>::subst_identity(self) -> T if these queries are used in the identity context and don't need to actually substitute anything.

Not having EarlyBinder be the default can very easily result in incorrect uses of these queries, e.g.

EarlyBinder(self.normalize_ty(span, tcx.at(span).type_of(def_id)))
which I found while reviewing #101947. The normalize_ty normalizes type_of(def_id) in the wrong environment as we only call subst afterwards.

@lcnr lcnr added A-type-system Area: Type system E-mentor Call for participation: This issue has a mentor. Use #t-compiler/help on Zulip for discussion. E-medium Call for participation: Medium difficulty. Experience needed to fix: Intermediate. T-types Relevant to the types team, which will review and decide on the PR/issue. labels Dec 16, 2022
@kylematsuda
Copy link
Contributor

@rustbot claim

@kylematsuda
Copy link
Contributor

Hi @lcnr, I was able to check this out a bit and I think I understand what needs to be done here. Just to be sure: the queries that we want to change correspond to all of the bound_X methods on TyCtx?

I played around with this a bit on the fn_sig query and was able to get x.py check working again for the whole repo again after changing fn_sig's return type to EarlyBinder<..>, so I think I have an idea of how to get started on the other queries.

For some of the queries (namely fn_sig, type_of, impl_trait_ref, and [explicit_]predicates_of), it looks like the non-bound version is used in many more places than the bound version. I just wanted to double check that these queries should also be changed?

@lcnr
Copy link
Contributor Author

lcnr commented Dec 20, 2022

Yes that sounds correct to me.

There are quite a bit of places where we currently implicitly instantiate_identity so this code adds some additional noise. I still think that it is worth it as it clarifies the intention here and sill requires people to quickly think whether they're in the right context.

When accessing parts of the return type of these queries which don't access generic parameters, e.g. the number of arguments or c-variadic for fn_sig, and we're also not in the identity context, please use skip_binder instead of instantiate_identity. It's fine to get that wrong at some places as we should catch it during review. E.g. the following should use skip_binder() as its talking about a fn_sig from outside of the current item. Alternatively, add a method fn abi(self) to EarlyBinder<ty::PolyFnSig<'tcx>>.

if tcx.fn_sig(did).abi() == RustIntrinsic && tcx.item_name(did) == sym::transmute {

@kylematsuda
Copy link
Contributor

Ok great, thanks for all the tips! I'll work on this and let you know if I get confused.

@kylematsuda
Copy link
Contributor

Hi, I’ve been playing with this and have been able to implement the changes to the queries for some of the most used ones (fn_sig, type_of, impl_trait_ref, predicates_of, …). I've checked that ui tests are passing.

I want to clean some things up and then open a PR, but I have a few questions:

  1. What does “identity context” mean? I know it’s probably not necessary for me to understand this but I also don’t want to make this harder to review by doing the wrong thing everywhere 😃.
    1. Here’s my current understanding: The compiler has the concept of early and late bound parameters. EarlyBinder represents an item that might have generics, but before early parameters are substituted. For example, consider a function fn foo<T>() { ... }. If we are looking at a call to foo like let x = foo<i32>(), the type of T is known to be i32. In this case, we call EarlyBinder::subst to set T = i32. If instead we’re inside of the declaration of foo, T could be anything, so we substitute it with a placeholder type T by using EarlyBinder::subst_identity. When we make this “identity substitution”, we are in the identity context. In other words, we're in the identity context when we're trying to "look at" the item definition, not at any usage/call/instantiation. Is this roughly correct?
    2. EarlyBinder::skip_binder would be implemented the same way as EarlyBinder::subst_identity, but means something different: “I am explicitly ignoring the types of generic parameters” vs “I am substituting placeholder types for the generic parameters”. (Side note: can we call this skip_early_binder instead of skip_binder? Wondering if it is confusing with Binder::skip_binder.)
  2. Is it necessary to add EarlyBinder to the predicates_of query? Currently, predicates_of returns a GenericPredicates, and GenericPredicates::instantiate_X is almost always immediately called. From what I can tell, the instantiate_X methods are doing conceptually the same thing as EarlyBinder::subst (and even call it internally). After adding EarlyBinder, we start getting things that look like tcx.predicates_of(..).subst_identity().instantiate_identity(), or even tcx.predicates_of(..).subst_identity().instantiate(tcx, subst), which looks contradictory to me. Could predicates_of be left as-is? We could also consider removing bound_predicates_of, which is only used in 2 places in the code (although I don’t really understand what’s happening at either of those places).
  3. Should the types stored in rustc_metadata also be changed to use EarlyBinder? I assume that the metadata is supposed to stay synced with the corresponding TyCtxt queries, which would mean that these should be changed, but I wanted to check.
  4. Any tips on structuring the PR? Here is what I was thinking:
    1. Add EarlyBinder::subst_identity, EarlyBinder::skip_early_binder, etc
    2. Change calls of query X to bound_X, and add the necessary subst_identity or skip_early_binder calls
    3. Change the query X return type to have EarlyBinder; rename all usages of bound_X to X; update the types stored in the metadata
    4. Remove bound_X method from TyCtxt

Sorry in advance for so many questions 😅. If this general plan sounds alright, I'll open a PR. Thanks!

@lcnr
Copy link
Contributor Author

lcnr commented Jan 9, 2023

sorry for not replying earlier. I have been on vacation.

1.i. mostly correct. EarlyBinder is used for all generics of items except for late-bound lifetimes on functions, to ensure that we don't forget to provide generic arguments when using these items. In rustc, we use ty::Param (ty::ConstKind::Param/ty::ReEarlyBound) to represent both the universally bound variables, of for<T> fn foo<T> whenever we use foo, and the universal placeholder when we're inside of foo, e.g. when typechecking that function.

This may not help too much, but ty::Param is equivalent to either ty::Bound or ty::Placeholder depending on whether we're "outside" or "inside" of a given item. EarlyBinder is used when we're outside of an item, and subst_identity can be used while we're inside.

1.ii. yes, as ty::Param is used to represent generic parameters, both when used as bound variables and when used as placeholders, subst_identity is just a noop and there is only a conceptual difference between it and skip_binder. EarlyBinder and Binder should ideally be the same thing, so I prefer using skip_binder for both as it conceptually represents the same operation both times.

  1. GenericPredicates is interesting. In the past, EarlyBinder was always implicit, so GenericPredicates ended up having the same concept in instantiate. Having EarlyBinder<GenericPredicates> isn't really meaningful unless you directly access the predicates field. It may make sense to keep using GenericPredicates but change predicates to be &'tcx [EarlyBinder<(Predicate<'tcx>, Span)>],. Alternatively it's fine to ignore that query for now.

  2. 🤔 yes, we should probably move those to EarlyBinder<T> as well. This should not change the runtime behavior though as EarlyBinder itself doesn't contain any data. You may have to implement some additional traits there or whatever, would have to check 😁

  3. iii. and iv. can be done in the same commit. Apart from that this seems like the best approach 👍

Thanks for working on this ❤️ it feels difficult to explain 1. so I hope that it made at least some sense. Feel free to reach out about that on zulip if you have any questions. Explaining it in sync is easier 😁

@kylematsuda
Copy link
Contributor

Thanks so much for the detailed response! This helps a lot, I really appreciate it. (And sorry to have asked so many questions right before/during the holidays...:sweat_smile:). I should have some time this week to work on this :+1:

@lcnr
Copy link
Contributor Author

lcnr commented Jan 16, 2023

This has been implemented for const_param_default and impl_trait_ref by #106696. There are still other queries which need the same work.

matthiaskrgr added a commit to matthiaskrgr/rust that referenced this issue Jan 17, 2023
…ackh726

Document `EarlyBinder::subst_identity` and `skip_binder`

Finishing implementing rust-lang#105779 will change several commonly used queries to return `EarlyBinder` by default. This PR adds documentation for two of the methods used to access data inside the `EarlyBinder`. I tried to summarize some of the [discussion from the issue](rust-lang#105779 (comment)) in writing this.

r? `@lcnr`
matthiaskrgr added a commit to matthiaskrgr/rust that referenced this issue Jan 17, 2023
…s, r=lcnr

Switch to `EarlyBinder` for `item_bounds` query

Part of the work to finish rust-lang#105779 (also see rust-lang/types-team#78).

Several queries `X` have a `bound_X` variant that wraps the output in `EarlyBinder`. This adds `EarlyBinder` to the return type of the `item_bounds` query and removes `bound_item_bounds`.

r? `@lcnr`
bors added a commit to rust-lang-ci/rust that referenced this issue Jan 27, 2023
Switch to `EarlyBinder` for `fn_sig` query

Part of the work to finish rust-lang#105779 (also see rust-lang/types-team#78).

Several queries `X` have a `bound_X` variant that wraps the output in [`EarlyBinder`](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/subst/struct.EarlyBinder.html). This adds `EarlyBinder` to the return type of the `fn_sig` query and removes `bound_fn_sig`.

r? `@lcnr`
bjorn3 pushed a commit to bjorn3/rust that referenced this issue Feb 9, 2023
Switch to `EarlyBinder` for `fn_sig` query

Part of the work to finish rust-lang#105779 (also see rust-lang/types-team#78).

Several queries `X` have a `bound_X` variant that wraps the output in [`EarlyBinder`](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/subst/struct.EarlyBinder.html). This adds `EarlyBinder` to the return type of the `fn_sig` query and removes `bound_fn_sig`.

r? `@lcnr`
ckxkexing added a commit to ckxkexing/rust that referenced this issue Feb 17, 2023
Switch to `EarlyBinder` for `type_of` query

Part of the work to finish rust-lang#105779 and implement rust-lang/types-team#78.

Several queries `X` have a `bound_X` variant that wraps the output in `EarlyBinder`. This adds `EarlyBinder` to the return type of the `type_of` query and removes `bound_type_of`.

r? `@lcnr`
bors added a commit to rust-lang-ci/rust that referenced this issue Feb 17, 2023
Switch to `EarlyBinder` for `type_of` query

Part of the work to finish rust-lang#105779 and implement rust-lang/types-team#78.

Several queries `X` have a `bound_X` variant that wraps the output in `EarlyBinder`. This adds `EarlyBinder` to the return type of the `type_of` query and removes `bound_type_of`.

r? `@lcnr`
Jarcho pushed a commit to Jarcho/rust that referenced this issue Feb 26, 2023
Switch to `EarlyBinder` for `type_of` query

Part of the work to finish rust-lang#105779 and implement rust-lang/types-team#78.

Several queries `X` have a `bound_X` variant that wraps the output in `EarlyBinder`. This adds `EarlyBinder` to the return type of the `type_of` query and removes `bound_type_of`.

r? `@lcnr`
@Noratrieb Noratrieb added the T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. label Apr 5, 2023
@lcnr
Copy link
Contributor Author

lcnr commented Apr 13, 2023

@kylematsuda are you still working on this? going to unassign you but definitely don't hesitate to reassign yourself if you intend to continue working on it.

Thanks a lot for your good work here ❤️

@kylematsuda
Copy link
Contributor

kylematsuda commented Apr 13, 2023

Ah sorry @lcnr! I got a bit busy and dropped the ball on this... but I should have time to work on it more now. Thanks for the reminder 😅

Looking in the nightly docs, here are the bound_X queries that remain:

  • bound_abstract_const (non-bound version is thir_abstract_const)
  • bound_explicit_item_bounds
  • bound_impl_subject
  • bound_return_position_impl_trait_in_trait_tys (non-bound version is collect_return_position_impl_trait_in_trait_tys)

Also I think at some point @BoxyUwU had suggested changing subst_and_normalize_erasing_regions to take EarlyBinder<T> instead of T, so I'll plan to do that too.

I have some questions about a few of these but I might just try and knock out the more straightforward-looking ones first.

@rustbot claim

Dylan-DPC added a commit to Dylan-DPC/rust that referenced this issue Apr 14, 2023
…ct, r=compiler-errors

Switch to `EarlyBinder` for `impl_subject` query

Part of the work to finish rust-lang#105779.

Several queries `X` have a `bound_X` variant that wraps the output in `EarlyBinder`. This adds `EarlyBinder` to the return type of the `impl_subject` query and removes `bound_impl_subject`.

r? `@lcnr`
matthiaskrgr added a commit to matthiaskrgr/rust that referenced this issue Apr 14, 2023
…ct, r=compiler-errors

Switch to `EarlyBinder` for `impl_subject` query

Part of the work to finish rust-lang#105779.

Several queries `X` have a `bound_X` variant that wraps the output in `EarlyBinder`. This adds `EarlyBinder` to the return type of the `impl_subject` query and removes `bound_impl_subject`.

r? ``@lcnr``
matthiaskrgr added a commit to matthiaskrgr/rust that referenced this issue Apr 14, 2023
…ct, r=compiler-errors

Switch to `EarlyBinder` for `impl_subject` query

Part of the work to finish rust-lang#105779.

Several queries `X` have a `bound_X` variant that wraps the output in `EarlyBinder`. This adds `EarlyBinder` to the return type of the `impl_subject` query and removes `bound_impl_subject`.

r? ```@lcnr```
matthiaskrgr added a commit to matthiaskrgr/rust that referenced this issue Apr 19, 2023
…, r=compiler-errors

Switch to `EarlyBinder` for `collect_return_position_impl_trait_in_trait_tys`

Part of the work to finish rust-lang#105779.

This PR adds `EarlyBinder` to the return type of the `collect_return_position_impl_trait_in_trait_tys` query and removes `bound_return_position_impl_trait_in_trait_tys`.

r? `@lcnr`
matthiaskrgr added a commit to matthiaskrgr/rust that referenced this issue Apr 25, 2023
…tem-bounds, r=compiler-errors

Switch to `EarlyBinder` for `explicit_item_bounds`

Part of the work to finish rust-lang#105779.

This PR adds `EarlyBinder` to the return type of the `explicit_item_bounds` query and removes `bound_explicit_item_bounds`.

r? `@compiler-errors` (hope it's okay to request you, since you reviewed rust-lang#110299 and rust-lang#110498 😃)
flip1995 pushed a commit to flip1995/rust-clippy that referenced this issue May 5, 2023
…s, r=compiler-errors

Switch to `EarlyBinder` for `explicit_item_bounds`

Part of the work to finish rust-lang/rust#105779.

This PR adds `EarlyBinder` to the return type of the `explicit_item_bounds` query and removes `bound_explicit_item_bounds`.

r? `@compiler-errors` (hope it's okay to request you, since you reviewed #110299 and #110498 😃)
Dylan-DPC added a commit to Dylan-DPC/rust that referenced this issue May 8, 2023
… r=BoxyUwU

Make `(try_)subst_and_normalize_erasing_regions` take `EarlyBinder`

Changes `subst_and_normalize_erasing_regions` and `try_subst_and_normalize_erasing_regions` to take  `EarlyBinder<T>` instead of `T`.

(related to rust-lang#105779)

This was suggested by `@BoxyUwU` in rust-lang#107753 (comment). After changing `type_of` to return `EarlyBinder`, there were several places where the binder was immediately skipped to call `tcx.subst_and_normalize_erasing_regions`, only for the binder to be reconstructed inside of that method.

r? `@BoxyUwU`
matthiaskrgr added a commit to matthiaskrgr/rust that referenced this issue May 10, 2023
…onst, r=BoxyUwU

Switch to `EarlyBinder` for `thir_abstract_const` query

Part of the work to finish rust-lang#105779.

This PR adds `EarlyBinder` to the return type of the `thir_abstract_const` query and removes `bound_abstract_const`.

r? `@compiler-errors`
@kylematsuda
Copy link
Contributor

After merging #111410, all of the existing bound_X methods on tcx have been removed. Thank you to everyone who helped review PRs, and especially @lcnr for mentoring this issue!

There is a bit of follow-up work that can be done (I'm happy to work on some of these if it would be helpful):

  1. Clippy: in review, both @lcnr and @BoxyUwU noticed places in Clippy where we probably should use subst instead of subst_identity, and suggested opening an issue on Clippy
  2. Further bubbling up EarlyBinder: We started this a bit in Make (try_)subst_and_normalize_erasing_regions take EarlyBinder #110297, but there might be more interfaces that should have EarlyBinder added. Also, there might be other queries that logically should return EarlyBinder, but don't have a bound_ version, so haven't been converted yet
  3. Other things?

@lcnr
Copy link
Contributor Author

lcnr commented May 16, 2023

for clippy I am not sure how helpful opening an issue like "hey, some of the subst_identity calls may be wrong" would be. Might be good to add a bunch of FIXMEs to the code instead (or directly fix them when doing so). I don't have the time to do so myself atm.

Further bubbling up EarlyBinder is good but probably can't be tracked in this issue

I think it would also be valuable to try to make the value in EarlyBinder private and require users to explicitly use skip_binder whenever they access the inner value.

@lcnr lcnr closed this as completed May 16, 2023
bors added a commit to rust-lang-ci/rust that referenced this issue May 28, 2023
…ackh726

Make `EarlyBinder`'s inner value private

Currently, `EarlyBinder(T)`'s inner value is public, which allows implicitly skipping the binder by indexing into the tuple struct (i.e., `x.0`). `@lcnr` suggested making `EarlyBinder`'s inner value private so users are required to explicitly call `skip_binder` (rust-lang#105779 (comment)) .

This PR makes the inner value private, adds `EarlyBinder::new` for constructing a new instance, and replaces uses of `x.0` with `x.skip_binder()` (or similar). It also adds some documentation to `EarlyBinder::skip_binder` explaining how to skip the binder of `&EarlyBinder<T>` to get `&T` now that the inner value is private (since previously we could just do `&x.0`).

r? `@lcnr`
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-type-system Area: Type system E-medium Call for participation: Medium difficulty. Experience needed to fix: Intermediate. E-mentor Call for participation: This issue has a mentor. Use #t-compiler/help on Zulip for discussion. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-types Relevant to the types team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants