Version 2.13.0-37.0.dev
Merge commit '4216dc8d122f410d38d70ef6fc9bcedc5f2a0f0e' into 'dev'
diff --git a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
index 85e7a89..614454c 100644
--- a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
@@ -1226,7 +1226,9 @@
replacementNode = buildStaticInvocation(
resolvedTarget,
forest.createArguments(noLocation, arguments.positional,
- types: arguments.types, named: arguments.named),
+ types: arguments.types,
+ named: arguments.named,
+ hasExplicitTypeArguments: hasExplicitTypeArguments(arguments)),
constness:
isConst ? Constness.explicitConst : Constness.explicitNew,
charOffset: fileOffset);
@@ -1298,7 +1300,10 @@
}
Arguments invocationArguments = forest.createArguments(
noLocation, invocation.arguments.positional,
- types: invocationTypeArguments, named: invocation.arguments.named);
+ types: invocationTypeArguments,
+ named: invocation.arguments.named,
+ hasExplicitTypeArguments:
+ hasExplicitTypeArguments(invocation.arguments));
invocation.replaceWith(_resolveRedirectingFactoryTarget(invocation.target,
invocationArguments, invocation.fileOffset, invocation.isConst));
}
@@ -4162,7 +4167,8 @@
isConst: isConst)
..fileOffset = charOffset;
libraryBuilder.checkBoundsInFactoryInvocation(
- node, typeEnvironment, uri);
+ node, typeEnvironment, uri,
+ inferred: !hasExplicitTypeArguments(arguments));
} else {
node = new TypeAliasedFactoryInvocationJudgment(
typeAliasBuilder, target, arguments,
diff --git a/pkg/front_end/lib/src/fasta/kernel/forest.dart b/pkg/front_end/lib/src/fasta/kernel/forest.dart
index 4c6d8d9..30344fd 100644
--- a/pkg/front_end/lib/src/fasta/kernel/forest.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/forest.dart
@@ -30,9 +30,18 @@
const Forest();
Arguments createArguments(int fileOffset, List<Expression> positional,
- {List<DartType> types, List<NamedExpression> named}) {
- return new ArgumentsImpl(positional, types: types, named: named)
- ..fileOffset = fileOffset ?? TreeNode.noOffset;
+ {List<DartType> types,
+ List<NamedExpression> named,
+ bool hasExplicitTypeArguments = true}) {
+ if (!hasExplicitTypeArguments) {
+ ArgumentsImpl arguments =
+ new ArgumentsImpl(positional, types: <DartType>[], named: named);
+ arguments.types.addAll(types);
+ return arguments;
+ } else {
+ return new ArgumentsImpl(positional, types: types, named: named)
+ ..fileOffset = fileOffset ?? TreeNode.noOffset;
+ }
}
Arguments createArgumentsForExtensionMethod(
diff --git a/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart b/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
index 4ad0b97..be8f723 100644
--- a/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
@@ -404,8 +404,7 @@
ExpressionInferenceResult visitConstructorInvocation(
ConstructorInvocation node, DartType typeContext) {
inferrer.inferConstructorParameterTypes(node.target);
- bool hasExplicitTypeArguments =
- getExplicitTypeArguments(node.arguments) != null;
+ bool hadExplicitTypeArguments = hasExplicitTypeArguments(node.arguments);
FunctionType functionType = replaceReturnType(
node.target.function
.computeThisFunctionType(inferrer.library.nonNullable),
@@ -415,7 +414,7 @@
isConst: node.isConst, staticTarget: node.target);
if (!inferrer.isTopLevel) {
SourceLibraryBuilder library = inferrer.library;
- if (!hasExplicitTypeArguments) {
+ if (!hadExplicitTypeArguments) {
library.checkBoundsInConstructorInvocation(
node, inferrer.typeSchemaEnvironment, inferrer.helper.uri,
inferred: true);
@@ -692,8 +691,7 @@
ExpressionInferenceResult visitFactoryConstructorInvocationJudgment(
FactoryConstructorInvocationJudgment node, DartType typeContext) {
- bool hadExplicitTypeArguments =
- getExplicitTypeArguments(node.arguments) != null;
+ bool hadExplicitTypeArguments = hasExplicitTypeArguments(node.arguments);
FunctionType functionType = replaceReturnType(
node.target.function
@@ -740,7 +738,7 @@
SourceLibraryBuilder library = inferrer.library;
library.checkBoundsInType(result.inferredType,
inferrer.typeSchemaEnvironment, inferrer.helper.uri, node.fileOffset,
- inferred: true);
+ inferred: true, allowSuperBounded: false);
if (inferrer.isNonNullableByDefault) {
if (node.target == inferrer.coreTypes.listDefaultConstructor) {
resultNode = inferrer.helper.wrapInProblem(node,
@@ -768,7 +766,7 @@
SourceLibraryBuilder library = inferrer.library;
library.checkBoundsInType(result.inferredType,
inferrer.typeSchemaEnvironment, inferrer.helper.uri, node.fileOffset,
- inferred: true);
+ inferred: true, allowSuperBounded: false);
if (inferrer.isNonNullableByDefault) {
if (node.target == inferrer.coreTypes.listDefaultConstructor) {
resultNode = inferrer.helper.wrapInProblem(node,
diff --git a/pkg/front_end/lib/src/fasta/kernel/internal_ast.dart b/pkg/front_end/lib/src/fasta/kernel/internal_ast.dart
index b5160f0..89039e8 100644
--- a/pkg/front_end/lib/src/fasta/kernel/internal_ast.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/internal_ast.dart
@@ -183,6 +183,10 @@
}
}
+bool hasExplicitTypeArguments(Arguments arguments) {
+ return getExplicitTypeArguments(arguments) != null;
+}
+
/// Information associated with a class during type inference.
class ClassInferenceInfo {
/// The builder associated with this class.
diff --git a/pkg/front_end/test/compile_benchmark.dart b/pkg/front_end/test/compile_benchmark.dart
new file mode 100644
index 0000000..da61b76
--- /dev/null
+++ b/pkg/front_end/test/compile_benchmark.dart
@@ -0,0 +1,458 @@
+// @dart = 2.9
+
+import 'dart:convert';
+import 'dart:io';
+import 'dart:typed_data';
+
+import 'package:front_end/src/fasta/kernel/utils.dart';
+import 'package:kernel/kernel.dart';
+import 'package:kernel/binary/ast_from_binary.dart';
+
+import "simple_stats.dart";
+
+/// This pass all parameters after "--" to the compiler (via `compileEntryPoint`
+/// via the helper `compile_benchmark_helper.dart`). It is meant to "benchmark"/
+/// instrument the compiler to give insights into specific runs, i.e. specific
+/// compiles (compiling program `a` and program `b` might have different
+/// characteristics). Said another way, it will always instrument and give
+/// insights on *the compiler*, not the program it's asked to compile (not
+/// directly anyway -- if you want to know if that program successfully
+/// exercises a specific part of the compiler it _does_ do that).
+
+final Uri benchmarkHelper =
+ Platform.script.resolve("compile_benchmark_helper.dart");
+
+main(List<String> args) {
+ List<String> arguments;
+ bool tryToAnnotate = false;
+ bool tryToSlowDown = false;
+ bool timeInsteadOfCount = false;
+ for (int i = 0; i < args.length; i++) {
+ if (args[i] == "--tryToAnnotate") {
+ tryToAnnotate = true;
+ } else if (args[i] == "--tryToSlowDown") {
+ tryToSlowDown = true;
+ } else if (args[i] == "--timeInsteadOfCount") {
+ timeInsteadOfCount = true;
+ } else if (args[i] == "--") {
+ arguments = args.sublist(i + 1);
+ break;
+ } else {
+ throw "Unknown argument '${args[i]}'";
+ }
+ }
+ if (arguments == null || arguments.isEmpty) {
+ throw "No arguments given to compiler.\n"
+ "Give arguments as `dart compile_benchmark.dart -- "
+ "argument1 argument2 (etc)`";
+ }
+
+ Directory tmp = Directory.systemTemp.createTempSync("benchmark");
+ try {
+ // Compile the helper to get a dill we can compile with.
+ final Uri helperDill = tmp.uri.resolve("compile.dill");
+
+ print("Compiling $benchmarkHelper into $helperDill");
+ runXTimes(1, [
+ benchmarkHelper.toString(),
+ benchmarkHelper.toString(),
+ "-o",
+ helperDill.toString(),
+ ]);
+ File f = new File.fromUri(helperDill);
+ if (!f.existsSync()) throw "$f doesn't exist!";
+
+ List<int> dillData = new File.fromUri(helperDill).readAsBytesSync();
+ doWork(
+ tmp,
+ dillData,
+ arguments,
+ tryToAnnotate: tryToAnnotate,
+ tryToSlowDown: tryToSlowDown,
+ timeInsteadOfCount: timeInsteadOfCount,
+ );
+ } finally {
+ tmp.deleteSync(recursive: true);
+ }
+}
+
+/// Perform asked operations on the dill data provided.
+///
+/// Will save files into the provided tmp directory, do timing or counting
+/// instructions and run that with the arguments passed, and then do comparative
+/// runs of transformed/non-transformed versions of the dill. The possible
+/// transformations are:
+/// * [tryToAnnotate] which will add a `@pragma("vm:prefer-inline")` annotation
+/// to each procedure one at a time.
+/// * [tryToSlowDown] which will add a busywait for approximately 0.002 ms to
+/// each procedure one at a time. (Unsurprisingly this makes it slower
+/// proportional to the number of times the procedure is called, so the
+/// counting annotation is likely at least as useful).
+///
+void doWork(Directory tmp, List<int> dillData, List<String> arguments,
+ {bool tryToAnnotate: false,
+ bool tryToSlowDown: false,
+ bool timeInsteadOfCount: false}) {
+ File f = new File.fromUri(tmp.uri.resolve("a.dill"));
+ f.writeAsBytesSync(dillData);
+ Uri dillOrgInTmp = f.uri;
+ print("Wrote to $f");
+
+ List<Procedure> sortedProcedures;
+ if (timeInsteadOfCount) {
+ sortedProcedures = doTimingInstrumentation(dillData, tmp, arguments);
+ } else {
+ sortedProcedures = doCountingInstrumentation(dillData, tmp, arguments);
+ }
+ print("\n\n");
+
+ bool didSomething = false;
+
+ if (tryToAnnotate) {
+ didSomething = true;
+ for (Procedure p in sortedProcedures) {
+ print("Prefer inline $p (${p.location})");
+ Uri preferredInlined = preferInlineProcedure(
+ dillData,
+ tmp.uri,
+ (lib) => lib.importUri == p.enclosingLibrary.importUri,
+ p.enclosingClass?.name,
+ p.name.text);
+
+ print("\nOriginal runs:");
+ List<int> runtimesA =
+ runXTimes(5, [dillOrgInTmp.toString(), ...arguments]);
+
+ print("\nModified runs:");
+ List<int> runtimesB =
+ runXTimes(5, [preferredInlined.toString(), ...arguments]);
+
+ print(SimpleTTestStat.ttest(runtimesB, runtimesA));
+ print("\n------------\n");
+ }
+ }
+
+ if (tryToSlowDown) {
+ didSomething = true;
+ for (Procedure p in sortedProcedures) {
+ Uri busyWaiting = busyWaitProcedure(
+ dillData,
+ tmp.uri,
+ (lib) => lib.importUri == p.enclosingLibrary.importUri,
+ p.enclosingClass?.name,
+ p.name.text);
+ if (busyWaiting == null) continue;
+
+ print("Slow down $p (${p.location})");
+
+ print("\nOriginal runs:");
+ List<int> runtimesA =
+ runXTimes(2, [dillOrgInTmp.toString(), ...arguments]);
+
+ print("\nModified runs:");
+ List<int> runtimesB =
+ runXTimes(2, [busyWaiting.toString(), ...arguments]);
+
+ print(SimpleTTestStat.ttest(runtimesB, runtimesA));
+ print("\n------------\n");
+ }
+ }
+
+ if (!didSomething) {
+ runXTimes(10, [dillOrgInTmp.toString(), ...arguments]);
+ }
+}
+
+/// Instrument the [dillData] so that each procedure-call can be registered
+/// (in package:front_end) and we find out how many times each procedure is
+/// called for a specific run, then run it, print the result and return the
+/// procedures in sorted order (most calls first).
+List<Procedure> doCountingInstrumentation(
+ List<int> dillData, Directory tmp, List<String> arguments) {
+ Instrumented instrumented = instrumentCallsCount(dillData, tmp.uri);
+ List<dynamic> stdout = [];
+ runXTimes(1, [instrumented.dill.toString(), ...arguments], stdout);
+ List<int> procedureCountsTmp = new List<int>.from(jsonDecode(stdout.single));
+ List<IntPair> procedureCounts = [];
+ for (int i = 0; i < procedureCountsTmp.length; i += 2) {
+ procedureCounts
+ .add(new IntPair(procedureCountsTmp[i], procedureCountsTmp[i + 1]));
+ }
+ // Sort highest call-count first.
+ procedureCounts.sort((a, b) => b.value - a.value);
+ List<Procedure> sortedProcedures = [];
+ for (IntPair p in procedureCounts) {
+ if (p.value > 1000) {
+ Procedure procedure = instrumented.procedures[p.key];
+ String location = procedure.location.toString();
+ if (location.length > 50) {
+ location = location.substring(location.length - 50);
+ }
+ print("Called $procedure ${p.value} times ($location)");
+ sortedProcedures.add(procedure);
+ }
+ }
+ return sortedProcedures;
+}
+
+/// Instrument the [dillData] so that each (sync) procedure-call can be timed
+/// (time on stack, i.e. not only the procedure itself, but also the
+/// procedure-calls it makes) (in package:front_end) and we find out how long
+/// each procedure is on the stack for a specific run, then run it, print the
+/// result and return the procedures in sorted order (most time on stack first).
+List<Procedure> doTimingInstrumentation(
+ List<int> dillData, Directory tmp, List<String> arguments) {
+ Instrumented instrumented = instrumentCallsTiming(dillData, tmp.uri);
+ List<dynamic> stdout = [];
+ runXTimes(1, [instrumented.dill.toString(), ...arguments], stdout);
+ List<int> procedureTimeTmp = new List<int>.from(jsonDecode(stdout.single));
+ List<IntPair> procedureTime = [];
+ for (int i = 0; i < procedureTimeTmp.length; i += 2) {
+ procedureTime
+ .add(new IntPair(procedureTimeTmp[i], procedureTimeTmp[i + 1]));
+ }
+ // Sort highest time-on-stack first.
+ procedureTime.sort((a, b) => b.value - a.value);
+ List<Procedure> sortedProcedures = [];
+ for (IntPair p in procedureTime) {
+ if (p.value > 1000) {
+ Procedure procedure = instrumented.procedures[p.key];
+ String location = procedure.location.toString();
+ if (location.length > 50) {
+ location = location.substring(location.length - 50);
+ }
+ print("$procedure was on stack for ${p.value} microseconds ($location)");
+ sortedProcedures.add(procedure);
+ }
+ }
+ return sortedProcedures;
+}
+
+class IntPair {
+ final int key;
+ final int value;
+
+ IntPair(this.key, this.value);
+
+ String toString() {
+ return "IntPair[$key: $value]";
+ }
+}
+
+/// Adds the annotation `@pragma("vm:prefer-inline")` to the specified procedure
+/// and serialize the resulting dill into `b.dill` (return uri).
+///
+/// The annotation is copied from the [preferInlineMe] method in the helper.
+Uri preferInlineProcedure(List<int> dillData, Uri tmp,
+ bool libraryMatcher(Library lib), String className, String procedureName) {
+ Component component = new Component();
+ new BinaryBuilder(dillData, disableLazyReading: true)
+ .readComponent(component);
+ Procedure preferInlineMeProcedure = getProcedure(component,
+ (lib) => lib.fileUri == benchmarkHelper, null, "preferInlineMe");
+ ConstantExpression annotation = preferInlineMeProcedure.annotations.single;
+ Procedure markProcedure =
+ getProcedure(component, libraryMatcher, className, procedureName);
+ markProcedure.addAnnotation(
+ new ConstantExpression(annotation.constant, annotation.type));
+
+ Uint8List newDillData = serializeComponent(component);
+ File f = new File.fromUri(tmp.resolve("b.dill"));
+ f.writeAsBytesSync(newDillData);
+ return f.uri;
+}
+
+/// Makes the procedure specified call [busyWait] from the helper and serialize
+/// the resulting dill into `c.dill` (return uri).
+///
+/// This will make the procedure busy-wait approximately 0.002 ms for each
+/// invocation (+ whatever overhead and imprecision).
+Uri busyWaitProcedure(List<int> dillData, Uri tmp,
+ bool libraryMatcher(Library lib), String className, String procedureName) {
+ Component component = new Component();
+ new BinaryBuilder(dillData, disableLazyReading: true)
+ .readComponent(component);
+ Procedure busyWaitProcedure = getProcedure(
+ component, (lib) => lib.fileUri == benchmarkHelper, null, "busyWait");
+
+ Procedure markProcedure =
+ getProcedure(component, libraryMatcher, className, procedureName);
+ if (markProcedure.function.body == null) return null;
+
+ Statement orgBody = markProcedure.function.body;
+ markProcedure.function.body = new Block([
+ new ExpressionStatement(new StaticInvocation(
+ busyWaitProcedure, new Arguments([new IntLiteral(2 /* 0.002 ms */)]))),
+ orgBody
+ ])
+ ..parent = markProcedure.function;
+
+ Uint8List newDillData = serializeComponent(component);
+ File f = new File.fromUri(tmp.resolve("c.dill"));
+ f.writeAsBytesSync(newDillData);
+ return f.uri;
+}
+
+/// Instrument the [dillData] so that each procedure-call can be registered
+/// (in package:front_end) and we find out how many times each procedure is
+/// called for a specific run.
+///
+/// Uses the [registerCall] in the helper.
+/// Numbers each procedure, saves the instrumented dill and returns both the
+/// dill and the list of procedures so that procedure i in the list will be
+/// annotated with a call to `registerCall(i)`.
+Instrumented instrumentCallsCount(List<int> dillData, Uri tmp) {
+ Component component = new Component();
+ new BinaryBuilder(dillData, disableLazyReading: true)
+ .readComponent(component);
+ Procedure registerCallProcedure = getProcedure(
+ component, (lib) => lib.fileUri == benchmarkHelper, null, "registerCall");
+ RegisterCallTransformer registerCallTransformer =
+ new RegisterCallTransformer(registerCallProcedure);
+ component.accept(registerCallTransformer);
+
+ Uint8List newDillData = serializeComponent(component);
+ File f = new File.fromUri(tmp.resolve("counting.dill"));
+ f.writeAsBytesSync(newDillData);
+
+ return new Instrumented(f.uri, registerCallTransformer.procedures);
+}
+
+/// Instrument the [dillData] so that each (sync) procedure-call can be timed
+/// (time on stack, i.e. not only the procedure itself, but also the
+/// procedure-calls it makes) (in package:front_end) and we find out how long
+/// each procedure is on the stack for a specific run.
+///
+/// Uses [registerCallStart] and [registerCallEnd] from the helper.
+/// Numbers each sync procedure, saves the instrumented dill and returns both
+/// the dill and the list of procedures so that procedure i in the list will be
+/// annotated with a call-pair to `registerCallStart(i)` and
+/// `registerCallEnd(i)`.
+Instrumented instrumentCallsTiming(List<int> dillData, Uri tmp) {
+ Component component = new Component();
+ new BinaryBuilder(dillData, disableLazyReading: true)
+ .readComponent(component);
+ Procedure registerCallStartProcedure = getProcedure(component,
+ (lib) => lib.fileUri == benchmarkHelper, null, "registerCallStart");
+ Procedure registerCallEndProcedure = getProcedure(component,
+ (lib) => lib.fileUri == benchmarkHelper, null, "registerCallEnd");
+ RegisterTimeTransformer registerTimeTransformer = new RegisterTimeTransformer(
+ registerCallStartProcedure, registerCallEndProcedure);
+ component.accept(registerTimeTransformer);
+
+ Uint8List newDillData = serializeComponent(component);
+ File f = new File.fromUri(tmp.resolve("timing.dill"));
+ f.writeAsBytesSync(newDillData);
+
+ return new Instrumented(f.uri, registerTimeTransformer.procedures);
+}
+
+/// Class holding both the uri of a saved dill file and a list of procedures (in
+/// order) that has some reference to the annotation added to the dill file.
+class Instrumented {
+ final Uri dill;
+ final List<Procedure> procedures;
+
+ Instrumented(this.dill, this.procedures);
+}
+
+class RegisterCallTransformer extends RecursiveVisitor {
+ final Procedure registerCallProcedure;
+ RegisterCallTransformer(this.registerCallProcedure);
+ List<Procedure> procedures = [];
+
+ visitLibrary(Library node) {
+ if (node.importUri.scheme == "package" &&
+ node.importUri.pathSegments.first == "front_end") {
+ super.visitLibrary(node);
+ }
+ }
+
+ visitProcedure(Procedure node) {
+ if (node.function.body == null) return;
+ int procedureNum = procedures.length;
+ procedures.add(node);
+ Statement orgBody = node.function.body;
+ node.function.body = new Block([
+ new ExpressionStatement(new StaticInvocation(registerCallProcedure,
+ new Arguments([new IntLiteral(procedureNum)]))),
+ orgBody
+ ]);
+ node.function.body.parent = node.function;
+ }
+}
+
+class RegisterTimeTransformer extends RecursiveVisitor {
+ final Procedure registerCallStartProcedure;
+ final Procedure registerCallEndProcedure;
+
+ RegisterTimeTransformer(
+ this.registerCallStartProcedure, this.registerCallEndProcedure);
+
+ List<Procedure> procedures = [];
+
+ visitLibrary(Library node) {
+ if (node.importUri.scheme == "package" &&
+ node.importUri.pathSegments.first == "front_end") {
+ super.visitLibrary(node);
+ }
+ }
+
+ visitProcedure(Procedure node) {
+ if (node.function.body == null) return;
+ if (node.function.dartAsyncMarker != AsyncMarker.Sync) return;
+ int procedureNum = procedures.length;
+ procedures.add(node);
+ Statement orgBody = node.function.body;
+ // Rewrite as
+ // {
+ // registerCallStartProcedure(x);
+ // try {
+ // originalBody
+ // } finally {
+ // registerCallEndProcedure(x);
+ // }
+ // }
+ Block block = new Block([
+ new ExpressionStatement(new StaticInvocation(registerCallStartProcedure,
+ new Arguments([new IntLiteral(procedureNum)]))),
+ new TryFinally(
+ orgBody,
+ new ExpressionStatement(new StaticInvocation(registerCallEndProcedure,
+ new Arguments([new IntLiteral(procedureNum)]))),
+ )
+ ]);
+ node.function.body = block;
+ node.function.body.parent = node.function;
+ }
+}
+
+Procedure getProcedure(Component component, bool libraryMatcher(Library lib),
+ String className, String procedureName) {
+ Library lib = component.libraries.where(libraryMatcher).single;
+ List<Procedure> procedures = lib.procedures;
+ if (className != null) {
+ Class cls = lib.classes.where((c) => c.name == className).single;
+ procedures = cls.procedures;
+ }
+ // TODO: This will fail for getter/setter pairs. Fix that.
+ return procedures.where((p) => p.name.text == procedureName).single;
+}
+
+List<int> runXTimes(int x, List<String> arguments, [List<dynamic> stdout]) {
+ List<int> result = [];
+ Stopwatch stopwatch = new Stopwatch()..start();
+ for (int i = 0; i < x; i++) {
+ stopwatch.reset();
+ ProcessResult run = Process.runSync(Platform.resolvedExecutable, arguments,
+ runInShell: true);
+ int ms = stopwatch.elapsedMilliseconds;
+ result.add(ms);
+ print(ms);
+ if (run.exitCode != 0) throw "Got exit code ${run.exitCode}";
+ if (stdout != null) {
+ stdout.add(run.stdout);
+ }
+ }
+ return result;
+}
diff --git a/pkg/front_end/test/compile_benchmark_helper.dart b/pkg/front_end/test/compile_benchmark_helper.dart
new file mode 100644
index 0000000..1b187e8
--- /dev/null
+++ b/pkg/front_end/test/compile_benchmark_helper.dart
@@ -0,0 +1,100 @@
+// Copyright (c) 2017, 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.9
+
+import '../tool/_fasta/entry_points.dart' show compileEntryPoint;
+
+main(List<String> arguments) async {
+ await compileEntryPoint(arguments);
+ if (numCalls.isNotEmpty) {
+ print("[");
+ bool printed = false;
+ for (int i = 0; i < numCalls.length; i++) {
+ int value = numCalls[i];
+ if (value != null && value > 0) {
+ if (printed) print(",");
+ print("$i, $value");
+ printed = true;
+ }
+ }
+ print("]");
+ } else if (inCall.isNotEmpty) {
+ print("[");
+ bool printed = false;
+ for (int i = 0; i < inCall.length; i++) {
+ int value = inCall[i];
+ if (value == null) continue;
+ if (value != 0) throw "$i has value $value";
+ if (printed) print(",");
+ int time = callTimes[i];
+ print("$i, $time");
+ printed = true;
+ }
+ print("]");
+ }
+}
+
+List<int> numCalls = [];
+
+void registerCall(int procedureNum) {
+ while (numCalls.length <= procedureNum) {
+ if (numCalls.length < 8) {
+ numCalls.length = 8;
+ } else {
+ numCalls.length *= 2;
+ }
+ }
+ numCalls[procedureNum] ??= 0;
+ numCalls[procedureNum]++;
+}
+
+List<int> inCall = [];
+List<int> callTimes = [];
+Stopwatch stopwatch = new Stopwatch()..start();
+
+void registerCallStart(int procedureNum) {
+ while (inCall.length <= procedureNum) {
+ if (inCall.length < 8) {
+ inCall.length = 8;
+ callTimes.length = 8;
+ } else {
+ inCall.length *= 2;
+ callTimes.length *= 2;
+ }
+ }
+ inCall[procedureNum] ??= 0;
+ callTimes[procedureNum] ??= 0;
+ if (inCall[procedureNum]++ == 0) {
+ // First --- start a timer-ish.
+ callTimes[procedureNum] -= stopwatch.elapsedMicroseconds;
+ }
+}
+
+void registerCallEnd(int procedureNum) {
+ if (inCall[procedureNum]-- == 1) {
+ // Last --- stop the timer-ish.
+ callTimes[procedureNum] += stopwatch.elapsedMicroseconds;
+ }
+}
+
+@pragma("vm:prefer-inline")
+void preferInlineMe() {
+ // This makes it easier to get the annotation :).
+}
+
+int busyWait(int micro) {
+ int count = 0;
+ Stopwatch stopwatch = new Stopwatch()..start();
+ while (true) {
+ for (int i = 0; i < 1000; i++) {
+ count++;
+ }
+ int elapsed = stopwatch.elapsedMicroseconds;
+ if (elapsed >= micro) {
+ print("Bye from busywait after $count iterations ($elapsed vs $micro)!");
+ return count;
+ }
+ }
+}
diff --git a/pkg/front_end/test/simple_stats.dart b/pkg/front_end/test/simple_stats.dart
index b163e67..dd69a4e 100644
--- a/pkg/front_end/test/simple_stats.dart
+++ b/pkg/front_end/test/simple_stats.dart
@@ -7,9 +7,9 @@
import 'dart:math' as math;
class SimpleTTestStat {
- static TTestResult ttest(List<int> a, List<int> b) {
- int aSum = a.reduce((value, element) => (value + element));
- int bSum = b.reduce((value, element) => (value + element));
+ static TTestResult ttest<E extends num>(List<E> a, List<E> b) {
+ E aSum = a.reduce((value, element) => (value + element));
+ E bSum = b.reduce((value, element) => (value + element));
int aCount = a.length;
int bCount = b.length;
double aMean = aSum / aCount;
@@ -36,13 +36,13 @@
}
}
- static double variance(List<int> data) {
- int sum = data.reduce((value, element) => (value + element));
+ static double variance<E extends num>(List<E> data) {
+ E sum = data.reduce((value, element) => (value + element));
int count = data.length;
double average = sum / count;
double diffSquareSum = 0;
- for (int value in data) {
+ for (E value in data) {
double diff = value - average;
double squared = diff * diff;
diffSquareSum += squared;
diff --git a/pkg/front_end/test/spell_checking_list_tests.txt b/pkg/front_end/test/spell_checking_list_tests.txt
index 2ec515d..1b92362 100644
--- a/pkg/front_end/test/spell_checking_list_tests.txt
+++ b/pkg/front_end/test/spell_checking_list_tests.txt
@@ -86,6 +86,8 @@
builddir
bulk2
bulkcompile
+busy
+busywait
bye
c's
c59cdee365b94ce066344840f9e3412d642019b
@@ -99,6 +101,7 @@
ccc
cell
cf
+characteristics
charset
checkme
checkout
@@ -127,6 +130,7 @@
commented
commit
companion
+comparative
comparer
compilations
compiler's
@@ -362,6 +366,7 @@
image
images
implementor
+imprecision
in1
in2
inclosure
@@ -375,7 +380,9 @@
inlinable
inlineable
inlines
+insights
instance2
+instrument
insufficient
intdiv
interactive
@@ -527,6 +534,7 @@
out2
outbound
outlined
+overhead
overlay
ox
pack
@@ -566,6 +574,7 @@
property5b
property8a
property8b
+proportional
protected
proved
provider
@@ -627,6 +636,7 @@
rows
runtimes
rv
+saves
scans
scheduler
screen
@@ -750,6 +760,7 @@
unpatched
unpaused
unregistered
+unsurprisingly
untransformed
untrimmed
unusual
diff --git a/pkg/front_end/testcases/nonfunction_type_aliases/issue42446.dart b/pkg/front_end/testcases/nonfunction_type_aliases/issue42446.dart
new file mode 100644
index 0000000..5b11f3d
--- /dev/null
+++ b/pkg/front_end/testcases/nonfunction_type_aliases/issue42446.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.
+
+class A<X extends A<X>> {}
+typedef B<X extends A<X>> = A<X>;
+
+class A2<X extends A2<X>> {
+ factory A2() => throw 42;
+}
+typedef B2<X extends A2<X>> = A2<X>;
+
+foo() {
+ B(); // Error.
+ A(); // Error.
+ B2(); // Error.
+ A2(); // Error.
+}
+
+main() {}
diff --git a/pkg/front_end/testcases/nonfunction_type_aliases/issue42446.dart.textual_outline.expect b/pkg/front_end/testcases/nonfunction_type_aliases/issue42446.dart.textual_outline.expect
new file mode 100644
index 0000000..611ce04
--- /dev/null
+++ b/pkg/front_end/testcases/nonfunction_type_aliases/issue42446.dart.textual_outline.expect
@@ -0,0 +1,8 @@
+class A<X extends A<X>> {}
+typedef B<X extends A<X>> = A<X>;
+class A2<X extends A2<X>> {
+ factory A2() => throw 42;
+}
+typedef B2<X extends A2<X>> = A2<X>;
+foo() {}
+main() {}
diff --git a/pkg/front_end/testcases/nonfunction_type_aliases/issue42446.dart.weak.expect b/pkg/front_end/testcases/nonfunction_type_aliases/issue42446.dart.weak.expect
new file mode 100644
index 0000000..29df1fe
--- /dev/null
+++ b/pkg/front_end/testcases/nonfunction_type_aliases/issue42446.dart.weak.expect
@@ -0,0 +1,65 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue42446.dart:14:3: Error: Inferred type argument 'A<Object?>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'A'.
+// - 'A' is from 'pkg/front_end/testcases/nonfunction_type_aliases/issue42446.dart'.
+// - 'Object' is from 'dart:core'.
+// Try specifying type arguments explicitly so that they conform to the bounds.
+// B(); // Error.
+// ^
+// pkg/front_end/testcases/nonfunction_type_aliases/issue42446.dart:5:9: Context: This is the type variable whose bound isn't conformed to.
+// class A<X extends A<X>> {}
+// ^
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue42446.dart:15:3: Error: Inferred type argument 'A<Object?>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'A'.
+// - 'A' is from 'pkg/front_end/testcases/nonfunction_type_aliases/issue42446.dart'.
+// - 'Object' is from 'dart:core'.
+// Try specifying type arguments explicitly so that they conform to the bounds.
+// A(); // Error.
+// ^
+// pkg/front_end/testcases/nonfunction_type_aliases/issue42446.dart:5:9: Context: This is the type variable whose bound isn't conformed to.
+// class A<X extends A<X>> {}
+// ^
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue42446.dart:16:3: Error: Inferred type argument 'A2<Object?>' doesn't conform to the bound 'A2<X>' of the type variable 'X' on 'A2'.
+// - 'A2' is from 'pkg/front_end/testcases/nonfunction_type_aliases/issue42446.dart'.
+// - 'Object' is from 'dart:core'.
+// Try specifying type arguments explicitly so that they conform to the bounds.
+// B2(); // Error.
+// ^
+// pkg/front_end/testcases/nonfunction_type_aliases/issue42446.dart:8:10: Context: This is the type variable whose bound isn't conformed to.
+// class A2<X extends A2<X>> {
+// ^
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue42446.dart:17:3: Error: Inferred type argument 'A2<Object?>' doesn't conform to the bound 'A2<X>' of the type variable 'X' on 'A2'.
+// - 'A2' is from 'pkg/front_end/testcases/nonfunction_type_aliases/issue42446.dart'.
+// - 'Object' is from 'dart:core'.
+// Try specifying type arguments explicitly so that they conform to the bounds.
+// A2(); // Error.
+// ^
+// pkg/front_end/testcases/nonfunction_type_aliases/issue42446.dart:8:10: Context: This is the type variable whose bound isn't conformed to.
+// class A2<X extends A2<X>> {
+// ^
+//
+import self as self;
+import "dart:core" as core;
+
+typedef B<X extends self::A<X> = self::A<dynamic>> = self::A<X>;
+typedef B2<X extends self::A2<X> = self::A2<dynamic>> = self::A2<X>;
+class A<X extends self::A<self::A::X> = self::A<dynamic>> extends core::Object {
+ synthetic constructor •() → self::A<self::A::X>
+ : super core::Object::•()
+ ;
+}
+class A2<X extends self::A2<self::A2::X> = self::A2<dynamic>> extends core::Object {
+ static factory •<X extends self::A2<self::A2::•::X> = self::A2<dynamic>>() → self::A2<self::A2::•::X>
+ return throw 42;
+}
+static method foo() → dynamic {
+ new self::A::•<self::A<core::Object?>>();
+ new self::A::•<self::A<core::Object?>>();
+ self::A2::•<self::A2<core::Object?>>();
+ self::A2::•<self::A2<core::Object?>>();
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/nonfunction_type_aliases/issue42446.dart.weak.transformed.expect b/pkg/front_end/testcases/nonfunction_type_aliases/issue42446.dart.weak.transformed.expect
new file mode 100644
index 0000000..29df1fe
--- /dev/null
+++ b/pkg/front_end/testcases/nonfunction_type_aliases/issue42446.dart.weak.transformed.expect
@@ -0,0 +1,65 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue42446.dart:14:3: Error: Inferred type argument 'A<Object?>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'A'.
+// - 'A' is from 'pkg/front_end/testcases/nonfunction_type_aliases/issue42446.dart'.
+// - 'Object' is from 'dart:core'.
+// Try specifying type arguments explicitly so that they conform to the bounds.
+// B(); // Error.
+// ^
+// pkg/front_end/testcases/nonfunction_type_aliases/issue42446.dart:5:9: Context: This is the type variable whose bound isn't conformed to.
+// class A<X extends A<X>> {}
+// ^
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue42446.dart:15:3: Error: Inferred type argument 'A<Object?>' doesn't conform to the bound 'A<X>' of the type variable 'X' on 'A'.
+// - 'A' is from 'pkg/front_end/testcases/nonfunction_type_aliases/issue42446.dart'.
+// - 'Object' is from 'dart:core'.
+// Try specifying type arguments explicitly so that they conform to the bounds.
+// A(); // Error.
+// ^
+// pkg/front_end/testcases/nonfunction_type_aliases/issue42446.dart:5:9: Context: This is the type variable whose bound isn't conformed to.
+// class A<X extends A<X>> {}
+// ^
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue42446.dart:16:3: Error: Inferred type argument 'A2<Object?>' doesn't conform to the bound 'A2<X>' of the type variable 'X' on 'A2'.
+// - 'A2' is from 'pkg/front_end/testcases/nonfunction_type_aliases/issue42446.dart'.
+// - 'Object' is from 'dart:core'.
+// Try specifying type arguments explicitly so that they conform to the bounds.
+// B2(); // Error.
+// ^
+// pkg/front_end/testcases/nonfunction_type_aliases/issue42446.dart:8:10: Context: This is the type variable whose bound isn't conformed to.
+// class A2<X extends A2<X>> {
+// ^
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue42446.dart:17:3: Error: Inferred type argument 'A2<Object?>' doesn't conform to the bound 'A2<X>' of the type variable 'X' on 'A2'.
+// - 'A2' is from 'pkg/front_end/testcases/nonfunction_type_aliases/issue42446.dart'.
+// - 'Object' is from 'dart:core'.
+// Try specifying type arguments explicitly so that they conform to the bounds.
+// A2(); // Error.
+// ^
+// pkg/front_end/testcases/nonfunction_type_aliases/issue42446.dart:8:10: Context: This is the type variable whose bound isn't conformed to.
+// class A2<X extends A2<X>> {
+// ^
+//
+import self as self;
+import "dart:core" as core;
+
+typedef B<X extends self::A<X> = self::A<dynamic>> = self::A<X>;
+typedef B2<X extends self::A2<X> = self::A2<dynamic>> = self::A2<X>;
+class A<X extends self::A<self::A::X> = self::A<dynamic>> extends core::Object {
+ synthetic constructor •() → self::A<self::A::X>
+ : super core::Object::•()
+ ;
+}
+class A2<X extends self::A2<self::A2::X> = self::A2<dynamic>> extends core::Object {
+ static factory •<X extends self::A2<self::A2::•::X> = self::A2<dynamic>>() → self::A2<self::A2::•::X>
+ return throw 42;
+}
+static method foo() → dynamic {
+ new self::A::•<self::A<core::Object?>>();
+ new self::A::•<self::A<core::Object?>>();
+ self::A2::•<self::A2<core::Object?>>();
+ self::A2::•<self::A2<core::Object?>>();
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/textual_outline.status b/pkg/front_end/testcases/textual_outline.status
index 68e7e6b..4c36c59 100644
--- a/pkg/front_end/testcases/textual_outline.status
+++ b/pkg/front_end/testcases/textual_outline.status
@@ -20,21 +20,12 @@
regress/issue_39091_2: EmptyOutput
regress/utf_16_le_content.crash: EmptyOutput
+
extensions/extension_constructor: FormatterCrash
extensions/extension_field_with_type_parameter_usage: FormatterCrash
extensions/issue38600: FormatterCrash
extensions/issue38712: FormatterCrash
extensions/issue38745: FormatterCrash
-general_nnbd_opt_out/annotation_eof: FormatterCrash
-general_nnbd_opt_out/bad_setter_abstract: FormatterCrash
-general_nnbd_opt_out/bug31124: FormatterCrash
-general_nnbd_opt_out/clone_function_type: FormatterCrash
-general_nnbd_opt_out/constructor_initializer_invalid: FormatterCrash
-general_nnbd_opt_out/duplicated_declarations: FormatterCrash
-general_nnbd_opt_out/function_type_default_value: FormatterCrash
-general_nnbd_opt_out/incomplete_field_formal_parameter: FormatterCrash
-general_nnbd_opt_out/many_errors: FormatterCrash
-general_nnbd_opt_out/var_as_type_name: FormatterCrash
general/annotation_eof: FormatterCrash
general/bad_setter_abstract: FormatterCrash
general/bug31124: FormatterCrash
@@ -62,21 +53,31 @@
general/error_recovery/issue_43090.crash: FormatterCrash
general/function_type_default_value: FormatterCrash
general/incomplete_field_formal_parameter: FormatterCrash
-general/invalid_operator: FormatterCrash
general/invalid_operator2: FormatterCrash
+general/invalid_operator: FormatterCrash
general/issue42997: FormatterCrash
general/issue43363: FormatterCrash
general/many_errors: FormatterCrash
general/null_safety_invalid_experiment_and_language_version: FormatterCrash
general/type_parameters_on_void: FormatterCrash
general/var_as_type_name: FormatterCrash
+general_nnbd_opt_out/annotation_eof: FormatterCrash
+general_nnbd_opt_out/bad_setter_abstract: FormatterCrash
+general_nnbd_opt_out/bug31124: FormatterCrash
+general_nnbd_opt_out/clone_function_type: FormatterCrash
+general_nnbd_opt_out/constructor_initializer_invalid: FormatterCrash
+general_nnbd_opt_out/duplicated_declarations: FormatterCrash
+general_nnbd_opt_out/function_type_default_value: FormatterCrash
+general_nnbd_opt_out/incomplete_field_formal_parameter: FormatterCrash
+general_nnbd_opt_out/many_errors: FormatterCrash
+general_nnbd_opt_out/var_as_type_name: FormatterCrash
inference/unsafe_block_closure_inference_function_call_explicit_dynamic_param_via_expr1: FormatterCrash
inference/unsafe_block_closure_inference_function_call_explicit_type_param_via_expr1: FormatterCrash
late_lowering/covariant_late_field: FormatterCrash
late_lowering/getter_vs_setter_type: FormatterCrash
late_lowering/infer_late_field_type: FormatterCrash
-late_lowering/initializer_rewrite_from_opt_out: FormatterCrash
late_lowering/initializer_rewrite: FormatterCrash
+late_lowering/initializer_rewrite_from_opt_out: FormatterCrash
late_lowering/instance_field_with_initializer: FormatterCrash
late_lowering/instance_field_without_initializer: FormatterCrash
late_lowering/instance_final_field_without_initializer: FormatterCrash
@@ -103,19 +104,16 @@
late_lowering/late_nullable_field_without_initializer: FormatterCrash
late_lowering/late_override: FormatterCrash
late_lowering/later: FormatterCrash
-late_lowering/override_getter_setter: FormatterCrash
late_lowering/override: FormatterCrash
+late_lowering/override_getter_setter: FormatterCrash
late_lowering/skip_late_final_uninitialized_instance_fields/main: FormatterCrash
late_lowering/uninitialized_non_nullable_late_fields: FormatterCrash
-nnbd_mixed/inheritance_from_opt_in: FormatterCrash
-nnbd_mixed/issue41597: FormatterCrash
-nnbd_mixed/null_safety_invalid_language_version: FormatterCrash
nnbd/abstract_field_errors: FormatterCrash
nnbd/covariant_late_field: FormatterCrash
-nnbd/duplicates_instance_extension: FormatterCrash
nnbd/duplicates_instance: FormatterCrash
-nnbd/duplicates_static_extension: FormatterCrash
+nnbd/duplicates_instance_extension: FormatterCrash
nnbd/duplicates_static: FormatterCrash
+nnbd/duplicates_static_extension: FormatterCrash
nnbd/field_vs_setter: FormatterCrash
nnbd/forbidden_supers: FormatterCrash
nnbd/getter_vs_setter_type_late: FormatterCrash
@@ -132,7 +130,11 @@
nnbd/opt_out: FormatterCrash
nnbd/potentially_non_nullable_field: FormatterCrash
nnbd/uninitialized_non_nullable_late_fields: FormatterCrash
+nnbd_mixed/inheritance_from_opt_in: FormatterCrash
+nnbd_mixed/issue41597: FormatterCrash
+nnbd_mixed/null_safety_invalid_language_version: FormatterCrash
nonfunction_type_aliases/issue41501: FormatterCrash
+nonfunction_type_aliases/issue42446: FormatterCrash
rasta/bad_redirection: FormatterCrash
rasta/issue_000032: FormatterCrash
rasta/issue_000034: FormatterCrash
@@ -172,4 +174,3 @@
variance/generic_covariance_sound_variance: FormatterCrash
variance/mixin_type_parameter_modifier: FormatterCrash
variance/unconstrained_inference: FormatterCrash
-
diff --git a/tools/VERSION b/tools/VERSION
index 6440191..99f3979 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 13
PATCH 0
-PRERELEASE 36
+PRERELEASE 37
PRERELEASE_PATCH 0
\ No newline at end of file