Version 2.13.0-119.0.dev

Merge commit '0ac716d098791ff9a8893f3ffce44a08439e90f2' into 'dev'
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/add_ne_null.dart b/pkg/analysis_server/lib/src/services/correction/dart/add_ne_null.dart
index ef38ab3..dcaffb5 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/add_ne_null.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/add_ne_null.dart
@@ -4,6 +4,9 @@
 
 import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart';
 import 'package:analysis_server/src/services/correction/fix.dart';
+import 'package:analyzer/dart/analysis/features.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/dart/element/nullability_suffix.dart';
 import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
 import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
 
@@ -16,6 +19,13 @@
 
   @override
   Future<void> compute(ChangeBuilder builder) async {
+    if (unit.featureSet.isEnabled(Feature.non_nullable)) {
+      var node = this.node;
+      if (node is Expression &&
+          node.staticType?.nullabilitySuffix == NullabilitySuffix.none) {
+        return;
+      }
+    }
     var problemMessage = diagnostic.problemMessage;
     await builder.addDartFileEdit(file, (builder) {
       builder.addSimpleInsertion(
diff --git a/pkg/analysis_server/test/src/services/correction/fix/add_ne_null_test.dart b/pkg/analysis_server/test/src/services/correction/fix/add_ne_null_test.dart
index fe07091..0d52849 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/add_ne_null_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/add_ne_null_test.dart
@@ -7,6 +7,7 @@
 import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
+import '../../../../abstract_context.dart';
 import 'fix_processor.dart';
 
 void main() {
@@ -17,13 +18,11 @@
 }
 
 @reflectiveTest
-class AddNeNullMultiTest extends FixProcessorTest {
+class AddNeNullMultiTest extends FixProcessorTest with WithNullSafetyMixin {
   @override
   FixKind get kind => DartFixKind.ADD_NE_NULL_MULTI;
 
-  // todo (pq): add null-safe aware tests
-  // see: https://dart-review.googlesource.com/c/sdk/+/188681
-  Future<void> test_nonBoolCondition_all() async {
+  Future<void> test_nonBoolCondition_all_nonNullable() async {
     await resolveTestCode('''
 f(String p, String q) {
   if (p) {
@@ -34,8 +33,22 @@
   }
 }
 ''');
+    await assertNoFixAllFix(CompileTimeErrorCode.NON_BOOL_CONDITION);
+  }
+
+  Future<void> test_nonBoolCondition_all_nullable() async {
+    await resolveTestCode('''
+f(String? p, String? q) {
+  if (p) {
+    print(p);
+  }
+  if (q) {
+    print(q);
+  }
+}
+''');
     await assertHasFixAllFix(CompileTimeErrorCode.NON_BOOL_CONDITION, '''
-f(String p, String q) {
+f(String? p, String? q) {
   if (p != null) {
     print(p);
   }
@@ -48,11 +61,11 @@
 }
 
 @reflectiveTest
-class AddNeNullTest extends FixProcessorTest {
+class AddNeNullTest extends FixProcessorTest with WithNullSafetyMixin {
   @override
   FixKind get kind => DartFixKind.ADD_NE_NULL;
 
-  Future<void> test_nonBoolCondition() async {
+  Future<void> test_nonBoolCondition_nonNullable() async {
     await resolveTestCode('''
 f(String p) {
   if (p) {
@@ -60,8 +73,19 @@
   }
 }
 ''');
+    await assertNoFix();
+  }
+
+  Future<void> test_nonBoolCondition_nullable() async {
+    await resolveTestCode('''
+f(String? p) {
+  if (p) {
+    print(p);
+  }
+}
+''');
     await assertHasFix('''
-f(String p) {
+f(String? p) {
   if (p != null) {
     print(p);
   }
diff --git a/pkg/analysis_server/test/src/services/correction/fix/fix_processor.dart b/pkg/analysis_server/test/src/services/correction/fix/fix_processor.dart
index 67e4ffe..7ca8451 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/fix_processor.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/fix_processor.dart
@@ -279,6 +279,11 @@
     await _assertNoFix(error);
   }
 
+  Future<void> assertNoFixAllFix(ErrorCode errorCode) async {
+    var error = await _findErrorToFixOfType(errorCode);
+    await _assertNoFixAllFix(error);
+  }
+
   List<LinkedEditSuggestion> expectedSuggestions(
       LinkedEditSuggestionKind kind, List<String> values) {
     return values.map((value) {
@@ -383,6 +388,19 @@
     }
   }
 
+  Future<void> _assertNoFixAllFix(AnalysisError error) async {
+    if (!kind.canBeAppliedTogether()) {
+      fail('Expected to find and return fix-all FixKind for $kind, '
+          'but kind.canBeAppliedTogether is ${kind.canBeAppliedTogether}');
+    }
+    var fixes = await _computeFixes(error);
+    for (var fix in fixes) {
+      if (fix.kind == kind && fix.isFixAllFix()) {
+        fail('Unexpected fix $kind in\n${fixes.join('\n')}');
+      }
+    }
+  }
+
   /// Computes fixes for the given [error] in [testUnit].
   Future<List<Fix>> _computeFixes(AnalysisError error) async {
     var analysisContext = contextFor(testFile);
diff --git a/pkg/analyzer/lib/src/dart/micro/analysis_context.dart b/pkg/analyzer/lib/src/dart/micro/analysis_context.dart
index 0193a85..6c8865c 100644
--- a/pkg/analyzer/lib/src/dart/micro/analysis_context.dart
+++ b/pkg/analyzer/lib/src/dart/micro/analysis_context.dart
@@ -7,6 +7,7 @@
 import 'package:analyzer/dart/analysis/declared_variables.dart';
 import 'package:analyzer/dart/analysis/results.dart';
 import 'package:analyzer/dart/analysis/uri_converter.dart';
+import 'package:analyzer/dart/element/element.dart';
 import 'package:analyzer/file_system/file_system.dart';
 import 'package:analyzer/src/context/context.dart';
 import 'package:analyzer/src/dart/analysis/context_root.dart';
@@ -178,6 +179,11 @@
   }
 
   @override
+  Future<LibraryElement> getLibraryByUri(String uriStr) async {
+    return analysisContext.fileResolver.getLibraryByUri(uriStr: uriStr);
+  }
+
+  @override
   Future<ResolvedLibraryResult> getResolvedLibrary(String path) async {
     var resolvedUnit = await getResolvedUnit(path);
     return ResolvedLibraryResultImpl(
diff --git a/pkg/analyzer/lib/src/dart/micro/resolve_file.dart b/pkg/analyzer/lib/src/dart/micro/resolve_file.dart
index 583f799..14c858c 100644
--- a/pkg/analyzer/lib/src/dart/micro/resolve_file.dart
+++ b/pkg/analyzer/lib/src/dart/micro/resolve_file.dart
@@ -5,6 +5,7 @@
 import 'dart:typed_data';
 
 import 'package:analyzer/dart/analysis/results.dart';
+import 'package:analyzer/dart/element/element.dart';
 import 'package:analyzer/error/error.dart';
 import 'package:analyzer/error/listener.dart';
 import 'package:analyzer/file_system/file_system.dart';
@@ -256,6 +257,39 @@
     });
   }
 
+  LibraryElement getLibraryByUri({
+    required String uriStr,
+    OperationPerformanceImpl? performance,
+  }) {
+    performance ??= OperationPerformanceImpl('<default>');
+
+    var uri = Uri.parse(uriStr);
+    var path = sourceFactory.forUri2(uri)?.fullName;
+
+    if (path == null) {
+      throw ArgumentError('$uri cannot be resolved to a file.');
+    }
+
+    var fileContext = getFileContext(
+      path: path,
+      performance: performance,
+    );
+    var file = fileContext.file;
+
+    if (file.partOfLibrary != null) {
+      throw ArgumentError('$uri is not a library.');
+    }
+
+    performance.run('libraryContext', (performance) {
+      libraryContext!.load2(
+        targetLibrary: file,
+        performance: performance,
+      );
+    });
+
+    return libraryContext!.elementFactory.libraryOfUri2(uriStr);
+  }
+
   String getLibraryLinkedSignature({
     required String path,
     required OperationPerformanceImpl performance,
diff --git a/pkg/analyzer/test/src/dart/micro/simple_file_resolver_test.dart b/pkg/analyzer/test/src/dart/micro/simple_file_resolver_test.dart
index 8709a1f..2b84c88 100644
--- a/pkg/analyzer/test/src/dart/micro/simple_file_resolver_test.dart
+++ b/pkg/analyzer/test/src/dart/micro/simple_file_resolver_test.dart
@@ -438,6 +438,42 @@
     expect(fileResolver.testView!.resolvedFiles, <Object>[]);
   }
 
+  test_getLibraryByUri() {
+    newFile('/workspace/dart/my/lib/a.dart', content: r'''
+class A {}
+''');
+
+    var element = fileResolver.getLibraryByUri(
+      uriStr: 'package:dart.my/a.dart',
+    );
+    expect(element.definingCompilationUnit.types, hasLength(1));
+  }
+
+  test_getLibraryByUri_notExistingFile() {
+    var element = fileResolver.getLibraryByUri(
+      uriStr: 'package:dart.my/a.dart',
+    );
+    expect(element.definingCompilationUnit.types, isEmpty);
+  }
+
+  test_getLibraryByUri_partOf() {
+    newFile('/workspace/dart/my/lib/a.dart', content: r'''
+part of 'b.dart';
+''');
+
+    expect(() {
+      fileResolver.getLibraryByUri(
+        uriStr: 'package:dart.my/a.dart',
+      );
+    }, throwsArgumentError);
+  }
+
+  test_getLibraryByUri_unresolvedUri() {
+    expect(() {
+      fileResolver.getLibraryByUri(uriStr: 'my:unresolved');
+    }, throwsArgumentError);
+  }
+
   test_hint() async {
     await assertErrorsInCode(r'''
 import 'dart:math';
diff --git a/pkg/compiler/lib/src/deferred_load.dart b/pkg/compiler/lib/src/deferred_load.dart
index a78e3bfc4..2a65eb6 100644
--- a/pkg/compiler/lib/src/deferred_load.dart
+++ b/pkg/compiler/lib/src/deferred_load.dart
@@ -865,6 +865,7 @@
     Map<ImportEntity, int> importMap = {};
     var entities = _deferredImportDescriptions.keys.toList();
     entities.sort(_compareImportEntities);
+    entities = entities.reversed.toList();
     for (var key in entities) {
       importMap[key] = id++;
     }
diff --git a/pkg/compiler/test/deferred/load_mapping_test.dart b/pkg/compiler/test/deferred/load_mapping_test.dart
index c158dc4..b6a6821 100644
--- a/pkg/compiler/test/deferred/load_mapping_test.dart
+++ b/pkg/compiler/test/deferred/load_mapping_test.dart
@@ -50,7 +50,9 @@
   // result we expect to have an 6 output files:
   //  * one for code that is only use by an individual deferred import, and
   //  * an extra one for the intersection of lib1 and lib2.
-  var expected = ['10000', '01000', '00100', '00010', '00001', '01100'];
+  var expected = ['10000', '01000', '00100', '00010', '00001', '00110'];
+  expected.sort();
+  actual.sort();
   Expect.listEquals(expected, actual);
 }
 
diff --git a/pkg/compiler/test/tool/graph_isomorphizer/golden/less_simple/lib1.dart b/pkg/compiler/test/tool/graph_isomorphizer/golden/less_simple/lib1.dart
new file mode 100644
index 0000000..91a0c15
--- /dev/null
+++ b/pkg/compiler/test/tool/graph_isomorphizer/golden/less_simple/lib1.dart
@@ -0,0 +1,12 @@
+// 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.
+
+// This file was autogenerated by the pkg/compiler/tool/graph_isomorphizer.dart.
+
+import 'lib_100_0.dart' deferred as b1;
+
+entryLib1() async {
+  await b1.loadLibrary();
+  b1.g_100_0();
+}
diff --git a/pkg/compiler/test/tool/graph_isomorphizer/golden/less_simple/lib2.dart b/pkg/compiler/test/tool/graph_isomorphizer/golden/less_simple/lib2.dart
new file mode 100644
index 0000000..37a0a7c
--- /dev/null
+++ b/pkg/compiler/test/tool/graph_isomorphizer/golden/less_simple/lib2.dart
@@ -0,0 +1,12 @@
+// 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.
+
+// This file was autogenerated by the pkg/compiler/tool/graph_isomorphizer.dart.
+
+import 'lib_010_0.dart' deferred as b2;
+
+entryLib2() async {
+  await b2.loadLibrary();
+  b2.g_010_0();
+}
diff --git a/pkg/compiler/test/tool/graph_isomorphizer/golden/less_simple/lib3.dart b/pkg/compiler/test/tool/graph_isomorphizer/golden/less_simple/lib3.dart
new file mode 100644
index 0000000..597682a
--- /dev/null
+++ b/pkg/compiler/test/tool/graph_isomorphizer/golden/less_simple/lib3.dart
@@ -0,0 +1,12 @@
+// 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.
+
+// This file was autogenerated by the pkg/compiler/tool/graph_isomorphizer.dart.
+
+import 'lib_001_0.dart' deferred as b3;
+
+entryLib3() async {
+  await b3.loadLibrary();
+  b3.g_001_0();
+}
diff --git a/pkg/compiler/test/tool/graph_isomorphizer/golden/less_simple/lib4.dart b/pkg/compiler/test/tool/graph_isomorphizer/golden/less_simple/lib4.dart
new file mode 100644
index 0000000..41a32f6
--- /dev/null
+++ b/pkg/compiler/test/tool/graph_isomorphizer/golden/less_simple/lib4.dart
@@ -0,0 +1,12 @@
+// 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.
+
+// This file was autogenerated by the pkg/compiler/tool/graph_isomorphizer.dart.
+
+import 'lib_000_1.dart' deferred as b4;
+
+entryLib4() async {
+  await b4.loadLibrary();
+  b4.g_000_1();
+}
diff --git a/pkg/compiler/test/tool/graph_isomorphizer/golden/less_simple/libImport.dart b/pkg/compiler/test/tool/graph_isomorphizer/golden/less_simple/libImport.dart
new file mode 100644
index 0000000..5731c13
--- /dev/null
+++ b/pkg/compiler/test/tool/graph_isomorphizer/golden/less_simple/libImport.dart
@@ -0,0 +1,31 @@
+// 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.
+
+// This file was autogenerated by the pkg/compiler/tool/graph_isomorphizer.dart.
+
+import "package:expect/expect.dart";
+
+void v(Set<String> u, String name, int bit) {
+  Expect.isTrue(u.add(name));
+  Expect.equals(name[bit], '1');
+}
+
+@pragma('dart2js:noInline')
+f_100_0(Set<String> u, int b) => v(u, '1000', b);
+@pragma('dart2js:noInline')
+f_110_0(Set<String> u, int b) => v(u, '1100', b);
+@pragma('dart2js:noInline')
+f_101_1(Set<String> u, int b) => v(u, '1011', b);
+@pragma('dart2js:noInline')
+f_111_1(Set<String> u, int b) => v(u, '1111', b);
+@pragma('dart2js:noInline')
+f_010_0(Set<String> u, int b) => v(u, '0100', b);
+@pragma('dart2js:noInline')
+f_010_1(Set<String> u, int b) => v(u, '0101', b);
+@pragma('dart2js:noInline')
+f_011_1(Set<String> u, int b) => v(u, '0111', b);
+@pragma('dart2js:noInline')
+f_001_0(Set<String> u, int b) => v(u, '0010', b);
+@pragma('dart2js:noInline')
+f_000_1(Set<String> u, int b) => v(u, '0001', b);
diff --git a/pkg/compiler/test/tool/graph_isomorphizer/golden/less_simple/lib_000_1.dart b/pkg/compiler/test/tool/graph_isomorphizer/golden/less_simple/lib_000_1.dart
new file mode 100644
index 0000000..37a1c2e
--- /dev/null
+++ b/pkg/compiler/test/tool/graph_isomorphizer/golden/less_simple/lib_000_1.dart
@@ -0,0 +1,22 @@
+// 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.
+
+// This file was autogenerated by the pkg/compiler/tool/graph_isomorphizer.dart.
+
+import "package:expect/expect.dart";
+
+import 'libImport.dart';
+
+@pragma('dart2js:noInline')
+g_000_1() {
+  Set<String> uniques = {};
+
+  // f_***_1;
+  f_000_1(uniques, 3);
+  f_010_1(uniques, 3);
+  f_011_1(uniques, 3);
+  f_101_1(uniques, 3);
+  f_111_1(uniques, 3);
+  Expect.equals(5, uniques.length);
+}
diff --git a/pkg/compiler/test/tool/graph_isomorphizer/golden/less_simple/lib_001_0.dart b/pkg/compiler/test/tool/graph_isomorphizer/golden/less_simple/lib_001_0.dart
new file mode 100644
index 0000000..3534e43
--- /dev/null
+++ b/pkg/compiler/test/tool/graph_isomorphizer/golden/less_simple/lib_001_0.dart
@@ -0,0 +1,21 @@
+// 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.
+
+// This file was autogenerated by the pkg/compiler/tool/graph_isomorphizer.dart.
+
+import "package:expect/expect.dart";
+
+import 'libImport.dart';
+
+@pragma('dart2js:noInline')
+g_001_0() {
+  Set<String> uniques = {};
+
+  // f_**1_*;
+  f_001_0(uniques, 2);
+  f_011_1(uniques, 2);
+  f_101_1(uniques, 2);
+  f_111_1(uniques, 2);
+  Expect.equals(4, uniques.length);
+}
diff --git a/pkg/compiler/test/tool/graph_isomorphizer/golden/less_simple/lib_010_0.dart b/pkg/compiler/test/tool/graph_isomorphizer/golden/less_simple/lib_010_0.dart
new file mode 100644
index 0000000..d9ef060
--- /dev/null
+++ b/pkg/compiler/test/tool/graph_isomorphizer/golden/less_simple/lib_010_0.dart
@@ -0,0 +1,22 @@
+// 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.
+
+// This file was autogenerated by the pkg/compiler/tool/graph_isomorphizer.dart.
+
+import "package:expect/expect.dart";
+
+import 'libImport.dart';
+
+@pragma('dart2js:noInline')
+g_010_0() {
+  Set<String> uniques = {};
+
+  // f_*1*_*;
+  f_010_0(uniques, 1);
+  f_010_1(uniques, 1);
+  f_011_1(uniques, 1);
+  f_110_0(uniques, 1);
+  f_111_1(uniques, 1);
+  Expect.equals(5, uniques.length);
+}
diff --git a/pkg/compiler/test/tool/graph_isomorphizer/golden/less_simple/lib_100_0.dart b/pkg/compiler/test/tool/graph_isomorphizer/golden/less_simple/lib_100_0.dart
new file mode 100644
index 0000000..6d2c07f
--- /dev/null
+++ b/pkg/compiler/test/tool/graph_isomorphizer/golden/less_simple/lib_100_0.dart
@@ -0,0 +1,21 @@
+// 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.
+
+// This file was autogenerated by the pkg/compiler/tool/graph_isomorphizer.dart.
+
+import "package:expect/expect.dart";
+
+import 'libImport.dart';
+
+@pragma('dart2js:noInline')
+g_100_0() {
+  Set<String> uniques = {};
+
+  // f_1**_*;
+  f_100_0(uniques, 0);
+  f_101_1(uniques, 0);
+  f_110_0(uniques, 0);
+  f_111_1(uniques, 0);
+  Expect.equals(4, uniques.length);
+}
diff --git a/pkg/compiler/test/tool/graph_isomorphizer/golden/less_simple/main.dart b/pkg/compiler/test/tool/graph_isomorphizer/golden/less_simple/main.dart
new file mode 100644
index 0000000..fb21cef
--- /dev/null
+++ b/pkg/compiler/test/tool/graph_isomorphizer/golden/less_simple/main.dart
@@ -0,0 +1,17 @@
+// 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.
+
+// This file was autogenerated by the pkg/compiler/tool/graph_isomorphizer.dart.
+
+import 'lib1.dart';
+import 'lib2.dart';
+import 'lib3.dart';
+import 'lib4.dart';
+
+main() {
+  entryLib1();
+  entryLib2();
+  entryLib3();
+  entryLib4();
+}
diff --git a/pkg/compiler/test/tool/graph_isomorphizer/golden/simple/lib1.dart b/pkg/compiler/test/tool/graph_isomorphizer/golden/simple/lib1.dart
new file mode 100644
index 0000000..c664b0c
--- /dev/null
+++ b/pkg/compiler/test/tool/graph_isomorphizer/golden/simple/lib1.dart
@@ -0,0 +1,12 @@
+// 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.
+
+// This file was autogenerated by the pkg/compiler/tool/graph_isomorphizer.dart.
+
+import 'lib_100.dart' deferred as b1;
+
+entryLib1() async {
+  await b1.loadLibrary();
+  b1.g_100();
+}
diff --git a/pkg/compiler/test/tool/graph_isomorphizer/golden/simple/lib2.dart b/pkg/compiler/test/tool/graph_isomorphizer/golden/simple/lib2.dart
new file mode 100644
index 0000000..6741292
--- /dev/null
+++ b/pkg/compiler/test/tool/graph_isomorphizer/golden/simple/lib2.dart
@@ -0,0 +1,12 @@
+// 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.
+
+// This file was autogenerated by the pkg/compiler/tool/graph_isomorphizer.dart.
+
+import 'lib_010.dart' deferred as b2;
+
+entryLib2() async {
+  await b2.loadLibrary();
+  b2.g_010();
+}
diff --git a/pkg/compiler/test/tool/graph_isomorphizer/golden/simple/lib3.dart b/pkg/compiler/test/tool/graph_isomorphizer/golden/simple/lib3.dart
new file mode 100644
index 0000000..2133ef7
--- /dev/null
+++ b/pkg/compiler/test/tool/graph_isomorphizer/golden/simple/lib3.dart
@@ -0,0 +1,12 @@
+// 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.
+
+// This file was autogenerated by the pkg/compiler/tool/graph_isomorphizer.dart.
+
+import 'lib_001.dart' deferred as b3;
+
+entryLib3() async {
+  await b3.loadLibrary();
+  b3.g_001();
+}
diff --git a/pkg/compiler/test/tool/graph_isomorphizer/golden/simple/libImport.dart b/pkg/compiler/test/tool/graph_isomorphizer/golden/simple/libImport.dart
new file mode 100644
index 0000000..8d1058b
--- /dev/null
+++ b/pkg/compiler/test/tool/graph_isomorphizer/golden/simple/libImport.dart
@@ -0,0 +1,25 @@
+// 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.
+
+// This file was autogenerated by the pkg/compiler/tool/graph_isomorphizer.dart.
+
+import "package:expect/expect.dart";
+
+void v(Set<String> u, String name, int bit) {
+  Expect.isTrue(u.add(name));
+  Expect.equals(name[bit], '1');
+}
+
+@pragma('dart2js:noInline')
+f_100(Set<String> u, int b) => v(u, '100', b);
+@pragma('dart2js:noInline')
+f_101(Set<String> u, int b) => v(u, '101', b);
+@pragma('dart2js:noInline')
+f_111(Set<String> u, int b) => v(u, '111', b);
+@pragma('dart2js:noInline')
+f_010(Set<String> u, int b) => v(u, '010', b);
+@pragma('dart2js:noInline')
+f_011(Set<String> u, int b) => v(u, '011', b);
+@pragma('dart2js:noInline')
+f_001(Set<String> u, int b) => v(u, '001', b);
diff --git a/pkg/compiler/test/tool/graph_isomorphizer/golden/simple/lib_001.dart b/pkg/compiler/test/tool/graph_isomorphizer/golden/simple/lib_001.dart
new file mode 100644
index 0000000..16c561a
--- /dev/null
+++ b/pkg/compiler/test/tool/graph_isomorphizer/golden/simple/lib_001.dart
@@ -0,0 +1,21 @@
+// 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.
+
+// This file was autogenerated by the pkg/compiler/tool/graph_isomorphizer.dart.
+
+import "package:expect/expect.dart";
+
+import 'libImport.dart';
+
+@pragma('dart2js:noInline')
+g_001() {
+  Set<String> uniques = {};
+
+  // f_**1;
+  f_001(uniques, 2);
+  f_011(uniques, 2);
+  f_101(uniques, 2);
+  f_111(uniques, 2);
+  Expect.equals(4, uniques.length);
+}
diff --git a/pkg/compiler/test/tool/graph_isomorphizer/golden/simple/lib_010.dart b/pkg/compiler/test/tool/graph_isomorphizer/golden/simple/lib_010.dart
new file mode 100644
index 0000000..3ac623a
--- /dev/null
+++ b/pkg/compiler/test/tool/graph_isomorphizer/golden/simple/lib_010.dart
@@ -0,0 +1,20 @@
+// 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.
+
+// This file was autogenerated by the pkg/compiler/tool/graph_isomorphizer.dart.
+
+import "package:expect/expect.dart";
+
+import 'libImport.dart';
+
+@pragma('dart2js:noInline')
+g_010() {
+  Set<String> uniques = {};
+
+  // f_*1*;
+  f_010(uniques, 1);
+  f_011(uniques, 1);
+  f_111(uniques, 1);
+  Expect.equals(3, uniques.length);
+}
diff --git a/pkg/compiler/test/tool/graph_isomorphizer/golden/simple/lib_100.dart b/pkg/compiler/test/tool/graph_isomorphizer/golden/simple/lib_100.dart
new file mode 100644
index 0000000..631b02b
--- /dev/null
+++ b/pkg/compiler/test/tool/graph_isomorphizer/golden/simple/lib_100.dart
@@ -0,0 +1,20 @@
+// 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.
+
+// This file was autogenerated by the pkg/compiler/tool/graph_isomorphizer.dart.
+
+import "package:expect/expect.dart";
+
+import 'libImport.dart';
+
+@pragma('dart2js:noInline')
+g_100() {
+  Set<String> uniques = {};
+
+  // f_1**;
+  f_100(uniques, 0);
+  f_101(uniques, 0);
+  f_111(uniques, 0);
+  Expect.equals(3, uniques.length);
+}
diff --git a/pkg/compiler/test/tool/graph_isomorphizer/golden/simple/main.dart b/pkg/compiler/test/tool/graph_isomorphizer/golden/simple/main.dart
new file mode 100644
index 0000000..ef1a426
--- /dev/null
+++ b/pkg/compiler/test/tool/graph_isomorphizer/golden/simple/main.dart
@@ -0,0 +1,15 @@
+// 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.
+
+// This file was autogenerated by the pkg/compiler/tool/graph_isomorphizer.dart.
+
+import 'lib1.dart';
+import 'lib2.dart';
+import 'lib3.dart';
+
+main() {
+  entryLib1();
+  entryLib2();
+  entryLib3();
+}
diff --git a/pkg/compiler/test/tool/graph_isomorphizer/graph_isomorphizer_test.dart b/pkg/compiler/test/tool/graph_isomorphizer/graph_isomorphizer_test.dart
new file mode 100644
index 0000000..eda8526
--- /dev/null
+++ b/pkg/compiler/test/tool/graph_isomorphizer/graph_isomorphizer_test.dart
@@ -0,0 +1,130 @@
+// Copyright (c) 2015, 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.
+
+// @dart = 2.
+
+import 'dart:io';
+
+import 'package:expect/expect.dart';
+import 'package:compiler/compiler_new.dart';
+import '../../helpers/memory_compiler.dart';
+import '../../../tool/graph_isomorphizer.dart';
+
+/// Only generate goldens from the root sdk directory.
+const String goldenDirectory =
+    'pkg/compiler/test/tool/graph_isomorphizer/golden';
+
+/// A map of folder name to graph file lines. When adding new tests, its
+/// probably best to get the ordering of these lines from the compiler.
+const Map<String, List<String>> testCases = {
+  'simple': ['100', '010', '001', '101', '011', '111'],
+  'less_simple': [
+    '1000',
+    '0100',
+    '0010',
+    '0001',
+    '1100',
+    '0101',
+    '1011',
+    '0111',
+    '1111'
+  ]
+};
+
+void unorderedListEquals(List<String> expected, List<String> actual) {
+  var sortedExpected = expected.toList();
+  var sortedActual = actual.toList();
+  sortedExpected.sort();
+  sortedActual.sort();
+  Expect.listEquals(sortedExpected, sortedActual);
+}
+
+void verifyGeneratedFile(
+    String filename, StringBuffer contents, Map<String, String> expectations) {
+  Expect.stringEquals(contents.toString(), expectations[filename]);
+}
+
+GraphIsomorphizer generateFiles(List<String> graphFileLines,
+    {String outDirectory: '.'}) {
+  Map<int, List<List<int>>> names = {};
+  int maxBit = namesFromGraphFileLines(graphFileLines, names);
+  var graphIsomorphizer =
+      GraphIsomorphizer(names, maxBit, outDirectory: outDirectory);
+  graphIsomorphizer.generateFiles();
+  return graphIsomorphizer;
+}
+
+/// Tests isomorphicity of the code generated by the GraphIsomorphizer.
+void verifyGraphFileLines(
+    List<String> graphFileLines, Map<String, String> expectations) async {
+  // Generate files.
+  var graphIsomorphizer = generateFiles(graphFileLines);
+
+  // Verify generated files.
+  verifyGeneratedFile(graphIsomorphizer.mainFilename,
+      graphIsomorphizer.mainBuffer, expectations);
+  verifyGeneratedFile(graphIsomorphizer.rootImportFilename,
+      graphIsomorphizer.rootImportBuffer, expectations);
+  graphIsomorphizer.entryLibBuffers.forEach((filename, contents) {
+    verifyGeneratedFile(filename, contents, expectations);
+  });
+  graphIsomorphizer.mixerLibBuffers.forEach((filename, contents) {
+    verifyGeneratedFile(filename, contents, expectations);
+  });
+
+  // Compile generated code and dump deferred graph shape.
+  var collector = new OutputCollector();
+  await runCompiler(
+      memorySourceFiles: expectations,
+      options: ['--dump-deferred-graph=deferred_graph.txt'],
+      outputProvider: collector);
+  var actual =
+      collector.getOutput("deferred_graph.txt", OutputType.debug).split('\n');
+
+  // Confirm new graph is isomorphic.
+  unorderedListEquals(graphFileLines, actual);
+}
+
+void generateGoldens() {
+  testCases.forEach((name, test) {
+    var graphIsomorphizer =
+        generateFiles(test, outDirectory: goldenDirectory + '/' + name);
+    graphIsomorphizer.writeFiles();
+  });
+}
+
+String getFilename(String path) {
+  var lastSlash = path.lastIndexOf('/');
+  return path.substring(lastSlash + 1, path.length);
+}
+
+void verifyGoldens() {
+  testCases.forEach((name, test) {
+    Map<String, String> expectations = {};
+    var golden = Directory(goldenDirectory + '/' + name);
+    var files = golden.listSync();
+    for (var file in files) {
+      assert(file is File);
+      var contents = (file as File).readAsStringSync();
+      var filename = getFilename(file.path);
+      expectations[filename] = contents;
+    }
+    verifyGraphFileLines(test, expectations);
+  });
+}
+
+void main(List<String> args) {
+  bool generate = false;
+  for (var arg in args) {
+    if (arg == '-g') {
+      generate = true;
+    }
+  }
+
+  if (generate) {
+    generateGoldens();
+  } else {
+    verifyGoldens();
+  }
+}
diff --git a/pkg/compiler/tool/graph_isomorphizer.dart b/pkg/compiler/tool/graph_isomorphizer.dart
new file mode 100644
index 0000000..dca1cf1
--- /dev/null
+++ b/pkg/compiler/tool/graph_isomorphizer.dart
@@ -0,0 +1,355 @@
+// 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.
+
+/// This tool builds a program with a deferred graph isomorphic to the provided
+/// graph, or generates permutations of bits and the associated files to
+/// generate complex deferred graphs.
+
+/// For example, if 5 bits are permuted, we end up with files like:
+///   lib1.dart
+///   lib2.dart
+///   lib3.dart
+///   lib4.dart
+///   lib5.dart
+///   libB.dart
+///   lib_000_01.dart
+///   lib_000_10.dart
+///   lib_001_00.dart
+///   lib_010_00.dart
+///   lib_100_00.dart
+///   main.dart
+///
+/// Where
+///   main.dart contains main().
+///   libX.dart contains the actual deferred import of the file with the bit at
+///     the X position starting from the left, ie lib1 imports lib_100_00, lib2
+///     imports lib_010_00, etc.
+///   libImport.dart is the 'top of the diamond' which contains all of the code.
+///   lib_XXX_XX.dart invokes all of the functions in libImport which have a
+///     1 bit at that position, ie lib_100_00 invokes all code in libImport with
+///     a first bit of 1, f100_00, f110_00, etc.
+///
+/// Note: There are restrictions to what we can generate. Specifically, certain
+/// OutputUnits can never be empty, namely we will always generate one file for
+/// each entryLib, and because of our dependency on expect, we will always have
+/// a file representing the intersection of all import entities. So, for example
+/// with three bits, each of 100, 010, 001 and 111 must be present in the graph
+/// file, but 110, 101, and 011 are optional.
+
+import 'dart:io';
+import 'dart:math';
+
+typedef NameFunc = String Function(List<int>, int);
+
+/// A simple constant pass through function for bit strings that don't need
+/// special printing, ie stops.
+String passThroughNameFunc(List<int> l, int i) => l[i].toString();
+
+/// Generates all permutations of bits recursively.
+void generatePermutedNames(
+    Map<int, List<List<int>>> names, int maxBit, List<int> bits, int bit) {
+  if (bit == maxBit) {
+    for (int i = 0; i < bits.length; i++) {
+      if (bits[i] == 1) {
+        names.putIfAbsent(i, () => []);
+        names[i].add(List.from(bits));
+      }
+    }
+    return;
+  }
+  generatePermutedNames(names, maxBit, bits, bit + 1);
+  bits[bit] = 1;
+  generatePermutedNames(names, maxBit, bits, bit + 1);
+  bits[bit] = 0;
+}
+
+/// A helper function to generate names from lists of strings of bits.
+int namesFromGraphFileLines(
+    List<String> lines, Map<int, List<List<int>>> names) {
+  int maxBit = 0;
+  for (var line in lines) {
+    List<int> name = [];
+    // Each line should have the same length.
+    assert(maxBit == 0 || maxBit == line.length);
+    maxBit = max(maxBit, line.length);
+    for (int i = 0; i < line.length; i++) {
+      var bit = line[i];
+      if (bit == '1') {
+        name.add(1);
+        (names[i] ??= []).add(name);
+      } else {
+        name.add(0);
+      }
+    }
+  }
+  return maxBit;
+}
+
+/// Parses names from a graph file dumped from dart2js and returns the max bit.
+int namesFromGraphFile(String graphFile, Map<int, List<List<int>>> names) {
+  var lines = File(graphFile).readAsLinesSync();
+  return namesFromGraphFileLines(lines, names);
+}
+
+class ImportData {
+  String import;
+  String entryPoint;
+
+  ImportData(this.import, this.entryPoint);
+}
+
+class GraphIsomorphizer {
+  /// The output directory, only relevant if files are written out.
+  final String outDirectory;
+
+  /// Various buffers for the files the GraphIsomorphizer generates.
+  StringBuffer rootImportBuffer = StringBuffer();
+  StringBuffer mainBuffer = StringBuffer();
+  Map<String, StringBuffer> mixerLibBuffers = {};
+  Map<String, StringBuffer> entryLibBuffers = {};
+
+  /// A map of bit positions to lists of bit lists.
+  final Map<int, List<List<int>>> names;
+
+  /// We will permute bits up until the maximum bit.
+  int maxBit = 0;
+
+  /// The 'top of the diamond' import file containing all code.
+  final String rootImportFilename = 'libImport.dart';
+
+  /// The main filename.
+  final String mainFilename = 'main.dart';
+
+  /// A bool to omit the comment block.
+  final bool skipCopyright;
+
+  GraphIsomorphizer(this.names, this.maxBit,
+      {this.outDirectory: '.', this.skipCopyright: false});
+
+  void noInlineDecorator(StringBuffer out) {
+    out.write("@pragma('dart2js:noInline')\n");
+  }
+
+  void importExpect(StringBuffer out) {
+    out.write('import "package:expect/expect.dart";\n\n');
+  }
+
+  /// Generates the header for a file.
+  void generateHeader(StringBuffer out) {
+    if (!skipCopyright) {
+      out.write("""
+// 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.
+
+// This file was autogenerated by the pkg/compiler/tool/graph_isomorphizer.dart.
+
+""");
+    }
+  }
+
+  void generateRootImport(StringBuffer out) {
+    generateHeader(out);
+    importExpect(out);
+
+    // We verify that each function in libImport is invoked only once from each
+    // mixerLib and that only the correct functions are called, ie for lib_001,
+    // only functions with XX1 are invoked.
+    out.write('void v(Set<String> u, String name, int bit) {\n' +
+        '  Expect.isTrue(u.add(name));\n' +
+        "  Expect.equals(name[bit], '1');\n" +
+        '}\n\n');
+
+    Set<String> uniques = {};
+
+    // Sort the names to ensure they are in a canonical order.
+    var nameKeys = names.keys.toList();
+    nameKeys.sort();
+    for (var name in nameKeys) {
+      var bitsList = names[name];
+      for (var bits in bitsList) {
+        var name = generateBitString(bits);
+        if (uniques.add(name)) {
+          noInlineDecorator(out);
+          var stringBits = generateBitString(bits, withStops: false);
+          out.write(
+              "f$name(Set<String> u, int b) => v(u, '$stringBits', b);\n");
+        }
+      }
+    }
+  }
+
+  /// Generates a mixerLib which will be loaded as a deferred library from an entryLib.
+  void generateMixerLib(
+      String name, StringBuffer out, String import, List<int> bits, int bit) {
+    generateHeader(out);
+    importExpect(out);
+    out.write("import '$import';\n\n");
+    noInlineDecorator(out);
+    out.write('g$name() {\n'
+        '  Set<String> uniques = {};\n\n'
+        '  // f${generateCommentName(bits, bit)};\n');
+    int count = 0;
+
+    // Collect the names so we can sort them and put them in a canonical order.
+    List<String> namesBits = [];
+    names[bit].forEach((nameBits) {
+      var nameString = generateBitString(nameBits);
+      namesBits.add(nameString);
+      count++;
+    });
+
+    namesBits.sort();
+    for (var name in namesBits) {
+      out.write('  f$name(uniques, $bit);\n');
+    }
+
+    // We expect 'count' unique strings added to be added to 'uniques'.
+    out.write("  Expect.equals($count, uniques.length);\n"
+        '}\n');
+  }
+
+  /// Generates a string of bits, with optional parameters to control how the
+  /// bits print.
+  String generateBitString(List<int> bits,
+      {NameFunc f: passThroughNameFunc, bool withStops: true}) {
+    int stop = 0;
+    StringBuffer sb = StringBuffer();
+    for (int i = 0; i < bits.length; i++) {
+      if (stop++ % 3 == 0 && withStops) {
+        sb.write('_');
+      }
+      sb.write(f(bits, i));
+    }
+    return sb.toString();
+  }
+
+  /// Generates a pretty bit string for use in comments.
+  String generateCommentName(List<int> bits, int fixBit) {
+    return generateBitString(bits,
+        f: (List<int> bits, int bit) => bit == fixBit ? '1' : '*');
+  }
+
+  /// Generates an entryLib file.
+  void generateEntryLib(StringBuffer out, String mainName, String funcName,
+      String import, int bit) {
+    generateHeader(out);
+    var name = 'b$bit';
+    out.write("import '$import' deferred as $name;\n\n"
+        '$mainName async {\n'
+        '  await $name.loadLibrary();\n'
+        '  $name.g$funcName();\n'
+        '}\n');
+  }
+
+  /// Generates entry and mixer libs for the supplied names.
+  List<ImportData> generateEntryAndMixerLibs() {
+    // Generates each lib_XXX.dart and the associated entryLib file.
+    List<ImportData> importData = [];
+    for (int i = 1; i <= maxBit; i++) {
+      // Generate the bit list representing this library. ie a list of all
+      // 0s with a single 1 bit flipped.
+      int oneBit = i - 1;
+      List<int> bits = [];
+      for (int j = 0; j < maxBit; j++) bits.add(j == oneBit ? 1 : 0);
+
+      // Generate the mixerLib for this entryLib.
+      var name = generateBitString(bits);
+      var mixerLibBuffer = StringBuffer();
+      var mixerLibName = "lib$name.dart";
+      generateMixerLib(name, mixerLibBuffer, rootImportFilename, bits, oneBit);
+      mixerLibBuffers[mixerLibName] = mixerLibBuffer;
+
+      // Generate the entryLib for this mixerLib.
+      var entryLibName = 'lib$i.dart';
+      var entryFuncName = 'entryLib$i()';
+      var entryLibBuffer = StringBuffer();
+      generateEntryLib(entryLibBuffer, entryFuncName, name, mixerLibName, i);
+      entryLibBuffers[entryLibName] = entryLibBuffer;
+
+      // Stash the entry point in entryLib for later reference in the main file.
+      importData.add(ImportData(entryLibName, entryFuncName));
+    }
+    return importData;
+  }
+
+  /// Generates the main file.
+  void generateMain(StringBuffer out, List<ImportData> importData) {
+    generateHeader(mainBuffer);
+    for (var data in importData) {
+      out.write("import '${data.import}';\n");
+    }
+    out.write('\n'
+        'main() {\n');
+    for (var data in importData) {
+      out.write('  ${data.entryPoint};\n');
+    }
+    out.write('}\n');
+  }
+
+  /// Generates all files into buffers.
+  void generateFiles() {
+    generateRootImport(rootImportBuffer);
+    var importData = generateEntryAndMixerLibs();
+    generateMain(mainBuffer, importData);
+  }
+
+  /// Helper to dump contents to file.
+  void writeToFile(String filename, StringBuffer contents) {
+    var file = File(this.outDirectory + '/' + filename);
+    file.createSync(recursive: true);
+    var sink = file.openWrite();
+    sink.write(contents);
+    sink.close();
+  }
+
+  /// Writes all buffers to files.
+  void writeFiles() {
+    mixerLibBuffers.forEach(writeToFile);
+    entryLibBuffers.forEach(writeToFile);
+    writeToFile(rootImportFilename, rootImportBuffer);
+    writeToFile(mainFilename, mainBuffer);
+  }
+
+  /// Generate and write files.
+  void run() {
+    generateFiles();
+    writeFiles();
+  }
+}
+
+/// Creates a GraphIsomorphizer based on the provided args.
+GraphIsomorphizer createGraphIsomorphizer(List<String> args) {
+  int maxBit = 0;
+  String graphFile = '';
+  String outDirectory = '.';
+
+  for (var arg in args) {
+    if (arg.startsWith('--max-bit')) {
+      maxBit = int.parse(arg.substring('--max-bit='.length));
+    }
+    if (arg.startsWith('--graph-file')) {
+      graphFile = arg.substring('--graph-file='.length);
+    }
+    if (arg.startsWith('--out-dir')) {
+      outDirectory = arg.substring('--out-dir='.length);
+    }
+  }
+
+  // If we don't have a graphFile, then we generate all permutations of bits up
+  // to maxBit.
+  Map<int, List<List<int>>> names = {};
+  if (graphFile.isEmpty) {
+    List<int> bits = List.filled(maxBit, 0);
+    generatePermutedNames(names, maxBit, bits, 0);
+  } else {
+    maxBit = namesFromGraphFile(graphFile, names);
+  }
+  return GraphIsomorphizer(names, maxBit, outDirectory: outDirectory);
+}
+
+void main(List<String> args) {
+  var graphIsomorphizer = createGraphIsomorphizer(args);
+  graphIsomorphizer.run();
+}
diff --git a/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart b/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
index 5b0237b..7c9aad9 100644
--- a/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
@@ -991,7 +991,12 @@
   Constant execute(Statement statement) {
     StatementConstantEvaluator statementEvaluator =
         new StatementConstantEvaluator(this);
-    return statement.accept(statementEvaluator);
+    ExecutionStatus status = statement.accept(statementEvaluator);
+    if (status is ReturnStatus) {
+      return status.value;
+    }
+    return createInvalidExpressionConstant(statement,
+        'No valid constant returned from the execution of $statement.');
   }
 
   /// Create an error-constant indicating that an error has been detected during
@@ -2377,15 +2382,21 @@
     // TODO(kustermann): The heuristic of allowing all [VariableGet]s on [Let]
     // variables might allow more than it should.
     final VariableDeclaration variable = node.variable;
-    if (variable.parent is Let || _isFormalParameter(variable)) {
-      return env.lookupVariable(node.variable) ??
-          createErrorConstant(
-              node,
-              templateConstEvalNonConstantVariableGet
-                  .withArguments(variable.name));
-    }
-    if (variable.isConst) {
-      return _evaluateSubexpression(variable.initializer);
+    if (enableConstFunctions) {
+      return env.lookupVariable(variable) ??
+          createInvalidExpressionConstant(
+              node, 'Variable get of an unknown value.');
+    } else {
+      if (variable.parent is Let || _isFormalParameter(variable)) {
+        return env.lookupVariable(node.variable) ??
+            createErrorConstant(
+                node,
+                templateConstEvalNonConstantVariableGet
+                    .withArguments(variable.name));
+      }
+      if (variable.isConst) {
+        return _evaluateSubexpression(variable.initializer);
+      }
     }
     return createInvalidExpressionConstant(
         node, 'Variable get of a non-const variable.');
@@ -3189,7 +3200,7 @@
   }
 }
 
-class StatementConstantEvaluator extends StatementVisitor<Constant> {
+class StatementConstantEvaluator extends StatementVisitor<ExecutionStatus> {
   ConstantEvaluator exprEvaluator;
 
   StatementConstantEvaluator(this.exprEvaluator) {
@@ -3202,12 +3213,31 @@
   Constant evaluate(Expression expr) => expr.accept(exprEvaluator);
 
   @override
-  Constant defaultStatement(Statement node) => throw new UnsupportedError(
-      'Statement constant evaluation does not support ${node.runtimeType}.');
+  ExecutionStatus defaultStatement(Statement node) {
+    throw new UnsupportedError(
+        'Statement constant evaluation does not support ${node.runtimeType}.');
+  }
 
   @override
-  Constant visitReturnStatement(ReturnStatement node) =>
-      evaluate(node.expression);
+  ExecutionStatus visitBlock(Block node) {
+    for (Statement statement in node.statements) {
+      final ExecutionStatus status = statement.accept(this);
+      if (status is! ProceedStatus) return status;
+    }
+    return const ProceedStatus();
+  }
+
+  @override
+  ExecutionStatus visitReturnStatement(ReturnStatement node) =>
+      new ReturnStatus(evaluate(node.expression));
+
+  @override
+  ExecutionStatus visitVariableDeclaration(VariableDeclaration node) {
+    Constant value = evaluate(node.initializer);
+    if (value is AbortConstant) return new ReturnStatus(value);
+    exprEvaluator.env.updateVariableValue(node, value);
+    return const ProceedStatus();
+  }
 }
 
 class ConstantCoverage {
@@ -3345,6 +3375,22 @@
   EvaluationReference(this.value);
 }
 
+/// Represents a status for statement execution.
+abstract class ExecutionStatus {
+  const ExecutionStatus();
+}
+
+/// Status that the statement completed execution successfully.
+class ProceedStatus extends ExecutionStatus {
+  const ProceedStatus();
+}
+
+/// Status that the statement returned a valid [Constant] value.
+class ReturnStatus extends ExecutionStatus {
+  final Constant value;
+  ReturnStatus(this.value);
+}
+
 /// An intermediate result that is used within the [ConstantEvaluator].
 class IntermediateValue implements Constant {
   dynamic value;
diff --git a/pkg/front_end/testcases/const_functions/const_functions_variable_declarations.dart b/pkg/front_end/testcases/const_functions/const_functions_variable_declarations.dart
new file mode 100644
index 0000000..691f46f
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_variable_declarations.dart
@@ -0,0 +1,49 @@
+// 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.
+
+// Tests creating new local variables within const functions.
+
+// SharedOptions=--enable-experiment=const-functions
+
+import "package:expect/expect.dart";
+
+const var1 = function1(1, 2);
+int function1(int a, int b) {
+  var x = 1 + a + b;
+  return x;
+}
+
+const var2 = function2();
+String function2() {
+  dynamic x = "string";
+  return x;
+}
+
+const var3 = function3();
+int function3() {
+  var first = 2;
+  var second = 2 + first;
+  return 2 + second;
+}
+
+const var4 = function4();
+int function4() {
+  var first = 2;
+  var second = 0;
+  return first + second;
+}
+
+const var5 = function5();
+int function5() {
+  const constant = -2;
+  return constant;
+}
+
+void main() {
+  Expect.equals(var1, 4);
+  Expect.equals(var2, "string");
+  Expect.equals(var3, 6);
+  Expect.equals(var4, 2);
+  Expect.equals(var5, -2);
+}
diff --git a/pkg/front_end/testcases/const_functions/const_functions_variable_declarations.dart.strong.expect b/pkg/front_end/testcases/const_functions/const_functions_variable_declarations.dart.strong.expect
new file mode 100644
index 0000000..0be3638
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_variable_declarations.dart.strong.expect
@@ -0,0 +1,48 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "package:expect/expect.dart" as exp;
+
+import "package:expect/expect.dart";
+
+static const field core::int var1 = #C1;
+static const field core::String var2 = #C2;
+static const field core::int var3 = #C3;
+static const field core::int var4 = #C4;
+static const field core::int var5 = #C5;
+static method function1(core::int a, core::int b) → core::int {
+  core::int x = 1.{core::num::+}(a).{core::num::+}(b);
+  return x;
+}
+static method function2() → core::String {
+  dynamic x = "string";
+  return x as{TypeError,ForDynamic,ForNonNullableByDefault} core::String;
+}
+static method function3() → core::int {
+  core::int first = 2;
+  core::int second = 2.{core::num::+}(first);
+  return 2.{core::num::+}(second);
+}
+static method function4() → core::int {
+  core::int first = 2;
+  core::int second = 0;
+  return first.{core::num::+}(second);
+}
+static method function5() → core::int {
+  return #C5;
+}
+static method main() → void {
+  exp::Expect::equals(#C1, 4);
+  exp::Expect::equals(#C2, "string");
+  exp::Expect::equals(#C3, 6);
+  exp::Expect::equals(#C4, 2);
+  exp::Expect::equals(#C5, 2.{core::int::unary-}());
+}
+
+constants  {
+  #C1 = 4
+  #C2 = "string"
+  #C3 = 6
+  #C4 = 2
+  #C5 = -2
+}
diff --git a/pkg/front_end/testcases/const_functions/const_functions_variable_declarations.dart.strong.transformed.expect b/pkg/front_end/testcases/const_functions/const_functions_variable_declarations.dart.strong.transformed.expect
new file mode 100644
index 0000000..cf33701
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_variable_declarations.dart.strong.transformed.expect
@@ -0,0 +1,52 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "package:expect/expect.dart" as exp;
+
+import "package:expect/expect.dart";
+
+static const field core::int var1 = #C1;
+static const field core::String var2 = #C2;
+static const field core::int var3 = #C3;
+static const field core::int var4 = #C4;
+static const field core::int var5 = #C5;
+static method function1(core::int a, core::int b) → core::int {
+  core::int x = 1.{core::num::+}(a).{core::num::+}(b);
+  return x;
+}
+static method function2() → core::String {
+  dynamic x = "string";
+  return x as{TypeError,ForDynamic,ForNonNullableByDefault} core::String;
+}
+static method function3() → core::int {
+  core::int first = 2;
+  core::int second = 2.{core::num::+}(first);
+  return 2.{core::num::+}(second);
+}
+static method function4() → core::int {
+  core::int first = 2;
+  core::int second = 0;
+  return first.{core::num::+}(second);
+}
+static method function5() → core::int {
+  return #C5;
+}
+static method main() → void {
+  exp::Expect::equals(#C1, 4);
+  exp::Expect::equals(#C2, "string");
+  exp::Expect::equals(#C3, 6);
+  exp::Expect::equals(#C4, 2);
+  exp::Expect::equals(#C5, 2.{core::int::unary-}());
+}
+
+constants  {
+  #C1 = 4
+  #C2 = "string"
+  #C3 = 6
+  #C4 = 2
+  #C5 = -2
+}
+
+Extra constant evaluation status:
+Evaluated: MethodInvocation @ org-dartlang-testcase:///const_functions_variable_declarations.dart:48:23 -> IntConstant(-2)
+Extra constant evaluation: evaluated: 20, effectively constant: 1
diff --git a/pkg/front_end/testcases/const_functions/const_functions_variable_declarations.dart.textual_outline.expect b/pkg/front_end/testcases/const_functions/const_functions_variable_declarations.dart.textual_outline.expect
new file mode 100644
index 0000000..723b134
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_variable_declarations.dart.textual_outline.expect
@@ -0,0 +1,13 @@
+import "package:expect/expect.dart";
+
+const var1 = function1(1, 2);
+int function1(int a, int b) {}
+const var2 = function2();
+String function2() {}
+const var3 = function3();
+int function3() {}
+const var4 = function4();
+int function4() {}
+const var5 = function5();
+int function5() {}
+void main() {}
diff --git a/pkg/front_end/testcases/const_functions/const_functions_variable_declarations.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/const_functions/const_functions_variable_declarations.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..d1c0ddb
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_variable_declarations.dart.textual_outline_modelled.expect
@@ -0,0 +1,13 @@
+import "package:expect/expect.dart";
+
+String function2() {}
+const var1 = function1(1, 2);
+const var2 = function2();
+const var3 = function3();
+const var4 = function4();
+const var5 = function5();
+int function1(int a, int b) {}
+int function3() {}
+int function4() {}
+int function5() {}
+void main() {}
diff --git a/pkg/front_end/testcases/const_functions/const_functions_variable_declarations.dart.weak.expect b/pkg/front_end/testcases/const_functions/const_functions_variable_declarations.dart.weak.expect
new file mode 100644
index 0000000..0be3638
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_variable_declarations.dart.weak.expect
@@ -0,0 +1,48 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "package:expect/expect.dart" as exp;
+
+import "package:expect/expect.dart";
+
+static const field core::int var1 = #C1;
+static const field core::String var2 = #C2;
+static const field core::int var3 = #C3;
+static const field core::int var4 = #C4;
+static const field core::int var5 = #C5;
+static method function1(core::int a, core::int b) → core::int {
+  core::int x = 1.{core::num::+}(a).{core::num::+}(b);
+  return x;
+}
+static method function2() → core::String {
+  dynamic x = "string";
+  return x as{TypeError,ForDynamic,ForNonNullableByDefault} core::String;
+}
+static method function3() → core::int {
+  core::int first = 2;
+  core::int second = 2.{core::num::+}(first);
+  return 2.{core::num::+}(second);
+}
+static method function4() → core::int {
+  core::int first = 2;
+  core::int second = 0;
+  return first.{core::num::+}(second);
+}
+static method function5() → core::int {
+  return #C5;
+}
+static method main() → void {
+  exp::Expect::equals(#C1, 4);
+  exp::Expect::equals(#C2, "string");
+  exp::Expect::equals(#C3, 6);
+  exp::Expect::equals(#C4, 2);
+  exp::Expect::equals(#C5, 2.{core::int::unary-}());
+}
+
+constants  {
+  #C1 = 4
+  #C2 = "string"
+  #C3 = 6
+  #C4 = 2
+  #C5 = -2
+}
diff --git a/pkg/front_end/testcases/const_functions/const_functions_variable_declarations.dart.weak.outline.expect b/pkg/front_end/testcases/const_functions/const_functions_variable_declarations.dart.weak.outline.expect
new file mode 100644
index 0000000..5941219
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_variable_declarations.dart.weak.outline.expect
@@ -0,0 +1,23 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+import "package:expect/expect.dart";
+
+static const field core::int var1 = self::function1(1, 2);
+static const field core::String var2 = self::function2();
+static const field core::int var3 = self::function3();
+static const field core::int var4 = self::function4();
+static const field core::int var5 = self::function5();
+static method function1(core::int a, core::int b) → core::int
+  ;
+static method function2() → core::String
+  ;
+static method function3() → core::int
+  ;
+static method function4() → core::int
+  ;
+static method function5() → core::int
+  ;
+static method main() → void
+  ;
diff --git a/pkg/front_end/testcases/const_functions/const_functions_variable_declarations.dart.weak.transformed.expect b/pkg/front_end/testcases/const_functions/const_functions_variable_declarations.dart.weak.transformed.expect
new file mode 100644
index 0000000..cf33701
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_variable_declarations.dart.weak.transformed.expect
@@ -0,0 +1,52 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "package:expect/expect.dart" as exp;
+
+import "package:expect/expect.dart";
+
+static const field core::int var1 = #C1;
+static const field core::String var2 = #C2;
+static const field core::int var3 = #C3;
+static const field core::int var4 = #C4;
+static const field core::int var5 = #C5;
+static method function1(core::int a, core::int b) → core::int {
+  core::int x = 1.{core::num::+}(a).{core::num::+}(b);
+  return x;
+}
+static method function2() → core::String {
+  dynamic x = "string";
+  return x as{TypeError,ForDynamic,ForNonNullableByDefault} core::String;
+}
+static method function3() → core::int {
+  core::int first = 2;
+  core::int second = 2.{core::num::+}(first);
+  return 2.{core::num::+}(second);
+}
+static method function4() → core::int {
+  core::int first = 2;
+  core::int second = 0;
+  return first.{core::num::+}(second);
+}
+static method function5() → core::int {
+  return #C5;
+}
+static method main() → void {
+  exp::Expect::equals(#C1, 4);
+  exp::Expect::equals(#C2, "string");
+  exp::Expect::equals(#C3, 6);
+  exp::Expect::equals(#C4, 2);
+  exp::Expect::equals(#C5, 2.{core::int::unary-}());
+}
+
+constants  {
+  #C1 = 4
+  #C2 = "string"
+  #C3 = 6
+  #C4 = 2
+  #C5 = -2
+}
+
+Extra constant evaluation status:
+Evaluated: MethodInvocation @ org-dartlang-testcase:///const_functions_variable_declarations.dart:48:23 -> IntConstant(-2)
+Extra constant evaluation: evaluated: 20, effectively constant: 1
diff --git a/tests/language/const_functions/const_functions_variable_declarations_test.dart b/tests/language/const_functions/const_functions_variable_declarations_test.dart
new file mode 100644
index 0000000..1bd577f
--- /dev/null
+++ b/tests/language/const_functions/const_functions_variable_declarations_test.dart
@@ -0,0 +1,59 @@
+// 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.
+
+// Tests creating new local variables within const functions.
+
+// SharedOptions=--enable-experiment=const-functions
+
+import "package:expect/expect.dart";
+
+const var1 = function1(1, 2);
+//           ^^^^^^^^^^^^^^^
+// [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
+int function1(int a, int b) {
+  var x = 1 + a + b;
+  return x;
+}
+
+const var2 = function2();
+//           ^^^^^^^^^^^
+// [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
+String function2() {
+  dynamic x = "string";
+  return x;
+}
+
+const var3 = function3();
+//           ^^^^^^^^^^^
+// [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
+int function3() {
+  var first = 2;
+  var second = 2 + first;
+  return 2 + second;
+}
+
+const var4 = function4();
+//           ^^^^^^^^^^^
+// [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
+int function4() {
+  var first = 2;
+  var second = 0;
+  return first + second;
+}
+
+const var5 = function5();
+//           ^^^^^^^^^^^
+// [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
+int function5() {
+  const constant = -2;
+  return constant;
+}
+
+void main() {
+  Expect.equals(var1, 4);
+  Expect.equals(var2, "string");
+  Expect.equals(var3, 6);
+  Expect.equals(var4, 2);
+  Expect.equals(var5, -2);
+}
diff --git a/tools/VERSION b/tools/VERSION
index cffe45d2..5f9ce2d 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 13
 PATCH 0
-PRERELEASE 118
+PRERELEASE 119
 PRERELEASE_PATCH 0
\ No newline at end of file