Skip to content

Impossible to return unboxed closure from some functions. #66426

Closed

Description

@wdanilo

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 | | }
   | |_^

Activity

  1. Aaron1011 commented on Nov 15, 2019

    @Aaron1011
    Contributor

    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]`
    
  2. wdanilo commented on Nov 15, 2019

    @wdanilo
    Author

    @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 :)

  3. Aaron1011 commented on Nov 15, 2019

    @Aaron1011
    Contributor

    I've opened #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.

  4. added
    requires-nightlyThis issue requires a nightly compiler in some way. When possible, use a F-* label instead.
    T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.
    T-langRelevant to the language team
    on Nov 15, 2019
  5. added a commit that references this issue on Nov 19, 2019
  6. added a commit that references this issue on Nov 19, 2019
  7. Aaron1011 commented on Jan 9, 2020

    @Aaron1011
    Contributor

    This now compiles on the latest nightly.

  8. Aaron1011 commented on Feb 9, 2020

    @Aaron1011
    Contributor

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

  9. jackh726 commented on Feb 4, 2021

    @jackh726
    Member

    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
    
  10. nikomatsakis commented on Jun 29, 2021

    @nikomatsakis
    Contributor

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

  11. nikomatsakis commented on Jun 29, 2021

    @nikomatsakis
    Contributor

    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.

  12. nikomatsakis commented on Jun 29, 2021

    @nikomatsakis
    Contributor

    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.

  13. oli-obk commented on Sep 9, 2022

    @oli-obk
    Contributor

    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
       |            +++++
    
  14. oli-obk commented on Sep 9, 2022

    @oli-obk
    Contributor

    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

  15. Repository owner moved this from Todo to Done in type alias impl trait stabilizationon Sep 9, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    F-trait_alias`#![feature(trait_alias)]`F-type_alias_impl_trait`#[feature(type_alias_impl_trait)]`T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.T-langRelevant to the language teamrequires-nightlyThis issue requires a nightly compiler in some way. When possible, use a F-* label instead.

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions