[dart2js] Implement Cell lowering for uninitialized locals.
Change-Id: I7233e1484cc19e5f24d215bfd2c1e34c8f12961b
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/193588
Commit-Queue: Mayank Patke <fishythefish@google.com>
Reviewed-by: Stephen Adams <sra@google.com>
Reviewed-by: Johnni Winther <johnniwinther@google.com>
diff --git a/pkg/compiler/lib/src/kernel/dart2js_target.dart b/pkg/compiler/lib/src/kernel/dart2js_target.dart
index 878f2be..d41438e 100644
--- a/pkg/compiler/lib/src/kernel/dart2js_target.dart
+++ b/pkg/compiler/lib/src/kernel/dart2js_target.dart
@@ -62,10 +62,6 @@
/// Late lowerings which the frontend performs for dart2js.
const List<int> _allEnabledLateLowerings = [
- LateLowering.nullableUninitializedNonFinalLocal,
- LateLowering.nonNullableUninitializedNonFinalLocal,
- LateLowering.nullableUninitializedFinalLocal,
- LateLowering.nonNullableUninitializedFinalLocal,
LateLowering.nullableInitializedNonFinalLocal,
LateLowering.nonNullableInitializedNonFinalLocal,
LateLowering.nullableInitializedFinalLocal,
@@ -167,8 +163,7 @@
_nativeClasses)
.visitLibrary(library);
}
- lowering.transformLibraries(
- libraries, coreTypes, hierarchy, flags.enableNullSafety);
+ lowering.transformLibraries(libraries, coreTypes, hierarchy);
logger?.call("Lowering transformations performed");
}
diff --git a/pkg/compiler/lib/src/kernel/transformations/late_lowering.dart b/pkg/compiler/lib/src/kernel/transformations/late_lowering.dart
new file mode 100644
index 0000000..cf5c62c
--- /dev/null
+++ b/pkg/compiler/lib/src/kernel/transformations/late_lowering.dart
@@ -0,0 +1,112 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:kernel/ast.dart';
+import 'package:kernel/library_index.dart';
+import 'package:kernel/type_algebra.dart';
+
+bool _shouldLowerVariable(VariableDeclaration node) =>
+ node.initializer == null && node.isLate;
+
+class LateLowering {
+ final Class _cellClass;
+ final Constructor _cellConstructor;
+
+ final Procedure _readLocal;
+ List<TypeParameter> _readLocalTypeParameters;
+ FunctionType _readLocalTypeWithoutTypeParameters;
+
+ final Procedure _setValue;
+ final Procedure _setFinalLocalValue;
+
+ final Map<VariableDeclaration, VariableDeclaration> _cells = {};
+
+ Member _contextMember;
+
+ LateLowering(LibraryIndex index)
+ : _cellClass = index.getClass('dart:_late_helper', '_Cell'),
+ _cellConstructor = index.getMember('dart:_late_helper', '_Cell', ''),
+ _readLocal = index.getMember('dart:_late_helper', '_Cell', 'readLocal'),
+ _setValue = index.getMember('dart:_late_helper', '_Cell', 'set:value'),
+ _setFinalLocalValue = index.getMember(
+ 'dart:_late_helper', '_Cell', 'set:finalLocalValue') {
+ FunctionType _readLocalType = _readLocal.getterType;
+ _readLocalTypeParameters = _readLocalType.typeParameters;
+ _readLocalTypeWithoutTypeParameters = _readLocalType.withoutTypeParameters;
+ }
+
+ VariableDeclaration _variableCell(VariableDeclaration variable) {
+ assert(_shouldLowerVariable(variable));
+ return _cells.putIfAbsent(variable, () {
+ int fileOffset = variable.fileOffset;
+ return VariableDeclaration(variable.name,
+ initializer:
+ ConstructorInvocation(_cellConstructor, Arguments.empty())
+ ..fileOffset = fileOffset,
+ type: InterfaceType(
+ _cellClass, _contextMember.enclosingLibrary.nonNullable),
+ isFinal: true)
+ ..fileOffset = fileOffset;
+ });
+ }
+
+ VariableGet _variableCellAccess(
+ VariableDeclaration variable, int fileOffset) =>
+ VariableGet(_variableCell(variable))..fileOffset = fileOffset;
+
+ TreeNode transformVariableDeclaration(
+ VariableDeclaration node, Member contextMember) {
+ _contextMember = contextMember;
+
+ if (!_shouldLowerVariable(node)) return node;
+
+ // A [VariableDeclaration] being used as a statement must be a direct child
+ // of a [Block].
+ if (node.parent is! Block) return node;
+
+ return _variableCell(node);
+ }
+
+ TreeNode transformVariableGet(VariableGet node, Member contextMember) {
+ _contextMember = contextMember;
+
+ VariableDeclaration variable = node.variable;
+ if (!_shouldLowerVariable(variable)) return node;
+
+ int fileOffset = node.fileOffset;
+ VariableGet cell = _variableCellAccess(variable, fileOffset);
+ List<DartType> typeArguments = [node.promotedType ?? variable.type];
+ return InstanceInvocation(
+ InstanceAccessKind.Instance,
+ cell,
+ _readLocal.name,
+ Arguments(const [], types: typeArguments)..fileOffset = fileOffset,
+ interfaceTarget: _readLocal,
+ functionType:
+ Substitution.fromPairs(_readLocalTypeParameters, typeArguments)
+ .substituteType(_readLocalTypeWithoutTypeParameters))
+ ..fileOffset = fileOffset;
+ }
+
+ TreeNode transformVariableSet(VariableSet node, Member contextMember) {
+ _contextMember = contextMember;
+
+ VariableDeclaration variable = node.variable;
+ if (!_shouldLowerVariable(variable)) return node;
+
+ int fileOffset = node.fileOffset;
+ VariableGet cell = _variableCellAccess(variable, fileOffset);
+ if (variable.isFinal) {
+ return InstanceSet(InstanceAccessKind.Instance, cell,
+ _setFinalLocalValue.name, node.value,
+ interfaceTarget: _setFinalLocalValue)
+ ..fileOffset = fileOffset;
+ } else {
+ return InstanceSet(
+ InstanceAccessKind.Instance, cell, _setValue.name, node.value,
+ interfaceTarget: _setValue)
+ ..fileOffset = fileOffset;
+ }
+ }
+}
diff --git a/pkg/compiler/lib/src/kernel/transformations/lowering.dart b/pkg/compiler/lib/src/kernel/transformations/lowering.dart
index 72c4f15..915c5fb 100644
--- a/pkg/compiler/lib/src/kernel/transformations/lowering.dart
+++ b/pkg/compiler/lib/src/kernel/transformations/lowering.dart
@@ -5,52 +5,57 @@
import 'package:kernel/ast.dart';
import 'package:kernel/class_hierarchy.dart' show ClassHierarchy;
import 'package:kernel/core_types.dart' show CoreTypes;
-import 'package:kernel/type_environment.dart'
- show StaticTypeContext, TypeEnvironment;
import 'factory_specializer.dart';
+import 'late_lowering.dart';
/// dart2js-specific lowering transformations and optimizations combined into a
/// single transformation pass.
///
/// Each transformation is applied locally to AST nodes of certain types after
/// transforming children nodes.
-void transformLibraries(List<Library> libraries, CoreTypes coreTypes,
- ClassHierarchy hierarchy, bool nullSafety) {
- final transformer = _Lowering(coreTypes, hierarchy, nullSafety);
+void transformLibraries(
+ List<Library> libraries, CoreTypes coreTypes, ClassHierarchy hierarchy) {
+ final transformer = _Lowering(coreTypes, hierarchy);
libraries.forEach(transformer.visitLibrary);
}
class _Lowering extends Transformer {
- final TypeEnvironment env;
- final bool nullSafety;
final FactorySpecializer factorySpecializer;
+ final LateLowering _lateLowering;
Member _currentMember;
- StaticTypeContext _cachedStaticTypeContext;
- _Lowering(CoreTypes coreTypes, ClassHierarchy hierarchy, this.nullSafety)
- : env = TypeEnvironment(coreTypes, hierarchy),
- factorySpecializer = FactorySpecializer(coreTypes, hierarchy);
-
- // ignore: unused_element
- StaticTypeContext get _staticTypeContext =>
- _cachedStaticTypeContext ??= StaticTypeContext(_currentMember, env);
+ _Lowering(CoreTypes coreTypes, ClassHierarchy hierarchy)
+ : factorySpecializer = FactorySpecializer(coreTypes, hierarchy),
+ _lateLowering = LateLowering(coreTypes.index);
@override
- defaultMember(Member node) {
+ TreeNode defaultMember(Member node) {
_currentMember = node;
- _cachedStaticTypeContext = null;
-
- final result = super.defaultMember(node);
-
- _currentMember = null;
- _cachedStaticTypeContext = null;
- return result;
+ return super.defaultMember(node);
}
@override
- visitStaticInvocation(StaticInvocation node) {
+ TreeNode visitStaticInvocation(StaticInvocation node) {
node.transformChildren(this);
return factorySpecializer.transformStaticInvocation(node, _currentMember);
}
+
+ @override
+ TreeNode visitVariableDeclaration(VariableDeclaration node) {
+ node.transformChildren(this);
+ return _lateLowering.transformVariableDeclaration(node, _currentMember);
+ }
+
+ @override
+ TreeNode visitVariableGet(VariableGet node) {
+ node.transformChildren(this);
+ return _lateLowering.transformVariableGet(node, _currentMember);
+ }
+
+ @override
+ TreeNode visitVariableSet(VariableSet node) {
+ node.transformChildren(this);
+ return _lateLowering.transformVariableSet(node, _currentMember);
+ }
}
diff --git a/pkg/front_end/testcases/dart2js/late_locals.dart.strong.expect b/pkg/front_end/testcases/dart2js/late_locals.dart.strong.expect
index fe43261..e90385e 100644
--- a/pkg/front_end/testcases/dart2js/late_locals.dart.strong.expect
+++ b/pkg/front_end/testcases/dart2js/late_locals.dart.strong.expect
@@ -14,61 +14,39 @@
self::testNonNullableInitializedFinalLocal();
}
static method testNullableUninitializedNonFinalLocal() → void {
- lowered core::int? #x = _in::createSentinel<core::int?>();
- function #x#get() → core::int?
- return let final core::int? #t1 = #x in _in::isSentinel(#t1) ?{core::int?} throw new _in::LateError::localNI("x") : #t1{core::int?};
- function #x#set(core::int? #t2) → dynamic
- return #x = #t2;
- #x#set(42){(core::int?) → dynamic};
- core::print(#x#get(){() → core::int?});
+ late core::int? x;
+ x = 42;
+ core::print(x{core::int});
}
static method testNonNullableUninitializedNonFinalLocal() → void {
- lowered core::int? #x;
- function #x#get() → core::int
- return let final core::int? #t3 = #x in #t3 == null ?{core::int} throw new _in::LateError::localNI("x") : #t3{core::int};
- function #x#set(core::int #t4) → dynamic
- return #x = #t4;
- #x#set(42){(core::int) → dynamic};
- core::print(#x#get(){() → core::int});
+ late core::int x;
+ x = 42;
+ core::print(x);
}
static method testNullableUninitializedFinalLocal() → void {
- lowered final core::int? #x = _in::createSentinel<core::int?>();
- function #x#get() → core::int?
- return let final core::int? #t5 = #x in _in::isSentinel(#t5) ?{core::int?} throw new _in::LateError::localNI("x") : #t5{core::int?};
- function #x#set(core::int? #t6) → dynamic
- if(_in::isSentinel(#x))
- return #x = #t6;
- else
- throw new _in::LateError::localAI("x");
- #x#set(42){(core::int?) → dynamic};
- core::print(#x#get(){() → core::int?});
+ late final core::int? x;
+ x = 42;
+ core::print(x{core::int});
}
static method testNonNullableUninitializedFinalLocal() → void {
- lowered final core::int? #x;
- function #x#get() → core::int
- return let final core::int? #t7 = #x in #t7 == null ?{core::int} throw new _in::LateError::localNI("x") : #t7{core::int};
- function #x#set(core::int #t8) → dynamic
- if(#x == null)
- return #x = #t8;
- else
- throw new _in::LateError::localAI("x");
- #x#set(42){(core::int) → dynamic};
- core::print(#x#get(){() → core::int});
+ late final core::int x;
+ x = 42;
+ core::print(x);
}
static method testNullableInitializedNonFinalLocal() → void {
lowered core::int? #x = _in::createSentinel<core::int?>();
function #x#get() → core::int?
- return let final core::int? #t9 = #x in _in::isSentinel(#t9) ?{core::int?} #x = 1.{core::int::unary-}(){() → core::int} : #t9{core::int?};
- function #x#set(core::int? #t10) → dynamic
- return #x = #t10;
+ return let final core::int? #t1 = #x in _in::isSentinel(#t1) ?{core::int?} #x = 1.{core::int::unary-}(){() → core::int} : #t1{core::int?};
+ function #x#set(core::int? #t2) → dynamic
+ return #x = #t2;
core::print(#x#get(){() → core::int?});
#x#set(42){(core::int?) → dynamic};
core::print(#x#get(){() → core::int?});
lowered core::int? #y = _in::createSentinel<core::int?>();
function #y#get() → core::int?
- return let final core::int? #t11 = #y in _in::isSentinel(#t11) ?{core::int?} #y = null : #t11{core::int?};
- function #y#set(core::int? #t12) → dynamic
- return #y = #t12;
+ return let final core::int? #t3 = #y in _in::isSentinel(#t3) ?{core::int?} #y = null : #t3{core::int?};
+ function #y#set(core::int? #t4) → dynamic
+ return #y = #t4;
core::print(#y#get(){() → core::int?});
#y#set(42){(core::int?) → dynamic};
core::print(#y#get(){() → core::int?});
@@ -76,9 +54,9 @@
static method testNonNullableInitializedNonFinalLocal() → void {
lowered core::int? #x;
function #x#get() → core::int
- return let final core::int? #t13 = #x in #t13 == null ?{core::int} #x = 1.{core::int::unary-}(){() → core::int} : #t13{core::int};
- function #x#set(core::int #t14) → dynamic
- return #x = #t14;
+ return let final core::int? #t5 = #x in #t5 == null ?{core::int} #x = 1.{core::int::unary-}(){() → core::int} : #t5{core::int};
+ function #x#set(core::int #t6) → dynamic
+ return #x = #t6;
core::print(#x#get(){() → core::int});
#x#set(42){(core::int) → dynamic};
core::print(#x#get(){() → core::int});
@@ -86,16 +64,16 @@
static method testNullableInitializedFinalLocal() → void {
lowered final core::int? #x = _in::createSentinel<core::int?>();
function #x#get() → core::int?
- return let final core::int? #t15 = #x in _in::isSentinel(#t15) ?{core::int?} let final core::int? #t16 = 1.{core::int::unary-}(){() → core::int} in _in::isSentinel(#x) ?{core::int?} #x = #t16 : throw new _in::LateError::localADI("x") : #t15;
+ return let final core::int? #t7 = #x in _in::isSentinel(#t7) ?{core::int?} let final core::int? #t8 = 1.{core::int::unary-}(){() → core::int} in _in::isSentinel(#x) ?{core::int?} #x = #t8 : throw new _in::LateError::localADI("x") : #t7;
core::print(#x#get(){() → core::int?});
lowered final core::int? #y = _in::createSentinel<core::int?>();
function #y#get() → core::int?
- return let final core::int? #t17 = #y in _in::isSentinel(#t17) ?{core::int?} let final core::int? #t18 = null in _in::isSentinel(#y) ?{core::int?} #y = #t18 : throw new _in::LateError::localADI("y") : #t17;
+ return let final core::int? #t9 = #y in _in::isSentinel(#t9) ?{core::int?} let final core::int? #t10 = null in _in::isSentinel(#y) ?{core::int?} #y = #t10 : throw new _in::LateError::localADI("y") : #t9;
core::print(#y#get(){() → core::int?});
}
static method testNonNullableInitializedFinalLocal() → void {
lowered final core::int? #x;
function #x#get() → core::int
- return let final core::int? #t19 = #x in #t19 == null ?{core::int} let final core::int #t20 = 1.{core::int::unary-}(){() → core::int} in #x == null ?{core::int} #x = #t20 : throw new _in::LateError::localADI("x") : #t19{core::int};
+ return let final core::int? #t11 = #x in #t11 == null ?{core::int} let final core::int #t12 = 1.{core::int::unary-}(){() → core::int} in #x == null ?{core::int} #x = #t12 : throw new _in::LateError::localADI("x") : #t11{core::int};
core::print(#x#get(){() → core::int});
}
diff --git a/pkg/front_end/testcases/dart2js/late_locals.dart.strong.transformed.expect b/pkg/front_end/testcases/dart2js/late_locals.dart.strong.transformed.expect
index ac86677..54ef534 100644
--- a/pkg/front_end/testcases/dart2js/late_locals.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/dart2js/late_locals.dart.strong.transformed.expect
@@ -1,5 +1,6 @@
library /*isNonNullableByDefault*/;
import self as self;
+import "dart:_late_helper" as _la;
import "dart:core" as core;
import "dart:_internal" as _in;
@@ -14,61 +15,39 @@
self::testNonNullableInitializedFinalLocal();
}
static method testNullableUninitializedNonFinalLocal() → void {
- lowered core::int? #x = _in::createSentinel<core::int?>();
- function #x#get() → core::int?
- return let final core::int? #t1 = #x in _in::isSentinel(#t1) ?{core::int?} throw new _in::LateError::localNI("x") : #t1{core::int?};
- function #x#set(core::int? #t2) → dynamic
- return #x = #t2;
- #x#set(42){(core::int?) → dynamic};
- core::print(#x#get(){() → core::int?});
+ final _la::_Cell x = new _la::_Cell::•();
+ x.{_la::_Cell::value} = 42;
+ core::print(x.{_la::_Cell::readLocal}<core::int>(){() → core::int});
}
static method testNonNullableUninitializedNonFinalLocal() → void {
- lowered core::int? #x;
- function #x#get() → core::int
- return let final core::int? #t3 = #x in #t3 == null ?{core::int} throw new _in::LateError::localNI("x") : #t3{core::int};
- function #x#set(core::int #t4) → dynamic
- return #x = #t4;
- #x#set(42){(core::int) → dynamic};
- core::print(#x#get(){() → core::int});
+ final _la::_Cell x = new _la::_Cell::•();
+ x.{_la::_Cell::value} = 42;
+ core::print(x.{_la::_Cell::readLocal}<core::int>(){() → core::int});
}
static method testNullableUninitializedFinalLocal() → void {
- lowered final core::int? #x = _in::createSentinel<core::int?>();
- function #x#get() → core::int?
- return let final core::int? #t5 = #x in _in::isSentinel(#t5) ?{core::int?} throw new _in::LateError::localNI("x") : #t5{core::int?};
- function #x#set(core::int? #t6) → dynamic
- if(_in::isSentinel(#x))
- return #x = #t6;
- else
- throw new _in::LateError::localAI("x");
- #x#set(42){(core::int?) → dynamic};
- core::print(#x#get(){() → core::int?});
+ final _la::_Cell x = new _la::_Cell::•();
+ x.{_la::_Cell::finalLocalValue} = 42;
+ core::print(x.{_la::_Cell::readLocal}<core::int>(){() → core::int});
}
static method testNonNullableUninitializedFinalLocal() → void {
- lowered final core::int? #x;
- function #x#get() → core::int
- return let final core::int? #t7 = #x in #t7 == null ?{core::int} throw new _in::LateError::localNI("x") : #t7{core::int};
- function #x#set(core::int #t8) → dynamic
- if(#x == null)
- return #x = #t8;
- else
- throw new _in::LateError::localAI("x");
- #x#set(42){(core::int) → dynamic};
- core::print(#x#get(){() → core::int});
+ final _la::_Cell x = new _la::_Cell::•();
+ x.{_la::_Cell::finalLocalValue} = 42;
+ core::print(x.{_la::_Cell::readLocal}<core::int>(){() → core::int});
}
static method testNullableInitializedNonFinalLocal() → void {
lowered core::int? #x = _in::createSentinel<core::int?>();
function #x#get() → core::int?
- return let final core::int? #t9 = #x in _in::isSentinel(#t9) ?{core::int?} #x = 1.{core::int::unary-}(){() → core::int} : #t9{core::int?};
- function #x#set(core::int? #t10) → dynamic
- return #x = #t10;
+ return let final core::int? #t1 = #x in _in::isSentinel(#t1) ?{core::int?} #x = 1.{core::int::unary-}(){() → core::int} : #t1{core::int?};
+ function #x#set(core::int? #t2) → dynamic
+ return #x = #t2;
core::print(#x#get(){() → core::int?});
#x#set(42){(core::int?) → dynamic};
core::print(#x#get(){() → core::int?});
lowered core::int? #y = _in::createSentinel<core::int?>();
function #y#get() → core::int?
- return let final core::int? #t11 = #y in _in::isSentinel(#t11) ?{core::int?} #y = null : #t11{core::int?};
- function #y#set(core::int? #t12) → dynamic
- return #y = #t12;
+ return let final core::int? #t3 = #y in _in::isSentinel(#t3) ?{core::int?} #y = null : #t3{core::int?};
+ function #y#set(core::int? #t4) → dynamic
+ return #y = #t4;
core::print(#y#get(){() → core::int?});
#y#set(42){(core::int?) → dynamic};
core::print(#y#get(){() → core::int?});
@@ -76,9 +55,9 @@
static method testNonNullableInitializedNonFinalLocal() → void {
lowered core::int? #x;
function #x#get() → core::int
- return let final core::int? #t13 = #x in #t13 == null ?{core::int} #x = 1.{core::int::unary-}(){() → core::int} : #t13{core::int};
- function #x#set(core::int #t14) → dynamic
- return #x = #t14;
+ return let final core::int? #t5 = #x in #t5 == null ?{core::int} #x = 1.{core::int::unary-}(){() → core::int} : #t5{core::int};
+ function #x#set(core::int #t6) → dynamic
+ return #x = #t6;
core::print(#x#get(){() → core::int});
#x#set(42){(core::int) → dynamic};
core::print(#x#get(){() → core::int});
@@ -86,17 +65,17 @@
static method testNullableInitializedFinalLocal() → void {
lowered final core::int? #x = _in::createSentinel<core::int?>();
function #x#get() → core::int?
- return let final core::int? #t15 = #x in _in::isSentinel(#t15) ?{core::int?} let final core::int? #t16 = 1.{core::int::unary-}(){() → core::int} in _in::isSentinel(#x) ?{core::int?} #x = #t16 : throw new _in::LateError::localADI("x") : #t15;
+ return let final core::int? #t7 = #x in _in::isSentinel(#t7) ?{core::int?} let final core::int? #t8 = 1.{core::int::unary-}(){() → core::int} in _in::isSentinel(#x) ?{core::int?} #x = #t8 : throw new _in::LateError::localADI("x") : #t7;
core::print(#x#get(){() → core::int?});
lowered final core::int? #y = _in::createSentinel<core::int?>();
function #y#get() → core::int?
- return let final core::int? #t17 = #y in _in::isSentinel(#t17) ?{core::int?} let final core::int? #t18 = null in _in::isSentinel(#y) ?{core::int?} #y = #t18 : throw new _in::LateError::localADI("y") : #t17;
+ return let final core::int? #t9 = #y in _in::isSentinel(#t9) ?{core::int?} let final core::int? #t10 = null in _in::isSentinel(#y) ?{core::int?} #y = #t10 : throw new _in::LateError::localADI("y") : #t9;
core::print(#y#get(){() → core::int?});
}
static method testNonNullableInitializedFinalLocal() → void {
lowered final core::int? #x;
function #x#get() → core::int
- return let final core::int? #t19 = #x in #t19 == null ?{core::int} let final core::int #t20 = 1.{core::int::unary-}(){() → core::int} in #x == null ?{core::int} #x = #t20 : throw new _in::LateError::localADI("x") : #t19{core::int};
+ return let final core::int? #t11 = #x in #t11 == null ?{core::int} let final core::int #t12 = 1.{core::int::unary-}(){() → core::int} in #x == null ?{core::int} #x = #t12 : throw new _in::LateError::localADI("x") : #t11{core::int};
core::print(#x#get(){() → core::int});
}
@@ -109,4 +88,4 @@
Evaluated: VariableGet @ org-dartlang-testcase:///late_locals.dart:63:19 -> NullConstant(null)
Evaluated: InstanceInvocation @ org-dartlang-testcase:///late_locals.dart:68:22 -> DoubleConstant(-1.0)
Evaluated: VariableGet @ org-dartlang-testcase:///late_locals.dart:68:18 -> DoubleConstant(-1.0)
-Extra constant evaluation: evaluated: 168, effectively constant: 7
+Extra constant evaluation: evaluated: 130, effectively constant: 7
diff --git a/pkg/front_end/testcases/dart2js/late_locals.dart.weak.expect b/pkg/front_end/testcases/dart2js/late_locals.dart.weak.expect
index 26979f0..0e44ebb 100644
--- a/pkg/front_end/testcases/dart2js/late_locals.dart.weak.expect
+++ b/pkg/front_end/testcases/dart2js/late_locals.dart.weak.expect
@@ -14,61 +14,39 @@
self::testNonNullableInitializedFinalLocal();
}
static method testNullableUninitializedNonFinalLocal() → void {
- lowered core::int? #x = _in::createSentinel<core::int?>();
- function #x#get() → core::int?
- return let final core::int? #t1 = #x in _in::isSentinel(#t1) ?{core::int?} throw new _in::LateError::localNI("x") : #t1{core::int?};
- function #x#set(core::int? #t2) → dynamic
- return #x = #t2;
- #x#set(42){(core::int?) → dynamic};
- core::print(#x#get(){() → core::int?});
+ late core::int? x;
+ x = 42;
+ core::print(x{core::int});
}
static method testNonNullableUninitializedNonFinalLocal() → void {
- lowered core::int? #x = _in::createSentinel<core::int>();
- function #x#get() → core::int
- return let final core::int? #t3 = #x in _in::isSentinel(#t3) ?{core::int} throw new _in::LateError::localNI("x") : #t3{core::int};
- function #x#set(core::int #t4) → dynamic
- return #x = #t4;
- #x#set(42){(core::int) → dynamic};
- core::print(#x#get(){() → core::int});
+ late core::int x;
+ x = 42;
+ core::print(x);
}
static method testNullableUninitializedFinalLocal() → void {
- lowered final core::int? #x = _in::createSentinel<core::int?>();
- function #x#get() → core::int?
- return let final core::int? #t5 = #x in _in::isSentinel(#t5) ?{core::int?} throw new _in::LateError::localNI("x") : #t5{core::int?};
- function #x#set(core::int? #t6) → dynamic
- if(_in::isSentinel(#x))
- return #x = #t6;
- else
- throw new _in::LateError::localAI("x");
- #x#set(42){(core::int?) → dynamic};
- core::print(#x#get(){() → core::int?});
+ late final core::int? x;
+ x = 42;
+ core::print(x{core::int});
}
static method testNonNullableUninitializedFinalLocal() → void {
- lowered final core::int? #x = _in::createSentinel<core::int>();
- function #x#get() → core::int
- return let final core::int? #t7 = #x in _in::isSentinel(#t7) ?{core::int} throw new _in::LateError::localNI("x") : #t7{core::int};
- function #x#set(core::int #t8) → dynamic
- if(_in::isSentinel(#x))
- return #x = #t8;
- else
- throw new _in::LateError::localAI("x");
- #x#set(42){(core::int) → dynamic};
- core::print(#x#get(){() → core::int});
+ late final core::int x;
+ x = 42;
+ core::print(x);
}
static method testNullableInitializedNonFinalLocal() → void {
lowered core::int? #x = _in::createSentinel<core::int?>();
function #x#get() → core::int?
- return let final core::int? #t9 = #x in _in::isSentinel(#t9) ?{core::int?} #x = 1.{core::int::unary-}(){() → core::int} : #t9{core::int?};
- function #x#set(core::int? #t10) → dynamic
- return #x = #t10;
+ return let final core::int? #t1 = #x in _in::isSentinel(#t1) ?{core::int?} #x = 1.{core::int::unary-}(){() → core::int} : #t1{core::int?};
+ function #x#set(core::int? #t2) → dynamic
+ return #x = #t2;
core::print(#x#get(){() → core::int?});
#x#set(42){(core::int?) → dynamic};
core::print(#x#get(){() → core::int?});
lowered core::int? #y = _in::createSentinel<core::int?>();
function #y#get() → core::int?
- return let final core::int? #t11 = #y in _in::isSentinel(#t11) ?{core::int?} #y = null : #t11{core::int?};
- function #y#set(core::int? #t12) → dynamic
- return #y = #t12;
+ return let final core::int? #t3 = #y in _in::isSentinel(#t3) ?{core::int?} #y = null : #t3{core::int?};
+ function #y#set(core::int? #t4) → dynamic
+ return #y = #t4;
core::print(#y#get(){() → core::int?});
#y#set(42){(core::int?) → dynamic};
core::print(#y#get(){() → core::int?});
@@ -76,9 +54,9 @@
static method testNonNullableInitializedNonFinalLocal() → void {
lowered core::int? #x = _in::createSentinel<core::int>();
function #x#get() → core::int
- return let final core::int? #t13 = #x in _in::isSentinel(#t13) ?{core::int} #x = 1.{core::int::unary-}(){() → core::int} : #t13{core::int};
- function #x#set(core::int #t14) → dynamic
- return #x = #t14;
+ return let final core::int? #t5 = #x in _in::isSentinel(#t5) ?{core::int} #x = 1.{core::int::unary-}(){() → core::int} : #t5{core::int};
+ function #x#set(core::int #t6) → dynamic
+ return #x = #t6;
core::print(#x#get(){() → core::int});
#x#set(42){(core::int) → dynamic};
core::print(#x#get(){() → core::int});
@@ -86,16 +64,16 @@
static method testNullableInitializedFinalLocal() → void {
lowered final core::int? #x = _in::createSentinel<core::int?>();
function #x#get() → core::int?
- return let final core::int? #t15 = #x in _in::isSentinel(#t15) ?{core::int?} let final core::int? #t16 = 1.{core::int::unary-}(){() → core::int} in _in::isSentinel(#x) ?{core::int?} #x = #t16 : throw new _in::LateError::localADI("x") : #t15;
+ return let final core::int? #t7 = #x in _in::isSentinel(#t7) ?{core::int?} let final core::int? #t8 = 1.{core::int::unary-}(){() → core::int} in _in::isSentinel(#x) ?{core::int?} #x = #t8 : throw new _in::LateError::localADI("x") : #t7;
core::print(#x#get(){() → core::int?});
lowered final core::int? #y = _in::createSentinel<core::int?>();
function #y#get() → core::int?
- return let final core::int? #t17 = #y in _in::isSentinel(#t17) ?{core::int?} let final core::int? #t18 = null in _in::isSentinel(#y) ?{core::int?} #y = #t18 : throw new _in::LateError::localADI("y") : #t17;
+ return let final core::int? #t9 = #y in _in::isSentinel(#t9) ?{core::int?} let final core::int? #t10 = null in _in::isSentinel(#y) ?{core::int?} #y = #t10 : throw new _in::LateError::localADI("y") : #t9;
core::print(#y#get(){() → core::int?});
}
static method testNonNullableInitializedFinalLocal() → void {
lowered final core::int? #x = _in::createSentinel<core::int>();
function #x#get() → core::int
- return let final core::int #t19 = #x in _in::isSentinel(#t19) ?{core::int} let final core::int #t20 = 1.{core::int::unary-}(){() → core::int} in _in::isSentinel(#x) ?{core::int} #x = #t20 : throw new _in::LateError::localADI("x") : #t19;
+ return let final core::int #t11 = #x in _in::isSentinel(#t11) ?{core::int} let final core::int #t12 = 1.{core::int::unary-}(){() → core::int} in _in::isSentinel(#x) ?{core::int} #x = #t12 : throw new _in::LateError::localADI("x") : #t11;
core::print(#x#get(){() → core::int});
}
diff --git a/pkg/front_end/testcases/dart2js/late_locals.dart.weak.transformed.expect b/pkg/front_end/testcases/dart2js/late_locals.dart.weak.transformed.expect
index 640d4d4..0d8bf0e 100644
--- a/pkg/front_end/testcases/dart2js/late_locals.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/dart2js/late_locals.dart.weak.transformed.expect
@@ -1,5 +1,6 @@
library /*isNonNullableByDefault*/;
import self as self;
+import "dart:_late_helper" as _la;
import "dart:core" as core;
import "dart:_internal" as _in;
@@ -14,61 +15,39 @@
self::testNonNullableInitializedFinalLocal();
}
static method testNullableUninitializedNonFinalLocal() → void {
- lowered core::int? #x = _in::createSentinel<core::int?>();
- function #x#get() → core::int?
- return let final core::int? #t1 = #x in _in::isSentinel(#t1) ?{core::int?} throw new _in::LateError::localNI("x") : #t1{core::int?};
- function #x#set(core::int? #t2) → dynamic
- return #x = #t2;
- #x#set(42){(core::int?) → dynamic};
- core::print(#x#get(){() → core::int?});
+ final _la::_Cell x = new _la::_Cell::•();
+ x.{_la::_Cell::value} = 42;
+ core::print(x.{_la::_Cell::readLocal}<core::int>(){() → core::int});
}
static method testNonNullableUninitializedNonFinalLocal() → void {
- lowered core::int? #x = _in::createSentinel<core::int>();
- function #x#get() → core::int
- return let final core::int? #t3 = #x in _in::isSentinel(#t3) ?{core::int} throw new _in::LateError::localNI("x") : #t3{core::int};
- function #x#set(core::int #t4) → dynamic
- return #x = #t4;
- #x#set(42){(core::int) → dynamic};
- core::print(#x#get(){() → core::int});
+ final _la::_Cell x = new _la::_Cell::•();
+ x.{_la::_Cell::value} = 42;
+ core::print(x.{_la::_Cell::readLocal}<core::int>(){() → core::int});
}
static method testNullableUninitializedFinalLocal() → void {
- lowered final core::int? #x = _in::createSentinel<core::int?>();
- function #x#get() → core::int?
- return let final core::int? #t5 = #x in _in::isSentinel(#t5) ?{core::int?} throw new _in::LateError::localNI("x") : #t5{core::int?};
- function #x#set(core::int? #t6) → dynamic
- if(_in::isSentinel(#x))
- return #x = #t6;
- else
- throw new _in::LateError::localAI("x");
- #x#set(42){(core::int?) → dynamic};
- core::print(#x#get(){() → core::int?});
+ final _la::_Cell x = new _la::_Cell::•();
+ x.{_la::_Cell::finalLocalValue} = 42;
+ core::print(x.{_la::_Cell::readLocal}<core::int>(){() → core::int});
}
static method testNonNullableUninitializedFinalLocal() → void {
- lowered final core::int? #x = _in::createSentinel<core::int>();
- function #x#get() → core::int
- return let final core::int? #t7 = #x in _in::isSentinel(#t7) ?{core::int} throw new _in::LateError::localNI("x") : #t7{core::int};
- function #x#set(core::int #t8) → dynamic
- if(_in::isSentinel(#x))
- return #x = #t8;
- else
- throw new _in::LateError::localAI("x");
- #x#set(42){(core::int) → dynamic};
- core::print(#x#get(){() → core::int});
+ final _la::_Cell x = new _la::_Cell::•();
+ x.{_la::_Cell::finalLocalValue} = 42;
+ core::print(x.{_la::_Cell::readLocal}<core::int>(){() → core::int});
}
static method testNullableInitializedNonFinalLocal() → void {
lowered core::int? #x = _in::createSentinel<core::int?>();
function #x#get() → core::int?
- return let final core::int? #t9 = #x in _in::isSentinel(#t9) ?{core::int?} #x = 1.{core::int::unary-}(){() → core::int} : #t9{core::int?};
- function #x#set(core::int? #t10) → dynamic
- return #x = #t10;
+ return let final core::int? #t1 = #x in _in::isSentinel(#t1) ?{core::int?} #x = 1.{core::int::unary-}(){() → core::int} : #t1{core::int?};
+ function #x#set(core::int? #t2) → dynamic
+ return #x = #t2;
core::print(#x#get(){() → core::int?});
#x#set(42){(core::int?) → dynamic};
core::print(#x#get(){() → core::int?});
lowered core::int? #y = _in::createSentinel<core::int?>();
function #y#get() → core::int?
- return let final core::int? #t11 = #y in _in::isSentinel(#t11) ?{core::int?} #y = null : #t11{core::int?};
- function #y#set(core::int? #t12) → dynamic
- return #y = #t12;
+ return let final core::int? #t3 = #y in _in::isSentinel(#t3) ?{core::int?} #y = null : #t3{core::int?};
+ function #y#set(core::int? #t4) → dynamic
+ return #y = #t4;
core::print(#y#get(){() → core::int?});
#y#set(42){(core::int?) → dynamic};
core::print(#y#get(){() → core::int?});
@@ -76,9 +55,9 @@
static method testNonNullableInitializedNonFinalLocal() → void {
lowered core::int? #x = _in::createSentinel<core::int>();
function #x#get() → core::int
- return let final core::int? #t13 = #x in _in::isSentinel(#t13) ?{core::int} #x = 1.{core::int::unary-}(){() → core::int} : #t13{core::int};
- function #x#set(core::int #t14) → dynamic
- return #x = #t14;
+ return let final core::int? #t5 = #x in _in::isSentinel(#t5) ?{core::int} #x = 1.{core::int::unary-}(){() → core::int} : #t5{core::int};
+ function #x#set(core::int #t6) → dynamic
+ return #x = #t6;
core::print(#x#get(){() → core::int});
#x#set(42){(core::int) → dynamic};
core::print(#x#get(){() → core::int});
@@ -86,17 +65,17 @@
static method testNullableInitializedFinalLocal() → void {
lowered final core::int? #x = _in::createSentinel<core::int?>();
function #x#get() → core::int?
- return let final core::int? #t15 = #x in _in::isSentinel(#t15) ?{core::int?} let final core::int? #t16 = 1.{core::int::unary-}(){() → core::int} in _in::isSentinel(#x) ?{core::int?} #x = #t16 : throw new _in::LateError::localADI("x") : #t15;
+ return let final core::int? #t7 = #x in _in::isSentinel(#t7) ?{core::int?} let final core::int? #t8 = 1.{core::int::unary-}(){() → core::int} in _in::isSentinel(#x) ?{core::int?} #x = #t8 : throw new _in::LateError::localADI("x") : #t7;
core::print(#x#get(){() → core::int?});
lowered final core::int? #y = _in::createSentinel<core::int?>();
function #y#get() → core::int?
- return let final core::int? #t17 = #y in _in::isSentinel(#t17) ?{core::int?} let final core::int? #t18 = null in _in::isSentinel(#y) ?{core::int?} #y = #t18 : throw new _in::LateError::localADI("y") : #t17;
+ return let final core::int? #t9 = #y in _in::isSentinel(#t9) ?{core::int?} let final core::int? #t10 = null in _in::isSentinel(#y) ?{core::int?} #y = #t10 : throw new _in::LateError::localADI("y") : #t9;
core::print(#y#get(){() → core::int?});
}
static method testNonNullableInitializedFinalLocal() → void {
lowered final core::int? #x = _in::createSentinel<core::int>();
function #x#get() → core::int
- return let final core::int #t19 = #x in _in::isSentinel(#t19) ?{core::int} let final core::int #t20 = 1.{core::int::unary-}(){() → core::int} in _in::isSentinel(#x) ?{core::int} #x = #t20 : throw new _in::LateError::localADI("x") : #t19;
+ return let final core::int #t11 = #x in _in::isSentinel(#t11) ?{core::int} let final core::int #t12 = 1.{core::int::unary-}(){() → core::int} in _in::isSentinel(#x) ?{core::int} #x = #t12 : throw new _in::LateError::localADI("x") : #t11;
core::print(#x#get(){() → core::int});
}
@@ -109,4 +88,4 @@
Evaluated: VariableGet @ org-dartlang-testcase:///late_locals.dart:63:19 -> NullConstant(null)
Evaluated: InstanceInvocation @ org-dartlang-testcase:///late_locals.dart:68:22 -> DoubleConstant(-1.0)
Evaluated: VariableGet @ org-dartlang-testcase:///late_locals.dart:68:18 -> DoubleConstant(-1.0)
-Extra constant evaluation: evaluated: 172, effectively constant: 7
+Extra constant evaluation: evaluated: 132, effectively constant: 7
diff --git a/tests/language/nnbd/late/chained_assignment_test.dart b/tests/language/nnbd/late/chained_assignment_test.dart
new file mode 100644
index 0000000..1ca1c65
--- /dev/null
+++ b/tests/language/nnbd/late/chained_assignment_test.dart
@@ -0,0 +1,104 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:expect/expect.dart';
+
+class Foo {
+ int x = 0;
+
+ int _y = 0;
+ int get y => _y;
+ void set y(int y) => _y = y;
+
+ static late int z;
+ late int w;
+}
+
+void main() {
+ final foo = Foo();
+ int a = 0;
+ late int b;
+ late int c;
+ late int d;
+
+ b = a = 1;
+ Expect.equals(a, 1);
+ Expect.equals(b, 1);
+
+ a = b = 2;
+ Expect.equals(a, 2);
+ Expect.equals(b, 2);
+
+ b = foo.x = 3;
+ Expect.equals(foo.x, 3);
+ Expect.equals(b, 3);
+
+ foo.x = b = 4;
+ Expect.equals(foo.x, 4);
+ Expect.equals(b, 4);
+
+ b = foo.y = 5;
+ Expect.equals(foo.y, 5);
+ Expect.equals(b, 5);
+
+ foo.y = b = 6;
+ Expect.equals(foo.y, 6);
+ Expect.equals(b, 6);
+
+ b = c = 7;
+ Expect.equals(b, 7);
+ Expect.equals(c, 7);
+
+ d = b = 8;
+ Expect.equals(b, 8);
+ Expect.equals(d, 8);
+
+ Foo.z = a = 9;
+ Expect.equals(a, 9);
+ Expect.equals(Foo.z, 9);
+
+ a = Foo.z = 10;
+ Expect.equals(a, 10);
+ Expect.equals(Foo.z, 10);
+
+ Foo.z = foo.x = 11;
+ Expect.equals(foo.x, 11);
+ Expect.equals(Foo.z, 11);
+
+ foo.x = Foo.z = 12;
+ Expect.equals(foo.x, 12);
+ Expect.equals(Foo.z, 12);
+
+ Foo.z = foo.y = 13;
+ Expect.equals(foo.y, 13);
+ Expect.equals(Foo.z, 13);
+
+ foo.y = Foo.z = 14;
+ Expect.equals(foo.y, 14);
+ Expect.equals(Foo.z, 14);
+
+ foo.w = a = 15;
+ Expect.equals(a, 15);
+ Expect.equals(foo.w, 15);
+
+ a = foo.w = 16;
+ Expect.equals(a, 16);
+ Expect.equals(foo.w, 16);
+
+ foo.w = foo.x = 17;
+ Expect.equals(foo.x, 17);
+ Expect.equals(foo.w, 17);
+
+ foo.x = foo.w = 18;
+ Expect.equals(foo.x, 18);
+ Expect.equals(foo.w, 18);
+
+ foo.w = foo.y = 19;
+ Expect.equals(foo.y, 19);
+ Expect.equals(foo.w, 19);
+
+ foo.y = foo.w = 20;
+ Expect.equals(foo.y, 20);
+ Expect.equals(foo.w, 20);
+}