mirror of
https://github.com/dart-lang/sdk
synced 2024-10-14 19:10:05 +00:00
Fix operator handling.
Review URL: https://codereview.chromium.org//11348067 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@15524 260f80e4-7a28-3924-810f-c04153c831b5
This commit is contained in:
parent
e08d148977
commit
985223c91a
|
@ -1949,7 +1949,8 @@ class Elements {
|
|||
}
|
||||
}
|
||||
|
||||
static SourceString constructOperatorName(SourceString op, bool isUnary) {
|
||||
static SourceString constructOperatorNameOrNull(SourceString op,
|
||||
bool isUnary) {
|
||||
String value = op.stringValue;
|
||||
if ((identical(value, '==')) ||
|
||||
(identical(value, '~')) ||
|
||||
|
@ -1974,11 +1975,17 @@ class Elements {
|
|||
} else if (identical(value, '-')) {
|
||||
return isUnary ? const SourceString('unary-') : op;
|
||||
} else {
|
||||
throw 'Unhandled operator: ${op.slowToString()}';
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static SourceString mapToUserOperator(SourceString op) {
|
||||
static SourceString constructOperatorName(SourceString op, bool isUnary) {
|
||||
SourceString operatorName = constructOperatorNameOrNull(op, isUnary);
|
||||
if (operatorName == null) throw 'Unhandled operator: ${op.slowToString()}';
|
||||
else return operatorName;
|
||||
}
|
||||
|
||||
static SourceString mapToUserOperatorOrNull(SourceString op) {
|
||||
String value = op.stringValue;
|
||||
|
||||
if (identical(value, '!=')) return const SourceString('==');
|
||||
|
@ -1994,7 +2001,13 @@ class Elements {
|
|||
if (identical(value, '^=')) return const SourceString('^');
|
||||
if (identical(value, '|=')) return const SourceString('|');
|
||||
|
||||
throw 'Unhandled operator: ${op.slowToString()}';
|
||||
return null;
|
||||
}
|
||||
|
||||
static SourceString mapToUserOperator(SourceString op) {
|
||||
SourceString userOperator = mapToUserOperatorOrNull(op);
|
||||
if (userOperator == null) throw 'Unhandled operator: ${op.slowToString()}';
|
||||
else return userOperator;
|
||||
}
|
||||
|
||||
static bool isNumberOrStringSupertype(Element element, Compiler compiler) {
|
||||
|
|
|
@ -94,6 +94,7 @@ abstract class ConcreteType {
|
|||
|
||||
ConcreteType union(ConcreteType other);
|
||||
bool isUnkown();
|
||||
bool isEmpty();
|
||||
Set<BaseType> get baseTypes;
|
||||
|
||||
/**
|
||||
|
@ -109,6 +110,7 @@ abstract class ConcreteType {
|
|||
class UnknownConcreteType implements ConcreteType {
|
||||
const UnknownConcreteType();
|
||||
bool isUnkown() => true;
|
||||
bool isEmpty() => false;
|
||||
bool operator ==(ConcreteType other) => identical(this, other);
|
||||
Set<BaseType> get baseTypes =>
|
||||
new Set<BaseType>.from([const UnknownBaseType()]);
|
||||
|
@ -131,6 +133,7 @@ class UnionType implements ConcreteType {
|
|||
UnionType(this.baseTypes);
|
||||
|
||||
bool isUnkown() => false;
|
||||
bool isEmpty() => baseTypes.isEmpty;
|
||||
|
||||
bool operator ==(ConcreteType other) {
|
||||
if (other is! UnionType) return false;
|
||||
|
@ -1055,31 +1058,54 @@ class TypeInferrerVisitor extends ResolvedVisitor<ConcreteType> {
|
|||
return argumentType;
|
||||
}
|
||||
|
||||
SourceString canonicalizeCompoundOperator(String op) {
|
||||
SourceString canonicalizeCompoundOperator(SourceString op) {
|
||||
// TODO(ahe): This class should work on elements or selectors, not
|
||||
// names. Otherwise, it is repeating work the resolver has
|
||||
// already done (or should have done). In this case, the problem
|
||||
// is that the resolver is not recording the selectors it is
|
||||
// registering in registerBinaryOperator in
|
||||
// ResolverVisitor.visitSendSet.
|
||||
if (op == '++') return const SourceString(r'+');
|
||||
else return const SourceString(r'-');
|
||||
String stringValue = op.stringValue;
|
||||
if (stringValue == '++') return const SourceString(r'+');
|
||||
else if (stringValue == '--') return const SourceString(r'-');
|
||||
else return Elements.mapToUserOperatorOrNull(op);
|
||||
}
|
||||
|
||||
// TODO(polux): handle sendset as expression
|
||||
ConcreteType visitSendSet(SendSet node) {
|
||||
Identifier selector = node.selector;
|
||||
final name = node.assignmentOperator.source.stringValue;
|
||||
// Operator []= has a different behaviour than other send sets: it is
|
||||
// actually a send whose return type is that of its second argument.
|
||||
if (node.selector.asIdentifier().source.stringValue == '[]') {
|
||||
ConcreteType receiverType = analyze(node.receiver);
|
||||
ArgumentsTypes argumentsTypes = analyzeArguments(node.arguments);
|
||||
analyzeDynamicSend(receiverType, const SourceString('[]='),
|
||||
argumentsTypes);
|
||||
return argumentsTypes.positional[1];
|
||||
}
|
||||
|
||||
// All other operators have a single argument (++ and -- have an implicit
|
||||
// argument: 1). We will store its type in argumentType.
|
||||
ConcreteType argumentType;
|
||||
if (name == '++' || name == '--') {
|
||||
SourceString operatorName = node.assignmentOperator.source;
|
||||
SourceString compoundOperatorName =
|
||||
canonicalizeCompoundOperator(node.assignmentOperator.source);
|
||||
// ++, --, +=, -=, ...
|
||||
if (compoundOperatorName != null) {
|
||||
ConcreteType receiverType = visitGetterSend(node);
|
||||
SourceString canonicalizedMethodName = canonicalizeCompoundOperator(name);
|
||||
List<ConcreteType> positionalArguments = <ConcreteType>[
|
||||
new ConcreteType.singleton(inferrer.baseTypes.intBaseType)];
|
||||
ArgumentsTypes argumentsTypes =
|
||||
new ArgumentsTypes(positionalArguments, new Map());
|
||||
argumentType = analyzeDynamicSend(receiverType, canonicalizedMethodName,
|
||||
// argumentsTypes is either computed from the actual arguments or [{int}]
|
||||
// in case of ++ or --.
|
||||
ArgumentsTypes argumentsTypes;
|
||||
if (operatorName.stringValue == '++'
|
||||
|| operatorName.stringValue == '--') {
|
||||
List<ConcreteType> positionalArguments = <ConcreteType>[
|
||||
new ConcreteType.singleton(inferrer.baseTypes.intBaseType)];
|
||||
argumentsTypes = new ArgumentsTypes(positionalArguments, new Map());
|
||||
} else {
|
||||
argumentsTypes = analyzeArguments(node.arguments);
|
||||
}
|
||||
argumentType = analyzeDynamicSend(receiverType, compoundOperatorName,
|
||||
argumentsTypes);
|
||||
// The simple assignment case: receiver = argument.
|
||||
} else {
|
||||
argumentType = analyze(node.argumentsNode);
|
||||
}
|
||||
|
@ -1370,47 +1396,6 @@ class TypeInferrerVisitor extends ResolvedVisitor<ConcreteType> {
|
|||
inferrer.fail(node, 'not implemented');
|
||||
}
|
||||
|
||||
// TODO(polux): handle unary operators and share this list with the rest of
|
||||
// dart2js.
|
||||
final Set<SourceString> operators = new Set<SourceString>()
|
||||
..add(const SourceString('=='))
|
||||
..add(const SourceString('!='))
|
||||
..add(const SourceString('~'))
|
||||
..add(const SourceString('[]'))
|
||||
..add(const SourceString('[]='))
|
||||
..add(const SourceString('*'))
|
||||
..add(const SourceString('*='))
|
||||
..add(const SourceString('/'))
|
||||
..add(const SourceString('/='))
|
||||
..add(const SourceString('%'))
|
||||
..add(const SourceString('%='))
|
||||
..add(const SourceString('~/'))
|
||||
..add(const SourceString('~/='))
|
||||
..add(const SourceString('+'))
|
||||
..add(const SourceString('+='))
|
||||
..add(const SourceString('-'))
|
||||
..add(const SourceString('-='))
|
||||
..add(const SourceString('<<'))
|
||||
..add(const SourceString('<<='))
|
||||
..add(const SourceString('>>'))
|
||||
..add(const SourceString('>>='))
|
||||
..add(const SourceString('>='))
|
||||
..add(const SourceString('>'))
|
||||
..add(const SourceString('<='))
|
||||
..add(const SourceString('<'))
|
||||
..add(const SourceString('&'))
|
||||
..add(const SourceString('&='))
|
||||
..add(const SourceString('^'))
|
||||
..add(const SourceString('^='))
|
||||
..add(const SourceString('|'))
|
||||
..add(const SourceString('|='));
|
||||
|
||||
SourceString canonicalizeMethodName(SourceString s) {
|
||||
return operators.contains(s)
|
||||
? Elements.constructOperatorName(s, false)
|
||||
: s;
|
||||
}
|
||||
|
||||
ConcreteType analyzeDynamicSend(ConcreteType receiverType,
|
||||
SourceString canonicalizedMethodName,
|
||||
ArgumentsTypes argumentsTypes) {
|
||||
|
@ -1448,6 +1433,14 @@ class TypeInferrerVisitor extends ResolvedVisitor<ConcreteType> {
|
|||
return result;
|
||||
}
|
||||
|
||||
SourceString canonicalizeMethodName(SourceString name) {
|
||||
// TODO(polux): handle unary-
|
||||
SourceString operatorName =
|
||||
Elements.constructOperatorNameOrNull(name, false);
|
||||
if (operatorName != null) return operatorName;
|
||||
return name;
|
||||
}
|
||||
|
||||
ConcreteType visitDynamicSend(Send node) {
|
||||
ConcreteType receiverType = (node.receiver != null)
|
||||
? analyze(node.receiver)
|
||||
|
@ -1456,7 +1449,16 @@ class TypeInferrerVisitor extends ResolvedVisitor<ConcreteType> {
|
|||
SourceString name =
|
||||
canonicalizeMethodName(node.selector.asIdentifier().source);
|
||||
ArgumentsTypes argumentsTypes = analyzeArguments(node.arguments);
|
||||
return analyzeDynamicSend(receiverType, name, argumentsTypes);
|
||||
if (name.stringValue == '!=') {
|
||||
ConcreteType returnType = analyzeDynamicSend(receiverType,
|
||||
const SourceString('=='),
|
||||
argumentsTypes);
|
||||
return returnType.isEmpty()
|
||||
? returnType
|
||||
: new ConcreteType.singleton(inferrer.baseTypes.boolBaseType);
|
||||
} else {
|
||||
return analyzeDynamicSend(receiverType, name, argumentsTypes);
|
||||
}
|
||||
}
|
||||
|
||||
ConcreteType visitForeignSend(Send node) {
|
||||
|
|
|
@ -142,7 +142,12 @@ class AnalysisResult {
|
|||
|
||||
const String CORELIB = r'''
|
||||
print(var obj) {}
|
||||
abstract class num { operator +(x); operator *(x); operator -(x); }
|
||||
abstract class num {
|
||||
operator +(x);
|
||||
operator *(x);
|
||||
operator -(x);
|
||||
operator ==(x);
|
||||
}
|
||||
abstract class int extends num { }
|
||||
abstract class double extends num { }
|
||||
class bool {}
|
||||
|
@ -637,25 +642,55 @@ testOperators() {
|
|||
result.checkNodeHasType('y', [result.string]);
|
||||
}
|
||||
|
||||
testSetIndexOperator() {
|
||||
final String source = r"""
|
||||
class A {
|
||||
var witness1;
|
||||
var witness2;
|
||||
operator []=(i, x) { witness1 = i; witness2 = x; }
|
||||
}
|
||||
main() {
|
||||
var x = new A()[42] = "abc";
|
||||
x;
|
||||
}
|
||||
""";
|
||||
AnalysisResult result = analyze(source);
|
||||
result.checkNodeHasType('x', [result.string]);
|
||||
// TODO(polux): the two following results should be [:[null, string:], see
|
||||
// testFieldInitialization().
|
||||
result.checkFieldHasType('A', 'witness1', [result.int]);
|
||||
result.checkFieldHasType('A', 'witness2', [result.string]);
|
||||
}
|
||||
|
||||
testCompoundOperators1() {
|
||||
final String source = r"""
|
||||
class A {
|
||||
operator +(x) => "foo";
|
||||
}
|
||||
main() {
|
||||
var x1 = 1; x1++;
|
||||
var x2 = 1; ++x2;
|
||||
var x3 = new A(); x3++;
|
||||
var x4 = new A(); ++x4;
|
||||
var x1 = 1;
|
||||
x1++;
|
||||
var x2 = 1;
|
||||
++x2;
|
||||
var x3 = 1;
|
||||
x3 += 42;
|
||||
var x4 = new A();
|
||||
x4++;
|
||||
var x5 = new A();
|
||||
++x5;
|
||||
var x6 = new A();
|
||||
x6 += true;
|
||||
|
||||
x1; x2; x3; x4;
|
||||
x1; x2; x3; x4; x5; x6;
|
||||
}
|
||||
""";
|
||||
AnalysisResult result = analyze(source);
|
||||
result.checkNodeHasType('x1', [result.int]);
|
||||
result.checkNodeHasType('x2', [result.int]);
|
||||
result.checkNodeHasType('x3', [result.string]);
|
||||
result.checkNodeHasType('x3', [result.int]);
|
||||
result.checkNodeHasType('x4', [result.string]);
|
||||
result.checkNodeHasType('x5', [result.string]);
|
||||
result.checkNodeHasType('x6', [result.string]);
|
||||
}
|
||||
|
||||
|
||||
|
@ -663,24 +698,58 @@ testCompoundOperators2() {
|
|||
final String source = r"""
|
||||
class A {
|
||||
var xx;
|
||||
var yy;
|
||||
var witness1;
|
||||
var witness2;
|
||||
var witness3;
|
||||
var witness4;
|
||||
|
||||
A(this.xx);
|
||||
A(this.xx, this.yy);
|
||||
get x { witness1 = "foo"; return xx; }
|
||||
set x(y) { witness2 = "foo"; xx = y; }
|
||||
set x(a) { witness2 = "foo"; xx = a; }
|
||||
get y { witness3 = "foo"; return yy; }
|
||||
set y(a) { witness4 = "foo"; yy = a; }
|
||||
}
|
||||
main () {
|
||||
var a = new A(1);
|
||||
var a = new A(1, 1);
|
||||
a.x++;
|
||||
a.y++;
|
||||
}
|
||||
""";
|
||||
AnalysisResult result = analyze(source);
|
||||
result.checkFieldHasType('A', 'xx', [result.int]);
|
||||
// TODO(polux): the two following results should be {null, string}, see
|
||||
// fieldInitialization().
|
||||
result.checkFieldHasType('A', 'yy', [result.int]);
|
||||
// TODO(polux): the four following results should be [:[null, string]:], see
|
||||
// testFieldInitialization().
|
||||
result.checkFieldHasType('A', 'witness1', [result.string]);
|
||||
result.checkFieldHasType('A', 'witness2', [result.string]);
|
||||
result.checkFieldHasType('A', 'witness3', [result.string]);
|
||||
result.checkFieldHasType('A', 'witness4', [result.string]);
|
||||
}
|
||||
|
||||
testInequality() {
|
||||
final String source = r"""
|
||||
class A {
|
||||
var witness;
|
||||
operator ==(x) { witness = "foo"; return "abc"; }
|
||||
}
|
||||
class B {
|
||||
operator ==(x) { throw "error"; }
|
||||
}
|
||||
main() {
|
||||
var foo = 1 != 2;
|
||||
var bar = new A() != 2;
|
||||
var baz = new B() != 2;
|
||||
foo; bar; baz;
|
||||
}
|
||||
""";
|
||||
AnalysisResult result = analyze(source);
|
||||
result.checkNodeHasType('foo', [result.bool]);
|
||||
result.checkNodeHasType('bar', [result.bool]);
|
||||
result.checkNodeHasType('baz', []);
|
||||
// TODO(polux): the following result should be [:[null, string]:], see
|
||||
// fieldInitialization().
|
||||
result.checkFieldHasType('A', 'witness', [result.string]);
|
||||
}
|
||||
|
||||
testFieldInitialization() {
|
||||
|
@ -762,6 +831,8 @@ void main() {
|
|||
testOperators();
|
||||
testCompoundOperators1();
|
||||
testCompoundOperators2();
|
||||
testSetIndexOperator();
|
||||
testInequality();
|
||||
// testFieldInitialization(); // TODO(polux)
|
||||
testSendWithWrongArity();
|
||||
testDynamicIsAbsorbing();
|
||||
|
|
Loading…
Reference in a new issue