-
Notifications
You must be signed in to change notification settings - Fork 12.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
where clauses are only elaborated for supertraits, and not other things #20671
Comments
Possibly related to #20469 but it is not fixed by |
Note, this blocks the transition from |
Sorry, but I don't see why this should work without an explicit #![crate_type = "lib"]
// these two are equivalent
struct Rev1<I> where I: DoubleEndedIterator {
it: I,
}
struct Rev2<I: DoubleEndedIterator> {
it: I,
}
// these two don't work because `Rev<I>` doesn't imply that `I: DoubleEndedIterator`
fn foo1<I>(mut rev: Rev1<I>) {
rev.it.next_back();
}
fn foo2<I>(mut rev: Rev2<I>) {
rev.it.next_back();
}
// even these won't work unless you explicitly add `I: DoubleEndedIterator` as a bound
fn bar1<I>(mut rev: Rev1<I>) {}
fn bar2<I>(mut rev: Rev2<I>) {} I skimmed over the |
@japaric My assumption that this would work was based partly on conversations with @nikomatsakis after the initial RFC. In particular, we've discussed that trait Foo<T> {
fn foo(&self) -> &T;
}
//trait Bar<A> where Self: Foo<A> {} <--- this produces an error
trait Bar<A>: Foo<A> {}
fn foobar<A, B: Bar<A>>(b: &B) -> &A {
b.foo()
} That seems inconsistent and I don't know of any good reason for it. (And of course it's a pain to have to rewrite bounds everywhere when those bounds must already hold.) I do take your point that in general we do not propagate bounds, but I think we want to head toward more propagation, as in http://smallcultfollowing.com/babysteps/blog/2014/07/06/implied-bounds/ -- and I thought we'd already started down this road. |
@japaric It is also my understanding (from conversations with @nikomatsakis) that this should work. It seems related to the generalization of bounds checking. One of the annoyances I started purging from the compiler is the I think this is an important question to resolve generally because like @aturon it was my understanding that any super trait bound should be identical to bounding the In my imagined scheme we should probably collect any Self type constraints and introduce them like we have been with super trait bounds. It seems this would be the simplest strategy, and avoids introducing a breaking change for anyone relying on the current behavior (esp. if this isn't resolved pre-1.0). |
I think this is a grey area :) At present we only "elaborate" supertraits (meaning |
So I was missing a piece of information.
I agree with this. But want to point out that this second example is a "supertrait", whereas the first one is not. So having to explicitly add the
I'm not opposed to having implied bounds, but I would like it to be implemented in a general way (such that my struct bounds example would also compile), instead of making it more ad hoc ("we only propagate bounds on traits with where-clause bounds that have a
I agree!
I think that at this point in time supertraits are ingrained in rustaceans' minds as something natural even if they are arbitrary. In fact, I just realized that it's a (very) special case of implied bounds. Thanks everyone for their explanations! |
all where-clauses, but supertrait considers a more limited set. Supertrait expansion is suitable for virtual tables and associated type inclusion, elaboration for more general reasoning. Fixes rust-lang#20671.
Not sure if it is related, but this doesn't work either: use std::ops::*;
trait Vector<S> where
for<'a, 'b> &'a Self: Add<&'b Self>,
for<'a> &'a Self: Mul<S>,
{
fn test(&self) {}
}
fn test<S, V: Vector<S>>(v: V) { v.test() }
This is needed for cgmath to move full to operators, and remove our ugly |
…ations We can't yet remove the operator methods, due to rust-lang/rust#20671 This also removes the implementations of `Zero` and `One` for vectors.
We can't yet remove the operator methods, due to rust-lang/rust#20671
We can't yet remove the operator methods, due to rust-lang/rust#20671
Same "issue" |
I ran into this issue as well, and was really surprised by it. One situation where it would be very useful is refining traits that are used in higher-kinded trait bounds like in this example: https://gist.github.com/anonymous/e3a8fe834bb383504bb4 If we don't propagate associated type bounds for where clause traits, I don't know how to actually put the bound I need in the linked example on the function: the bound itself is polymorphic over the lifetime, which seems to not be expressible in the grammar. So, it seems that without this propagation there are useful bounds that we can't express. |
@russellmcc what bound are you unable to express precisely? |
In that example, I want to say that Making the trait bounds propagate for associated types would allow me to express the exact same bound in a convenient way, as in my previous example. |
I ran into something like this trying to create an |
One other case where this appears (#85243): pub trait Trait1 {
fn f();
}
pub trait Trait2 {
type Type: Trait1;
}
pub trait Trait3 {
type Type2: Trait2<Type = Self>;
fn f2() {
<Self as Trait1>::f();
}
} I expected to see this compile successfully since in Instead, this happened:
|
Would it be feasible to lint and point folks to this issue? Or is that just as hard as elaborating would be? |
Another related snippet (I guess). Also not sure if this would be fixed by trait Foo: Iterator
where
Self::Item: Default,
{
}
fn bar<T>(_: T)
where
T: Foo,
// Why the necessity of this bound? `Foo` already implies `Item: Default`
T::Item: Default
{
} |
While isize displacement is enough for native compilation we may also want to generate code for a foreign architecture and this is realitively simple change. The only complication is rust-lang/rust#20671 Because of that limitation we have a lot of code duplication in that module. Also add smoke tests to verify that all supported operations work.
I'm running into this. Specifically something along the lines of:
For any Is there an idiomatic workaround? The best I could think of is to restate the associated types, but this still seems suboptimal. |
That is the only workaround I know of. I would be very surprised if there was another way. |
As a relatively new Rust user I just ran into this and found it super confusing, and seemingly inconsistent; in fact, I assumed it was a bug and tried upgrading to As mentioned in the issue title, the distinction is that "bounds on associated type are not super traits", and "only super traits are propagated", but this is not a satisfactory answer from a (this!) user point of view. Indeed, I didn't even have a precise concept of "super trait" in mind before I found the explanation here; the associated-type bound just felt like another bound. When I ran into this in the wild, I eventually reduced my "real" code to this minimal example (similar to other examples above): trait HasA {
type A;
fn get_a(&self) -> Self::A;
}
trait Foo {
fn foo(&self);
}
trait Helper
where
Self: HasA,
Self::A: Foo,
{
}
fn test1<T: Helper>(t: T) {} I was totally baffled that the Moreover, it's super confusing that the function It took me an hour+ to reduce my "real" code to this simple illustration of the problem, where my actual usage is more analogous to the following fn test2<T: Helper>(t: T) {
t.get_a().foo();
} p.s. as mentioned above, apparently this doesn't affect trait aliases, which hopefully means this is easy to fix? Unfortunately, I can't use |
A friend showed me a workaround. It's confusing when described abstractly, so probably better to just read the code below. But in summary: it seems that bounds on types associated with the trait being defined do get propagated, whereas we saw above that bounds on types associated with traits that bound the trait being defined don't get propagated. Compare // Doesn't type check
mod M1 {
trait HasA {
type A;
fn get_a(&self) -> Self::A;
}
trait Foo {
fn foo(&self);
}
trait Helper
where
// This `HasA` bound on `Self` propagates.
Self: HasA,
// This `Foo` bound on `<Self as HasA>::A` doesn't propagate.
Self::A: Foo,
{
}
// Doesn't type check: the bound `T::A: Foo` is not satisfied!?
fn test1<T: Helper>(t: T) {}
// Doesn't type check: same as above.
fn test2<T: Helper>(t: T) {
t.get_a().foo();
}
}
// Variation on `M1` that does type check
mod M2 {
trait HasA {
type A;
fn get_a(&self) -> Self::A;
}
trait Foo {
fn foo(&self);
}
// Trick: define a "dummy" associated type `Helper::_A` that is
// constrained to be equal to the associated type `HasA::A` that we
// actually want to bound. By bounding `Helper::_A` we then implicitly
// bound `HasA::A`, since apparently the equality constraint *does*
// propagate.
trait Helper: HasA<A = <Self as Helper>::_A> {
// This `Foo` bound on `<Self as Helper>::_A` does propagate,
// even tho a `Foo` bound on `<Self as HasA>::A` doesn't :/
type _A: Foo;
}
fn test1<T: Helper>(t: T) {}
fn test2<T: Helper>(t: T) {
t.get_a().foo();
}
} |
164bde9 miniscript: add parsing benchmarks (Andrew Poelstra) bbafc39 expression: add parsing benchmark (Andrew Poelstra) 30d2d11 miniscript: factor out a couple parts of Terminal::from_tree (Andrew Poelstra) f22dc3a miniscript: use new TRUE/FALSE constants throughout the codebase (Andrew Poelstra) be41d8b miniscript: add constants for 1 and 0 (Andrew Poelstra) 270e65d Replace macros with blanket impls (Andrew Poelstra) 5ec0f44 add `blanket_traits` module to replace very noisy trait definitions (Andrew Poelstra) Pull request description: There are several macros we use to implement functions when our `Pk` types satisfy a large bundle of trait conditions. There is a trick rust-lang/rust#20671 (comment) that we can use instead. While I'm at it, do several other small cleanups. As a weekend project I rewrote the expression module and was able to get a 10-15% speedup on parsing miniscripts, while eliminating recursion and simplifying the algorithms. I am still working on cleaning up the code and improving the error handling. This is the first set of commits from that branch, which should be simple and uncontroversial. ACKs for top commit: apoelstra: > ACK [164bde9](164bde9). Are you taking a performance refactor stab at rust-miniscript? sanket1729: ACK 164bde9. Are you taking a performance refactor stab at rust-miniscript? Tree-SHA512: 42e11f5f45fa705e14334e79126a66c97577fc6e807f804beaa1532bf3693a6c41a8b714bc4d1436209a1e0d808dc6a3ccc7198f20ff467f40f12126d1ee02f3
The following example:
fails with "error: type
&A
does not implement any method in scope namedfoo
".This UFCS variant
fails with "error: the trait
Foo<_>
is not implemented for the typeA
".The text was updated successfully, but these errors were encountered: