# PR 123962 https://github.com/rust-lang/rust/pull/123962 ## Structurally resolution niko's mental model (probably broken) * auto-deref chain (when invoked on an opaque whose def-id which is in the defining scope) * structurally resolve * invokes the trait checker to normalize * OLD: no-op, returns opaque as is * NEW: returns an inference variable * match on the result * if it is an inference variable, we error * or if it is an opaque def-id that is in the defining scope, we error? * niko would like to see links to the kind of code that breaks * https://github.com/rust-lang/rust/pull/123962#issuecomment-2067883092 * https://github.com/rust-lang/rust/pull/120798#issuecomment-1938414794 * https://github.com/kurnevsky/esxtool/blob/88538ca7c8e068b5e38d4b386eb1bf3d5cede3d0/src/mwscript/parser.rs#L186-L191 * https://rust-lang.zulipchat.com/#narrow/stream/144729-t-types/topic/opaque.20type.20method.20resolution.20special.20casing * else, we continue assembling candidates for the opaque * check methods without autoref, then try to add reference `&self` and `&mut self` ty `whatever`, then &whatever`, `&mut whatever` etc * if we allow defining the opaque there, it unifies with all self types * ... ## Examples ```rust //! The recursive method call yields the opaque type. The //! `next` method call then constrains the hidden type to `&mut _` //! because `next` takes `&mut self`. We never resolve the inference //! variable, but get a type mismatch when comparing `&mut _` with //! `std::iter::Empty`. //@ revisions: current next //@[next] compile-flags: -Znext-solver //@[current] check-pass fn foo(b: bool) -> impl Iterator<Item = ()> { if b { foo(false).next().unwrap(); //[next]~^ type annotations needed } std::iter::empty() //[next]~^ mismatched types } fn main() {} ``` ```rust trait Trait { fn foo(self: &mut &Self) {} } fn foo<T: Trait>(x: T) { x.foo(); // nope let mut y = &x; y.foo(); // ok } ``` `autorefd_self_ty: &mut impl Trait` ```rust trait Trait { fn foo(self: &mut &Self) -> Self; } fn use_trait<T: Trait>() -> impl Trait { use_trait::<T>().foo() } ``` ### `-> impl Trait` in return position without import D'oh. ```rust use std::fmt::Debug as _; // uncomment this and it stops compiling fn foo(f: &mut std::fmt::Formatter<'_>) -> impl std::fmt::Debug { if false { let x = foo(f); x.fmt(f); } () } fn foo1(f: &mut std::fmt::Formatter<'_>) -> impl std::fmt::Debug { if false { let x = &mut foo(f); x.fmt(f); } () } // inconsistent with this fn bar<T>(t: impl std::fmt::Debug, f:&mut std::fmt::Formatter<'_>) { t.fmt(f); } // and the desugared version, of course fn baz<T: std::fmt::Debug>(t: T, f:&mut std::fmt::Formatter<'_>) { t.fmt(f); } ``` ### interesting example this is interesting because it requires an extension trait ```rust trait MyDebug { fn my_debug(&self); } impl<T> MyDebug for T where T: std::fmt::Debug, { fn my_debug(&self) { } } fn my_foo() -> impl std::fmt::Debug { if false { let x = my_foo(); x.my_debug(); } () } ``` ### fun and weird example This works today but we think it will fail with ambiguity with the PR The `&opaque` will be unified with `&&_` from the impl, constraining `opaque = &_`, and hence ambiguity. ```rust trait MyDebug { fn my_debug(&self); } impl<T> MyDebug for &T where T: std::fmt::Debug, { fn my_debug(&self) { } } fn my_foo() -> impl std::fmt::Debug { if false { let x = &my_foo(); x.my_debug(); } () } ``` ### constraining opaques via method dispatch The `&opaque` will be unified with `&&()` from the impl. This did not compile before but it will now. ```rust trait MyDebug { fn my_debug(&self); } impl MyDebug for &() { fn my_debug(&self) { } } fn my_foo() -> impl std::fmt::Debug { if false { let x = &my_foo(); x.my_debug(); } () } ``` ### example that changes behavior https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=0543258c04f67d66453f92aeb0f1ef13 ```rust #![feature(precise_capturing)] trait Get { fn get(&mut self) -> u32; } impl Get for () { fn get(&mut self) -> u32 { 0 } } impl<T> Get for &mut T where T: Get, { fn get(&mut self) -> u32 { T::get(self) + 1 } } fn foo(n: usize, m: &mut ()) -> impl use<'_> Get { if n > 0 { let mut iter = foo(n - 1, m); println!("{}", iter.get()); } m } fn main() { let g = foo(1, &mut ()).get(); println!("{}", g); } ```