Skip to content

Infering default generic arguments through traits #50107

Closed

Description

@Ekleog

I've recently come across an issue of type inference that can be summed up like this:

trait Trait {
    type A;
    type B;
}

enum Foo<A, B> { A(A), B(B) }
impl<A, B> Trait for Foo<A, B> { type A = A; type B = B; }

enum Many<A, B, T1 = Foo<A, B>, T2 = Foo<A, B>, T3 = Foo<A, B>>
where T1: Trait<A = A, B = B>,
      T2: Trait<A = A, B = B>,
      T3: Trait<A = A, B = B>,
{
    M1(T1), M2(T2), M3(T3)
}

fn main() {
    let x = match 3 {
        1 => Many::M1(Foo::A(3u8)),
        _ => Many::M2(Foo::B(3u16)),
    };
}

returns

error[E0282]: type annotations needed
  --> src/main.rs:19:14
   |
18 |     let x = match 3 {
   |         - consider giving `x` a type
19 |         1 => Many::M1(Foo::A(3u8)),
   |              ^^^^^^^^ cannot infer type for `T3`

(playground)

I would have thought rustc could be able to infer the type for T3 as Foo<u8, u16>, through this procedure:

  1. the match arms return Many<'a, 'b, 't1, 't2, 't3> with 't1 = Foo<u8, 'b1> and 't2 = Foo<'a2, u16>
  2. The where clause says 't1: Trait<'a, 'b>, and we only have 't1: Trait<u8, 'b1>, so we can unify and get 'a = u8 and 'b = 'b1
  3. The where clause also says 't2: Trait<'a, 'b>, and we only have 't2: Trait<'a2, u16>, so we can unify and get 'a = 'a2 and 'b = u16
  4. So currently we are at the match arms return Many<u8, u16, Foo<u8, u16>, Foo<u8, u16>, 't3>
  5. Applying the default for 't3 gives 't3 = Foo<u8, u16>, which is what I'm hoping for

In the meantime, would you happen to know of a way to do what I'm trying to do? Ideally it'd be a variadic enum, but not being able to figure out a way to do it I just decided to have a cap on the number of variants, and hit this bug. If this could not be a solution based on auto-generating a rust file for all possible numbers of variants it'd be even better, but…

Activity

  1. Ekleog commented on Apr 20, 2018

    @Ekleog
    Author

    By the way, here is a maybe simpler example, using Void instead of Foo<A, B>, so that type inference wouldn't even have to use trait definitions to infer the types represented by A and B, that fails in the same way:

    trait Trait {
        type A;
        type B;
    }
    
    enum Void {}
    impl Trait for Void { type A = Void; type B = Void; }
    
    enum Foo<A, B> { A(A), B(B) }
    impl<A, B> Trait for Foo<A, B> { type A = A; type B = B; }
    
    enum Many<A, B, T1 = Void, T2 = Void, T3 = Void>
    where T1: Trait<A = A, B = B>,
          T2: Trait<A = A, B = B>,
          T3: Trait<A = A, B = B>,
    {
        M1(T1), M2(T2), M3(T3)
    }
    
    fn main() {
        let x = match 3 {
            1 => Many::M1(Foo::A(3u8)),
            _ => Many::M2(Foo::B(3u16)),
        };
    }
  2. added
    C-enhancementCategory: An issue proposing an enhancement or a PR with one.
    T-langRelevant to the language team
    on Sep 25, 2018
  3. Spoonbender commented on Mar 22, 2022

    @Spoonbender

    Triage: no change

  4. estebank commented on Aug 14, 2023

    @estebank
    Contributor

    Current output:

    error[E0282]: type annotations needed
      --> f71.rs:19:14
       |
    19 |         1 => Many::M1(Foo::A(3u8)),
       |              ^^^^^^^^ cannot infer type of the type parameter `T3` declared on the enum `Many`
       |
    help: consider specifying the generic arguments
       |
    19 |         1 => Many::<u8, u16, Foo<u8, u16>, Foo<u8, u16>, T3>::M1(Foo::A(3u8)),
       |                  +++++++++++++++++++++++++++++++++++++++++++
    

    With #114811:

    error[E0283]: type annotations needed
      --> f71.rs:19:14
       |
    19 |         1 => Many::M1(Foo::A(3u8)),
       |              ^^^^^^^^ cannot infer type of the type parameter `T3` declared on the enum `Many`
       |
       = note: cannot satisfy `_: Trait`
       = help: the trait `Trait` is implemented for `Foo<A, B>`
    note: required by a bound in `Many::M1`
      --> f71.rs:12:11
       |
    12 |       T3: Trait<A = A, B = B>,
       |           ^^^^^^^^^^^^^^^^^^^ required by this bound in `Many::M1`
    13 | {
    14 |     M1(T1), M2(T2), M3(T3)
       |     -- required by a bound in this tuple variant
    help: consider specifying the generic arguments
       |
    19 |         1 => Many::<u8, u16, Foo<u8, u16>, Foo<u8, u16>, T3>::M1(Foo::A(3u8)),
       |                  +++++++++++++++++++++++++++++++++++++++++++
    
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

    A-inferenceArea: Type inferenceC-enhancementCategory: An issue proposing an enhancement or a PR with one.T-langRelevant to the language team

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions