dart-lang / sdk

The Dart SDK, including the VM, JS and Wasm compilers, analysis, core libraries, and more.
https://dart.dev
BSD 3-Clause "New" or "Revised" License
10.23k stars 1.58k forks source link

Late members declared with initializers are not initialized at optimization level 0 of dart2js #49599

Open whesse opened 2 years ago

whesse commented 2 years ago

A class with a late member, declared with an initializer, fails to get initialized when accessed if the program is compiled with dart2js optimization level 0.

The initialization is carried out correctly at higher optimization levels.

The command

dart compile js -O0  test.dart -o test.dart.js

creates a compiled program that fails on Chrome, with the exception:

Uncaught TypeError: this.get$lateMap(...).putIfAbsent$2 is not a function
    at Foo.mapLookup$1 (test.dart:15:20)

on the program

void main()
{
  final foo = Foo();
  final bar = foo.mapLookup('a');
}

class Foo {
  late var lateMap = <String, String>{};
  var map = <String, String>{};

  String mapLookup(String key) {
    final result = map.putIfAbsent(key, () => 'b'); // Succeeds
    final getIt = lateMap; // Not needed to reproduce failure
    // print(getIt); // Fails - toString and runtimeType are absent.
    return lateMap.putIfAbsent(key, () =>'c'); // Fails

  }
}

The error is present on the stable branch, and on current tip-of-tree 2.19.0-59.0.dev.

eernstg commented 2 years ago

Looking at the generated code, I find this:

    isSentinel(value) {
      return false;
    },

and that seems to make the logic around the late final variable go wrong: The variable lateFinalMap is considered initialized even though it isn't.

    get$lateFinalMap() {
      var t1, result,
        value = this.__Foo_lateFinalMap_FI;
      if (A.boolConversionCheck(A.isSentinel(value))) {
        t1 = type$.String;
        result = A.LinkedHashMap_LinkedHashMap$_empty(t1, t1);
        this.__Foo_lateFinalMap_FI !== $ && A.throwLateFieldADI("lateFinalMap");
        this.set$__Foo_lateFinalMap_FI(result);
        value = result;
      }
      return value;
    },

(Edit: I got the example from a different place, the variable was declared as late final lateFinalMap = <String, String>{}; in the example I compiled.)

fishythefish commented 2 years ago

This is failing due to --disable-type-inference. See #48312 for a similar issue - I closed that one since some improvements we landed happened to fix it, but I can start digging into the root cause.