Skip to content

void is inconsistent about whether non-void values can be assigned to it #32558

Open
@Hixie

Description

@Hixie

I may be confused, but my understanding and intuition is that void means nothing, and something of type void cannot take on any values. This understanding is supported by the way that Dart doesn't let you return anything from void-returning methods:

void foo1() {
  return 0; // "The return type 'int' isn't a 'void', as defined by the method 'foo1'"
}

It also doesn't let you read anything from arguments that are explicitly typed void:

import 'dart:async';

void main() {
  final Completer<void> completer = new Completer<void>();
  completer.complete(0);
  completer.future.then((void value) {
    print(value); // "The expression here has a type of 'void', and therefore cannot be used"
  });
}

(In both these cases, these are only analyzer restructions; the compiler allows both. But that's fine, so long as one of them complains, I consider it "invalid Dart".)

Somehow, though, this doesn't always hold. For example, you can return a value from an async method that returns Future<void>:

Future<void> foo2() async {
  return 0; // no error (!!)
}

You can also pass values to methods expecting void with no ill effects:

void main() {
  final List<void> a = <void>[];
  a.add(0); // no error at analysis time or runtime (!!)
  print(a); // prints [0] (!!)
}

According to the analyzer, you can pass "void"-expecting objects into APIs that expect other types, though that is a runtime error in Dart2 (not Dart1):

void addNumber(List<int> list) {
  list.add(0);
}

void main() {
  final List<void> list = <void>[];
  addNumber(list); // no analyzer error, though the Dart2 runtime does throw here
  print(list);
}

You can pass non-void expecting objects into APIs that expect void objects, with unexpected results:

void printVoidList(List<void> list) {
  print(list); // prints [0] (!!)
}

void main() {
  printVoidList(const <int>[0]); // no error at analysis time or runtime (!!)
}

This leads to situations where you can be completely messing up the types, but neither the analyzer nor the compiler will complain. This code is based on actual code in the Flutter framework that tries to use generics to catch programmer errors, but currently fails to catch any errors because of this:

import 'dart:async';

List<Completer<dynamic>> pendingWork = <Completer<dynamic>>[];
     
int addWork<T>(Completer<T> work) {
  pendingWork.add(work);
  return pendingWork.length - 1;
}

void completeWork<T>(int index, T value) {
  pendingWork[index].complete(value);
}

void main() {
  Completer<int> completer = new Completer<int>();
  int index = addWork<void>(completer); // no error (!!)                                                                                                                                                                                                       
  completeWork<void>(index, 0); // no error (!!)       	                                                                                                                                                                                                       
  completer.future.then(print); // prints "0"                                                                                                                                                                                                                  
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2A bug or feature request we're likely to work onarea-dart-modelFor issues related to conformance to the language spec in the parser, compilers or the CLI analyzer.dart-model-analyzer-specIssues with the analyzer's implementation of the language specstrict-analysis-neededMark issues where strict-raw-types, strict-inference and similar analysis options were helpfultype-bugIncorrect behavior (everything from a crash to more subtle misbehavior)

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions