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

Commit 0f472e5

Browse files
committed
Handle calls through call methods
This CL makes us recognizes calls through call methods as not requiring dynamic invokes. The analyzer seems to do the wrong thing on the return types though, so we'll still get casts on the results. Bug filed here: dartbug.com/23252 . This fixes #7. BUG= [email protected] Review URL: https://codereview.chromium.org/1092183003
1 parent 9156469 commit 0f472e5

File tree

3 files changed

+85
-19
lines changed

3 files changed

+85
-19
lines changed

lib/src/checker/checker.dart

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -505,11 +505,11 @@ class CodeChecker extends RecursiveAstVisitor {
505505
void checkFunctionApplication(
506506
Expression node, Expression f, ArgumentList list) {
507507
if (_rules.isDynamicCall(f)) {
508-
// TODO(vsm): For a function object, we should still be able to derive a
509-
// function type from it.
508+
// If f is Function and this is a method invocation, we should have
509+
// gotten an analyzer error, so no need to issue another error.
510510
_recordDynamicInvoke(node);
511511
} else {
512-
checkArgumentList(list, _rules.getStaticType(f));
512+
checkArgumentList(list, _rules.getTypeAsCaller(f));
513513
}
514514
}
515515

lib/src/checker/rules.dart

Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,32 @@ abstract class TypeRules {
4242

4343
DartType getStaticType(Expression expr) => expr.staticType;
4444

45+
/// Given a type t, if t is an interface type with a call method
46+
/// defined, return the function type for the call method, otherwise
47+
/// return null.
48+
FunctionType getCallMethodType(DartType t) {
49+
if (t is InterfaceType) {
50+
ClassElement element = t.element;
51+
InheritanceManager manager = new InheritanceManager(element.library);
52+
FunctionType callType = manager.lookupMemberType(t, "call");
53+
return callType;
54+
}
55+
return null;
56+
}
57+
58+
/// Given an expression, return its type assuming it is
59+
/// in the caller position of a call (that is, accounting
60+
/// for the possibility of a call method). Returns null
61+
/// if expression is not statically callable.
62+
FunctionType getTypeAsCaller(Expression applicand) {
63+
var t = getStaticType(applicand);
64+
if (t is InterfaceType) {
65+
return getCallMethodType(t);
66+
}
67+
if (t is FunctionType) return t;
68+
return null;
69+
}
70+
4571
DartType elementType(Element e);
4672

4773
bool isDynamic(DartType t);
@@ -185,16 +211,6 @@ class RestrictedRules extends TypeRules {
185211
throw new StateError("Unexpected type");
186212
}
187213

188-
FunctionType getCallMethodType(DartType t) {
189-
if (t is InterfaceType) {
190-
ClassElement element = t.element;
191-
InheritanceManager manager = new InheritanceManager(element.library);
192-
FunctionType callType = manager.lookupMemberType(t, "call");
193-
return callType;
194-
}
195-
return null;
196-
}
197-
198214
/// Check that f1 is a subtype of f2. [ignoreReturn] is used in the DDC
199215
/// checker to determine whether f1 would be a subtype of f2 if the return
200216
/// type of f1 is set to match f2's return type.
@@ -536,12 +552,12 @@ class RestrictedRules extends TypeRules {
536552
/// invocation.
537553
bool isDynamicCall(Expression call) {
538554
if (options.ignoreTypes) return true;
539-
var t = getStaticType(call);
540-
// TODO(jmesserly): fix handling of types with `call` methods. These are not
541-
// FunctionType, but they also aren't dynamic calls.
542-
if (t.isDynamic || t.isDartCoreFunction || t is! FunctionType) {
543-
return true;
544-
}
555+
var t = getTypeAsCaller(call);
556+
// TODO(leafp): This will currently return true if t is Function
557+
// This is probably the most correct thing to do for now, since
558+
// this code is also used by the back end. Maybe revisit at some
559+
// point?
560+
if (t == null) return true;
545561
// Dynamic as the parameter type is treated as bottom. A function with
546562
// a dynamic parameter type requires a dynamic call in general.
547563
// However, as an optimization, if we have an original definition, we know

test/checker/checker_test.dart

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,55 @@ import '../test_util.dart';
1414
void main() {
1515
configureTest();
1616

17+
test('dynamic invocation', () {
18+
testChecker({
19+
'/main.dart': '''
20+
21+
class A {
22+
dynamic call(dynamic x) => x;
23+
}
24+
class B extends A {
25+
int call(int x) => x;
26+
double col(double x) => x;
27+
}
28+
void main() {
29+
{
30+
B f = new B();
31+
int x;
32+
double y;
33+
// The analyzer has what I believe is a bug (dartbug.com/23252) which
34+
// causes the return type of calls to f to be treated as dynamic.
35+
x = /*info:DynamicCast should be pass*/f(3);
36+
x = /*severe:StaticTypeError*/f.col(3.0);
37+
y = /*info:DynamicCast should be severe:StaticTypeError*/f(3);
38+
y = f.col(3.0);
39+
f(/*severe:StaticTypeError*/3.0);
40+
f.col(/*severe:StaticTypeError*/3);
41+
}
42+
{
43+
Function f = new B();
44+
int x;
45+
double y;
46+
x = /*info:DynamicCast, info:DynamicInvoke*/f(3);
47+
x = /*info:DynamicCast, info:DynamicInvoke*/f.col(3.0);
48+
y = /*info:DynamicCast, info:DynamicInvoke*/f(3);
49+
y = /*info:DynamicCast, info:DynamicInvoke*/f.col(3.0);
50+
(/*info:DynamicInvoke*/f(3.0));
51+
(/*info:DynamicInvoke*/f.col(3));
52+
}
53+
{
54+
A f = new B();
55+
int x;
56+
double y;
57+
x = /*info:DynamicCast, info:DynamicInvoke*/f(3);
58+
y = /*info:DynamicCast, info:DynamicInvoke*/f(3);
59+
(/*info:DynamicInvoke*/f(3.0));
60+
}
61+
}
62+
'''
63+
});
64+
});
65+
1766
test('conversion and dynamic invoke', () {
1867
testChecker({
1968
'/helper.dart': '''
@@ -91,6 +140,7 @@ void main() {
91140
92141
baz().toString();
93142
baz().hashCode;
143+
}
94144
'''
95145
});
96146
});

0 commit comments

Comments
 (0)