Description
EDIT: To anyone coming to this from the This Week in Rust CFP: This is currently an unsolved issue, so the first step to tackling this is to figure out where the bug is! That alone would be a huge help. If you want to implement a fix, that'd be great too :)
=====
The following program should compile on stable, but doesn't:
trait ConstDefault {
const Default: Self;
}
trait Foo: Sized {}
trait FooExt: Foo {
type T: ConstDefault;
}
trait Bar<F: FooExt> {
const T: F::T;
}
impl<F: FooExt> Bar<F> for () {
const T: F::T = <F::T as ConstDefault>::Default;
}
It fails to compile with the following error:
error[E0277]: the trait bound `F: FooExt` is not satisfied
--> src/lib.rs:16:5
|
16 | const T: F::T = <F::T as ConstDefault>::Default;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `FooExt` is not implemented for `F`
|
= help: consider adding a `where F: FooExt` bound
Note that if we amend as either of the following, it works (credit to @ezrosent for figuring this out):
trait Bar<T: ConstDefault, F: FooExt<T=T>> {
const C: T;
}
impl<T: ConstDefault, F: FooExt<T=T>> Bar<T, F> for () {
const C: T = F::T::Default;
const C: T = T::Default;
}
Activity
kyren commentedon Mar 10, 2019
Okay, so I came here from the TWIR CFP post, and I thought I would take a stab at this.
This is the first time I have ever dug deeply into the internals of rustc itself, so take everything I'm about to say with a grain of salt. I don't have a real understanding of most of the rustc internals yet, so I approached this bug purely experimentally. It may be that what I'm saying is very obvious to somebody who already understands how typechecking and trait resolution work, so I apologize if I'm wasting everyone's time.
So I started by playing with the failing example, and there are lots of ways here to get some obviously wrong output from rustc:
So I started by looking into why in the world the compiler would output
the trait Foo is not implemented for F
when checking the associated constant when it's obviously in the where clause for the trait impl. After a lot of logging and staring at the code inlibrustc/traits/
, I figured out where the trait resolver is supposed to get this information: each obligation'sparam_env
field. I found that thisparam_env
during trait resolution for<F as Foo>::T
was empty! so clearly it made sense that the compiler couldn't find an implementation of Foo for F. From there, I figured out that thisParamEnv
instance ultimately comes from compare_method.rs:920 where it is purposefully constructed as empty. Quickly checking the history ofcompare_method.rs
I saw that the original implementation of associated constants did not use an emptyParamEnv
, and I'm not sure why an emptyParamEnv
is constructed now (though I'm not really sure of anything, I barely understand what's going on here).After some experimentation, I was able to make all of the examples here compile by changing:
to
to put the where clause for
F: Foo
in scope. However, this causes two tests to fail:ui/associated-const/associated-const-generic-obligations.rs
andui/nll/trait-associated-constant.rs
.Through experimentation I'm able to fix
ui/nll/trait-associated-constant.rs
by limiting where the non-emptyParamEnv
instance is passed to only here and passing in an emptyParamEnv
in other places, but I'm unable to fixui/associated-const/associated-const-generic-obligations.rs
without again causing this bug.So I don't think I'm actually fixing the bug here, because I don't understand things well enough yet to know what the right thing to do is, and there's a huge chance I have a lot of this wrong. I'm including all of this information simply in case it helps somebody more knowledgable actually fix it.
I hope this is helpful, if it's not helpful then feel free to ignore.
vickenty commentedon Mar 10, 2019
I'm no expert either, but just from the test failures it seems that @kyren's fix is actually correct.
Test failures after applying the fix: https://gist.github.com/vickenty/1ea9ea56c97e5e4902f6158a5f2410f1
ui/associated-const/associated-const-generic-obligations.rs
has an expected failure, but checked for specific error message, that this issue is about. After the fix the code is still (correctly) rejected, but the error message is more specific to the issue in the code.In
ui/nll/trait-associated-constant.rs
the compiler now accepts code that was rejected before. This is no longer an error:The error message appears similar to the one on this issue:
the lifetime 'a [..] .does not necessarily outlive the lifetime 'b
while pointing right at a place in the code that says otherwise.Since impls can use more generic lifetimes (as was recently discussed), it would make sense if this code was allowed as well.
kyren commentedon Mar 10, 2019
Oh, I should have paid more attention to what the tests actually did, thank you for looking at that!
joshlf commentedon Mar 10, 2019
Wow, fantastic work @kyren ! I'm not familiar enough with rustc to be able to say anything more than @vickenty has, but I guess just throw up a PR and we'll see what the reviewers say!
Rollup merge of rust-lang#59083 - kyren:master, r=varkor
Auto merge of #59151 - Centril:rollup, r=Centril
joshlf commentedon Mar 13, 2019
@kyren I saw your PR got merged; thanks again for your work digging into this and for the fix!
kyren commentedon Mar 13, 2019
No problem, glad to help 👍
It was more straightforward than I thought it was going to be.