-
Notifications
You must be signed in to change notification settings - Fork 1.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
feat: Implement Return Position Impl Trait In Traits correctly #19394
base: master
Are you sure you want to change the base?
Conversation
They don't exist in hir-def, only hir-ty. The idea is that the trait/impl datum query will compute and intern them, then other queries can treat them as (partially) normal associated types. (Lowering RPITIT to synthesized associated types, like rustc does, is required to properly support Return Type Notation).
($id:ident, $loc:ident) => { | ||
#[salsa::interned(no_debug, no_lifetime)] | ||
pub struct $id { | ||
#[return_ref] |
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.
Next salsa will hopefully flip this to be the default mode (once someone implements it salsa-rs/salsa#719)
Instead of lowering them as opaque type, in the trait they should get lowered into a synthesized associated type. Opaques "mostly worked" here, but they break when trying to implement Return Type Notation, which is essentially a way to refer in code to this synthesized associated type. So we need to do the correct thing.
It broke due to the RPITIT changes. And also make it more robust, by relying on semantic instead of textual match.
The previous setup led to a cycle in scenario like the following: ```rust trait Trait { type Assoc; fn foo() -> impl Sized; } impl Trait for () { type Assoc = (); fn foo() -> Self::Assoc; } ``` Because we asked Chalk to normalize the return type of the method, and for that it asked us the datum of the impl, which again causes us to evaluate the RPITITs. Instead, we only put the associated type ID in `impl_datum()`, and we compute it on `associated_ty_value()`. This still causes a cycle because Chalk needlessly evaluates all associated types for an impl, but at least it'll work for the new trait solver, and compiler-errors will try to fix that for Chalk too.
Chalk apparently requires associated type values to contain both the impl and the associated ty binders, completely unlike the docs (which say they should only contain those of the associated type). So fix that.
b86adf9
to
1922299
Compare
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.
not the most thorough review, but i focused mostly on the docs and explanations as to how this works
(note: i don't really understand show this works)
// In this situation, we don't know even that the trait and impl generics match, therefore | ||
// the only binders we can give to comply with the trait's binders are the trait's binders. | ||
// However, for impl associated types chalk wants only their own generics, excluding | ||
// those of the impl (unlike in traits), therefore we filter them here. | ||
// Completely unlike the docs, Chalk requires both the impl generics and the associated type | ||
// generics in the binder. |
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.
Slight rewrite for clarity and emphasis of some key points. Note that I don't really understand much in hir-ty
, so my rewrite might not be correct, but one of my goals in rewriting this was to better understand hir-ty
!
// In this situation, we don't know even that the trait and impl generics match, therefore | |
// the only binders we can give to comply with the trait's binders are the trait's binders. | |
// However, for impl associated types chalk wants only their own generics, excluding | |
// those of the impl (unlike in traits), therefore we filter them here. | |
// Completely unlike the docs, Chalk requires both the impl generics and the associated type | |
// generics in the binder. | |
// In this situation, we don't know that the trait and impl generics even match. Therefore, | |
// the only binders we can offer to comply with the trait's binders are the trait's binders. | |
// This is tautological. | |
// | |
// However, for impl-associated types, Chalk wants only the type's *own* generics, excluding | |
// those of the `impl`. This is unlike the behavior in traits, making it necessary to filter | |
// out all other generics. | |
// | |
// Note that unlike Chalk's documentation, Chalk, in reality, requires both the | |
// impl generics and the associated type generics to be in the binder. |
Two clarifying questions:
- What are "impl associated types"?
- What does Chalk's documentation actually say that you're doing differently here?
match from_assoc_type_id(db, id) { | ||
AnyTraitAssocType::Normal(type_alias) => match type_alias.lookup(db.upcast()).container { | ||
ItemContainerId::TraitId(trait_id) => trait_id, | ||
_ => panic!("`AssocTypeId` without parent trait"), |
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.
_ => panic!("`AssocTypeId` without parent trait"), | |
_ => panic!("`AssocTypeId` without parent trait; this is a bug."), |
(The "this is a bug" suffix is a thing we do in tracing
; I've found it makes people more proactive in reporting bugs)
// FIXME: This isn't entirely correct, the generics of the RPITIT assoc type may differ from its method | ||
// wrt. lifetimes, but we don't handle that currently. See https://rustc-dev-guide.rust-lang.org/return-position-impl-trait-in-trait.html. |
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.
// FIXME: This isn't entirely correct, the generics of the RPITIT assoc type may differ from its method | |
// wrt. lifetimes, but we don't handle that currently. See https://rustc-dev-guide.rust-lang.org/return-position-impl-trait-in-trait.html. | |
// FIXME: This isn't entirely correct, the generics of the RPITIT assoc type may differ from its method | |
// with regard to lifetimes, but rust-analyzer doesn't really handle lifetimes in the first place, so whatever. | |
// For details how on how to handle this properly (e.g., as rustc does), see | |
// https://rustc-dev-guide.rust-lang.org/return-position-impl-trait-in-trait.html. |
AnyTraitAssocType::Normal(it) => it, | ||
AnyTraitAssocType::Rpitit(_) => { | ||
unreachable!( | ||
"Rust does not currently have a way to specify alias equation on RPITIT" |
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.
Not sure I'd... use "equation". It's correct, I guess, but might be a bit too impose if a user sees this error message (can they see this error message)?
"Rust does not currently have a way to specify alias equation on RPITIT" | |
"Rust does not currently have a way to specify aliases on RPITIT" |
AnyTraitAssocType::Normal(it) => it, | ||
AnyTraitAssocType::Rpitit(_) => { | ||
unreachable!( | ||
"Rust does not currently have a way to specify alias equation on RPITIT" |
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.
Same nit about "equation" as above.
/// fn foo(&self) -> Option<impl Debug>; | ||
/// } | ||
/// ``` | ||
/// The equation will tell us that the hidden associated type has value `Option<impl Debug>` (note: 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.
/// The equation will tell us that the hidden associated type has value `Option<impl Debug>` (note: this | |
/// The above process will tell us that the hidden associated type is `Option<impl Debug>` (note: this |
/// An associated type synthesized from a Return Position Impl Trait In Trait | ||
/// of the trait (not the impls). |
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.
/// An associated type synthesized from a Return Position Impl Trait In Trait | |
/// of the trait (not the impls). | |
/// An associated type synthesized from a Return Position Impl Trait In Trait. | |
/// of the *trait*. | |
/// | |
/// This is not come from the the impls; see [`RpititImplAssocTy`] instead. |
/// An associated type synthesized from a Return Position Impl Trait In Trait | ||
/// of the impl (not the trait). |
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.
/// An associated type synthesized from a Return Position Impl Trait In Trait | |
/// of the impl (not the trait). | |
/// An associated type synthesized from a Return Position Impl Trait In Trait | |
/// of the *impl*. | |
/// | |
/// This is not come from the the trait; see [`RpititTraitAssocTy`] instead. |
/// The generics are the generics of the method (with some modifications that we | ||
/// don't currently implement, see https://rustc-dev-guide.rust-lang.org/return-position-impl-trait-in-trait.html). |
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 things we don't implement is the lifetime lowering, right?
impl_id: ImplId, | ||
trait_method_id: FunctionId, | ||
) -> Box<[Arc<AssociatedTyValue>]> { | ||
tracing::error!("impl_method_rpitit_values()"); |
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 you want this?
tracing::error!("impl_method_rpitit_values()"); | |
tracing::span!("impl_method_rpitit_values", Level::INFO); |
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 was a debugging aid, I pushed the PR in a not-ready-for-review state because @compiler-errors wanted to debug the Chalk issue.
This is a requirement to landing Return Type Notation.
Phew! That is not a lot of code, but it was pretty complicated. I'm still not 100% sure I did everything correctly, so I will appreciate a thorough review.