-
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
HRTB impl selection does not cover type parameters not directly related to the trait. #30867
Comments
@nikomatsakis Is this the same issue as with the impls for |
I'm trying to decide if this is an actual bug :) It does seem like the "HR capture" logic is being unnecessarily conservative. Let me poke at the debug logs and see if that reveals to me why it is rejecting the impl when you include |
@nikomatsakis I believe it breaks for type parameters which cannot be extracted from the |
Hmm, the problem is a bit of a subtle one and, in this particular example, tied to the associated type projection. We start out with a trait reference to solve:
We match this against the impl
Which, internally, is lowered to:
In so doing, we first "open" the
where
Note that I think that the basic strategy here is somewhat wrong. It would be OK in the absence of variables like One possible fix here would be to recursively (and before returning) handle the projection constraints, so that If we had had lazy normalization, this would be much easier, because then we could just unify (The other solutions I can imagine are quite a bit more complicated. It seems like you would need to capture, in the tree, what variables are bound where. What makes this hard is that UPDATE: Remove sentence I don't think. I think that for this problem to arise you really need an associated type, since you need a type variable that is not found in the trait reference, which is otherwise verboten. |
cc @arielb1 -- this seems related to the changes you made to order projection predicates earlier in the list. Does my earlier comment make sense to you? |
I am not sure we should not evaluate associated type projections recursively - RFC447 basically forces this to work. Unfortunately, it will work imperfectly without lazy normalization, because we can't represent a constraint like |
On Thu, Jan 14, 2016 at 12:54:36PM -0800, arielb1 wrote:
Sorry, the double negative in this sentence is throwing me a bit. I'm |
Unfortunately, it will work imperfectly without lazy normalization, because we can't represent a constraint like `for<'a> <_ as Fn<(&'a u32,)>>::Output: Sized`.
Can you elaborate just a bit on this? When does this kind of constraint arise?
|
impl<T> Foo for Option<T> where
T: Fn(&u32),
for<'a> <T as Fn(&'a u32)>::Output: Sized
{}
// evaluate <Option<_> as Foo> |
Recursively applying constraining-projections during confirmation should work, except for the type-inference case. |
Sorry for not replying. I'm afraid I still don't quite follow you, so let me try and work it out. First off, I guess you meant to have impl<T> Foo for Option<T> where
for<'a> T: Fn<(&'a u32,)>,
for<'a> <T as Fn<(&'a u32,)>>::Output: Sized
{}
// evaluate <Option<_> as Foo> I'm not quite sure what the last line means, but if we were to follow the model of the original problem, we'd start with something to prove like
and hence
which we would could then cover back up as:
This all seems...OK. Ah, wait, I guess your point is that we would need to normalize the |
I was bitten by this bug... simplifying a lot, my code looked like this (https://play.rust-lang.org/?gist=76fd1bad2b276b335db7): trait Cast<T> {
fn cast(self) -> T;
}
impl<'a> Cast<&'a str> for &'static str {
fn cast(self) -> &'a str { self }
}
struct Const<S>(S);
pub trait IterFunction1<I>
where I: Iterator
{
fn apply(self, iter: I) -> I::Item;
}
impl<I,S,T> IterFunction1<I> for Const<S>
where I: Iterator<Item = T>, S: Cast<T>
{
fn apply(self, _: I) -> T { self.0.cast() }
} but this isn't lifetime-polymporphic: fn expects_polymorphic_function1<F>(f: F)
where F: for<'a> IterFunction1<std::vec::Drain<'a,&'a str>>
{
let mut vec = vec!["abc"];
f.apply(vec.drain(..));
}
fn main() {
expects_polymorphic_function1(Const("hi"));
} produces:
but if you make the iterator item type a type argument, then it all works: pub trait IterFunction2<I,T>
where I: Iterator<Item = T>
{
fn apply(self, iter: I) -> T;
}
impl<I,S,T> IterFunction2<I,T> for Const<S>
where I: Iterator<Item = T>, S: Cast<T>
{
fn apply(self, _: I) -> T { self.0.cast() }
}
fn expects_polymorphic_function2<F>(f: F)
where F: for<'a> IterFunction2<std::vec::Drain<'a,&'a str>, &'a str>
{
let mut vec = vec!["abc"];
f.apply(vec.drain(..));
}
fn main() {
expects_polymorphic_function2(Const("hi"));
} so I need to rewrite a lot of code to take an extra type argument in order to be lifetime-polymorphic! |
The problem is rust-lang/rust#30867, which means that any type that wants to be polymorphic over `I: Iterator`, and lifetime-polymorpic over `I::Item` needs to take `I::Item` as a type parameter. Concretely, this means that `Stateful<Str>` where `Str: Iterator` needs to be replaced by `Stateful<Ch, Str>` where `Str: Iterator<Item = Ch>`, and ditto the other types. While I was doing a root-and-branch rewrite, I fixed issues #4 and #5.
I'm working on data-parallelism library, heavily inspired by Rayon but focused on describing certain computations on a dataset in an SQL-like manner, somewhat akin to Diesel. One part of this combines HRTBs with associated types. I've bumped into a few issues that have made things a bit tricky ([#30472](rust-lang/rust#30472), [#30867](rust-lang/rust#30867), [#53943](rust-lang/rust#53943), and similar), the hardest to work around however has been that I don't believe it's currently possible to write this: ```rust type Task = for<'a> <B as Abc<&'a A::Item>>::Task; ``` Currently I've resorted to a dummy trait, manually reimplemented on various structs without the reference, such that I can do: ```rust type Task = <B as AbcDummy<A::Item>>::Task; ``` and (horribly) transmute between the two. I'd be very interested to discuss 1) issues I've bumped into combining HRTBs and associated types; 2) how to get rid of this transmute and the dummy trait; 3) any feedback on the library before I publish and promote it, given it's heavily inspired by your work on Rayon! I can do any of the Monday or Friday times listed, though I have a preference for the 16 - 16.30 slots. Again, much appreciation for you doing this, I think it's awesome!
Both the original example and asajeffrey's example compile now. |
Add tests for some old fixed issues Closes rust-lang#30867 Closes rust-lang#30472 Closes rust-lang#28994 Closes rust-lang#26719 (and migrates the relevant test to the new run-make) Closes rust-lang#23600 cc `@jieyouxu` for the run-make-support changes try-job: x86_64-msvc
Rollup merge of rust-lang#131355 - clubby789:old-tests, r=jieyouxu Add tests for some old fixed issues Closes rust-lang#30867 Closes rust-lang#30472 Closes rust-lang#28994 Closes rust-lang#26719 (and migrates the relevant test to the new run-make) Closes rust-lang#23600 cc `@jieyouxu` for the run-make-support changes try-job: x86_64-msvc
The following doesn't compile, as it tries to assign
U
a concrete&'x i32
instead of&'a i32
:Removing the
U
type parameter makes it work -U
is only used for the associated typeFn::Output
anyway:AFAICT, the selection results in
for<'a, U> F: Fn<(&'a i32,), Output=U>
in the first case, andfor<'a> F: Fn<(&'a i32,)>
in the second case, and we refuse to allowU
to be parameterized by'a
in the former case, even though it's only used to satisfy the sugary form of theFn
trait.The text was updated successfully, but these errors were encountered: