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

Inferring void for a toplevel is strange #34171

Closed
MichaelRFairhurst opened this issue Aug 17, 2018 · 5 comments
Closed

Inferring void for a toplevel is strange #34171

MichaelRFairhurst opened this issue Aug 17, 2018 · 5 comments
Labels
area-language Dart language related items (some items might be better tracked at github.com/dart-lang/language).

Comments

@MichaelRFairhurst
Copy link
Contributor

This is a downside of allowing void-to-void assignment that I don't think we fully considered.

void f() {}
void main() {
  var x = f(); // no error
  x.y; // Error
}

This is not very good locality for the error here. It is extremely unlikely that the user meant the first line. For instance, I ran into this when testing if List.removeWhere() returned an Iterable of the removed items, or int count, or just void. I did not get any errors for assigning it to a variable, which I thought ruled out the last case.

final items = list.removeWhere(...); // no error! Must not return void! So I thought to myself.

In fact, it gets worse. In the first example, y gets highlighted instead of the x on the second line! (or x.y for that matter).

This makes sense for f().y, but in this example, the highlight + bad locatily + error message combine poorly, and it looks like x is of some type that has some member y of type void, and then you are sitting there wondering how that would be possible (a void getter?) and why that would be a problem (to get a void getter?). So the real problem is not obvious.

This could probably be covered as part of void_checks in the linter, but should it be? Should it maybe instead be a rule in the language spec, that a variable cannot have the type void inferred?

@eernstg @leafpetersen

@MichaelRFairhurst MichaelRFairhurst added the area-language Dart language related items (some items might be better tracked at github.com/dart-lang/language). label Aug 17, 2018
@eernstg
Copy link
Member

eernstg commented Aug 17, 2018

One good reason for having a variable of type void would be that it satisfies an implementation constraint:

abstract class I<X> {
  X get x;
  // ...
}

class C implements I<void> {
  void x; // Required by `implements I<void>`.
  // ... 
}

class D implements I<void> {
  var x; // Inferred type, due to member signature inference based on `I<void>`.
}

I think it would very likely be a good idea to flag (lint?) all variable declarations whose type is void, when that type was inferred from the initializing expression.

@MichaelRFairhurst
Copy link
Contributor Author

I'm going to close this and file for a lint, unless anyone objects.

@leafpetersen
Copy link
Member

There's no good way to avoid inferring variables as having type void in general, because of Future<void>.then. We could disallow inferring local variables as void, but I don't see this as adding much value, and it makes doing things like code generation harder, since it must then be non-uniform for types like void. The original example doesn't seem like a bad error user experience to me (particularly compared to all of the places that Dart doesn't give any warning about). You bind a variable, which is fine. If you try to call a method on that variable, you get told that the variable doesn't have that method, because it has type void. Why is this a worse experience than:

int foo() => 3;
void main() {
  var x = foo();
  x.length; // error, x has no length field
}

@MichaelRFairhurst
Copy link
Contributor Author

Yeah, didn't think about how this is what Future.then() does. Obviously its suspect to make language rules where that's allowed for parameters but not locals etc.

It threw me off, and I implemented it! But I could have just been having a brain fart.

In most languages, var x = f() would err, and my mistake was thinking Dart was like most languages here, and then having to backtrack on that later on in my code. I think other users will hit it and I think that's what makes it different when f() returns void vs int.

A quick guinea pig test shows I'm not the only one that wishes "removed" were flagged, but, a lint should be just fine, and its not like this will cause soundness issues in peoples' code or anything.

@leafpetersen
Copy link
Member

Yeah, void in Dart is kind of closer to a unit (single inhabitant) type than an empty type, but not, because of legacy subtyping. @lrhn 's goal of making it only inhabited by null would be nice.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-language Dart language related items (some items might be better tracked at github.com/dart-lang/language).
Projects
None yet
Development

No branches or pull requests

3 participants