Version 2.13.0-91.0.dev
Merge commit 'e0f45c2cdf3fed270ffbcf23b28cf0ad09c38337' into 'dev'
diff --git a/pkg/analyzer/lib/src/error/dead_code_verifier.dart b/pkg/analyzer/lib/src/error/dead_code_verifier.dart
index eea542d..ec56191 100644
--- a/pkg/analyzer/lib/src/error/dead_code_verifier.dart
+++ b/pkg/analyzer/lib/src/error/dead_code_verifier.dart
@@ -568,6 +568,11 @@
if (flowAnalysis == null) return;
flowAnalysis.checkUnreachableNode(node);
+ // If the first dead node is not `null`, even if this new new node is
+ // unreachable, we can ignore it as it is part of the same dead code
+ // range anyway.
+ if (_firstDeadNode != null) return;
+
var flow = flowAnalysis.flow;
if (flow == null) return;
@@ -580,7 +585,6 @@
}
}
- if (_firstDeadNode != null) return;
_firstDeadNode = node;
}
diff --git a/pkg/front_end/lib/src/fasta/builder/constructor_reference_builder.dart b/pkg/front_end/lib/src/fasta/builder/constructor_reference_builder.dart
index 824d08f..79ece35 100644
--- a/pkg/front_end/lib/src/fasta/builder/constructor_reference_builder.dart
+++ b/pkg/front_end/lib/src/fasta/builder/constructor_reference_builder.dart
@@ -67,6 +67,10 @@
}
} else {
declaration = scope.lookup(name, charOffset, fileUri);
+ if (declaration is TypeAliasBuilder) {
+ TypeAliasBuilder aliasBuilder = declaration;
+ declaration = aliasBuilder.unaliasDeclaration(typeArguments);
+ }
}
if (declaration is ClassBuilder) {
target = declaration.findConstructorOrFactory(
diff --git a/pkg/front_end/lib/src/fasta/fasta_codes_cfe_generated.dart b/pkg/front_end/lib/src/fasta/fasta_codes_cfe_generated.dart
index a145ee7..127d61d 100644
--- a/pkg/front_end/lib/src/fasta/fasta_codes_cfe_generated.dart
+++ b/pkg/front_end/lib/src/fasta/fasta_codes_cfe_generated.dart
@@ -4730,6 +4730,43 @@
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
const Template<
+ Message Function(
+ String name, DartType _type, bool isNonNullableByDefault)>
+ templateUndefinedExtensionMethod = const Template<
+ Message Function(
+ String name, DartType _type, bool isNonNullableByDefault)>(
+ messageTemplate:
+ r"""The method '#name' isn't defined for the extension '#type'.""",
+ tipTemplate:
+ r"""Try correcting the name to the name of an existing method, or defining a method name '#name'.""",
+ withArguments: _withArgumentsUndefinedExtensionMethod);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<
+ Message Function(String name, DartType _type,
+ bool isNonNullableByDefault)> codeUndefinedExtensionMethod = const Code<
+ Message Function(String name, DartType _type, bool isNonNullableByDefault)>(
+ "UndefinedExtensionMethod",
+);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+Message _withArgumentsUndefinedExtensionMethod(
+ String name, DartType _type, bool isNonNullableByDefault) {
+ if (name.isEmpty) throw 'No name provided';
+ name = demangleMixinApplicationName(name);
+ TypeLabeler labeler = new TypeLabeler(isNonNullableByDefault);
+ List<Object> typeParts = labeler.labelType(_type);
+ String type = typeParts.join();
+ return new Message(codeUndefinedExtensionMethod,
+ message:
+ """The method '${name}' isn't defined for the extension '${type}'.""" +
+ labeler.originMessages,
+ tip: """Try correcting the name to the name of an existing method, or defining a method name '${name}'.""",
+ arguments: {'name': name, 'type': _type});
+}
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Template<
Message Function(
String name,
DartType _type,
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 3ea1788..06bdd72 100644
--- a/pkg/front_end/lib/src/fasta/kernel/internal_ast.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/internal_ast.dart
@@ -54,6 +54,8 @@
import 'inference_visitor.dart';
+import 'type_labeler.dart';
+
/// Computes the return type of a (possibly factory) constructor.
InterfaceType computeConstructorReturnType(
Member constructor, CoreTypes coreTypes) {
@@ -4303,6 +4305,30 @@
// TODO(dmitryas): Remove the following line and implement
// BinaryPrinter.visitExtensionType when it's available.
return onType.accept(v);
+ } else if (v is TypeLabeler) {
+ // TODO(dmitryas): Move this guarded code into
+ // TypeLabeler.visitExtensionType when it's available.
+ TypeLabeler typeLabeler = v as TypeLabeler;
+ typeLabeler.result.add(typeLabeler.nameForEntity(
+ extensionNode,
+ extensionNode.name,
+ extensionNode.enclosingLibrary.importUri,
+ extensionNode.enclosingLibrary.fileUri));
+ if (typeArguments.isNotEmpty) {
+ typeLabeler.result.add("<");
+ bool first = true;
+ for (DartType typeArg in typeArguments) {
+ if (!first) typeLabeler.result.add(", ");
+ typeArg.accept(typeLabeler);
+ first = false;
+ }
+ typeLabeler.result.add(">");
+ }
+ typeLabeler.addNullability(declaredNullability);
+ // The following line is needed to supply the return value and make the
+ // compiler happy. It should go away once ExtensionType is moved to
+ // ast.dart.
+ return null;
}
// TODO(dmitryas): Change this to `v.visitExtensionType(this)` when
// ExtensionType is moved to ast.dart.
@@ -4363,7 +4389,7 @@
@override
String toString() {
- return "ExtensionType(${toStringInternal})";
+ return "ExtensionType(${toStringInternal()})";
}
@override
diff --git a/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart b/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
index a7282a4..b450ab3 100644
--- a/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
+++ b/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
@@ -868,6 +868,41 @@
return inferredTypes;
}
+ /// Returns extension member declared immediately for [receiverType].
+ ///
+ /// If none is found, [defaultTarget] is returned.
+ ObjectAccessTarget _findDirectExtensionMember(
+ ExtensionType receiverType, Name name, int fileOffset,
+ {ObjectAccessTarget defaultTarget}) {
+ Member targetMethod;
+ Member targetTearoff;
+ for (ExtensionMemberDescriptor descriptor
+ in receiverType.extensionNode.members) {
+ if (descriptor.name == name) {
+ switch (descriptor.kind) {
+ case ExtensionMemberKind.Method:
+ targetMethod = descriptor.member.asMember;
+ break;
+ case ExtensionMemberKind.TearOff:
+ targetTearoff = descriptor.member.asMember;
+ break;
+ default:
+ unhandled("${descriptor.kind}", "findInterfaceMember", fileOffset,
+ library.fileUri);
+ }
+ }
+ }
+ if (targetMethod != null) {
+ return new ObjectAccessTarget.extensionMember(
+ targetMethod,
+ targetTearoff ?? targetMethod,
+ ProcedureKind.Method,
+ receiverType.typeArguments);
+ } else {
+ return defaultTarget;
+ }
+ }
+
/// Returns the extension member access by the given [name] for a receiver
/// with the static [receiverType].
///
@@ -1104,6 +1139,10 @@
target = isReceiverTypePotentiallyNullable
? const ObjectAccessTarget.nullableCallFunction()
: const ObjectAccessTarget.callFunction();
+ } else if (library.enableExtensionTypesInLibrary &&
+ receiverBound is ExtensionType) {
+ target = _findDirectExtensionMember(receiverBound, name, fileOffset,
+ defaultTarget: const ObjectAccessTarget.missing());
} else {
target = const ObjectAccessTarget.missing();
}
@@ -4069,7 +4108,9 @@
receiverType,
name,
extensionAccessCandidates,
- templateUndefinedMethod,
+ receiverType is ExtensionType
+ ? templateUndefinedExtensionMethod
+ : templateUndefinedMethod,
templateAmbiguousExtensionMethod);
}
}
diff --git a/pkg/front_end/lib/src/fasta/type_inference/type_schema_environment.dart b/pkg/front_end/lib/src/fasta/type_inference/type_schema_environment.dart
index 7b37c14..172476b 100644
--- a/pkg/front_end/lib/src/fasta/type_inference/type_schema_environment.dart
+++ b/pkg/front_end/lib/src/fasta/type_inference/type_schema_environment.dart
@@ -17,6 +17,8 @@
import 'package:kernel/src/hierarchy_based_type_environment.dart'
show HierarchyBasedTypeEnvironment;
+import '../kernel/internal_ast.dart' show ExtensionType;
+
import 'standard_bounds.dart' show TypeSchemaStandardBounds;
import 'type_constraint_gatherer.dart' show TypeConstraintGatherer;
@@ -363,12 +365,52 @@
IsSubtypeOf performNullabilityAwareSubtypeCheck(
DartType subtype, DartType supertype) {
if (subtype is UnknownType) return const IsSubtypeOf.always();
+
+ // For now, extension types are only related to themselves, top types, and
+ // bottom types.
+ // TODO(dmitryas): Implement subtyping rules for extension types.
+ if (subtype is ExtensionType) {
+ if (coreTypes.isTop(supertype)) {
+ return const IsSubtypeOf.always();
+ } else if (supertype is ExtensionType &&
+ subtype.extensionNode == supertype.extensionNode) {
+ assert(subtype.typeArguments.length == supertype.typeArguments.length);
+ IsSubtypeOf result = const IsSubtypeOf.always();
+ for (int i = 0; i < subtype.typeArguments.length; ++i) {
+ result.and(performNullabilityAwareMutualSubtypesCheck(
+ subtype.typeArguments[i], supertype.typeArguments[i]));
+ }
+ return result;
+ } else {
+ return const IsSubtypeOf.never();
+ }
+ }
+
DartType unwrappedSupertype = supertype;
while (unwrappedSupertype is FutureOrType) {
unwrappedSupertype = (unwrappedSupertype as FutureOrType).typeArgument;
}
if (unwrappedSupertype is UnknownType) {
return const IsSubtypeOf.always();
+ } else if (unwrappedSupertype is ExtensionType) {
+ // For now, extension types are only related to themselves, top types, and
+ // bottom types.
+ // TODO(dmitryas): Implement subtyping rules for extension types.
+ if (coreTypes.isBottom(subtype)) {
+ return const IsSubtypeOf.always();
+ } else if (subtype is ExtensionType &&
+ subtype.extensionNode == unwrappedSupertype.extensionNode) {
+ assert(subtype.typeArguments.length ==
+ unwrappedSupertype.typeArguments.length);
+ IsSubtypeOf result = const IsSubtypeOf.always();
+ for (int i = 0; i < subtype.typeArguments.length; ++i) {
+ result.and(performNullabilityAwareMutualSubtypesCheck(
+ subtype.typeArguments[i], unwrappedSupertype.typeArguments[i]));
+ }
+ return result;
+ } else {
+ return const IsSubtypeOf.never();
+ }
}
return super.performNullabilityAwareSubtypeCheck(subtype, supertype);
}
diff --git a/pkg/front_end/messages.status b/pkg/front_end/messages.status
index 8e24091..9c120af 100644
--- a/pkg/front_end/messages.status
+++ b/pkg/front_end/messages.status
@@ -772,6 +772,8 @@
TypedefNullableType/analyzerCode: Fail
TypedefTypeVariableNotConstructor/analyzerCode: Fail # Feature not yet enabled by default.
TypedefTypeVariableNotConstructor/example: Fail # Feature not yet enabled by default.
+UndefinedExtensionMethod/analyzerCode: Fail
+UndefinedExtensionMethod/example: Fail
UnexpectedToken/part_wrapped_script1: Fail
UnexpectedToken/script1: Fail
UnmatchedToken/part_wrapped_script1: Fail
diff --git a/pkg/front_end/messages.yaml b/pkg/front_end/messages.yaml
index cc8fb0d..36d6bcc 100644
--- a/pkg/front_end/messages.yaml
+++ b/pkg/front_end/messages.yaml
@@ -3491,6 +3491,10 @@
c + 0;
}
+UndefinedExtensionMethod:
+ template: "The method '#name' isn't defined for the extension '#type'."
+ tip: "Try correcting the name to the name of an existing method, or defining a method name '#name'."
+
AmbiguousExtensionMethod:
template: "The method '#name' is defined in multiple extensions for '#type' and neither is more specific."
tip: "Try using an explicit extension application of the wanted extension or hiding unwanted extensions from scope."
diff --git a/pkg/front_end/testcases/extension_types/simple_method_resolution.dart b/pkg/front_end/testcases/extension_types/simple_method_resolution.dart
new file mode 100644
index 0000000..0406a33
--- /dev/null
+++ b/pkg/front_end/testcases/extension_types/simple_method_resolution.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 {
+ void foo() {}
+}
+
+extension E on A {
+ void bar() => foo();
+}
+
+test(A a, E e) {
+ a.foo(); // Ok.
+ a.bar(); // Ok.
+ e.foo(); // Error.
+ e.bar(); // Ok.
+}
+
+main() {}
diff --git a/pkg/front_end/testcases/extension_types/simple_method_resolution.dart.strong.expect b/pkg/front_end/testcases/extension_types/simple_method_resolution.dart.strong.expect
new file mode 100644
index 0000000..207f18f
--- /dev/null
+++ b/pkg/front_end/testcases/extension_types/simple_method_resolution.dart.strong.expect
@@ -0,0 +1,36 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/extension_types/simple_method_resolution.dart:16:5: Error: The method 'foo' isn't defined for the extension 'E'.
+// Try correcting the name to the name of an existing method, or defining a method name 'foo'.
+// e.foo(); // Error.
+// ^^^
+//
+import self as self;
+import "dart:core" as core;
+
+class A extends core::Object {
+ synthetic constructor •() → self::A
+ : super core::Object::•()
+ ;
+ method foo() → void {}
+}
+extension E on self::A {
+ method bar = self::E|bar;
+ tearoff bar = self::E|get#bar;
+}
+static method E|bar(lowered final self::A #this) → void
+ return #this.{self::A::foo}();
+static method E|get#bar(lowered final self::A #this) → () → void
+ return () → void => self::E|bar(#this);
+static method test(self::A a, self::E e) → dynamic {
+ a.{self::A::foo}();
+ self::E|bar(a);
+ invalid-expression "pkg/front_end/testcases/extension_types/simple_method_resolution.dart:16:5: Error: The method 'foo' isn't defined for the extension 'E'.
+Try correcting the name to the name of an existing method, or defining a method name 'foo'.
+ e.foo(); // Error.
+ ^^^";
+ self::E|bar(e);
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/extension_types/simple_method_resolution.dart.strong.transformed.expect b/pkg/front_end/testcases/extension_types/simple_method_resolution.dart.strong.transformed.expect
new file mode 100644
index 0000000..207f18f
--- /dev/null
+++ b/pkg/front_end/testcases/extension_types/simple_method_resolution.dart.strong.transformed.expect
@@ -0,0 +1,36 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/extension_types/simple_method_resolution.dart:16:5: Error: The method 'foo' isn't defined for the extension 'E'.
+// Try correcting the name to the name of an existing method, or defining a method name 'foo'.
+// e.foo(); // Error.
+// ^^^
+//
+import self as self;
+import "dart:core" as core;
+
+class A extends core::Object {
+ synthetic constructor •() → self::A
+ : super core::Object::•()
+ ;
+ method foo() → void {}
+}
+extension E on self::A {
+ method bar = self::E|bar;
+ tearoff bar = self::E|get#bar;
+}
+static method E|bar(lowered final self::A #this) → void
+ return #this.{self::A::foo}();
+static method E|get#bar(lowered final self::A #this) → () → void
+ return () → void => self::E|bar(#this);
+static method test(self::A a, self::E e) → dynamic {
+ a.{self::A::foo}();
+ self::E|bar(a);
+ invalid-expression "pkg/front_end/testcases/extension_types/simple_method_resolution.dart:16:5: Error: The method 'foo' isn't defined for the extension 'E'.
+Try correcting the name to the name of an existing method, or defining a method name 'foo'.
+ e.foo(); // Error.
+ ^^^";
+ self::E|bar(e);
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/extension_types/simple_method_resolution.dart.textual_outline.expect b/pkg/front_end/testcases/extension_types/simple_method_resolution.dart.textual_outline.expect
new file mode 100644
index 0000000..d2f4abf
--- /dev/null
+++ b/pkg/front_end/testcases/extension_types/simple_method_resolution.dart.textual_outline.expect
@@ -0,0 +1,10 @@
+class A {
+ void foo() {}
+}
+
+extension E on A {
+ void bar() => foo();
+}
+
+test(A a, E e) {}
+main() {}
diff --git a/pkg/front_end/testcases/extension_types/simple_method_resolution.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/extension_types/simple_method_resolution.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..00002ee
--- /dev/null
+++ b/pkg/front_end/testcases/extension_types/simple_method_resolution.dart.textual_outline_modelled.expect
@@ -0,0 +1,10 @@
+class A {
+ void foo() {}
+}
+
+extension E on A {
+ void bar() => foo();
+}
+
+main() {}
+test(A a, E e) {}
diff --git a/pkg/front_end/testcases/extension_types/simple_method_resolution.dart.weak.expect b/pkg/front_end/testcases/extension_types/simple_method_resolution.dart.weak.expect
new file mode 100644
index 0000000..207f18f
--- /dev/null
+++ b/pkg/front_end/testcases/extension_types/simple_method_resolution.dart.weak.expect
@@ -0,0 +1,36 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/extension_types/simple_method_resolution.dart:16:5: Error: The method 'foo' isn't defined for the extension 'E'.
+// Try correcting the name to the name of an existing method, or defining a method name 'foo'.
+// e.foo(); // Error.
+// ^^^
+//
+import self as self;
+import "dart:core" as core;
+
+class A extends core::Object {
+ synthetic constructor •() → self::A
+ : super core::Object::•()
+ ;
+ method foo() → void {}
+}
+extension E on self::A {
+ method bar = self::E|bar;
+ tearoff bar = self::E|get#bar;
+}
+static method E|bar(lowered final self::A #this) → void
+ return #this.{self::A::foo}();
+static method E|get#bar(lowered final self::A #this) → () → void
+ return () → void => self::E|bar(#this);
+static method test(self::A a, self::E e) → dynamic {
+ a.{self::A::foo}();
+ self::E|bar(a);
+ invalid-expression "pkg/front_end/testcases/extension_types/simple_method_resolution.dart:16:5: Error: The method 'foo' isn't defined for the extension 'E'.
+Try correcting the name to the name of an existing method, or defining a method name 'foo'.
+ e.foo(); // Error.
+ ^^^";
+ self::E|bar(e);
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/extension_types/simple_method_resolution.dart.weak.outline.expect b/pkg/front_end/testcases/extension_types/simple_method_resolution.dart.weak.outline.expect
new file mode 100644
index 0000000..e77d076
--- /dev/null
+++ b/pkg/front_end/testcases/extension_types/simple_method_resolution.dart.weak.outline.expect
@@ -0,0 +1,22 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+class A extends core::Object {
+ synthetic constructor •() → self::A
+ ;
+ method foo() → void
+ ;
+}
+extension E on self::A {
+ method bar = self::E|bar;
+ tearoff bar = self::E|get#bar;
+}
+static method E|bar(lowered final self::A #this) → void
+ ;
+static method E|get#bar(lowered final self::A #this) → () → void
+ return () → void => self::E|bar(#this);
+static method test(self::A a, self::E e) → dynamic
+ ;
+static method main() → dynamic
+ ;
diff --git a/pkg/front_end/testcases/extension_types/simple_method_resolution.dart.weak.transformed.expect b/pkg/front_end/testcases/extension_types/simple_method_resolution.dart.weak.transformed.expect
new file mode 100644
index 0000000..207f18f
--- /dev/null
+++ b/pkg/front_end/testcases/extension_types/simple_method_resolution.dart.weak.transformed.expect
@@ -0,0 +1,36 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/extension_types/simple_method_resolution.dart:16:5: Error: The method 'foo' isn't defined for the extension 'E'.
+// Try correcting the name to the name of an existing method, or defining a method name 'foo'.
+// e.foo(); // Error.
+// ^^^
+//
+import self as self;
+import "dart:core" as core;
+
+class A extends core::Object {
+ synthetic constructor •() → self::A
+ : super core::Object::•()
+ ;
+ method foo() → void {}
+}
+extension E on self::A {
+ method bar = self::E|bar;
+ tearoff bar = self::E|get#bar;
+}
+static method E|bar(lowered final self::A #this) → void
+ return #this.{self::A::foo}();
+static method E|get#bar(lowered final self::A #this) → () → void
+ return () → void => self::E|bar(#this);
+static method test(self::A a, self::E e) → dynamic {
+ a.{self::A::foo}();
+ self::E|bar(a);
+ invalid-expression "pkg/front_end/testcases/extension_types/simple_method_resolution.dart:16:5: Error: The method 'foo' isn't defined for the extension 'E'.
+Try correcting the name to the name of an existing method, or defining a method name 'foo'.
+ e.foo(); // Error.
+ ^^^";
+ self::E|bar(e);
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/nonfunction_type_aliases/issue45051.dart b/pkg/front_end/testcases/nonfunction_type_aliases/issue45051.dart
new file mode 100644
index 0000000..4f2e02f
--- /dev/null
+++ b/pkg/front_end/testcases/nonfunction_type_aliases/issue45051.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.
+
+class A {
+ factory A() = BAlias;
+ factory A.named() = BAlias.named;
+}
+
+typedef BAlias = B;
+
+class B implements A {
+ B();
+ B.named();
+}
+
+main() {}
diff --git a/pkg/front_end/testcases/nonfunction_type_aliases/issue45051.dart.strong.expect b/pkg/front_end/testcases/nonfunction_type_aliases/issue45051.dart.strong.expect
new file mode 100644
index 0000000..435aedb
--- /dev/null
+++ b/pkg/front_end/testcases/nonfunction_type_aliases/issue45051.dart.strong.expect
@@ -0,0 +1,21 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+typedef BAlias = self::B;
+class A extends core::Object {
+ static final field dynamic _redirecting# = <dynamic>[self::A::•, self::A::named]/*isLegacy*/;
+ static factory •() → self::A
+ let dynamic #redirecting_factory = self::B::• in invalid-expression;
+ static factory named() → self::A
+ let dynamic #redirecting_factory = self::B::named in invalid-expression;
+}
+class B extends core::Object implements self::A {
+ constructor •() → self::B
+ : super core::Object::•()
+ ;
+ constructor named() → self::B
+ : super core::Object::•()
+ ;
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/nonfunction_type_aliases/issue45051.dart.strong.transformed.expect b/pkg/front_end/testcases/nonfunction_type_aliases/issue45051.dart.strong.transformed.expect
new file mode 100644
index 0000000..b58ffcf
--- /dev/null
+++ b/pkg/front_end/testcases/nonfunction_type_aliases/issue45051.dart.strong.transformed.expect
@@ -0,0 +1,21 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+typedef BAlias = self::B;
+class A extends core::Object {
+ static final field dynamic _redirecting# = <dynamic>[self::A::•, self::A::named]/*isLegacy*/;
+ static factory •() → self::A
+ let Never #redirecting_factory = self::B::• in invalid-expression;
+ static factory named() → self::A
+ let Never #redirecting_factory = self::B::named in invalid-expression;
+}
+class B extends core::Object implements self::A {
+ constructor •() → self::B
+ : super core::Object::•()
+ ;
+ constructor named() → self::B
+ : super core::Object::•()
+ ;
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/nonfunction_type_aliases/issue45051.dart.textual_outline.expect b/pkg/front_end/testcases/nonfunction_type_aliases/issue45051.dart.textual_outline.expect
new file mode 100644
index 0000000..08b1c6f
--- /dev/null
+++ b/pkg/front_end/testcases/nonfunction_type_aliases/issue45051.dart.textual_outline.expect
@@ -0,0 +1,10 @@
+class A {
+ factory A() = BAlias;
+ factory A.named() = BAlias.named;
+}
+typedef BAlias = B;
+class B implements A {
+ B();
+ B.named();
+}
+main() {}
diff --git a/pkg/front_end/testcases/nonfunction_type_aliases/issue45051.dart.weak.expect b/pkg/front_end/testcases/nonfunction_type_aliases/issue45051.dart.weak.expect
new file mode 100644
index 0000000..435aedb
--- /dev/null
+++ b/pkg/front_end/testcases/nonfunction_type_aliases/issue45051.dart.weak.expect
@@ -0,0 +1,21 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+typedef BAlias = self::B;
+class A extends core::Object {
+ static final field dynamic _redirecting# = <dynamic>[self::A::•, self::A::named]/*isLegacy*/;
+ static factory •() → self::A
+ let dynamic #redirecting_factory = self::B::• in invalid-expression;
+ static factory named() → self::A
+ let dynamic #redirecting_factory = self::B::named in invalid-expression;
+}
+class B extends core::Object implements self::A {
+ constructor •() → self::B
+ : super core::Object::•()
+ ;
+ constructor named() → self::B
+ : super core::Object::•()
+ ;
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/nonfunction_type_aliases/issue45051.dart.weak.outline.expect b/pkg/front_end/testcases/nonfunction_type_aliases/issue45051.dart.weak.outline.expect
new file mode 100644
index 0000000..65b2862
--- /dev/null
+++ b/pkg/front_end/testcases/nonfunction_type_aliases/issue45051.dart.weak.outline.expect
@@ -0,0 +1,20 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+typedef BAlias = self::B;
+class A extends core::Object {
+ static final field dynamic _redirecting# = <dynamic>[self::A::•, self::A::named]/*isLegacy*/;
+ static factory •() → self::A
+ let dynamic #redirecting_factory = self::B::• in invalid-expression;
+ static factory named() → self::A
+ let dynamic #redirecting_factory = self::B::named in invalid-expression;
+}
+class B extends core::Object implements self::A {
+ constructor •() → self::B
+ ;
+ constructor named() → self::B
+ ;
+}
+static method main() → dynamic
+ ;
diff --git a/pkg/front_end/testcases/nonfunction_type_aliases/issue45051.dart.weak.transformed.expect b/pkg/front_end/testcases/nonfunction_type_aliases/issue45051.dart.weak.transformed.expect
new file mode 100644
index 0000000..b58ffcf
--- /dev/null
+++ b/pkg/front_end/testcases/nonfunction_type_aliases/issue45051.dart.weak.transformed.expect
@@ -0,0 +1,21 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+typedef BAlias = self::B;
+class A extends core::Object {
+ static final field dynamic _redirecting# = <dynamic>[self::A::•, self::A::named]/*isLegacy*/;
+ static factory •() → self::A
+ let Never #redirecting_factory = self::B::• in invalid-expression;
+ static factory named() → self::A
+ let Never #redirecting_factory = self::B::named in invalid-expression;
+}
+class B extends core::Object implements self::A {
+ constructor •() → self::B
+ : super core::Object::•()
+ ;
+ constructor named() → self::B
+ : super core::Object::•()
+ ;
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/outline.status b/pkg/front_end/testcases/outline.status
index 4be494f..63e873f 100644
--- a/pkg/front_end/testcases/outline.status
+++ b/pkg/front_end/testcases/outline.status
@@ -3,6 +3,7 @@
# BSD-style license that can be found in the LICENSE.md file.
extension_types/simple: ExpectationFileMismatchSerialized
+extension_types/simple_method_resolution: ExpectationFileMismatchSerialized
general/abstract_members: TypeCheckError
general/bug30695: TypeCheckError
general/covariant_field: TypeCheckError
diff --git a/pkg/front_end/testcases/strong.status b/pkg/front_end/testcases/strong.status
index ba90386..757cc84 100644
--- a/pkg/front_end/testcases/strong.status
+++ b/pkg/front_end/testcases/strong.status
@@ -7,6 +7,7 @@
# strong-mode enabled.
extension_types/simple: ExpectationFileMismatchSerialized # Expected.
+extension_types/simple_method_resolution: ExpectationFileMismatchSerialized # Expected.
late_lowering/covariant_late_field: TypeCheckError
nnbd/covariant_late_field: TypeCheckError
nnbd/getter_vs_setter_type: TypeCheckError
diff --git a/pkg/front_end/testcases/text_serialization.status b/pkg/front_end/testcases/text_serialization.status
index b106449..bd4dc72 100644
--- a/pkg/front_end/testcases/text_serialization.status
+++ b/pkg/front_end/testcases/text_serialization.status
@@ -8,6 +8,7 @@
dartdevc/private_covariant: TextSerializationFailure
extension_types/simple: ExpectationFileMismatchSerialized # Expected.
+extension_types/simple_method_resolution: ExpectationFileMismatchSerialized # Expected.
extensions/call_methods: TypeCheckError
extensions/extension_setter_error: TypeCheckError
extensions/instance_access_of_static: RuntimeError
diff --git a/pkg/front_end/testcases/textual_outline.status b/pkg/front_end/testcases/textual_outline.status
index a9e5f3e..6fef290 100644
--- a/pkg/front_end/testcases/textual_outline.status
+++ b/pkg/front_end/testcases/textual_outline.status
@@ -127,6 +127,7 @@
nnbd_mixed/null_safety_invalid_language_version: FormatterCrash
nonfunction_type_aliases/issue41501: FormatterCrash
nonfunction_type_aliases/issue42446: FormatterCrash
+nonfunction_type_aliases/issue45051: FormatterCrash
rasta/bad_redirection: FormatterCrash
rasta/issue_000032: FormatterCrash
rasta/issue_000034: FormatterCrash
diff --git a/pkg/front_end/testcases/weak.status b/pkg/front_end/testcases/weak.status
index 6c4e1c1..1087328 100644
--- a/pkg/front_end/testcases/weak.status
+++ b/pkg/front_end/testcases/weak.status
@@ -11,6 +11,7 @@
# regress/utf_16_le_content.crash: Crash # semi fuzz fails but isn't currently enabled by default.
extension_types/simple: ExpectationFileMismatchSerialized # Expected.
+extension_types/simple_method_resolution: ExpectationFileMismatchSerialized # Expected.
extensions/call_methods: TypeCheckError
extensions/extension_setter_error: TypeCheckError
extensions/instance_access_of_static: RuntimeError
diff --git a/pkg/kernel/lib/naive_type_checker.dart b/pkg/kernel/lib/naive_type_checker.dart
index 080ccf8..449a9a3 100644
--- a/pkg/kernel/lib/naive_type_checker.dart
+++ b/pkg/kernel/lib/naive_type_checker.dart
@@ -111,6 +111,12 @@
/// Check if [subtype] is subtype of [supertype] after applying
/// type parameter [substitution].
bool _isSubtypeOf(DartType subtype, DartType supertype) {
+ // TODO(dmitryas): Remove this when ExtensionType is in ast.dart.
+ if (!_isKnownDartTypeImplementation(subtype) ||
+ !_isKnownDartTypeImplementation(supertype)) {
+ return true;
+ }
+
if (subtype is InvalidType || supertype is InvalidType) {
return true;
}
@@ -299,3 +305,16 @@
failures.reportFailure(where, message);
}
}
+
+bool _isKnownDartTypeImplementation(DartType type) {
+ return type is DynamicType ||
+ type is FunctionType ||
+ type is FutureOrType ||
+ type is InterfaceType ||
+ type is InvalidType ||
+ type is NeverType ||
+ type is NullType ||
+ type is TypeParameterType ||
+ type is TypedefType ||
+ type is VoidType;
+}
diff --git a/runtime/tests/vm/dart/regress_flutter76919_test.dart b/runtime/tests/vm/dart/regress_flutter76919_test.dart
new file mode 100644
index 0000000..669641a
--- /dev/null
+++ b/runtime/tests/vm/dart/regress_flutter76919_test.dart
@@ -0,0 +1,32 @@
+// 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.
+
+// Regression test for https://github.com/flutter/flutter/issues/76919
+// Verifies that we don't try to strengthen an environmentless assertion
+// with a class check in AOT mode which crashes AOT compiler.
+
+import 'dart:typed_data';
+import 'package:expect/expect.dart';
+
+class C<E, L extends List<E>> {
+ final L list;
+
+ C(this.list);
+
+ @pragma('vm:never-inline')
+ E operator [](int index) => list[index];
+
+ @pragma('vm:never-inline')
+ void operator []=(int index, E value) {
+ // We emit AssertAssignable(value, E) on entry.
+ // Speculative compilation of this line produces CheckSmi(value)
+ list[index] = value;
+ }
+}
+
+void main(List<String> args) {
+ final v = C<int, Uint8List>(Uint8List(1));
+ v[0] = 1;
+ Expect.equals(1, v[0]);
+}
diff --git a/runtime/tests/vm/dart_2/regress_flutter76919_test.dart b/runtime/tests/vm/dart_2/regress_flutter76919_test.dart
new file mode 100644
index 0000000..669641a
--- /dev/null
+++ b/runtime/tests/vm/dart_2/regress_flutter76919_test.dart
@@ -0,0 +1,32 @@
+// 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.
+
+// Regression test for https://github.com/flutter/flutter/issues/76919
+// Verifies that we don't try to strengthen an environmentless assertion
+// with a class check in AOT mode which crashes AOT compiler.
+
+import 'dart:typed_data';
+import 'package:expect/expect.dart';
+
+class C<E, L extends List<E>> {
+ final L list;
+
+ C(this.list);
+
+ @pragma('vm:never-inline')
+ E operator [](int index) => list[index];
+
+ @pragma('vm:never-inline')
+ void operator []=(int index, E value) {
+ // We emit AssertAssignable(value, E) on entry.
+ // Speculative compilation of this line produces CheckSmi(value)
+ list[index] = value;
+ }
+}
+
+void main(List<String> args) {
+ final v = C<int, Uint8List>(Uint8List(1));
+ v[0] = 1;
+ Expect.equals(1, v[0]);
+}
diff --git a/runtime/vm/compiler/backend/type_propagator.cc b/runtime/vm/compiler/backend/type_propagator.cc
index fd01d69..dce6e90 100644
--- a/runtime/vm/compiler/backend/type_propagator.cc
+++ b/runtime/vm/compiler/backend/type_propagator.cc
@@ -48,6 +48,7 @@
FlowGraphTypePropagator::FlowGraphTypePropagator(FlowGraph* flow_graph)
: FlowGraphVisitor(flow_graph->reverse_postorder()),
flow_graph_(flow_graph),
+ is_aot_(CompilerState::Current().is_aot()),
visited_blocks_(new (flow_graph->zone())
BitVector(flow_graph->zone(),
flow_graph->reverse_postorder().length())),
@@ -119,7 +120,13 @@
const intptr_t rollback_point = rollback_.length();
- StrengthenAsserts(block);
+ if (!is_aot_) {
+ // Don't try to strengthen asserts with class checks in AOT mode, this is a
+ // speculative optimization which only really makes sense in JIT mode.
+ // It is also written to expect environments to be attached to
+ // AssertAssignable instructions, which is not always a case in AOT mode.
+ StrengthenAsserts(block);
+ }
block->Accept(this);
diff --git a/runtime/vm/compiler/backend/type_propagator.h b/runtime/vm/compiler/backend/type_propagator.h
index 5c27420..435ef4a 100644
--- a/runtime/vm/compiler/backend/type_propagator.h
+++ b/runtime/vm/compiler/backend/type_propagator.h
@@ -77,6 +77,7 @@
Zone* zone() const { return flow_graph_->zone(); }
FlowGraph* flow_graph_;
+ const bool is_aot_;
BitVector* visited_blocks_;
diff --git a/runtime/vm/compiler/backend/type_propagator_test.cc b/runtime/vm/compiler/backend/type_propagator_test.cc
index 1090bc9..60d58c4 100644
--- a/runtime/vm/compiler/backend/type_propagator_test.cc
+++ b/runtime/vm/compiler/backend/type_propagator_test.cc
@@ -491,4 +491,53 @@
}
}
+// Verifies that Propagate does not crash when running in AOT mode on a graph
+// which contains both AssertAssignable and a CheckClass/Smi after
+// EliminateEnvironments was called.
+ISOLATE_UNIT_TEST_CASE(TypePropagator_RegressFlutter76919) {
+ CompilerState S(thread, /*is_aot=*/true, /*is_optimizing=*/true);
+
+ FlowGraphBuilderHelper H;
+
+ // Add a variable into the scope which would provide static type for the
+ // parameter.
+ LocalVariable* v0_var =
+ new LocalVariable(TokenPosition::kNoSource, TokenPosition::kNoSource,
+ String::Handle(Symbols::New(thread, "v0")),
+ AbstractType::ZoneHandle(Type::DynamicType()));
+ v0_var->set_type_check_mode(LocalVariable::kTypeCheckedByCaller);
+ H.flow_graph()->parsed_function().scope()->AddVariable(v0_var);
+
+ auto normal_entry = H.flow_graph()->graph_entry()->normal_entry();
+
+ // We are going to build the following graph:
+ //
+ // B0[graph_entry]:
+ // B1[function_entry]:
+ // v0 <- Parameter(0)
+ // AssertAssignable(v0, 'int')
+ // CheckSmi(v0)
+ // Return(v0)
+
+ {
+ BlockBuilder builder(H.flow_graph(), normal_entry);
+ Definition* v0 = builder.AddParameter(0, 0, /*with_frame=*/true, kTagged);
+ auto null_value = builder.AddNullDefinition();
+ builder.AddDefinition(new AssertAssignableInstr(
+ InstructionSource(), new Value(v0),
+ new Value(
+ H.flow_graph()->GetConstant(Type::ZoneHandle(Type::IntType()))),
+ new Value(null_value), new Value(null_value), Symbols::Value(),
+ S.GetNextDeoptId()));
+ builder.AddInstruction(new CheckSmiInstr(new Value(v0), S.GetNextDeoptId(),
+ InstructionSource()));
+ builder.AddReturn(new Value(v0));
+ }
+
+ H.FinishGraph();
+
+ H.flow_graph()->EliminateEnvironments();
+ FlowGraphTypePropagator::Propagate(H.flow_graph()); // Should not crash.
+}
+
} // namespace dart
diff --git a/sdk/lib/_internal/vm/lib/typed_data_patch.dart b/sdk/lib/_internal/vm/lib/typed_data_patch.dart
index d608ab6..b0c18d5 100644
--- a/sdk/lib/_internal/vm/lib/typed_data_patch.dart
+++ b/sdk/lib/_internal/vm/lib/typed_data_patch.dart
@@ -150,13 +150,7 @@
return -1;
}
- List<int> operator +(List<int> other) {
- int totalLength = this.length + other.length;
- return <int>[]
- ..length = totalLength
- ..setRange(0, this.length, this)
- ..setRange(this.length, totalLength, other);
- }
+ List<int> operator +(List<int> other) => [...this, ...other];
bool contains(Object? element) {
var len = this.length;
@@ -506,13 +500,7 @@
return -1;
}
- List<double> operator +(List<double> other) {
- int totalLength = this.length + other.length;
- return <double>[]
- ..length = totalLength
- ..setRange(0, this.length, this)
- ..setRange(this.length, totalLength, other);
- }
+ List<double> operator +(List<double> other) => [...this, ...other];
bool contains(Object? element) {
var len = this.length;
@@ -868,13 +856,7 @@
return -1;
}
- List<Float32x4> operator +(List<Float32x4> other) {
- int totalLength = this.length + other.length;
- return <Float32x4>[]
- ..length = totalLength
- ..setRange(0, this.length, this)
- ..setRange(this.length, totalLength, other);
- }
+ List<Float32x4> operator +(List<Float32x4> other) => [...this, ...other];
bool contains(Object? element) {
var len = this.length;
@@ -1228,13 +1210,7 @@
return -1;
}
- List<Int32x4> operator +(List<Int32x4> other) {
- int totalLength = this.length + other.length;
- return <Int32x4>[]
- ..length = totalLength
- ..setRange(0, this.length, this)
- ..setRange(this.length, totalLength, other);
- }
+ List<Int32x4> operator +(List<Int32x4> other) => [...this, ...other];
bool contains(Object? element) {
var len = this.length;
@@ -1587,13 +1563,7 @@
return -1;
}
- List<Float64x2> operator +(List<Float64x2> other) {
- int totalLength = this.length + other.length;
- return <Float64x2>[]
- ..length = totalLength
- ..setRange(0, this.length, this)
- ..setRange(this.length, totalLength, other);
- }
+ List<Float64x2> operator +(List<Float64x2> other) => [...this, ...other];
bool contains(Object? element) {
var len = this.length;
diff --git a/tests/lib/typed_data/regression_45140_test.dart b/tests/lib/typed_data/regression_45140_test.dart
new file mode 100644
index 0000000..864ce45
--- /dev/null
+++ b/tests/lib/typed_data/regression_45140_test.dart
@@ -0,0 +1,63 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:math' show Random;
+import 'dart:typed_data';
+import 'package:expect/expect.dart';
+
+void main() {
+ var r = Random();
+ int genInt() => r.nextInt(256);
+ double genDbl() => r.nextDouble();
+ Int32x4 genIx4() => Int32x4(genInt(), genInt(), genInt(), genInt());
+ Float32x4 genFx4() => Float32x4(genDbl(), genDbl(), genDbl(), genDbl());
+ Float64x2 genDx2() => Float64x2(genDbl(), genDbl());
+
+ test("Uint8List", (n) => Uint8List(n)..fill(genInt));
+ test("Uint16List", (n) => Uint16List(n)..fill(genInt));
+ test("Uint32List", (n) => Uint32List(n)..fill(genInt));
+ test("Int8List", (n) => Int8List(n)..fill(genInt));
+ test("Int16List", (n) => Int16List(n)..fill(genInt));
+ test("Int32List", (n) => Int32List(n)..fill(genInt));
+ test("Uint8ClampedList", (n) => Uint8ClampedList(n)..fill(genInt));
+ test("Float32List", (n) => Float32List(n)..fill(genDbl));
+ test("Float64List", (n) => Float64List(n)..fill(genDbl));
+ test("Int32x4List", (n) => Int32x4List(n)..fill(genIx4));
+ test("Float32x4List", (n) => Float32x4List(n)..fill(genFx4));
+ test("Float64x2List", (n) => Float64x2List(n)..fill(genDx2));
+}
+
+void test<T>(String name, List<T> create(int n)) {
+ var l1 = create(17);
+ var l2 = create(13);
+ List<T> l3;
+ try {
+ // Shouldn't throw:
+ l3 = l1 + l2;
+ } catch (e) {
+ // Until we change Expect.fail to return Never.
+ Expect.fail("$name: $e") as Never;
+ }
+ Expect.equals(30, l3.length);
+ if (0 is T || 0.0 is T) {
+ // Int32x4 etc. do not support `==`.
+ Expect.listEquals(l1, l3.sublist(0, 17), "$name first");
+ Expect.listEquals(l2, l3.sublist(17), "$name second");
+ }
+ // Result is growable, shouldn't throw.
+ try {
+ l3.add(l3.first);
+ } catch (e) {
+ Expect.fail("$name: $e");
+ }
+}
+
+// Fill a list with (random) generated values.
+extension<T> on List<T> {
+ void fill(T gen()) {
+ for (var i = 0; i < length; i++) {
+ this[i] = gen();
+ }
+ }
+}
diff --git a/tools/VERSION b/tools/VERSION
index d620660..f121dd3 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 13
PATCH 0
-PRERELEASE 90
+PRERELEASE 91
PRERELEASE_PATCH 0
\ No newline at end of file