From 8f832e2c6b7d96b05d83a25fb93e47f43e18f930 Mon Sep 17 00:00:00 2001 From: Mayank Patke Date: Wed, 7 Apr 2021 19:43:07 +0000 Subject: [PATCH] [dart2js] Implement Cell lowering for uninitialized locals. Change-Id: I7233e1484cc19e5f24d215bfd2c1e34c8f12961b Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/193588 Commit-Queue: Mayank Patke Reviewed-by: Stephen Adams Reviewed-by: Johnni Winther --- .../lib/src/kernel/dart2js_target.dart | 7 +- .../kernel/transformations/late_lowering.dart | 112 ++++++++++++++++++ .../src/kernel/transformations/lowering.dart | 53 +++++---- .../dart2js/late_locals.dart.strong.expect | 70 ++++------- ...late_locals.dart.strong.transformed.expect | 73 ++++-------- .../dart2js/late_locals.dart.weak.expect | 70 ++++------- .../late_locals.dart.weak.transformed.expect | 73 ++++-------- .../nnbd/late/chained_assignment_test.dart | 104 ++++++++++++++++ 8 files changed, 346 insertions(+), 216 deletions(-) create mode 100644 pkg/compiler/lib/src/kernel/transformations/late_lowering.dart create mode 100644 tests/language/nnbd/late/chained_assignment_test.dart diff --git a/pkg/compiler/lib/src/kernel/dart2js_target.dart b/pkg/compiler/lib/src/kernel/dart2js_target.dart index 878f2be79e0..d41438e017d 100644 --- a/pkg/compiler/lib/src/kernel/dart2js_target.dart +++ b/pkg/compiler/lib/src/kernel/dart2js_target.dart @@ -62,10 +62,6 @@ int _foldLateLowerings(List lowerings) => /// Late lowerings which the frontend performs for dart2js. const List _allEnabledLateLowerings = [ - LateLowering.nullableUninitializedNonFinalLocal, - LateLowering.nonNullableUninitializedNonFinalLocal, - LateLowering.nullableUninitializedFinalLocal, - LateLowering.nonNullableUninitializedFinalLocal, LateLowering.nullableInitializedNonFinalLocal, LateLowering.nonNullableInitializedNonFinalLocal, LateLowering.nullableInitializedFinalLocal, @@ -167,8 +163,7 @@ class Dart2jsTarget extends Target { _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 00000000000..cf5c62cba75 --- /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 _readLocalTypeParameters; + FunctionType _readLocalTypeWithoutTypeParameters; + + final Procedure _setValue; + final Procedure _setFinalLocalValue; + + final Map _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 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 72c4f153349..915c5fb46ad 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 libraries, CoreTypes coreTypes, - ClassHierarchy hierarchy, bool nullSafety) { - final transformer = _Lowering(coreTypes, hierarchy, nullSafety); +void transformLibraries( + List 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 fe4326150b7..e90385e5bfb 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 @@ static method main() → void { self::testNonNullableInitializedFinalLocal(); } static method testNullableUninitializedNonFinalLocal() → void { - lowered core::int? #x = _in::createSentinel(); - 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(); - 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(); 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(); 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 testNullableInitializedNonFinalLocal() → void { 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 testNonNullableInitializedNonFinalLocal() → void { static method testNullableInitializedFinalLocal() → void { lowered final core::int? #x = _in::createSentinel(); 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(); 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 ac86677154e..54ef5349763 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 @@ static method main() → void { self::testNonNullableInitializedFinalLocal(); } static method testNullableUninitializedNonFinalLocal() → void { - lowered core::int? #x = _in::createSentinel(); - 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}); } 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}); } static method testNullableUninitializedFinalLocal() → void { - lowered final core::int? #x = _in::createSentinel(); - 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}); } 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}); } static method testNullableInitializedNonFinalLocal() → void { lowered core::int? #x = _in::createSentinel(); 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(); 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 testNullableInitializedNonFinalLocal() → void { 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 testNonNullableInitializedNonFinalLocal() → void { static method testNullableInitializedFinalLocal() → void { lowered final core::int? #x = _in::createSentinel(); 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(); 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:60:19 -> Doub 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 26979f0d71a..0e44ebbc98e 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 @@ static method main() → void { self::testNonNullableInitializedFinalLocal(); } static method testNullableUninitializedNonFinalLocal() → void { - lowered core::int? #x = _in::createSentinel(); - 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(); - 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(); - 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(); - 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(); 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(); 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 testNullableInitializedNonFinalLocal() → void { static method testNonNullableInitializedNonFinalLocal() → void { lowered core::int? #x = _in::createSentinel(); 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 testNonNullableInitializedNonFinalLocal() → void { static method testNullableInitializedFinalLocal() → void { lowered final core::int? #x = _in::createSentinel(); 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(); 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(); 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 640d4d4e2b6..0d8bf0e58ad 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 @@ static method main() → void { self::testNonNullableInitializedFinalLocal(); } static method testNullableUninitializedNonFinalLocal() → void { - lowered core::int? #x = _in::createSentinel(); - 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}); } static method testNonNullableUninitializedNonFinalLocal() → void { - lowered core::int? #x = _in::createSentinel(); - 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}); } static method testNullableUninitializedFinalLocal() → void { - lowered final core::int? #x = _in::createSentinel(); - 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}); } static method testNonNullableUninitializedFinalLocal() → void { - lowered final core::int? #x = _in::createSentinel(); - 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}); } static method testNullableInitializedNonFinalLocal() → void { lowered core::int? #x = _in::createSentinel(); 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(); 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 testNullableInitializedNonFinalLocal() → void { static method testNonNullableInitializedNonFinalLocal() → void { lowered core::int? #x = _in::createSentinel(); 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 testNonNullableInitializedNonFinalLocal() → void { static method testNullableInitializedFinalLocal() → void { lowered final core::int? #x = _in::createSentinel(); 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(); 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(); 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:60:19 -> Doub 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 00000000000..1ca1c651107 --- /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); +}