mirror of
https://github.com/dart-lang/sdk
synced 2024-10-06 14:49:43 +00:00
Infer Object members for dynamic receivers
For method, getter, and setter invocations with names of methods on
Object on expressions with static type `dynamic`, if the invocation
cannot possibly be an invocation of noSuchMethod, infer the type of
the invocation using the type of the member of Object.
This implements the feature spec
472ec7780f
Fixes https://github.com/dart-lang/sdk/issues/32414
Change-Id: I135156346fe1468561d56a01cf3c5f0efde30739
Reviewed-on: https://dart-review.googlesource.com/56942
Commit-Queue: Kevin Millikin <kmillikin@google.com>
Reviewed-by: Dmitry Stefantsov <dmitryas@google.com>
This commit is contained in:
parent
b3bae4262d
commit
b62b438e8a
|
@ -547,8 +547,14 @@ abstract class TypeInferrerImpl extends TypeInferrer {
|
|||
/// Finds a member of [receiverType] called [name], and if it is found,
|
||||
/// reports it through instrumentation using [fileOffset].
|
||||
///
|
||||
/// For the special case where [receiverType] is a [FunctionType], and the
|
||||
/// method name is `call`, the string `call` is returned as a sentinel object.
|
||||
/// For the case where [receiverType] is a [FunctionType], and the name
|
||||
/// is `call`, the string 'call' is returned as a sentinel object.
|
||||
///
|
||||
/// For the case where [receiverType] is `dynamic`, and the name is declared
|
||||
/// in Object, the member from Object is returned though the call may not end
|
||||
/// up targeting it if the arguments do not match (the basic principle is that
|
||||
/// the Object member is used for inferring types only if noSuchMethod cannot
|
||||
/// be targeted due to, e.g., an incorrect argument count).
|
||||
Object findInterfaceMember(DartType receiverType, Name name, int fileOffset,
|
||||
{Template<Message Function(String, DartType)> errorTemplate,
|
||||
Expression expression,
|
||||
|
@ -568,16 +574,15 @@ abstract class TypeInferrerImpl extends TypeInferrer {
|
|||
return 'call';
|
||||
}
|
||||
|
||||
Member interfaceMember;
|
||||
if (receiverType is! DynamicType) {
|
||||
Class classNode = receiverType is InterfaceType
|
||||
? receiverType.classNode
|
||||
: coreTypes.objectClass;
|
||||
interfaceMember = _getInterfaceMember(classNode, name, setter);
|
||||
if (!silent && interfaceMember != null) {
|
||||
instrumentation?.record(uri, fileOffset, 'target',
|
||||
new InstrumentationValueForMember(interfaceMember));
|
||||
}
|
||||
Class classNode = receiverType is InterfaceType
|
||||
? receiverType.classNode
|
||||
: coreTypes.objectClass;
|
||||
Member interfaceMember = _getInterfaceMember(classNode, name, setter);
|
||||
if (!silent &&
|
||||
receiverType != const DynamicType() &&
|
||||
interfaceMember != null) {
|
||||
instrumentation?.record(uri, fileOffset, 'target',
|
||||
new InstrumentationValueForMember(interfaceMember));
|
||||
}
|
||||
|
||||
if (!isTopLevel &&
|
||||
|
@ -600,8 +605,8 @@ abstract class TypeInferrerImpl extends TypeInferrer {
|
|||
return interfaceMember;
|
||||
}
|
||||
|
||||
/// Finds a member of [receiverType] called [name], and if it is found,
|
||||
/// reports it through instrumentation and records it in [methodInvocation].
|
||||
/// Finds a member of [receiverType] called [name] and records it in
|
||||
/// [methodInvocation].
|
||||
Object findMethodInvocationMember(
|
||||
DartType receiverType, InvocationExpression methodInvocation,
|
||||
{bool silent: false}) {
|
||||
|
@ -614,11 +619,26 @@ abstract class TypeInferrerImpl extends TypeInferrer {
|
|||
expression: methodInvocation,
|
||||
receiver: methodInvocation.receiver,
|
||||
silent: silent);
|
||||
if (strongMode && interfaceMember is Member) {
|
||||
if (receiverType == const DynamicType() && interfaceMember is Procedure) {
|
||||
var arguments = methodInvocation.arguments;
|
||||
var signature = interfaceMember.function;
|
||||
if (arguments.positional.length < signature.requiredParameterCount ||
|
||||
arguments.positional.length >
|
||||
signature.positionalParameters.length) {
|
||||
return null;
|
||||
}
|
||||
for (var argument in arguments.named) {
|
||||
if (!signature.namedParameters
|
||||
.any((declaration) => declaration.name == argument.name)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
} else if (strongMode && interfaceMember is Member) {
|
||||
methodInvocation.interfaceTarget = interfaceMember;
|
||||
}
|
||||
return interfaceMember;
|
||||
} else if (methodInvocation is SuperMethodInvocation) {
|
||||
assert(receiverType != const DynamicType());
|
||||
var interfaceMember = findInterfaceMember(
|
||||
receiverType, methodInvocation.name, methodInvocation.fileOffset,
|
||||
silent: silent);
|
||||
|
@ -645,11 +665,14 @@ abstract class TypeInferrerImpl extends TypeInferrer {
|
|||
expression: propertyGet,
|
||||
receiver: propertyGet.receiver,
|
||||
silent: silent);
|
||||
if (strongMode && interfaceMember is Member) {
|
||||
if (strongMode &&
|
||||
receiverType != const DynamicType() &&
|
||||
interfaceMember is Member) {
|
||||
propertyGet.interfaceTarget = interfaceMember;
|
||||
}
|
||||
return interfaceMember;
|
||||
} else if (propertyGet is SuperPropertyGet) {
|
||||
assert(receiverType != const DynamicType());
|
||||
var interfaceMember = findInterfaceMember(
|
||||
receiverType, propertyGet.name, propertyGet.fileOffset,
|
||||
silent: silent);
|
||||
|
@ -675,11 +698,14 @@ abstract class TypeInferrerImpl extends TypeInferrer {
|
|||
receiver: propertySet.receiver,
|
||||
setter: true,
|
||||
silent: silent);
|
||||
if (strongMode && interfaceMember is Member) {
|
||||
if (strongMode &&
|
||||
receiverType != const DynamicType() &&
|
||||
interfaceMember is Member) {
|
||||
propertySet.interfaceTarget = interfaceMember;
|
||||
}
|
||||
return interfaceMember;
|
||||
} else if (propertySet is SuperPropertySet) {
|
||||
assert(receiverType != const DynamicType());
|
||||
var interfaceMember = findInterfaceMember(
|
||||
receiverType, propertySet.name, propertySet.fileOffset,
|
||||
setter: true, silent: silent);
|
||||
|
@ -1353,7 +1379,9 @@ abstract class TypeInferrerImpl extends TypeInferrer {
|
|||
errorTemplate: templateUndefinedGetter,
|
||||
expression: expression,
|
||||
receiver: receiver);
|
||||
if (interfaceMember is Member) {
|
||||
if (strongMode &&
|
||||
receiverType != const DynamicType() &&
|
||||
interfaceMember is Member) {
|
||||
desugaredGet.interfaceTarget = interfaceMember;
|
||||
}
|
||||
}
|
||||
|
|
11
pkg/front_end/testcases/bug32414a.dart
Normal file
11
pkg/front_end/testcases/bug32414a.dart
Normal file
|
@ -0,0 +1,11 @@
|
|||
// Copyright (c) 2018, 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.
|
||||
|
||||
void test() {
|
||||
dynamic a = 5;
|
||||
var b = a.toString();
|
||||
b = 42;
|
||||
}
|
||||
|
||||
void main() {}
|
9
pkg/front_end/testcases/bug32414a.dart.direct.expect
Normal file
9
pkg/front_end/testcases/bug32414a.dart.direct.expect
Normal file
|
@ -0,0 +1,9 @@
|
|||
library;
|
||||
import self as self;
|
||||
|
||||
static method test() → void {
|
||||
dynamic a = 5;
|
||||
dynamic b = a.toString();
|
||||
b = 42;
|
||||
}
|
||||
static method main() → void {}
|
|
@ -0,0 +1,9 @@
|
|||
library;
|
||||
import self as self;
|
||||
|
||||
static method test() → void {
|
||||
dynamic a = 5;
|
||||
dynamic b = a.toString();
|
||||
b = 42;
|
||||
}
|
||||
static method main() → void {}
|
7
pkg/front_end/testcases/bug32414a.dart.outline.expect
Normal file
7
pkg/front_end/testcases/bug32414a.dart.outline.expect
Normal file
|
@ -0,0 +1,7 @@
|
|||
library;
|
||||
import self as self;
|
||||
|
||||
static method test() → void
|
||||
;
|
||||
static method main() → void
|
||||
;
|
13
pkg/front_end/testcases/bug32414a.dart.strong.expect
Normal file
13
pkg/front_end/testcases/bug32414a.dart.strong.expect
Normal file
|
@ -0,0 +1,13 @@
|
|||
library;
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
|
||||
static method test() → void {
|
||||
dynamic a = 5;
|
||||
core::String b = a.toString();
|
||||
b = let final dynamic #t1 = let dynamic _ = null in invalid-expression "pkg/front_end/testcases/bug32414a.dart:8:7: Error: A value of type 'dart.core::int' can't be assigned to a variable of type 'dart.core::String'.
|
||||
Try changing the type of the left hand side, or casting the right hand side to 'dart.core::String'.
|
||||
b = 42;
|
||||
^" in let final dynamic #t2 = 42 in null;
|
||||
}
|
||||
static method main() → void {}
|
|
@ -0,0 +1,13 @@
|
|||
library;
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
|
||||
static method test() → void {
|
||||
dynamic a = 5;
|
||||
core::String b = a.toString();
|
||||
b = let final dynamic #t1 = let<BottomType> _ = null in invalid-expression "pkg/front_end/testcases/bug32414a.dart:8:7: Error: A value of type 'dart.core::int' can't be assigned to a variable of type 'dart.core::String'.
|
||||
Try changing the type of the left hand side, or casting the right hand side to 'dart.core::String'.
|
||||
b = 42;
|
||||
^" in let final core::int #t2 = 42 in null;
|
||||
}
|
||||
static method main() → void {}
|
10
pkg/front_end/testcases/bug32414b.dart
Normal file
10
pkg/front_end/testcases/bug32414b.dart
Normal file
|
@ -0,0 +1,10 @@
|
|||
// Copyright (c) 2018, 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.
|
||||
|
||||
void test() {
|
||||
List<dynamic> l = [1, "hello"];
|
||||
List<String> l2 = l.map((dynamic element) => element.toString()).toList();
|
||||
}
|
||||
|
||||
void main() {}
|
9
pkg/front_end/testcases/bug32414b.dart.direct.expect
Normal file
9
pkg/front_end/testcases/bug32414b.dart.direct.expect
Normal file
|
@ -0,0 +1,9 @@
|
|||
library;
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
|
||||
static method test() → void {
|
||||
core::List<dynamic> l = <dynamic>[1, "hello"];
|
||||
core::List<core::String> l2 = l.map((dynamic element) → dynamic => element.toString()).toList();
|
||||
}
|
||||
static method main() → void {}
|
|
@ -0,0 +1,9 @@
|
|||
library;
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
|
||||
static method test() → void {
|
||||
core::List<dynamic> l = <dynamic>[1, "hello"];
|
||||
core::List<core::String> l2 = l.map((dynamic element) → dynamic => element.toString()).toList();
|
||||
}
|
||||
static method main() → void {}
|
7
pkg/front_end/testcases/bug32414b.dart.outline.expect
Normal file
7
pkg/front_end/testcases/bug32414b.dart.outline.expect
Normal file
|
@ -0,0 +1,7 @@
|
|||
library;
|
||||
import self as self;
|
||||
|
||||
static method test() → void
|
||||
;
|
||||
static method main() → void
|
||||
;
|
9
pkg/front_end/testcases/bug32414b.dart.strong.expect
Normal file
9
pkg/front_end/testcases/bug32414b.dart.strong.expect
Normal file
|
@ -0,0 +1,9 @@
|
|||
library;
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
|
||||
static method test() → void {
|
||||
core::List<dynamic> l = <dynamic>[1, "hello"];
|
||||
core::List<core::String> l2 = l.{core::Iterable::map}<core::String>((dynamic element) → core::String => element.toString()).{core::Iterable::toList}();
|
||||
}
|
||||
static method main() → void {}
|
|
@ -0,0 +1,9 @@
|
|||
library;
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
|
||||
static method test() → void {
|
||||
core::List<dynamic> l = <dynamic>[1, "hello"];
|
||||
core::List<core::String> l2 = l.{core::Iterable::map}<core::String>((dynamic element) → core::String => element.toString()).{core::Iterable::toList}();
|
||||
}
|
||||
static method main() → void {}
|
|
@ -11,9 +11,9 @@ class Foo {
|
|||
|
||||
test() {
|
||||
dynamic d = new Foo();
|
||||
var /*@type=dynamic*/ get_hashCode = d.hashCode;
|
||||
var /*@type=int*/ get_hashCode = d.hashCode;
|
||||
var /*@type=dynamic*/ call_hashCode = d.hashCode();
|
||||
var /*@type=dynamic*/ call_toString = d.toString();
|
||||
var /*@type=String*/ call_toString = d.toString();
|
||||
var /*@type=dynamic*/ call_toStringArg = d.toString(color: "pink");
|
||||
var /*@type=dynamic*/ call_foo0 = d.foo();
|
||||
var /*@type=dynamic*/ call_foo1 = d.foo(1);
|
||||
|
|
|
@ -11,9 +11,9 @@ class Foo extends core::Object {
|
|||
}
|
||||
static method test() → dynamic {
|
||||
dynamic d = new self::Foo::•();
|
||||
dynamic get_hashCode = d.hashCode;
|
||||
core::int get_hashCode = d.hashCode;
|
||||
dynamic call_hashCode = d.hashCode();
|
||||
dynamic call_toString = d.toString();
|
||||
core::String call_toString = d.toString();
|
||||
dynamic call_toStringArg = d.toString(color: "pink");
|
||||
dynamic call_foo0 = d.foo();
|
||||
dynamic call_foo1 = d.foo(1);
|
||||
|
|
|
@ -11,9 +11,9 @@ class Foo extends core::Object {
|
|||
}
|
||||
static method test() → dynamic {
|
||||
dynamic d = new self::Foo::•();
|
||||
dynamic get_hashCode = d.hashCode;
|
||||
core::int get_hashCode = d.hashCode;
|
||||
dynamic call_hashCode = d.hashCode();
|
||||
dynamic call_toString = d.toString();
|
||||
core::String call_toString = d.toString();
|
||||
dynamic call_toStringArg = d.toString(color: "pink");
|
||||
dynamic call_foo0 = d.foo();
|
||||
dynamic call_foo1 = d.foo(1);
|
||||
|
|
|
@ -573,7 +573,7 @@ ConstantPool {
|
|||
[3] = Null
|
||||
}
|
||||
]static method _print(dynamic arg) → void {
|
||||
self::_printString(arg.toString() as{TypeError} core::String);
|
||||
self::_printString(arg.toString());
|
||||
}
|
||||
[@vm.bytecode=
|
||||
Bytecode {
|
|
@ -290,6 +290,7 @@ void_type_usage_test/paren_void_init: MissingCompileTimeError
|
|||
[ $compiler == dartdevk ]
|
||||
additional_interface_adds_optional_args_concrete_subclass_test: MissingCompileTimeError
|
||||
additional_interface_adds_optional_args_concrete_test: MissingCompileTimeError
|
||||
assert_with_message_test: RuntimeError # Issue 33293
|
||||
async_or_generator_return_type_stacktrace_test/01: MissingCompileTimeError
|
||||
async_or_generator_return_type_stacktrace_test/02: MissingCompileTimeError
|
||||
async_or_generator_return_type_stacktrace_test/03: MissingCompileTimeError
|
||||
|
|
Loading…
Reference in a new issue