-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
Fix subst issues with return-position impl Trait
in trait
#102334
Conversation
r? @estebank (rust-highfive has picked a reviewer for you, use r? to override) |
1a99804
to
727c941
Compare
☔ The latest upstream changes (presumably #102306) made this pull request unmergeable. Please resolve the merge conflicts. |
727c941
to
1ebcf46
Compare
name: e.name, | ||
index: (e.index as usize - trait_to_impl_substs.len() | ||
+ tcx.generics_of(impl_m.container_id(tcx)).params.len()) | ||
as u32, |
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.
Generics::param_def_id_to_index
should give the correct answer. Is e.def_id
the wrong definition to refer to?
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.
Yes, technically e.def_id
points to the RPIT lifetime on the trait. There is no corresponding RPIT generics for the impl.
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 see 3 cases:
- there exists some early-bound lifetime on the impl that we could map to;
- the corresponding lifetime in the impl is late-bound, and we should build the correct binder;
- there is no lifetime because there is no unification, and we should probably return
re_static
.
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'm not really sure what you mean by this. The inferred opaque type shouldn't have any late-bound regions -- associated types don't have late-bound regions, do they?
I guess an example might help explain my thinking in this change -- let's look at a manual desugaring of an RPITIT:
RPITIT:
trait Foo {
fn foo<'a>(&'a self) -> impl Display + 'a;
}
struct Wrapper<T>(T);
impl<T: Display> Foo for Wrapper<T> {
fn foo<'a>(&'a self) -> impl Display + 'a {
&self.0
}
}
GAT:
trait Foo {
type FooRpit<'a>: Display + 'a;
fn foo<'a>(&'a self) -> &'a T;
}
struct Wrapper<T>(T);
impl Foo for Wrapper<i32> {
type FooRpit<'a> = &'a i32;
fn foo<'a>(&'a self) -> Self::FooRpit<'a> {
&self.0
}
}
In the GAT example, when we're projecting <Wrapper<i32> as Foo>::FooRpit<'_#0r>
(substs = [Wrapper<i32>, '_#0r]
), we first infer that the {impl#0}
provides the asociated type. That impl
itself is inferred to have the substs []
, and the {impl#0}::FooRpit
unsubstituted type value is &'a/0 i32
, where 'a/0
is an early-bound region with index 0 in the substs list. We rebase the substs above onto the impl substs to get ['_#0r]
, substitute to get &'_#0r i32
.
However, in the RPITIT example {impl#0}::FooRpit
isn't actually a real item, so during the RPIT inference step, we need to "renumber" the opaque type's early-bound lifetimes (taken from the opaque item in the trait) counting up from the impl's own substs as if a corresponding associated item existed, so we can later substitute things correctly during projection. Since the impl
has no substs of its own, the early bound regions of FooRpit
start counting from zero, not one.
If we don't do this, then the type_of
value of the corresponding RPITIT to {impl#0}::FooRpit
would be &'a/1 i32
(since 'a/1
comes from the trait, which has a Self
subst in the 0th position), which would be out of bounds considering the rebased impl
substs I mentioned above.
the corresponding lifetime in the impl is late-bound, and we should build the correct binder
If we wrapped things in binders instead, then I think we'd be treating the function foo
in the impl
like for<'a> fn(&'a self) -> for<'b> &'b i32
even though the lifetime of &self
and &i32
in that above example should be related through substs.
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.
Thank you for the detailed explanation. This is definitely subtle.
Do you mind adding a comment in the code to explain that we are working around the non-existence of {impl#0}::FooRpit
?
You shift the subst index by the length difference between the trait generic length and the impl generic length. I would have expected the difference between the trait method generic length and the impl method generic length, since the opaques are based on them in generics_of
. We should perhaps explain that this is ok since we check the number of generics before reaching this point.
r=me with that comment
☔ The latest upstream changes (presumably #101632) made this pull request unmergeable. Please resolve the merge conflicts. |
r=me after rebasing |
index: (e.index as usize - trait_to_impl_substs.len() | ||
+ tcx.generics_of(impl_m.container_id(tcx)).params.len()) | ||
as u32, | ||
})) | ||
}); | ||
debug!(%ty); |
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.
While trying to understand what happens, I remarked that this scheme leaks inference variables.
For instance, in src/test/ui/async-await/in-trait/issue-102310.rs
, you get ty=Opaque(DefId(0:14 ~ issue_102310[ed7a]::{impl#0}::transaction::{opaque#0}), [F, R, '_#0r])
.
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.
Fixed by equating the full signature instead of just equating the return types, which allows us to infer things fully.
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.
Also #102903 asserts against this more strongly, since fully_resolve
really shouldn't be returning inference variables..
1ebcf46
to
19af7eb
Compare
19af7eb
to
e994de8
Compare
@bors r=cjgillot |
☀️ Test successful - checks-actions |
Finished benchmarking commit (8be3ce9): comparison URL. Overall result: ✅ improvements - no action needed@rustbot label: -perf-regression Instruction countThis is a highly reliable metric that was used to determine the overall result at the top of this comment.
Max RSS (memory usage)ResultsThis is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.
CyclesResultsThis is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.
Footnotes |
…pty-hack, r=TaKO8Ki Remove a lifetime resolution hack from `compare_predicate_entailment` This is not needed anymore, probably due to rust-lang#102334 equating the function signatures fully in `collect_trait_impl_trait_tys`. Also, the assertion in in rust-lang#102903 makes sure that this is actually fixed, so I'm pretty confident this isn't needed.
Fixes #102301
Fixes #102310
Fixes #102334
Fixes #102918