Skip to content
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

Hidden type of "discarded" and thus unconstrained RPIT(IT) opaque types gets inferred to be the unit type () #134311

Open
fmease opened this issue Dec 14, 2024 · 1 comment
Labels
A-impl-trait Area: `impl Trait`. Universally / existentially quantified anonymous types with static dispatch. A-inference Area: Type inference C-bug Category: This is a bug. fixed-by-next-solver Fixed by the next-generation trait solver, `-Znext-solver`. P-low Low priority T-types Relevant to the types team, which will review and decide on the PR/issue.

Comments

@fmease
Copy link
Member

fmease commented Dec 14, 2024

This only affects RPIT(IT), not TAIT or ATPIT.
Fixing this issue would be a breaking change.
This exploits the issue #132212 (syntactically rejecting impl-Trait inside non-final path segments & inside fn ptr types is futile).


All subsequent snippets assume the following definitions:

type DiscardT<T> = <T as Discard>::Output;
trait Discard { type Output; }
impl<T: ?Sized> Discard for T { type Output = Point; } // you can substitute `Point` with `()` everywhere, this is just for clarity
struct Point;
trait NobodyImplsThis {}

RPIT check-pass reproducer (playground):

fn demo0() -> DiscardT<impl ?Sized> { Point }

RPIT check-fail reproducer (playground):

fn demo1() -> DiscardT<impl NobodyImplsThis + ?Sized> { Point }
//~^ ERROR the trait bound `(): NobodyImplsThis` is not satisfied

I expected both reproducers to get rejected by the compiler due to uninferable types or unconstrained opaque types (with an error roughly of the form item does not constrain demoN::{opaque#0}, but has it in its signature (mirroring TAIT/ATPIT)). The opaque types should be unconstrained because DiscardT<{opaque}> normalizes to Point which does not contain any opaque types.

However, in both cases the hidden type gets inferred to be (). See also the compiler log for the first reproducer:

rustc_hir_analysis::check::check::check_opaque_meets_bounds def_id=DefId(0:13 ~ oi[08b9]::demo0::{opaque#0}), span=oi.rs:6:24: 6:35 (#4), origin=FnReturn { parent: DefId(0:12 ~ oi[08b9]::demo0), in_trait_or_impl: None }
   rustc_middle::ty::sty::new_opaque def_id=DefId(0:13 ~ oi[08b9]::demo0::{opaque#0}), args=[]
   rustc_infer::infer::opaque_types::register_hidden_type opaque_type_key=OpaqueTypeKey { def_id: DefId(0:13 ~ oi[08b9]::demo0::{opaque#0}), args: [] }, span=oi.rs:6:24: 6:35 (#4), param_env=ParamEnv { caller_bounds: [], reveal: UserFacing }, hidden_ty=()
     rustc_infer::infer::opaque_types::table::register key=OpaqueTypeKey { def_id: DefId(0:13 ~ oi[08b9]::demo0::{opaque#0}), args: [] }, hidden_type=OpaqueHiddenType { span: oi.rs:6:24: 6:35 (#4), ty: () }

It's odd that some sort of type inference fallback occurs (in case you're wondering: In Rust 2024, it also falls back to (), not ! (since we don't have anything diverging here)).

Related: The fixed issue #96460 (PhantomData<{opaque}>).
Ranking this P-low because it's pretty artificial and not unsound.

For completeness, RPITIT reproducers
trait Demo2 { fn f() -> DiscardT<impl ?Sized> { Point } }
trait Demo3 { fn f() -> DiscardT<impl NobodyImplsThis + ?Sized> { Point } }
//~^ ERROR the trait bound `(): NobodyImplsThis` is not satisfied

For comparison, the TAIT and ATPIT analogues result in an error (though I'm not sure if that's an interesting comparison to draw).

TAIT/ATPIT
#![feature(type_alias_impl_trait, impl_trait_in_assoc_type)]

type Weak0 = impl ?Sized;
fn demo4() -> DiscardT<Weak0> { Point }
//~^ ERROR item does not constrain `Weak0::{opaque#0}`, but has it in its signature

type Weak1 = DiscardT<impl ?Sized>;
fn demo5() -> Weak1 { Point }
//~^ ERROR item does not constrain `Weak1::{opaque#0}`, but has it in its signature

trait Demo6 {
    type Proj: ?Sized;
    fn f() -> DiscardT<Self::Proj>;
}
impl Demo6 for i32 {
    type Proj = impl ?Sized;
    fn f() -> DiscardT<Self::Proj> { Point }
    //~^ ERROR item does not constrain `<i32 as Demo6>::Proj::{opaque#0}`, but has it in its signature
}

trait Demo7 { 
    type Proj;
    fn f() -> Self::Proj;
}
impl Demo7 for i32 {
    type Proj = DiscardT<impl ?Sized>;
    fn f() -> Self::Proj { Point }
    //~^ ERROR item does not constrain `<i32 as Demo7>::Proj::{opaque#0}`, but has it in its signature
}
@fmease fmease added A-impl-trait Area: `impl Trait`. Universally / existentially quantified anonymous types with static dispatch. C-bug Category: This is a bug. P-low Low priority T-types Relevant to the types team, which will review and decide on the PR/issue. labels Dec 14, 2024
@rustbot rustbot added the needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. label Dec 14, 2024
@fmease fmease added A-inference Area: Type inference fixed-by-next-solver Fixed by the next-generation trait solver, `-Znext-solver`. and removed needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. labels Dec 14, 2024
@fmease
Copy link
Member Author

fmease commented Dec 14, 2024

At the time of writing, the next-gen solver behaves as I would expect by rejecting both reproducers and by not falling back to () (though I don't know if that behavior is guaranteed to stay).

error[E0284]: type annotations needed: cannot satisfy `impl ?Sized == _`
 --> src/lib.rs:6:39
  |
6 | fn demo0() -> DiscardT<impl ?Sized> { Point }
  |                                       ^^^^^ cannot satisfy `impl ?Sized == _`
error[E0284]: type annotations needed: cannot satisfy `impl NobodyImplsThis + ?Sized == _`
 --> src/lib.rs:7:57
  |
7 | fn demo1() -> DiscardT<impl NobodyImplsThis + ?Sized> { Point }
  |                                                         ^^^^^ cannot satisfy `impl NobodyImplsThis + ?Sized == _`

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-impl-trait Area: `impl Trait`. Universally / existentially quantified anonymous types with static dispatch. A-inference Area: Type inference C-bug Category: This is a bug. fixed-by-next-solver Fixed by the next-generation trait solver, `-Znext-solver`. P-low Low priority T-types Relevant to the types team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

2 participants