Sdk: Allow "void" as a type argument

Created on 6 Oct 2016  路  14Comments  路  Source: dart-lang/sdk

The language will change to allow void as a type argument in generics (and as a type annotation in general).

This comes up in cases like:

  • A function that returns nothing asynchronously could return Future<void>. Today, code either uses Future, which doesn't give you great static checking of the result, or Future<Null>, which is kind of unfamiliar to most users.
  • Generic visitor classes that are parametric on their visit methods' return types. If you want to create a visitor that returns nothing, it would be nice to be able to explicitly extend AstVisitor<void>.

We also allow void to be the type annotation for locals, parameters, fields, ...

In Dart 1, void should simply be treated like Object, except as a return type for function types (where the old behavior should be kept).

In strong mode, an additional "voidness" rule will disallow assignments from void to Object when the assignment is statically visible. We will update the bug when we have a document ready.

area-language closed-duplicate customer-flutter language-strong-mode-polish

Most helpful comment

A void function can actually return anything (or more precisely: a function assignable to a function type with void return type, can return anything because of the way "more specific" is defined on functions).

That has led to errors where someone does

void foo() => delegate.foo();

but the delegate type's foo overrides the expected type with a function returning non-void and returns a non-null value. In checked mode that's an error.

The fix for that will likely be to allow anything to be returned by a void function. That makes void act like Object (and at runtime, it will likely _be_ Object) except that you can't _use_ a value with static type void for anything.

All 14 comments

A void function can actually return anything (or more precisely: a function assignable to a function type with void return type, can return anything because of the way "more specific" is defined on functions).

That has led to errors where someone does

void foo() => delegate.foo();

but the delegate type's foo overrides the expected type with a function returning non-void and returns a non-null value. In checked mode that's an error.

The fix for that will likely be to allow anything to be returned by a void function. That makes void act like Object (and at runtime, it will likely _be_ Object) except that you can't _use_ a value with static type void for anything.

Status update: we need to do #27727 for 1.50, but can implement this issue at a later point.

Another use case could be that raw generic types could implicitly have void as their generic type parameter if the user has disabled implicit dynamic.

@dskloetg this is tackled already. See #27727 mentioned by floitschG above and #17518, #26275

@zoechi I'm not sure how that is related.
I mean the following. Today when I write Future (without type parameter), it's interpreted as Future<dynamic>. With --no-implicit-dynamic it could be interpreted as Future<void> instead.

@dskloetg Currently you can use Future<Null> to get what you expect from Future<void>. AFAIK the work done in the linked issues, is to allow void where currently Null is required.

<Null> is a hack (e.g. it has .toString()). I wouldn't argue to enable a hack with --no-implicit-dynamic.
I'm talking about implicitly having <void> when you don't specify it.

The STRONG_MODE_INVALID_CAST_FUNCTION_EXPR is ignored in ~24 places in the Flutter code base. This seems important to address.

I've run into two issues relating to this with code like the following:

import 'dart:async';

void foo(x) {}

void main() {
  Future.wait([
    new Future.value().then(foo),
    new Future<bool>.value(false)
  ]);
}

On 1.22, this produces the error The element type 'Future<bool>' can't be assigned to the list type 'Future<void>'. After talking to @leafpetersen, it seems like there are two issues here:

  1. Future<bool> should be assignable to Future<void>.
  2. The LUB for these two futures should be something they're both assignable to.

The LUB is a continuing problem. It's not very good at handling generic classes, so the LUB of Future<void> and Future<bool> is ... Object.
The LUB in the specification is based on super-interfaces, not super-types, and the only super-interface of Future<bool> is Object. Even if Future<void> is a super-type of Future<bool>, LUB(Future<void>, Future<bool>) is still Object.

We need a better LUB, even more so now that it's used in inference too.

I updated the issue description to reflect the current status.

Do we have tests for this feature?

Not yet. I'll be writing an informal spec, and I expect that we can obtain tests from multiple contributors including the language team, as we've done before.

This is a subset of the functionality described in Generalized void meta issue #30176

Was this page helpful?
0 / 5 - 0 ratings

Related issues

ranquild picture ranquild  路  3Comments

gspencergoog picture gspencergoog  路  3Comments

rinick picture rinick  路  3Comments

55555Mohit55555 picture 55555Mohit55555  路  3Comments

DartBot picture DartBot  路  3Comments