Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit b3bc5ac

Browse files
author
Dart CI
committed
Version 2.13.0-91.0.dev
Merge commit 'e0f45c2cdf3fed270ffbcf23b28cf0ad09c38337' into 'dev'
2 parents f7b05f6 + e0f45c2 commit b3bc5ac

37 files changed

+715
-40
lines changed

pkg/analyzer/lib/src/error/dead_code_verifier.dart

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -568,6 +568,11 @@ class NullSafetyDeadCodeVerifier {
568568
if (flowAnalysis == null) return;
569569
flowAnalysis.checkUnreachableNode(node);
570570

571+
// If the first dead node is not `null`, even if this new new node is
572+
// unreachable, we can ignore it as it is part of the same dead code
573+
// range anyway.
574+
if (_firstDeadNode != null) return;
575+
571576
var flow = flowAnalysis.flow;
572577
if (flow == null) return;
573578

@@ -580,7 +585,6 @@ class NullSafetyDeadCodeVerifier {
580585
}
581586
}
582587

583-
if (_firstDeadNode != null) return;
584588
_firstDeadNode = node;
585589
}
586590

pkg/front_end/lib/src/fasta/builder/constructor_reference_builder.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,10 @@ class ConstructorReferenceBuilder {
6767
}
6868
} else {
6969
declaration = scope.lookup(name, charOffset, fileUri);
70+
if (declaration is TypeAliasBuilder) {
71+
TypeAliasBuilder aliasBuilder = declaration;
72+
declaration = aliasBuilder.unaliasDeclaration(typeArguments);
73+
}
7074
}
7175
if (declaration is ClassBuilder) {
7276
target = declaration.findConstructorOrFactory(

pkg/front_end/lib/src/fasta/fasta_codes_cfe_generated.dart

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4728,6 +4728,43 @@ Message _withArgumentsThrowingNotAssignableToObjectError(
47284728
arguments: {'type': _type});
47294729
}
47304730

4731+
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
4732+
const Template<
4733+
Message Function(
4734+
String name, DartType _type, bool isNonNullableByDefault)>
4735+
templateUndefinedExtensionMethod = const Template<
4736+
Message Function(
4737+
String name, DartType _type, bool isNonNullableByDefault)>(
4738+
messageTemplate:
4739+
r"""The method '#name' isn't defined for the extension '#type'.""",
4740+
tipTemplate:
4741+
r"""Try correcting the name to the name of an existing method, or defining a method name '#name'.""",
4742+
withArguments: _withArgumentsUndefinedExtensionMethod);
4743+
4744+
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
4745+
const Code<
4746+
Message Function(String name, DartType _type,
4747+
bool isNonNullableByDefault)> codeUndefinedExtensionMethod = const Code<
4748+
Message Function(String name, DartType _type, bool isNonNullableByDefault)>(
4749+
"UndefinedExtensionMethod",
4750+
);
4751+
4752+
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
4753+
Message _withArgumentsUndefinedExtensionMethod(
4754+
String name, DartType _type, bool isNonNullableByDefault) {
4755+
if (name.isEmpty) throw 'No name provided';
4756+
name = demangleMixinApplicationName(name);
4757+
TypeLabeler labeler = new TypeLabeler(isNonNullableByDefault);
4758+
List<Object> typeParts = labeler.labelType(_type);
4759+
String type = typeParts.join();
4760+
return new Message(codeUndefinedExtensionMethod,
4761+
message:
4762+
"""The method '${name}' isn't defined for the extension '${type}'.""" +
4763+
labeler.originMessages,
4764+
tip: """Try correcting the name to the name of an existing method, or defining a method name '${name}'.""",
4765+
arguments: {'name': name, 'type': _type});
4766+
}
4767+
47314768
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
47324769
const Template<
47334770
Message Function(

pkg/front_end/lib/src/fasta/kernel/internal_ast.dart

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ import '../type_inference/type_schema_environment.dart'
5454

5555
import 'inference_visitor.dart';
5656

57+
import 'type_labeler.dart';
58+
5759
/// Computes the return type of a (possibly factory) constructor.
5860
InterfaceType computeConstructorReturnType(
5961
Member constructor, CoreTypes coreTypes) {
@@ -4303,6 +4305,30 @@ class ExtensionType extends DartType {
43034305
// TODO(dmitryas): Remove the following line and implement
43044306
// BinaryPrinter.visitExtensionType when it's available.
43054307
return onType.accept(v);
4308+
} else if (v is TypeLabeler) {
4309+
// TODO(dmitryas): Move this guarded code into
4310+
// TypeLabeler.visitExtensionType when it's available.
4311+
TypeLabeler typeLabeler = v as TypeLabeler;
4312+
typeLabeler.result.add(typeLabeler.nameForEntity(
4313+
extensionNode,
4314+
extensionNode.name,
4315+
extensionNode.enclosingLibrary.importUri,
4316+
extensionNode.enclosingLibrary.fileUri));
4317+
if (typeArguments.isNotEmpty) {
4318+
typeLabeler.result.add("<");
4319+
bool first = true;
4320+
for (DartType typeArg in typeArguments) {
4321+
if (!first) typeLabeler.result.add(", ");
4322+
typeArg.accept(typeLabeler);
4323+
first = false;
4324+
}
4325+
typeLabeler.result.add(">");
4326+
}
4327+
typeLabeler.addNullability(declaredNullability);
4328+
// The following line is needed to supply the return value and make the
4329+
// compiler happy. It should go away once ExtensionType is moved to
4330+
// ast.dart.
4331+
return null;
43064332
}
43074333
// TODO(dmitryas): Change this to `v.visitExtensionType(this)` when
43084334
// ExtensionType is moved to ast.dart.
@@ -4363,7 +4389,7 @@ class ExtensionType extends DartType {
43634389

43644390
@override
43654391
String toString() {
4366-
return "ExtensionType(${toStringInternal})";
4392+
return "ExtensionType(${toStringInternal()})";
43674393
}
43684394

43694395
@override

pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -868,6 +868,41 @@ class TypeInferrerImpl implements TypeInferrer {
868868
return inferredTypes;
869869
}
870870

871+
/// Returns extension member declared immediately for [receiverType].
872+
///
873+
/// If none is found, [defaultTarget] is returned.
874+
ObjectAccessTarget _findDirectExtensionMember(
875+
ExtensionType receiverType, Name name, int fileOffset,
876+
{ObjectAccessTarget defaultTarget}) {
877+
Member targetMethod;
878+
Member targetTearoff;
879+
for (ExtensionMemberDescriptor descriptor
880+
in receiverType.extensionNode.members) {
881+
if (descriptor.name == name) {
882+
switch (descriptor.kind) {
883+
case ExtensionMemberKind.Method:
884+
targetMethod = descriptor.member.asMember;
885+
break;
886+
case ExtensionMemberKind.TearOff:
887+
targetTearoff = descriptor.member.asMember;
888+
break;
889+
default:
890+
unhandled("${descriptor.kind}", "findInterfaceMember", fileOffset,
891+
library.fileUri);
892+
}
893+
}
894+
}
895+
if (targetMethod != null) {
896+
return new ObjectAccessTarget.extensionMember(
897+
targetMethod,
898+
targetTearoff ?? targetMethod,
899+
ProcedureKind.Method,
900+
receiverType.typeArguments);
901+
} else {
902+
return defaultTarget;
903+
}
904+
}
905+
871906
/// Returns the extension member access by the given [name] for a receiver
872907
/// with the static [receiverType].
873908
///
@@ -1104,6 +1139,10 @@ class TypeInferrerImpl implements TypeInferrer {
11041139
target = isReceiverTypePotentiallyNullable
11051140
? const ObjectAccessTarget.nullableCallFunction()
11061141
: const ObjectAccessTarget.callFunction();
1142+
} else if (library.enableExtensionTypesInLibrary &&
1143+
receiverBound is ExtensionType) {
1144+
target = _findDirectExtensionMember(receiverBound, name, fileOffset,
1145+
defaultTarget: const ObjectAccessTarget.missing());
11071146
} else {
11081147
target = const ObjectAccessTarget.missing();
11091148
}
@@ -4069,7 +4108,9 @@ class TypeInferrerImpl implements TypeInferrer {
40694108
receiverType,
40704109
name,
40714110
extensionAccessCandidates,
4072-
templateUndefinedMethod,
4111+
receiverType is ExtensionType
4112+
? templateUndefinedExtensionMethod
4113+
: templateUndefinedMethod,
40734114
templateAmbiguousExtensionMethod);
40744115
}
40754116
}

pkg/front_end/lib/src/fasta/type_inference/type_schema_environment.dart

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ import 'package:kernel/type_environment.dart';
1717
import 'package:kernel/src/hierarchy_based_type_environment.dart'
1818
show HierarchyBasedTypeEnvironment;
1919

20+
import '../kernel/internal_ast.dart' show ExtensionType;
21+
2022
import 'standard_bounds.dart' show TypeSchemaStandardBounds;
2123

2224
import 'type_constraint_gatherer.dart' show TypeConstraintGatherer;
@@ -363,12 +365,52 @@ class TypeSchemaEnvironment extends HierarchyBasedTypeEnvironment
363365
IsSubtypeOf performNullabilityAwareSubtypeCheck(
364366
DartType subtype, DartType supertype) {
365367
if (subtype is UnknownType) return const IsSubtypeOf.always();
368+
369+
// For now, extension types are only related to themselves, top types, and
370+
// bottom types.
371+
// TODO(dmitryas): Implement subtyping rules for extension types.
372+
if (subtype is ExtensionType) {
373+
if (coreTypes.isTop(supertype)) {
374+
return const IsSubtypeOf.always();
375+
} else if (supertype is ExtensionType &&
376+
subtype.extensionNode == supertype.extensionNode) {
377+
assert(subtype.typeArguments.length == supertype.typeArguments.length);
378+
IsSubtypeOf result = const IsSubtypeOf.always();
379+
for (int i = 0; i < subtype.typeArguments.length; ++i) {
380+
result.and(performNullabilityAwareMutualSubtypesCheck(
381+
subtype.typeArguments[i], supertype.typeArguments[i]));
382+
}
383+
return result;
384+
} else {
385+
return const IsSubtypeOf.never();
386+
}
387+
}
388+
366389
DartType unwrappedSupertype = supertype;
367390
while (unwrappedSupertype is FutureOrType) {
368391
unwrappedSupertype = (unwrappedSupertype as FutureOrType).typeArgument;
369392
}
370393
if (unwrappedSupertype is UnknownType) {
371394
return const IsSubtypeOf.always();
395+
} else if (unwrappedSupertype is ExtensionType) {
396+
// For now, extension types are only related to themselves, top types, and
397+
// bottom types.
398+
// TODO(dmitryas): Implement subtyping rules for extension types.
399+
if (coreTypes.isBottom(subtype)) {
400+
return const IsSubtypeOf.always();
401+
} else if (subtype is ExtensionType &&
402+
subtype.extensionNode == unwrappedSupertype.extensionNode) {
403+
assert(subtype.typeArguments.length ==
404+
unwrappedSupertype.typeArguments.length);
405+
IsSubtypeOf result = const IsSubtypeOf.always();
406+
for (int i = 0; i < subtype.typeArguments.length; ++i) {
407+
result.and(performNullabilityAwareMutualSubtypesCheck(
408+
subtype.typeArguments[i], unwrappedSupertype.typeArguments[i]));
409+
}
410+
return result;
411+
} else {
412+
return const IsSubtypeOf.never();
413+
}
372414
}
373415
return super.performNullabilityAwareSubtypeCheck(subtype, supertype);
374416
}

pkg/front_end/messages.status

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -772,6 +772,8 @@ TypedefNotType/example: Fail # Feature not yet enabled by default.
772772
TypedefNullableType/analyzerCode: Fail
773773
TypedefTypeVariableNotConstructor/analyzerCode: Fail # Feature not yet enabled by default.
774774
TypedefTypeVariableNotConstructor/example: Fail # Feature not yet enabled by default.
775+
UndefinedExtensionMethod/analyzerCode: Fail
776+
UndefinedExtensionMethod/example: Fail
775777
UnexpectedToken/part_wrapped_script1: Fail
776778
UnexpectedToken/script1: Fail
777779
UnmatchedToken/part_wrapped_script1: Fail

pkg/front_end/messages.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3491,6 +3491,10 @@ UndefinedOperator:
34913491
c + 0;
34923492
}
34933493
3494+
UndefinedExtensionMethod:
3495+
template: "The method '#name' isn't defined for the extension '#type'."
3496+
tip: "Try correcting the name to the name of an existing method, or defining a method name '#name'."
3497+
34943498
AmbiguousExtensionMethod:
34953499
template: "The method '#name' is defined in multiple extensions for '#type' and neither is more specific."
34963500
tip: "Try using an explicit extension application of the wanted extension or hiding unwanted extensions from scope."
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
class A {
6+
void foo() {}
7+
}
8+
9+
extension E on A {
10+
void bar() => foo();
11+
}
12+
13+
test(A a, E e) {
14+
a.foo(); // Ok.
15+
a.bar(); // Ok.
16+
e.foo(); // Error.
17+
e.bar(); // Ok.
18+
}
19+
20+
main() {}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
library /*isNonNullableByDefault*/;
2+
//
3+
// Problems in library:
4+
//
5+
// pkg/front_end/testcases/extension_types/simple_method_resolution.dart:16:5: Error: The method 'foo' isn't defined for the extension 'E'.
6+
// Try correcting the name to the name of an existing method, or defining a method name 'foo'.
7+
// e.foo(); // Error.
8+
// ^^^
9+
//
10+
import self as self;
11+
import "dart:core" as core;
12+
13+
class A extends core::Object {
14+
synthetic constructor •() → self::A
15+
: super core::Object::•()
16+
;
17+
method foo() → void {}
18+
}
19+
extension E on self::A {
20+
method bar = self::E|bar;
21+
tearoff bar = self::E|get#bar;
22+
}
23+
static method E|bar(lowered final self::A #this) → void
24+
return #this.{self::A::foo}();
25+
static method E|get#bar(lowered final self::A #this) → () → void
26+
return () → void => self::E|bar(#this);
27+
static method test(self::A a, self::E e) → dynamic {
28+
a.{self::A::foo}();
29+
self::E|bar(a);
30+
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'.
31+
Try correcting the name to the name of an existing method, or defining a method name 'foo'.
32+
e.foo(); // Error.
33+
^^^";
34+
self::E|bar(e);
35+
}
36+
static method main() → dynamic {}

0 commit comments

Comments
 (0)