Skip to content

Commit

Permalink
Version 2.13.0-91.0.dev
Browse files Browse the repository at this point in the history
Merge commit 'e0f45c2cdf3fed270ffbcf23b28cf0ad09c38337' into 'dev'
  • Loading branch information
Dart CI committed Mar 1, 2021
2 parents f7b05f6 + e0f45c2 commit b3bc5ac
Show file tree
Hide file tree
Showing 37 changed files with 715 additions and 40 deletions.
6 changes: 5 additions & 1 deletion pkg/analyzer/lib/src/error/dead_code_verifier.dart
Expand Up @@ -568,6 +568,11 @@ class NullSafetyDeadCodeVerifier {
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;

Expand All @@ -580,7 +585,6 @@ class NullSafetyDeadCodeVerifier {
}
}

if (_firstDeadNode != null) return;
_firstDeadNode = node;
}

Expand Down
Expand Up @@ -67,6 +67,10 @@ class ConstructorReferenceBuilder {
}
} 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(
Expand Down
37 changes: 37 additions & 0 deletions pkg/front_end/lib/src/fasta/fasta_codes_cfe_generated.dart
Expand Up @@ -4728,6 +4728,43 @@ Message _withArgumentsThrowingNotAssignableToObjectError(
arguments: {'type': _type});
}

// 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(
Expand Down
28 changes: 27 additions & 1 deletion pkg/front_end/lib/src/fasta/kernel/internal_ast.dart
Expand Up @@ -54,6 +54,8 @@ import '../type_inference/type_schema_environment.dart'

import 'inference_visitor.dart';

import 'type_labeler.dart';

/// Computes the return type of a (possibly factory) constructor.
InterfaceType computeConstructorReturnType(
Member constructor, CoreTypes coreTypes) {
Expand Down Expand Up @@ -4303,6 +4305,30 @@ class ExtensionType extends DartType {
// 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.
Expand Down Expand Up @@ -4363,7 +4389,7 @@ class ExtensionType extends DartType {

@override
String toString() {
return "ExtensionType(${toStringInternal})";
return "ExtensionType(${toStringInternal()})";
}

@override
Expand Down
43 changes: 42 additions & 1 deletion pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
Expand Up @@ -868,6 +868,41 @@ class TypeInferrerImpl implements TypeInferrer {
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].
///
Expand Down Expand Up @@ -1104,6 +1139,10 @@ class TypeInferrerImpl implements TypeInferrer {
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();
}
Expand Down Expand Up @@ -4069,7 +4108,9 @@ class TypeInferrerImpl implements TypeInferrer {
receiverType,
name,
extensionAccessCandidates,
templateUndefinedMethod,
receiverType is ExtensionType
? templateUndefinedExtensionMethod
: templateUndefinedMethod,
templateAmbiguousExtensionMethod);
}
}
Expand Down
Expand Up @@ -17,6 +17,8 @@ import 'package:kernel/type_environment.dart';
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;
Expand Down Expand Up @@ -363,12 +365,52 @@ class TypeSchemaEnvironment extends HierarchyBasedTypeEnvironment
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);
}
Expand Down
2 changes: 2 additions & 0 deletions pkg/front_end/messages.status
Expand Up @@ -772,6 +772,8 @@ TypedefNotType/example: Fail # Feature not yet enabled by default.
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
Expand Down
4 changes: 4 additions & 0 deletions pkg/front_end/messages.yaml
Expand Up @@ -3491,6 +3491,10 @@ UndefinedOperator:
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."
Expand Down
@@ -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() {}
@@ -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 {}
@@ -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 {}
@@ -0,0 +1,10 @@
class A {
void foo() {}
}

extension E on A {
void bar() => foo();
}

test(A a, E e) {}
main() {}
@@ -0,0 +1,10 @@
class A {
void foo() {}
}

extension E on A {
void bar() => foo();
}

main() {}
test(A a, E e) {}

0 comments on commit b3bc5ac

Please sign in to comment.