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

Type inference fails in light of generic type aliases with default parameter. #50822

Closed
glandium opened this issue May 17, 2018 · 14 comments
Closed
Labels
A-inference Area: Type inference A-type-system Area: Type system C-bug Category: This is a bug.

Comments

@glandium
Copy link
Contributor

Consider the following source:

pub struct Foo<A: Default, B: Default>(A, B);

impl<A: Default, B: Default> Foo<A, B> {
    fn new(a: A) -> Self { Foo(a, B::default()) }
}

pub type Bar<A, B=usize> = Foo<A, B>;

fn main() {
    let bar = Bar::new(42usize);
}

This fails to build with:

error[E0283]: type annotations required: cannot resolve `_: std::default::Default`
  --> src/main.rs:10:15
   |
10 |     let bar = Bar::new(42usize);
   |               ^^^^^^^^
   |
note: required by `<Foo<A, B>>::new`
  --> src/main.rs:4:5
   |
4  |     fn new(a: A) -> Self { Foo(a, B::default()) }
   |     ^^^^^^^^^^^^^^^^^^^^

IOW, it does want Bar::<usize>::new instead of Bar::new.

pub type Bar<A> = Foo<A, usize> also does Bar::new work.

@glandium
Copy link
Contributor Author

Huh, scrap that, it's not even type inference, because the following compiles:

pub struct Foo<A: Default, B: Default>(A, B);

impl<A: Default, B: Default> Foo<A, B> {
    fn new(a: A) -> Self { Foo(a, B::default()) }
}

pub type Bar<A, B=usize> = Foo<A, B>;

fn main() {
    let bar = Bar::<_>::new(42usize);
}

I don't know what the hell the problem is.

@Centril Centril added A-type-system Area: Type system A-inference Area: Type inference C-bug Category: This is a bug. labels May 17, 2018
@glandium
Copy link
Contributor Author

As mentioned by talchas on irc, this doesn't even need type aliases:

pub struct Foo<A: Default, B = usize>(A, B);

impl<A: Default, B> Foo<A, B> {
    fn new(a: A) -> Self { Foo(a, panic!()) }
}

fn main() {
    let bar = Foo::new(42usize);
}

Fails with:

error[E0282]: type annotations needed
 --> src/main.rs:8:15
  |
8 |     let bar = Foo::new(42usize);
  |         ---   ^^^^^^^^ cannot infer type for `B`
  |         |
  |         consider giving `bar` a type

In this example, Foo::<_>::new compiles, which is weird, considering the error is complaining about B.

glandium added a commit to glandium/allocator_api that referenced this issue Jun 20, 2018
And because of rust-lang/rust#50822 (comment)
bind Box::new and RawVec::new to Global.
@Ericson2314
Copy link
Contributor

Ericson2314 commented Jun 23, 2018

@glandium What happens if you repeat the default on the impl?

@glandium
Copy link
Contributor Author

error: defaults for type parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions.

@Ericson2314
Copy link
Contributor

CC @eddyb is this one of the situations there's no good solution for?

@eddyb
Copy link
Member

eddyb commented Jun 25, 2018

Foo::<_>::new is <Foo<_>>::new, i.e. <Foo<_, usize>>::new, while Foo::new is <Foo<_, _>>::new.
There's a feature-gate for actually using those defaults as a fallback in inference.
cc @nikomatsakis

@oberien
Copy link
Contributor

oberien commented Jun 30, 2018

I just ran into this with this (minified) example (playground):

enum Foo<A = (), B = ()> {
    A(A),
    B(B),
}

fn main() {
    let a = Foo::A(());
}

This fails to compile with cannot infer type for `B` . If I write let a: Foo = Foo::A(()) instead, it compiles just fine, I assume due to Foo's generic parameters defaulting to their default in a: Foo.

The same happens if I don't let A default to unit, but I need to annotate the variable with a: Foo<_> instead.

This feels very weird and unergonomic to me. Allowing it (apart from implementation complexity) should be a non-breaking change, because it allows strictly more code to compile.

@eddyb
Copy link
Member

eddyb commented Jul 13, 2018

@oberien It's not just implementation complexity, it's also predictability. See #27336.

@glandium
Copy link
Contributor Author

Come to think of it, inference shouldn't even be involved. For a type Foo<A=X, B=Y>, Foo means Foo<X, Y>, Foo<T> means Foo<T, Y> and Foo<T, U> means Foo<T, U>. All those are explicit types, they are not inferred nor should they be.

This is not made up, this is how rustc itself presents those types in errors. It even goes as far as presenting Foo<X> and Foo<X, Y> as Foo.
https://play.rust-lang.org/?gist=3f827a9b0c08ebf7505dd43b43b3bb93&version=stable&mode=debug&edition=2015

@eddyb
Copy link
Member

eddyb commented Jul 13, 2018

@glandium Foo::bar is not <Foo>::bar, it's <Foo<..>>::bar - that is, in a value context, a path infers type parameters by default, otherwise people would have to do HashMap::<_, _, _>::default.

@glandium
Copy link
Contributor Author

Well, this bug is exactly that people have to do the equivalent of HashMap::<_, _, _>::default... see #50822 (comment)

@eddyb
Copy link
Member

eddyb commented Jul 13, 2018

@glandium My example was about inferring S instead of using the default. In your case, you want to force the default, which requires specifying at least one type parameter, or using <Foo>::....

glandium added a commit to glandium/rust that referenced this issue Jul 19, 2018
This turns `Box<T>` into `Box<T, A: Alloc = Global>`. This is a
minimalist change to achieve this, not touching anything that could have
backwards incompatible consequences like requiring type annotations in
places where they currently aren't required,
per rust-lang#50822 (comment)
glandium added a commit to glandium/rust that referenced this issue Jul 24, 2018
This turns `Box<T>` into `Box<T, A: Alloc = Global>`. This is a
minimalist change to achieve this, not touching anything that could have
backwards incompatible consequences like requiring type annotations in
places where they currently aren't required,
per rust-lang#50822 (comment)
glandium added a commit to glandium/rust that referenced this issue Oct 17, 2018
This turns `Box<T>` into `Box<T, A: Alloc = Global>`. This is a
minimalist change to achieve this, not touching anything that could have
backwards incompatible consequences like requiring type annotations in
places where they currently aren't required,
per rust-lang#50822 (comment)
@steveklabnik
Copy link
Member

Triage: this still fails to compile

@eddyb
Copy link
Member

eddyb commented Feb 8, 2020

I'm closing this as "not a bug", as per #50822 (comment).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-inference Area: Type inference A-type-system Area: Type system C-bug Category: This is a bug.
Projects
None yet
Development

No branches or pull requests

6 participants