rust-lang / rust

Empowering everyone to build reliable and efficient software.
https://www.rust-lang.org
Other
97.54k stars 12.61k forks source link

Impossible to return unboxed closure from some functions. #66426

Closed wdanilo closed 2 years ago

wdanilo commented 4 years ago

Hi! I'm trying to write a lens library for Rust (see this for reference about lenses: https://github.com/ekmett/lens/wiki). I'm trying to write it in an zero-overhead style, so all lens-usages could be optimized away during compilation stage, thus, I'm trying to create unboxed closures and glue them together. However, a very strange error occurs here. Consider the following code:

#![feature(type_alias_impl_trait)]
#![feature(trait_alias)]

use std::marker::PhantomData;

trait OptGetter<'t,A,B> = Fn(&'t A) -> Option<&'t B>
    where A:'t, B:'t, Self:Copy;

struct Lens<'t,Get,A,B>
    where Get: OptGetter<'t,A,B> {
    get: Get,
    phantom: PhantomData<(&'t(),A,B)>
}

type CombinedOptGetter<'t,Get1,Get2,A,B,C> = impl OptGetter<'t,A,C>;
fn combine_opt_getter <'t,Get1,Get2,A,B,C>
(f1: Get1, f2: Get2) -> CombinedOptGetter<'t,Get1,Get2,A,B,C>
    where Get1:OptGetter<'t,A,B>,
          Get2:OptGetter<'t,B,C> {
    move |a| f1(a).and_then(f2)
}

impl<'t,Get,A,B> Lens<'t,Get,A,B>
    where Get: OptGetter<'t,A,B> {
    pub fn new(get: Get) -> Self {
        let phantom = PhantomData;
        Self { get, phantom }
    }

    pub fn combine<Get2, C>(self, that: Lens<'t,Get2,B,C>)
    -> Lens<'t,CombinedOptGetter<'t,Get,Get2,A,B,C>,A,C>
        where Get2: OptGetter<'t,B,C> {
        Lens::new(combine_opt_getter(self.get, that.get))
    }
}

trait OptionLens {
    type Out;
    fn _Some(self) -> Self::Out;
}

type SomeGetter<'t,A> = impl OptGetter<'t,Option<A>,A>;
fn some_getter<'t,A:'t>() -> Lens<'t,SomeGetter<'t,A>,Option<A>,A> {
    Lens::new(move|t: &'t Option<A>| t.as_ref())
}

fn _Some<'t, Get, A, B> (this: Lens<'t,Get,A,Option<B>>) -> i32
where Get: OptGetter<'t,A,Option<B>> {
    let wrapped: Lens<'t,SomeGetter<'t,B>,Option<B>,B> = some_getter();
    let wrapper: Lens<'t,CombinedOptGetter<'t,Get,SomeGetter<'t,B>,A,Option<B>,B>,A,B> = this.combine(wrapped);
    7
}

It compiles fine. However, if you try to return wrapper from the _Some function, the code will not compile and the errrors we get are not very explanatory either. With this code instead:

fn _Some<'t, Get, A, B> (this: Lens<'t,Get,A,Option<B>>) -> Lens<'t,CombinedOptGetter<'t,Get,SomeGetter<'t,B>,A,Option<B>,B>,A,B>
where Get: OptGetter<'t,A,Option<B>> {
    let wrapped: Lens<'t,SomeGetter<'t,B>,Option<B>,B> = some_getter();
    let wrapper: Lens<'t,CombinedOptGetter<'t,Get,SomeGetter<'t,B>,A,Option<B>,B>,A,B> = this.combine(wrapped);
    wrapper
}

We get:

error[E0283]: type annotations needed: cannot resolve `_: OptGetter<'t, std::option::Option<B>, B>`
  --> lib/optics/src/lib.rs:42:1
   |
42 | type SomeGetter<'t,A> = impl OptGetter<'t,Option<A>,A>;
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
   = note: the return type of a function must have a statically known size

error: concrete type differs from previous defining opaque type use
  --> lib/optics/src/lib.rs:47:1
   |
47 | / fn _Some<'t, Get, A, B> (this: Lens<'t,Get,A,Option<B>>) -> Lens<'t,CombinedOptGetter<'t,Get,SomeGetter<'t,B>,A,Option<B>,B>,A,B>
48 | | where Get: OptGetter<'t,A,Option<B>> {
49 | |     let wrapped: Lens<'t,SomeGetter<'t,B>,Option<B>,B> = some_getter();
50 | |     let wrapper: Lens<'t,CombinedOptGetter<'t,Get,SomeGetter<'t,B>,A,Option<B>,B>,A,B> = this.combine(wrapped);
51 | |     wrapper
52 | | }
   | |_^ expected `[closure@lib/optics/src/lib.rs:44:15: 44:48]`, got `[type error]`
   |
note: previous use here
  --> lib/optics/src/lib.rs:43:1
   |
43 | / fn some_getter<'t,A:'t>() -> Lens<'t,SomeGetter<'t,A>,Option<A>,A> {
44 | |     Lens::new(move|t: &'t Option<A>| t.as_ref())
45 | | }
   | |_^
Aaron1011 commented 4 years ago

Minimized:

#![feature(type_alias_impl_trait)]
#![feature(trait_alias)]

type Foo = impl Copy;

enum Wrapper<T> {
    First(T),
    Second
}

fn produce() -> Wrapper<Foo> {
    Wrapper::Second
}

This appears to be caused by the fact that we create a new inference variable for Foo from Wrapper<Foo>, but never unify it because produce does not constrain the opaque type.

As a side note, we should probably be cheking for type errors when we collect opaque type uses, so that we don't print useless errors like:

expected `[closure@lib/optics/src/lib.rs:44:15: 44:48]`, got `[type error]`
wdanilo commented 4 years ago

@Aaron1011 thanks for tracking it down. Tbh, I don't see why your code is a minimized version of mine – I don't return from any function variant without the impl trait. This is just a note – I don't know the internals of rustc so I can miss a lot of info here :)

Aaron1011 commented 4 years ago

I've opened https://github.com/rust-lang/rust/pull/66431. It addresses the first 'layer' of errors, but there appear to more (thankfully, better) errors that appear after this one is no longer emitted.

Aaron1011 commented 4 years ago

This now compiles on the latest nightly.

Aaron1011 commented 4 years ago

Actually, this does not compile - I didn't use the modified _Some function when testing.

jackh726 commented 3 years ago

Errors on current master:

error: non-defining opaque type use in defining scope
  --> src/main.rs:64:6
   |
64 | ) -> Lens<'t, CombinedOptGetter<'t, Get, SomeGetter<'t, B>, A, Option<B>, B>, A, B>
   |      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
note: used non-generic type `impl OptGetter<'t, Option<B>, B>` for generic parameter
  --> src/main.rs:20:34
   |
20 | type CombinedOptGetter<'t, Get1, Get2, A, B, C> = impl OptGetter<'t, A, C>;
   |                                  ^^^^

error: non-defining opaque type use in defining scope
  --> src/main.rs:64:6
   |
64 | ) -> Lens<'t, CombinedOptGetter<'t, Get, SomeGetter<'t, B>, A, Option<B>, B>, A, B>
   |      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
note: used non-generic type `Option<B>` for generic parameter
  --> src/main.rs:20:43
   |
20 | type CombinedOptGetter<'t, Get1, Get2, A, B, C> = impl OptGetter<'t, A, C>;
   |                                           ^

error: aborting due to 2 previous errors
nikomatsakis commented 3 years ago

For min_type_alias_impl_trait, I believe this is covered by #86732 I was mistaken

nikomatsakis commented 3 years ago

The minimized example (playground:

#![feature(min_type_alias_impl_trait)]

type Foo = impl Copy;

enum Wrapper<T> {
    First(T),
    Second
}

fn produce() -> Wrapper<Foo> {
    Wrapper::Second
}

gives what appears to me to be the correct error:

error: could not find defining uses
 --> src/lib.rs:3:12
  |
3 | type Foo = impl Copy;
  |            ^^^^^^^^^

but the diagnostics are not very good.

nikomatsakis commented 3 years ago

In particular, the value of Foo is not constrained at all by this function. If you extend to

#![feature(min_type_alias_impl_trait)]

type Foo = impl Copy;

enum Wrapper<T> {
    First(T),
    Second
}

fn produce() -> Wrapper<Foo> {
    Wrapper::First(22)
}

then it works.

oli-obk commented 2 years ago

The diagnostic for

#![feature(type_alias_impl_trait)]

type Foo = impl Copy;

enum Wrapper<T> {
    First(T),
    Second
}

fn produce() -> Wrapper<Foo> {
    Wrapper::Second
}

Playground

is now

10 | fn produce() -> Wrapper<Foo> {
   |                 ------------ type must be known at this point
11 |     Wrapper::Second
   |     ^^^^^^^^^^^^^^^ cannot infer type of the type parameter `T` declared on the enum `Wrapper`
   |
   = note: cannot satisfy `_: Copy`
help: consider specifying the generic argument
   |
11 |     Wrapper::<T>::Second
   |            +++++
oli-obk commented 2 years ago

I don't believe there is anything left to do here. Please open a new issue if there are issues beyond the failure to infer the type