Skip to content

Commit 116cfba

Browse files
scheglovcommit-bot@chromium.org
authored andcommitted
Issue 38198. Fix for isMoreSpecific() extension.
R=brianwilkerson@google.com Bug: #38198 Change-Id: I734d2353b9064a1a0e007495a1df546873e15c8f Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/116142 Reviewed-by: Brian Wilkerson <brianwilkerson@google.com> Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
1 parent 9391e15 commit 116cfba

2 files changed

Lines changed: 41 additions & 40 deletions

File tree

pkg/analyzer/lib/src/dart/resolver/extension_member_resolver.dart

Lines changed: 16 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -211,27 +211,6 @@ class ExtensionMemberResolver {
211211
/// identified.
212212
_InstantiatedExtension _chooseMostSpecific(
213213
List<_InstantiatedExtension> extensions) {
214-
//
215-
// https://github.com/dart-lang/language/blob/master/accepted/future-releases/static-extension-methods/feature-specification.md#extension-conflict-resolution:
216-
//
217-
// If more than one extension applies to a specific member invocation, then
218-
// we resort to a heuristic to choose one of the extensions to apply. If
219-
// exactly one of them is "more specific" than all the others, that one is
220-
// chosen. Otherwise it is a compile-time error.
221-
//
222-
// An extension with on type clause T1 is more specific than another
223-
// extension with on type clause T2 iff
224-
//
225-
// 1. T2 is declared in a platform library, and T1 is not, or
226-
// 2. they are both declared in platform libraries or both declared in
227-
// non-platform libraries, and
228-
// 3. the instantiated type (the type after applying type inference from the
229-
// receiver) of T1 is a subtype of the instantiated type of T2 and either
230-
// not vice versa, or
231-
// 4. the instantiate-to-bounds type of T1 is a subtype of the
232-
// instantiate-to-bounds type of T2 and not vice versa.
233-
//
234-
235214
for (var i = 0; i < extensions.length; i++) {
236215
var e1 = extensions[i];
237216
var isMoreSpecific = true;
@@ -418,7 +397,9 @@ class ExtensionMemberResolver {
418397
/// Return `true` is [e1] is more specific than [e2].
419398
bool _isMoreSpecific(_InstantiatedExtension e1, _InstantiatedExtension e2) {
420399
// 1. The latter extension is declared in a platform library, and the
421-
// former extension is not.
400+
// former extension is not.
401+
// 2. They are both declared in platform libraries, or both declared in
402+
// non-platform libraries.
422403
var e1_isInSdk = e1.element.library.isInSdk;
423404
var e2_isInSdk = e2.element.library.isInSdk;
424405
if (e1_isInSdk && !e2_isInSdk) {
@@ -430,30 +411,25 @@ class ExtensionMemberResolver {
430411
var extendedType1 = e1._extendedType;
431412
var extendedType2 = e2._extendedType;
432413

433-
// 2. they are both declared in platform libraries or both declared in
434-
// non-platform libraries, and
435-
if (_isSubtypeAndNotViceVersa(extendedType1, extendedType2)) {
436-
// 3. the instantiated type (the type after applying type inference from
437-
// the receiver) of T1 is a subtype of the instantiated type of T2 and
438-
// either not vice versa
414+
// 3. The instantiated type (the type after applying type inference from
415+
// the receiver) of T1 is a subtype of the instantiated type of T2,
416+
// and either...
417+
if (!_isSubtypeOf(extendedType1, extendedType2)) {
418+
return false;
419+
}
420+
421+
// 4. ...not vice versa, or...
422+
if (!_isSubtypeOf(extendedType2, extendedType1)) {
439423
return true;
440424
}
441425

426+
// 5. ...the instantiate-to-bounds type of T1 is a subtype of the
427+
// instantiate-to-bounds type of T2 and not vice versa.
442428
// TODO(scheglov) store instantiated types
443429
var extendedTypeBound1 = _instantiateToBounds(e1.element);
444430
var extendedTypeBound2 = _instantiateToBounds(e2.element);
445-
if (_isSubtypeAndNotViceVersa(extendedTypeBound1, extendedTypeBound2)) {
446-
// or:
447-
// 4. the instantiate-to-bounds type of T1 is a subtype of the
448-
// instantiate-to-bounds type of T2 and not vice versa.
449-
return true;
450-
}
451-
452-
return false;
453-
}
454-
455-
bool _isSubtypeAndNotViceVersa(DartType t1, DartType t2) {
456-
return _isSubtypeOf(t1, t2) && !_isSubtypeOf(t2, t1);
431+
return _isSubtypeOf(extendedTypeBound1, extendedTypeBound2) &&
432+
!_isSubtypeOf(extendedTypeBound2, extendedTypeBound1);
457433
}
458434

459435
/// Ask the type system for a subtype check.

pkg/analyzer/test/src/diagnostics/ambiguous_extension_member_access_test.dart

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,31 @@ f(A a) {
8080
]);
8181
}
8282

83+
test_noMoreSpecificExtension() async {
84+
await assertErrorsInCode(r'''
85+
class Target<T> {}
86+
87+
class SubTarget<T> extends Target<T> {}
88+
89+
extension E1 on SubTarget<Object> {
90+
int get foo => 0;
91+
}
92+
93+
extension E2<T> on Target<T> {
94+
int get foo => 0;
95+
}
96+
97+
f(SubTarget<num> t) {
98+
// The instantiated on type of `E1(t)` is `SubTarget<Object>`.
99+
// The instantiated on type of `E2(t)` is `Target<num>`.
100+
// Neither is a subtype of the other, so the resolution is ambiguous.
101+
t.foo;
102+
}
103+
''', [
104+
error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS, 396, 3),
105+
]);
106+
}
107+
83108
test_operator_binary() async {
84109
// There is no error reported.
85110
await assertErrorsInCode('''

0 commit comments

Comments
 (0)