Closed
Description
(spawned off of #58291)
Reduced example (play):
use std::panic::RefUnwindSafe;
trait Database { type Storage; }
trait HasQueryGroup { }
trait Query<DB> { type Data; }
trait SourceDatabase { fn parse(&self) { loop { } } }
struct ParseQuery;
struct RootDatabase { _runtime: Runtime<RootDatabase>, }
struct Runtime<DB: Database> { _storage: Box<DB::Storage> }
struct SalsaStorage { _parse: <ParseQuery as Query<RootDatabase>>::Data, }
impl Database for RootDatabase { type Storage = SalsaStorage; }
impl HasQueryGroup for RootDatabase {}
impl<DB> Query<DB> for ParseQuery where DB: SourceDatabase, DB: Database { type Data = RootDatabase; }
impl<T> SourceDatabase for T where T: RefUnwindSafe, T: HasQueryGroup {}
pub(crate) fn goto_implementation(db: &RootDatabase) -> u32 { db.parse(); loop { } }
fn main() { }
original code:
https://gist.github.com/dc3f0f8568ca093d9750653578bb8026
% rustc +nightly-2018-04-27 --allow dead_code src/main.rs
% rustc +nightly-2018-04-28 --allow dead_code src/main.rs
error[E0599]: no method named `parse` found for type `&RootDatabase` in the current scope
--> src/main.rs:231:6
|
231 | { db.parse(); loop { } }
| ^^^^^
|
= note: the method `parse` exists but the following trait bounds were not satisfied:
`RootDatabase : SourceDatabase`
`&RootDatabase : SourceDatabase`
`RootDatabase : SourceDatabase`
= help: items from traits can only be used if the trait is implemented and in scope
= note: the following trait defines an item `parse`, perhaps you need to implement it:
candidate #1: `SourceDatabase`
error: aborting due to previous error
For more information about this error, try `rustc --explain E0599`.
%
Metadata
Metadata
Assignees
Labels
Type
Projects
Milestone
Relationships
Development
No branches or pull requests
Activity
[-]trait resolution regression [/-][+]april 2018 trait resolution regression [/+]pnkfelix commentedon Apr 16, 2019
This change in behavior might be a bugfix. I'm still trying to understand the further narrowed-down test case, where it seems like one of the crucial details is the handling of
std::panic::RefUnwindSafe
:pnkfelix commentedon Apr 16, 2019
(note in particular that in this example, we have a blanket implementation of
SourceDatabase
for anyT
that implementsHasQueryGroup
andRefUnwindSafe
. There's an implementation of the former forRootDatabase
, but not for the latter.(But I'm not yet sure whether
RefUnwindSafe
is an OIBIT. Looking now.)Update: okay,
pub auto trait RefUnwindSafe
, so I thinkRootDatabase
implements it by default; I don't see any negative impls forBox
. But I also am not 100% sure how that auto traits interact with fields defined via associated types of generic type parameters...pnkfelix commentedon Apr 16, 2019
For reference, here is the log of changes between the two relevant nightlies. (I just transcribed this directly from #58291 (comment) )
ptr::Unique
with#[doc(hidden)]
#49858 (std: Markptr::Unique
with#[doc(hidden)]
)libsyntax
#50192 (Add some utilities tolibsyntax
)uwtable
for allocator shims #50263 (rustc: Emituwtable
for allocator shims)parking_lot
dependencies #50269 (Updateparking_lot
dependencies)impl Trait
in a trait impl #50227 (Fix ICE with erroneousimpl Trait
in a trait impl)evaluate_obligation
#48995 - aravind-pg:canonical-query, r=nikomatsakispnkfelix commentedon Apr 16, 2019
The two PR's that I think seem most likely to be at fault here are either #48995 or #50102
pnkfelix commentedon Apr 16, 2019
Okay I have now confirmed that this was injected by #48995
pnkfelix commentedon Apr 17, 2019
my current theory is that this is arising due to some interaction between inductive and co-inductive reasoning.
In particular, if you focus in on the
impl<T> SourceDatabase for T where T: RefUnwindSafe, T: HasQueryGroup {}
:commenting out either line [1] or line [2] above from the original code will cause the whole source input to be acccepted.
It is only when both are presented as preconditions on the blanket impl of
SourceDatabase
that we see a failure.I extended the
debug!
output a bit to print out the whole trait obligation stack (with syntaxelem1 :: elem2 :: elem3 :: []
) and am seeing this in one of the compiler's attempts to prove thatRootDatabase
may implementSourceDatabase
when its trying to resolve thatparse
method invocation:that is, it thinks the attempt to prove
RootDatabase
implementsSourceDatabase
requires recursive reasoning: because proving that requires proving thatRootDatabase: RefUnwindSafe
, which bubbles down toRuntime<RootDatabase>: RefUnwindSafe
and from there toSalsaStorage: RefUnwindSafe
.And that last bit,
SalsaStorage: RefUnwindSafe
, might be where we are hitting a problem, due to these definitions:so we now see why the compiler might reason that it has to prove
RootDatabase: SourceDatabase
when analyzing the structure ofSalsaStorage
: it needs to find the implementation of that trait so that it can find its associated::Data
type.But for some reason we don't hit the above problem if we remove the
T: HasQueryGroup
where clause on the blanket impl ofSourceDatabase
.nikomatsakis commentedon Apr 17, 2019
@pnkfelix I haven't had time to deeply look yet, but a few notes about induction/co-induction in general:
In short, a cycle in trait resolution is only considered "true" if all the traits involved are co-inductive (i.e., auto-traits). So if you have
Foo: Send
requires (indirectly, say)Foo: Send
, that's ok.But if you have
Foo: Send
requiresFoo: Debug
which requiresFoo: Send
, that's not ok.In this case, then, I expect that cycle to be "non-true", because
SourceDatabase
is an inductive trait and hence proving thatRootDatabase: SourceDatabase
cannot requireRootDatabase: SourceDatabase
.What I'm not sure about 100% is why the behavior changed here and whether the cycle ought to arise.
nikomatsakis commentedon May 1, 2019
Also curious:
If you do
SourceDatabase::parse(db)
instead ofdb.parse()
, it compiles.There are two distinct systems for trait evaluation (the "evaluate" code and the "confirm" code) which, I suppose, are disagreeing here. Digging a bit more.
4 remaining items