Skip to content

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

Open
@fmease

Description

@fmease

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
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-impl-traitArea: `impl Trait`. Universally / existentially quantified anonymous types with static dispatch.A-inferenceArea: Type inferenceC-bugCategory: This is a bug.P-lowLow priorityT-typesRelevant to the types team, which will review and decide on the PR/issue.fixed-by-next-solverFixed by the next-generation trait solver, `-Znext-solver`.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions