Version 2.18.0-231.0.dev
Merge commit 'ff03ff9f77b327cfd0e97ff88da3654e960191a5' into 'dev'
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5139f56..34479e4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -196,7 +196,7 @@
* `dart pub outdated` now shows which of your dependencies are discontinued.
* `dart pub publish` will now list all the files it is about to publish.
-## 2.17.4 - 2022-06-20
+## 2.17.5 - 2022-06-22
This is a patch release that fixes:
diff --git a/pkg/compiler/lib/src/js_backend/annotations.dart b/pkg/compiler/lib/src/js_backend/annotations.dart
index 7fc51bc..1736e2a 100644
--- a/pkg/compiler/lib/src/js_backend/annotations.dart
+++ b/pkg/compiler/lib/src/js_backend/annotations.dart
@@ -114,6 +114,18 @@
17, 'index-bounds:check',
forFunctionsOnly: false, internalOnly: false);
+ /// Annotation for a `late` field to omit the checks on the late field. The
+ /// annotation is not restricted to a field since it is copied from the field
+ /// to the getter and setter.
+ // TODO(45682): Make this annotation apply to local and static late variables.
+ static const PragmaAnnotation lateTrust = PragmaAnnotation(18, 'late:trust');
+
+ /// Annotation for a `late` field to perform the checks on the late field. The
+ /// annotation is not restricted to a field since it is copied from the field
+ /// to the getter and setter.
+ // TODO(45682): Make this annotation apply to local and static late variables.
+ static const PragmaAnnotation lateCheck = PragmaAnnotation(19, 'late:check');
+
static const List<PragmaAnnotation> values = [
noInline,
tryInline,
@@ -133,6 +145,8 @@
downcastCheck,
indexBoundsTrust,
indexBoundsCheck,
+ lateTrust,
+ lateCheck,
];
static const Map<PragmaAnnotation, Set<PragmaAnnotation>> implies = {
@@ -150,6 +164,8 @@
downcastCheck: {downcastTrust},
asTrust: {asCheck},
asCheck: {asTrust},
+ lateTrust: {lateCheck},
+ lateCheck: {lateTrust},
};
static const Map<PragmaAnnotation, Set<PragmaAnnotation>> requires = {
noThrows: {noInline},
@@ -317,12 +333,12 @@
/// annotation.
void forEachNoSideEffects(void f(FunctionEntity function));
- /// What should the compiler do with parameter type assertions in [member].
+ /// What the compiler should do with parameter type assertions in [member].
///
/// If [member] is `null`, the default policy is returned.
CheckPolicy getParameterCheckPolicy(MemberEntity? member);
- /// What should the compiler do with implicit downcasts in [member].
+ /// What the compiler should do with implicit downcasts in [member].
///
/// If [member] is `null`, the default policy is returned.
CheckPolicy getImplicitDowncastCheckPolicy(MemberEntity? member);
@@ -334,16 +350,20 @@
/// If [member] is `null`, the default policy is returned.
CheckPolicy getConditionCheckPolicy(MemberEntity? member);
- /// Whether should the compiler do with explicit casts in [member].
+ /// What the compiler should do with explicit casts in [member].
///
/// If [member] is `null`, the default policy is returned.
CheckPolicy getExplicitCastCheckPolicy(MemberEntity? member);
- /// What should the compiler do with index bounds checks `[]`, `[]=` and
+ /// What the compiler should do with index bounds checks `[]`, `[]=` and
/// `removeLast()` operations in the body of [member].
///
/// If [member] is `null`, the default policy is returned.
CheckPolicy getIndexBoundsCheckPolicy(MemberEntity? member);
+
+ /// What the compiler should do with late field checks in the body of
+ /// [member]. [member] is usually the getter or setter for a late field.
+ CheckPolicy getLateVariableCheckPolicy(MemberEntity member);
}
class AnnotationsDataImpl implements AnnotationsData {
@@ -356,6 +376,7 @@
final CheckPolicy _defaultConditionCheckPolicy;
final CheckPolicy _defaultExplicitCastCheckPolicy;
final CheckPolicy _defaultIndexBoundsCheckPolicy;
+ final CheckPolicy _defaultLateVariableCheckPolicy;
final bool _defaultDisableInlining;
final Map<MemberEntity, EnumSet<PragmaAnnotation>> pragmaAnnotations;
@@ -368,6 +389,7 @@
options.defaultExplicitCastCheckPolicy,
this._defaultIndexBoundsCheckPolicy =
options.defaultIndexBoundsCheckPolicy,
+ this._defaultLateVariableCheckPolicy = CheckPolicy.checked,
this._defaultDisableInlining = options.disableInlining;
factory AnnotationsDataImpl.readFromDataSource(
@@ -554,6 +576,20 @@
}
return _defaultIndexBoundsCheckPolicy;
}
+
+ @override
+ CheckPolicy getLateVariableCheckPolicy(MemberEntity member) {
+ EnumSet<PragmaAnnotation>? annotations = pragmaAnnotations[member];
+ if (annotations != null) {
+ if (annotations.contains(PragmaAnnotation.lateTrust)) {
+ return CheckPolicy.trusted;
+ } else if (annotations.contains(PragmaAnnotation.lateCheck)) {
+ return CheckPolicy.checked;
+ }
+ }
+ // TODO(sra): Look for annotations on enclosing class and library.
+ return _defaultLateVariableCheckPolicy;
+ }
}
class AnnotationsDataBuilder {
diff --git a/pkg/compiler/lib/src/kernel/transformations/late_lowering.dart b/pkg/compiler/lib/src/kernel/transformations/late_lowering.dart
index 11cfe13..aecd53d 100644
--- a/pkg/compiler/lib/src/kernel/transformations/late_lowering.dart
+++ b/pkg/compiler/lib/src/kernel/transformations/late_lowering.dart
@@ -540,6 +540,7 @@
// The initializer is copied from [field] to [getter] so we copy the
// transformer flags to reflect whether the getter contains super calls.
getter.transformerFlags = field.transformerFlags;
+ _copyAnnotations(getter, field);
enclosingClass.addProcedure(getter);
VariableDeclaration setterValue = VariableDeclaration('value', type: type)
@@ -593,12 +594,25 @@
reference: field.setterReference)
..fileOffset = fileOffset
..isNonNullableByDefault = true;
+ _copyAnnotations(setter, field);
enclosingClass.addProcedure(setter);
}
return backingField;
}
+ void _copyAnnotations(Member target, Member source) {
+ for (final annotation in source.annotations) {
+ if (annotation is ConstantExpression) {
+ target.addAnnotation(
+ ConstantExpression(annotation.constant, annotation.type)
+ ..fileOffset = annotation.fileOffset);
+ } else {
+ throw StateError('Non-constant annotation on $source');
+ }
+ }
+ }
+
TreeNode transformField(Field field, Member contextMember) {
_contextMember = contextMember;
diff --git a/pkg/compiler/lib/src/ssa/builder.dart b/pkg/compiler/lib/src/ssa/builder.dart
index 41b6d82..2f79457 100644
--- a/pkg/compiler/lib/src/ssa/builder.dart
+++ b/pkg/compiler/lib/src/ssa/builder.dart
@@ -4957,7 +4957,10 @@
HInstruction value = arguments[0];
HInstruction name = options.omitLateNames ? null : arguments[1];
- push(HLateReadCheck(value, name,
+ CheckPolicy policy = closedWorld.annotationsData
+ .getLateVariableCheckPolicy(_currentFrame.member);
+
+ push(HLateReadCheck(value, name, policy.isTrusted,
_abstractValueDomain.excludeLateSentinel(value.instructionType))
..sourceInformation = sourceInformation);
}
@@ -4979,7 +4982,11 @@
HInstruction value = arguments[0];
HInstruction name = options.omitLateNames ? null : arguments[1];
- push(HLateWriteOnceCheck(value, name, _abstractValueDomain.dynamicType)
+ CheckPolicy policy = closedWorld.annotationsData
+ .getLateVariableCheckPolicy(_currentFrame.member);
+
+ push(HLateWriteOnceCheck(
+ value, name, policy.isTrusted, _abstractValueDomain.dynamicType)
..sourceInformation = sourceInformation);
}
@@ -5000,7 +5007,11 @@
HInstruction value = arguments[0];
HInstruction name = options.omitLateNames ? null : arguments[1];
- push(HLateInitializeOnceCheck(value, name, _abstractValueDomain.dynamicType)
+ CheckPolicy policy = closedWorld.annotationsData
+ .getLateVariableCheckPolicy(_currentFrame.member);
+
+ push(HLateInitializeOnceCheck(
+ value, name, policy.isTrusted, _abstractValueDomain.dynamicType)
..sourceInformation = sourceInformation);
}
diff --git a/pkg/compiler/lib/src/ssa/codegen.dart b/pkg/compiler/lib/src/ssa/codegen.dart
index 718d79c..3e7e544 100644
--- a/pkg/compiler/lib/src/ssa/codegen.dart
+++ b/pkg/compiler/lib/src/ssa/codegen.dart
@@ -409,9 +409,12 @@
assert(graph.isValid(), 'Graph not valid after ${phase.name}');
}
+ // Remove trusted late checks first to uncover read-modify-write patterns in
+ // instruction selection.
+ runPhase(SsaTrustedLateCheckRemover(_abstractValueDomain));
runPhase(SsaInstructionSelection(_options, _closedWorld));
runPhase(SsaTypeKnownRemover());
- runPhase(SsaTrustedCheckRemover(_options));
+ runPhase(SsaTrustedPrimitiveCheckRemover(_options));
runPhase(SsaAssignmentChaining(_closedWorld));
runPhase(SsaInstructionMerger(_abstractValueDomain, generateAtUseSite));
runPhase(SsaConditionMerger(generateAtUseSite, controlFlowOperators));
diff --git a/pkg/compiler/lib/src/ssa/codegen_helpers.dart b/pkg/compiler/lib/src/ssa/codegen_helpers.dart
index dfc43d5..7adc686 100644
--- a/pkg/compiler/lib/src/ssa/codegen_helpers.dart
+++ b/pkg/compiler/lib/src/ssa/codegen_helpers.dart
@@ -404,10 +404,10 @@
/// Remove [HPrimitiveCheck] instructions from the graph in '--trust-primitives'
/// mode.
-class SsaTrustedCheckRemover extends HBaseVisitor with CodegenPhase {
+class SsaTrustedPrimitiveCheckRemover extends HBaseVisitor with CodegenPhase {
final CompilerOptions _options;
- SsaTrustedCheckRemover(this._options);
+ SsaTrustedPrimitiveCheckRemover(this._options);
@override
void visitGraph(HGraph graph) {
@@ -438,6 +438,56 @@
}
}
+/// Remove trusted late variable checks.
+class SsaTrustedLateCheckRemover extends HBaseVisitor with CodegenPhase {
+ final AbstractValueDomain _abstractValueDomain;
+
+ SsaTrustedLateCheckRemover(this._abstractValueDomain);
+
+ @override
+ void visitGraph(HGraph graph) {
+ visitDominatorTree(graph);
+ }
+
+ @override
+ void visitBasicBlock(HBasicBlock block) {
+ HInstruction instruction = block.first;
+ while (instruction != null) {
+ HInstruction next = instruction.next;
+ instruction.accept(this);
+ instruction = next;
+ }
+ }
+
+ @override
+ void visitLateCheck(HLateCheck instruction) {
+ if (!instruction.isTrusted) return;
+ final inputs = instruction.inputs.toList();
+ instruction.block.rewrite(instruction, instruction.checkedInput);
+ instruction.block.remove(instruction);
+ // TODO(sra): There might be a unused name.
+
+ // Remove pure unused inputs.
+ for (HInstruction input in inputs) {
+ if (input.usedBy.isNotEmpty) continue;
+ HBasicBlock block = input.block;
+ if (block == null) continue; // Already removed.
+ if (input.isPure(_abstractValueDomain)) {
+ // Special cases that are removed properly by other phases.
+ if (input is HParameterValue) continue;
+ if (input is HLocalValue) continue;
+ if (input is HPhi) continue;
+ block.remove(input);
+ continue;
+ }
+ if (input is HFieldGet) {
+ if (input.canThrow(_abstractValueDomain)) continue;
+ block.remove(input);
+ }
+ }
+ }
+}
+
/// Use the result of static and field assignments where it is profitable to use
/// the result of the assignment instead of the value.
///
diff --git a/pkg/compiler/lib/src/ssa/nodes.dart b/pkg/compiler/lib/src/ssa/nodes.dart
index b9c10ba..50b2f2e 100644
--- a/pkg/compiler/lib/src/ssa/nodes.dart
+++ b/pkg/compiler/lib/src/ssa/nodes.dart
@@ -594,13 +594,14 @@
visitBoolConversion(HBoolConversion node) => visitCheck(node);
@override
visitNullCheck(HNullCheck node) => visitCheck(node);
+ visitLateCheck(HLateCheck node) => visitCheck(node);
@override
- visitLateReadCheck(HLateReadCheck node) => visitCheck(node);
+ visitLateReadCheck(HLateReadCheck node) => visitLateCheck(node);
@override
- visitLateWriteOnceCheck(HLateWriteOnceCheck node) => visitCheck(node);
+ visitLateWriteOnceCheck(HLateWriteOnceCheck node) => visitLateCheck(node);
@override
visitLateInitializeOnceCheck(HLateInitializeOnceCheck node) =>
- visitCheck(node);
+ visitLateCheck(node);
@override
visitPrimitiveCheck(HPrimitiveCheck node) => visitCheck(node);
@override
@@ -3702,7 +3703,12 @@
/// A check for a late sentinel to determine if a late field may be read from or
/// written to.
abstract class HLateCheck extends HCheck {
- HLateCheck(HInstruction input, HInstruction /*?*/ name, AbstractValue type)
+ // Checks may be 'trusted' and result in no runtime check. This is done by
+ // compiling with the checks in place and removing them after optimizations.
+ final bool isTrusted;
+
+ HLateCheck(HInstruction input, HInstruction /*?*/ name, this.isTrusted,
+ AbstractValue type)
: super([input, if (name != null) name], type);
bool get hasName => inputs.length > 1;
@@ -3721,8 +3727,9 @@
/// A check that a late field has been initialized and can therefore be read.
class HLateReadCheck extends HLateCheck {
- HLateReadCheck(HInstruction input, HInstruction name, AbstractValue type)
- : super(input, name, type);
+ HLateReadCheck(
+ HInstruction input, HInstruction name, bool isTrusted, AbstractValue type)
+ : super(input, name, isTrusted, type);
@override
accept(HVisitor visitor) => visitor.visitLateReadCheck(this);
@@ -3734,7 +3741,7 @@
bool typeEquals(HInstruction other) => other is HLateReadCheck;
@override
- bool dataEquals(HLateReadCheck other) => true;
+ bool dataEquals(HLateReadCheck other) => isTrusted == other.isTrusted;
bool isRedundant(JClosedWorld closedWorld) {
AbstractValueDomain abstractValueDomain = closedWorld.abstractValueDomain;
@@ -3755,8 +3762,9 @@
/// is that the latter occurs on writes performed as part of the initializer
/// expression.
class HLateWriteOnceCheck extends HLateCheck {
- HLateWriteOnceCheck(HInstruction input, HInstruction name, AbstractValue type)
- : super(input, name, type);
+ HLateWriteOnceCheck(
+ HInstruction input, HInstruction name, bool isTrusted, AbstractValue type)
+ : super(input, name, isTrusted, type);
@override
accept(HVisitor visitor) => visitor.visitLateWriteOnceCheck(this);
@@ -3768,7 +3776,7 @@
bool typeEquals(HInstruction other) => other is HLateWriteOnceCheck;
@override
- bool dataEquals(HLateWriteOnceCheck other) => true;
+ bool dataEquals(HLateWriteOnceCheck other) => isTrusted == other.isTrusted;
bool isRedundant(JClosedWorld closedWorld) {
AbstractValueDomain abstractValueDomain = closedWorld.abstractValueDomain;
@@ -3790,8 +3798,8 @@
/// expression.
class HLateInitializeOnceCheck extends HLateCheck {
HLateInitializeOnceCheck(
- HInstruction input, HInstruction name, AbstractValue type)
- : super(input, name, type);
+ HInstruction input, HInstruction name, bool isTrusted, AbstractValue type)
+ : super(input, name, isTrusted, type);
@override
accept(HVisitor visitor) => visitor.visitLateInitializeOnceCheck(this);
@@ -3803,7 +3811,8 @@
bool typeEquals(HInstruction other) => other is HLateInitializeOnceCheck;
@override
- bool dataEquals(HLateInitializeOnceCheck other) => true;
+ bool dataEquals(HLateInitializeOnceCheck other) =>
+ isTrusted == other.isTrusted;
bool isRedundant(JClosedWorld closedWorld) {
AbstractValueDomain abstractValueDomain = closedWorld.abstractValueDomain;
diff --git a/pkg/compiler/lib/src/ssa/tracer.dart b/pkg/compiler/lib/src/ssa/tracer.dart
index f04fe16..5f7a465 100644
--- a/pkg/compiler/lib/src/ssa/tracer.dart
+++ b/pkg/compiler/lib/src/ssa/tracer.dart
@@ -670,22 +670,25 @@
@override
String visitLateReadCheck(HLateReadCheck node) {
String checkedInput = temporaryId(node.checkedInput);
+ String trust = node.isTrusted ? '.trusted' : '';
String comment = node.hasName ? "(${temporaryId(node.name)})" : "";
- return "LateReadCheck$comment: $checkedInput";
+ return "LateReadCheck$trust$comment: $checkedInput";
}
@override
String visitLateWriteOnceCheck(HLateWriteOnceCheck node) {
String checkedInput = temporaryId(node.checkedInput);
+ String trust = node.isTrusted ? '.trusted' : '';
String comment = node.hasName ? "(${temporaryId(node.name)})" : "";
- return "LateWriteOnceCheck$comment: $checkedInput";
+ return "LateWriteOnceCheck$trust$comment: $checkedInput";
}
@override
String visitLateInitializeOnceCheck(HLateInitializeOnceCheck node) {
String checkedInput = temporaryId(node.checkedInput);
+ String trust = node.isTrusted ? '.trusted' : '';
String comment = node.hasName ? "(${temporaryId(node.name)})" : "";
- return "LateInitializeOnceCheck$comment: $checkedInput";
+ return "LateInitializeOnceCheck$trust$comment: $checkedInput";
}
@override
diff --git a/pkg/front_end/testcases/dart2js/late_fields_with_annotation.dart b/pkg/front_end/testcases/dart2js/late_fields_with_annotation.dart
new file mode 100644
index 0000000..f425129a
--- /dev/null
+++ b/pkg/front_end/testcases/dart2js/late_fields_with_annotation.dart
@@ -0,0 +1,50 @@
+// Copyright (c) 2022, 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.
+
+void main() {
+ testUninitializedNonFinalInstanceField();
+ testUninitializedFinalInstanceField();
+ testInitializedNonFinalInstanceField();
+ testInitializedFinalInstanceField();
+}
+
+class C {
+ @pragma('dart2js:late:trust')
+ late int a;
+
+ @pragma('dart2js:late:check')
+ late final int b;
+
+ @pragma('dart2js:late:trust')
+ @pragma('dart2js:tryInline')
+ late int c = -1;
+
+ @pragma('dart2js:late:check')
+ @pragma('dart2js:noInline')
+ late final int d = -1;
+}
+
+var c = C();
+
+void testUninitializedNonFinalInstanceField() {
+ print(c.a);
+ c.a = 42;
+ print(c.a);
+}
+
+void testUninitializedFinalInstanceField() {
+ print(c.b);
+ c.b = 42;
+ print(c.b);
+}
+
+void testInitializedNonFinalInstanceField() {
+ print(c.c);
+ c.c = 42;
+ print(c.c);
+}
+
+void testInitializedFinalInstanceField() {
+ print(c.d);
+}
diff --git a/pkg/front_end/testcases/dart2js/late_fields_with_annotation.dart.strong.expect b/pkg/front_end/testcases/dart2js/late_fields_with_annotation.dart.strong.expect
new file mode 100644
index 0000000..da24f72
--- /dev/null
+++ b/pkg/front_end/testcases/dart2js/late_fields_with_annotation.dart.strong.expect
@@ -0,0 +1,64 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+class C extends core::Object {
+ @#C3
+ late field core::int a;
+ @#C5
+ late final [setter] field core::int b;
+ @#C3
+ @#C7
+ late field core::int c = 1.{core::int::unary-}(){() → core::int};
+ @#C5
+ @#C9
+ late final field core::int d = 1.{core::int::unary-}(){() → core::int};
+ synthetic constructor •() → self::C
+ : super core::Object::•()
+ ;
+ static method _#new#tearOff() → self::C
+ return new self::C::•();
+}
+static field self::C c = new self::C::•();
+static method main() → void {
+ self::testUninitializedNonFinalInstanceField();
+ self::testUninitializedFinalInstanceField();
+ self::testInitializedNonFinalInstanceField();
+ self::testInitializedFinalInstanceField();
+}
+static method testUninitializedNonFinalInstanceField() → void {
+ core::print(self::c.{self::C::a}{core::int});
+ self::c.{self::C::a} = 42;
+ core::print(self::c.{self::C::a}{core::int});
+}
+static method testUninitializedFinalInstanceField() → void {
+ core::print(self::c.{self::C::b}{core::int});
+ self::c.{self::C::b} = 42;
+ core::print(self::c.{self::C::b}{core::int});
+}
+static method testInitializedNonFinalInstanceField() → void {
+ core::print(self::c.{self::C::c}{core::int});
+ self::c.{self::C::c} = 42;
+ core::print(self::c.{self::C::c}{core::int});
+}
+static method testInitializedFinalInstanceField() → void {
+ core::print(self::c.{self::C::d}{core::int});
+}
+
+constants {
+ #C1 = "dart2js:late:trust"
+ #C2 = null
+ #C3 = core::pragma {name:#C1, options:#C2}
+ #C4 = "dart2js:late:check"
+ #C5 = core::pragma {name:#C4, options:#C2}
+ #C6 = "dart2js:tryInline"
+ #C7 = core::pragma {name:#C6, options:#C2}
+ #C8 = "dart2js:noInline"
+ #C9 = core::pragma {name:#C8, options:#C2}
+}
+
+
+Constructor coverage from constants:
+org-dartlang-testcase:///late_fields_with_annotation.dart:
+- pragma._ (from org-dartlang-sdk:///lib/core/annotations.dart:188:9)
+- Object. (from org-dartlang-sdk:///lib/core/object.dart:25:9)
diff --git a/pkg/front_end/testcases/dart2js/late_fields_with_annotation.dart.strong.transformed.expect b/pkg/front_end/testcases/dart2js/late_fields_with_annotation.dart.strong.transformed.expect
new file mode 100644
index 0000000..25f45c8
--- /dev/null
+++ b/pkg/front_end/testcases/dart2js/late_fields_with_annotation.dart.strong.transformed.expect
@@ -0,0 +1,102 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "dart:_late_helper" as _la;
+import "dart:_internal" as _in;
+
+class C extends core::Object {
+ field core::int _#C#a#A = _in::createSentinel<core::int>();
+ field core::int _#C#b#F = _in::createSentinel<core::int>();
+ field core::int _#C#c#AI = _in::createSentinel<core::int>();
+ field core::int _#C#d#FI = _in::createSentinel<core::int>();
+ synthetic constructor •() → self::C
+ : super core::Object::•()
+ ;
+ static method _#new#tearOff() → self::C
+ return new self::C::•();
+ @#C3
+ get a() → core::int
+ return _la::_lateReadCheck<core::int>(this.{self::C::_#C#a#A}{core::int}, "a");
+ @#C3
+ set a(core::int value) → void
+ this.{self::C::_#C#a#A} = value;
+ @#C5
+ get b() → core::int
+ return _la::_lateReadCheck<core::int>(this.{self::C::_#C#b#F}{core::int}, "b");
+ @#C5
+ set b(core::int value) → void {
+ _la::_lateWriteOnceCheck(this.{self::C::_#C#b#F}{core::int}, "b");
+ this.{self::C::_#C#b#F} = value;
+ }
+ @#C3
+ @#C7
+ get c() → core::int {
+ core::int value = this.{self::C::_#C#c#AI}{core::int};
+ if(_in::isSentinel(value))
+ value = this.{self::C::_#C#c#AI} = 1.{core::int::unary-}(){() → core::int};
+ return value;
+ }
+ @#C3
+ @#C7
+ set c(core::int value) → void
+ this.{self::C::_#C#c#AI} = value;
+ @#C5
+ @#C9
+ get d() → core::int {
+ core::int value = this.{self::C::_#C#d#FI}{core::int};
+ if(_in::isSentinel(value)) {
+ final core::int result = 1.{core::int::unary-}(){() → core::int};
+ _la::_lateInitializeOnceCheck(this.{self::C::_#C#d#FI}{core::int}, "d");
+ value = this.{self::C::_#C#d#FI} = result;
+ }
+ return value;
+ }
+}
+static field self::C c = new self::C::•();
+static method main() → void {
+ self::testUninitializedNonFinalInstanceField();
+ self::testUninitializedFinalInstanceField();
+ self::testInitializedNonFinalInstanceField();
+ self::testInitializedFinalInstanceField();
+}
+static method testUninitializedNonFinalInstanceField() → void {
+ core::print(self::c.{self::C::a}{core::int});
+ self::c.{self::C::a} = 42;
+ core::print(self::c.{self::C::a}{core::int});
+}
+static method testUninitializedFinalInstanceField() → void {
+ core::print(self::c.{self::C::b}{core::int});
+ self::c.{self::C::b} = 42;
+ core::print(self::c.{self::C::b}{core::int});
+}
+static method testInitializedNonFinalInstanceField() → void {
+ core::print(self::c.{self::C::c}{core::int});
+ self::c.{self::C::c} = 42;
+ core::print(self::c.{self::C::c}{core::int});
+}
+static method testInitializedFinalInstanceField() → void {
+ core::print(self::c.{self::C::d}{core::int});
+}
+
+constants {
+ #C1 = "dart2js:late:trust"
+ #C2 = null
+ #C3 = core::pragma {name:#C1, options:#C2}
+ #C4 = "dart2js:late:check"
+ #C5 = core::pragma {name:#C4, options:#C2}
+ #C6 = "dart2js:tryInline"
+ #C7 = core::pragma {name:#C6, options:#C2}
+ #C8 = "dart2js:noInline"
+ #C9 = core::pragma {name:#C8, options:#C2}
+}
+
+Extra constant evaluation status:
+Evaluated: InstanceInvocation @ org-dartlang-testcase:///late_fields_with_annotation.dart:21:16 -> DoubleConstant(-1.0)
+Evaluated: InstanceInvocation @ org-dartlang-testcase:///late_fields_with_annotation.dart:25:22 -> DoubleConstant(-1.0)
+Extra constant evaluation: evaluated: 77, effectively constant: 2
+
+
+Constructor coverage from constants:
+org-dartlang-testcase:///late_fields_with_annotation.dart:
+- pragma._ (from org-dartlang-sdk:///lib/core/annotations.dart:188:9)
+- Object. (from org-dartlang-sdk:///lib/core/object.dart:25:9)
diff --git a/pkg/front_end/testcases/dart2js/late_fields_with_annotation.dart.textual_outline.expect b/pkg/front_end/testcases/dart2js/late_fields_with_annotation.dart.textual_outline.expect
new file mode 100644
index 0000000..be65036
--- /dev/null
+++ b/pkg/front_end/testcases/dart2js/late_fields_with_annotation.dart.textual_outline.expect
@@ -0,0 +1,20 @@
+void main() {}
+
+class C {
+ @pragma('dart2js:late:trust')
+ late int a;
+ @pragma('dart2js:late:check')
+ late final int b;
+ @pragma('dart2js:late:trust')
+ @pragma('dart2js:tryInline')
+ late int c = -1;
+ @pragma('dart2js:late:check')
+ @pragma('dart2js:noInline')
+ late final int d = -1;
+}
+
+var c = C();
+void testUninitializedNonFinalInstanceField() {}
+void testUninitializedFinalInstanceField() {}
+void testInitializedNonFinalInstanceField() {}
+void testInitializedFinalInstanceField() {}
diff --git a/pkg/front_end/testcases/dart2js/late_fields_with_annotation.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/dart2js/late_fields_with_annotation.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..6a154d0
--- /dev/null
+++ b/pkg/front_end/testcases/dart2js/late_fields_with_annotation.dart.textual_outline_modelled.expect
@@ -0,0 +1,19 @@
+class C {
+ @pragma('dart2js:late:check')
+ late final int b;
+ @pragma('dart2js:late:check')
+ @pragma('dart2js:noInline')
+ late final int d = -1;
+ @pragma('dart2js:late:trust')
+ late int a;
+ @pragma('dart2js:late:trust')
+ @pragma('dart2js:tryInline')
+ late int c = -1;
+}
+
+var c = C();
+void main() {}
+void testInitializedFinalInstanceField() {}
+void testInitializedNonFinalInstanceField() {}
+void testUninitializedFinalInstanceField() {}
+void testUninitializedNonFinalInstanceField() {}
diff --git a/pkg/front_end/testcases/dart2js/late_fields_with_annotation.dart.weak.expect b/pkg/front_end/testcases/dart2js/late_fields_with_annotation.dart.weak.expect
new file mode 100644
index 0000000..da24f72
--- /dev/null
+++ b/pkg/front_end/testcases/dart2js/late_fields_with_annotation.dart.weak.expect
@@ -0,0 +1,64 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+class C extends core::Object {
+ @#C3
+ late field core::int a;
+ @#C5
+ late final [setter] field core::int b;
+ @#C3
+ @#C7
+ late field core::int c = 1.{core::int::unary-}(){() → core::int};
+ @#C5
+ @#C9
+ late final field core::int d = 1.{core::int::unary-}(){() → core::int};
+ synthetic constructor •() → self::C
+ : super core::Object::•()
+ ;
+ static method _#new#tearOff() → self::C
+ return new self::C::•();
+}
+static field self::C c = new self::C::•();
+static method main() → void {
+ self::testUninitializedNonFinalInstanceField();
+ self::testUninitializedFinalInstanceField();
+ self::testInitializedNonFinalInstanceField();
+ self::testInitializedFinalInstanceField();
+}
+static method testUninitializedNonFinalInstanceField() → void {
+ core::print(self::c.{self::C::a}{core::int});
+ self::c.{self::C::a} = 42;
+ core::print(self::c.{self::C::a}{core::int});
+}
+static method testUninitializedFinalInstanceField() → void {
+ core::print(self::c.{self::C::b}{core::int});
+ self::c.{self::C::b} = 42;
+ core::print(self::c.{self::C::b}{core::int});
+}
+static method testInitializedNonFinalInstanceField() → void {
+ core::print(self::c.{self::C::c}{core::int});
+ self::c.{self::C::c} = 42;
+ core::print(self::c.{self::C::c}{core::int});
+}
+static method testInitializedFinalInstanceField() → void {
+ core::print(self::c.{self::C::d}{core::int});
+}
+
+constants {
+ #C1 = "dart2js:late:trust"
+ #C2 = null
+ #C3 = core::pragma {name:#C1, options:#C2}
+ #C4 = "dart2js:late:check"
+ #C5 = core::pragma {name:#C4, options:#C2}
+ #C6 = "dart2js:tryInline"
+ #C7 = core::pragma {name:#C6, options:#C2}
+ #C8 = "dart2js:noInline"
+ #C9 = core::pragma {name:#C8, options:#C2}
+}
+
+
+Constructor coverage from constants:
+org-dartlang-testcase:///late_fields_with_annotation.dart:
+- pragma._ (from org-dartlang-sdk:///lib/core/annotations.dart:188:9)
+- Object. (from org-dartlang-sdk:///lib/core/object.dart:25:9)
diff --git a/pkg/front_end/testcases/dart2js/late_fields_with_annotation.dart.weak.modular.expect b/pkg/front_end/testcases/dart2js/late_fields_with_annotation.dart.weak.modular.expect
new file mode 100644
index 0000000..da24f72
--- /dev/null
+++ b/pkg/front_end/testcases/dart2js/late_fields_with_annotation.dart.weak.modular.expect
@@ -0,0 +1,64 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+class C extends core::Object {
+ @#C3
+ late field core::int a;
+ @#C5
+ late final [setter] field core::int b;
+ @#C3
+ @#C7
+ late field core::int c = 1.{core::int::unary-}(){() → core::int};
+ @#C5
+ @#C9
+ late final field core::int d = 1.{core::int::unary-}(){() → core::int};
+ synthetic constructor •() → self::C
+ : super core::Object::•()
+ ;
+ static method _#new#tearOff() → self::C
+ return new self::C::•();
+}
+static field self::C c = new self::C::•();
+static method main() → void {
+ self::testUninitializedNonFinalInstanceField();
+ self::testUninitializedFinalInstanceField();
+ self::testInitializedNonFinalInstanceField();
+ self::testInitializedFinalInstanceField();
+}
+static method testUninitializedNonFinalInstanceField() → void {
+ core::print(self::c.{self::C::a}{core::int});
+ self::c.{self::C::a} = 42;
+ core::print(self::c.{self::C::a}{core::int});
+}
+static method testUninitializedFinalInstanceField() → void {
+ core::print(self::c.{self::C::b}{core::int});
+ self::c.{self::C::b} = 42;
+ core::print(self::c.{self::C::b}{core::int});
+}
+static method testInitializedNonFinalInstanceField() → void {
+ core::print(self::c.{self::C::c}{core::int});
+ self::c.{self::C::c} = 42;
+ core::print(self::c.{self::C::c}{core::int});
+}
+static method testInitializedFinalInstanceField() → void {
+ core::print(self::c.{self::C::d}{core::int});
+}
+
+constants {
+ #C1 = "dart2js:late:trust"
+ #C2 = null
+ #C3 = core::pragma {name:#C1, options:#C2}
+ #C4 = "dart2js:late:check"
+ #C5 = core::pragma {name:#C4, options:#C2}
+ #C6 = "dart2js:tryInline"
+ #C7 = core::pragma {name:#C6, options:#C2}
+ #C8 = "dart2js:noInline"
+ #C9 = core::pragma {name:#C8, options:#C2}
+}
+
+
+Constructor coverage from constants:
+org-dartlang-testcase:///late_fields_with_annotation.dart:
+- pragma._ (from org-dartlang-sdk:///lib/core/annotations.dart:188:9)
+- Object. (from org-dartlang-sdk:///lib/core/object.dart:25:9)
diff --git a/pkg/front_end/testcases/dart2js/late_fields_with_annotation.dart.weak.outline.expect b/pkg/front_end/testcases/dart2js/late_fields_with_annotation.dart.weak.outline.expect
new file mode 100644
index 0000000..1142ec6
--- /dev/null
+++ b/pkg/front_end/testcases/dart2js/late_fields_with_annotation.dart.weak.outline.expect
@@ -0,0 +1,41 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+class C extends core::Object {
+ @core::pragma::_("dart2js:late:trust")
+ late field core::int a;
+ @core::pragma::_("dart2js:late:check")
+ late final [setter] field core::int b;
+ @core::pragma::_("dart2js:late:trust")
+ @core::pragma::_("dart2js:tryInline")
+ late field core::int c;
+ @core::pragma::_("dart2js:late:check")
+ @core::pragma::_("dart2js:noInline")
+ late final field core::int d;
+ synthetic constructor •() → self::C
+ ;
+ static method _#new#tearOff() → self::C
+ return new self::C::•();
+}
+static field self::C c;
+static method main() → void
+ ;
+static method testUninitializedNonFinalInstanceField() → void
+ ;
+static method testUninitializedFinalInstanceField() → void
+ ;
+static method testInitializedNonFinalInstanceField() → void
+ ;
+static method testInitializedFinalInstanceField() → void
+ ;
+
+
+Extra constant evaluation status:
+Evaluated: ConstructorInvocation @ org-dartlang-testcase:///late_fields_with_annotation.dart:13:4 -> InstanceConstant(const pragma{pragma.name: "dart2js:late:trust", pragma.options: null})
+Evaluated: ConstructorInvocation @ org-dartlang-testcase:///late_fields_with_annotation.dart:16:4 -> InstanceConstant(const pragma{pragma.name: "dart2js:late:check", pragma.options: null})
+Evaluated: ConstructorInvocation @ org-dartlang-testcase:///late_fields_with_annotation.dart:19:4 -> InstanceConstant(const pragma{pragma.name: "dart2js:late:trust", pragma.options: null})
+Evaluated: ConstructorInvocation @ org-dartlang-testcase:///late_fields_with_annotation.dart:20:4 -> InstanceConstant(const pragma{pragma.name: "dart2js:tryInline", pragma.options: null})
+Evaluated: ConstructorInvocation @ org-dartlang-testcase:///late_fields_with_annotation.dart:23:4 -> InstanceConstant(const pragma{pragma.name: "dart2js:late:check", pragma.options: null})
+Evaluated: ConstructorInvocation @ org-dartlang-testcase:///late_fields_with_annotation.dart:24:4 -> InstanceConstant(const pragma{pragma.name: "dart2js:noInline", pragma.options: null})
+Extra constant evaluation: evaluated: 7, effectively constant: 6
diff --git a/pkg/front_end/testcases/dart2js/late_fields_with_annotation.dart.weak.transformed.expect b/pkg/front_end/testcases/dart2js/late_fields_with_annotation.dart.weak.transformed.expect
new file mode 100644
index 0000000..25f45c8
--- /dev/null
+++ b/pkg/front_end/testcases/dart2js/late_fields_with_annotation.dart.weak.transformed.expect
@@ -0,0 +1,102 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "dart:_late_helper" as _la;
+import "dart:_internal" as _in;
+
+class C extends core::Object {
+ field core::int _#C#a#A = _in::createSentinel<core::int>();
+ field core::int _#C#b#F = _in::createSentinel<core::int>();
+ field core::int _#C#c#AI = _in::createSentinel<core::int>();
+ field core::int _#C#d#FI = _in::createSentinel<core::int>();
+ synthetic constructor •() → self::C
+ : super core::Object::•()
+ ;
+ static method _#new#tearOff() → self::C
+ return new self::C::•();
+ @#C3
+ get a() → core::int
+ return _la::_lateReadCheck<core::int>(this.{self::C::_#C#a#A}{core::int}, "a");
+ @#C3
+ set a(core::int value) → void
+ this.{self::C::_#C#a#A} = value;
+ @#C5
+ get b() → core::int
+ return _la::_lateReadCheck<core::int>(this.{self::C::_#C#b#F}{core::int}, "b");
+ @#C5
+ set b(core::int value) → void {
+ _la::_lateWriteOnceCheck(this.{self::C::_#C#b#F}{core::int}, "b");
+ this.{self::C::_#C#b#F} = value;
+ }
+ @#C3
+ @#C7
+ get c() → core::int {
+ core::int value = this.{self::C::_#C#c#AI}{core::int};
+ if(_in::isSentinel(value))
+ value = this.{self::C::_#C#c#AI} = 1.{core::int::unary-}(){() → core::int};
+ return value;
+ }
+ @#C3
+ @#C7
+ set c(core::int value) → void
+ this.{self::C::_#C#c#AI} = value;
+ @#C5
+ @#C9
+ get d() → core::int {
+ core::int value = this.{self::C::_#C#d#FI}{core::int};
+ if(_in::isSentinel(value)) {
+ final core::int result = 1.{core::int::unary-}(){() → core::int};
+ _la::_lateInitializeOnceCheck(this.{self::C::_#C#d#FI}{core::int}, "d");
+ value = this.{self::C::_#C#d#FI} = result;
+ }
+ return value;
+ }
+}
+static field self::C c = new self::C::•();
+static method main() → void {
+ self::testUninitializedNonFinalInstanceField();
+ self::testUninitializedFinalInstanceField();
+ self::testInitializedNonFinalInstanceField();
+ self::testInitializedFinalInstanceField();
+}
+static method testUninitializedNonFinalInstanceField() → void {
+ core::print(self::c.{self::C::a}{core::int});
+ self::c.{self::C::a} = 42;
+ core::print(self::c.{self::C::a}{core::int});
+}
+static method testUninitializedFinalInstanceField() → void {
+ core::print(self::c.{self::C::b}{core::int});
+ self::c.{self::C::b} = 42;
+ core::print(self::c.{self::C::b}{core::int});
+}
+static method testInitializedNonFinalInstanceField() → void {
+ core::print(self::c.{self::C::c}{core::int});
+ self::c.{self::C::c} = 42;
+ core::print(self::c.{self::C::c}{core::int});
+}
+static method testInitializedFinalInstanceField() → void {
+ core::print(self::c.{self::C::d}{core::int});
+}
+
+constants {
+ #C1 = "dart2js:late:trust"
+ #C2 = null
+ #C3 = core::pragma {name:#C1, options:#C2}
+ #C4 = "dart2js:late:check"
+ #C5 = core::pragma {name:#C4, options:#C2}
+ #C6 = "dart2js:tryInline"
+ #C7 = core::pragma {name:#C6, options:#C2}
+ #C8 = "dart2js:noInline"
+ #C9 = core::pragma {name:#C8, options:#C2}
+}
+
+Extra constant evaluation status:
+Evaluated: InstanceInvocation @ org-dartlang-testcase:///late_fields_with_annotation.dart:21:16 -> DoubleConstant(-1.0)
+Evaluated: InstanceInvocation @ org-dartlang-testcase:///late_fields_with_annotation.dart:25:22 -> DoubleConstant(-1.0)
+Extra constant evaluation: evaluated: 77, effectively constant: 2
+
+
+Constructor coverage from constants:
+org-dartlang-testcase:///late_fields_with_annotation.dart:
+- pragma._ (from org-dartlang-sdk:///lib/core/annotations.dart:188:9)
+- Object. (from org-dartlang-sdk:///lib/core/object.dart:25:9)
diff --git a/tools/VERSION b/tools/VERSION
index 7fe1fe7..580e6a0 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 18
PATCH 0
-PRERELEASE 230
+PRERELEASE 231
PRERELEASE_PATCH 0
\ No newline at end of file