Skip to content

Commit

Permalink
[cfe] Handle extension operators
Browse files Browse the repository at this point in the history
Change-Id: Ib63d7ab486dbe130cd2e37cc8a51abb2e64b7099
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/115400
Reviewed-by: Dmitry Stefantsov <dmitryas@google.com>
  • Loading branch information
johnniwinther authored and commit-bot@chromium.org committed Sep 6, 2019
1 parent 7ca157d commit deb2fcf
Show file tree
Hide file tree
Showing 8 changed files with 291 additions and 2 deletions.
4 changes: 2 additions & 2 deletions pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1060,6 +1060,7 @@ abstract class TypeInferrerImpl extends TypeInferrer {
case ObjectAccessTargetKind.extensionMember:
switch (target.extensionMethodKind) {
case ProcedureKind.Method:
case ProcedureKind.Operator:
FunctionType functionType = target.member.function.functionType;
List<TypeParameter> extensionTypeParameters = functionType
.typeParameters
Expand Down Expand Up @@ -1087,7 +1088,6 @@ abstract class TypeInferrerImpl extends TypeInferrer {
return substitution.substituteType(functionType.returnType);
case ProcedureKind.Setter:
case ProcedureKind.Factory:
case ProcedureKind.Operator:
break;
}
}
Expand Down Expand Up @@ -1158,13 +1158,13 @@ abstract class TypeInferrerImpl extends TypeInferrer {
case ObjectAccessTargetKind.extensionMember:
switch (target.extensionMethodKind) {
case ProcedureKind.Method:
case ProcedureKind.Operator:
return target.member.function.functionType;
case ProcedureKind.Getter:
// TODO(johnniwinther): Handle implicit .call on extension getter.
return _getFunctionType(target.member.function.returnType, false);
case ProcedureKind.Setter:
case ProcedureKind.Factory:
case ProcedureKind.Operator:
break;
}
}
Expand Down
1 change: 1 addition & 0 deletions pkg/front_end/test/spell_checking_list_common.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1254,6 +1254,7 @@ ignored
ignores
ignoring
illegal
imaginary
imitation
immediate
immediately
Expand Down
84 changes: 84 additions & 0 deletions pkg/front_end/testcases/extensions/operators.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// Copyright (c) 2019, 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.

class Complex {
final double real;
final double imaginary;

const Complex(this.real, this.imaginary);

Complex add(Complex other) {
return new Complex(real + other.real, imaginary + other.imaginary);
}

Complex sub(Complex other) {
return new Complex(real - other.real, imaginary - other.imaginary);
}

Complex negate() {
return new Complex(-real, -imaginary);
}

int get hashCode => real.hashCode * 13 + imaginary.hashCode * 19;

bool operator ==(Object other) {
if (identical(this, other)) return true;
return other is Complex &&
real == other.real &&
imaginary == other.imaginary;
}

String toString() => 'Complex($real,$imaginary)';
}

extension Operators on Complex {
Complex operator +(Complex other) => add(other);
Complex operator -(Complex other) => sub(other);
Complex operator -() => negate();
}

main() {
Complex c_m2 = new Complex(-2, 2);
Complex c_m1 = new Complex(-1, 1);
Complex c0 = new Complex(0, 0);
Complex c1 = new Complex(1, -1);
Complex c2 = new Complex(2, -2);

expect(c_m2, c0 + c_m2);
expect(c_m2, c_m2 + c0);
expect(c_m2, c_m1 + c_m1);
expect(c_m1, c0 + c_m1);
expect(c_m1, c_m1 + c0);
expect(c0, c_m2 + c2);
expect(c0, c2 + c_m2);
expect(c0, c_m1 + c1);
expect(c0, c1 + c_m1);
expect(c0, c0 + c0);
expect(c1, c0 + c1);
expect(c1, c1 + c0);
expect(c2, c0 + c2);
expect(c2, c2 + c0);
expect(c2, c1 + c1);

expect(c_m2, c0 - c2);
expect(c2, c2 - c0);
expect(c_m2, -c2);
expect(c_m1, c1 - c2);
expect(c1, c2 - c1);
expect(c_m1, c0 - c1);
expect(c1, c1 - c0);
expect(c_m1, -c1);
expect(c0, c2 - c2);
expect(c0, c1 - c1);
expect(c0, c0 - c0);
expect(c0, c_m1 - c_m1);
expect(c0, c_m2 - c_m2);
expect(c0, -c0);
}

expect(expected, actual) {
if (expected != actual) {
throw 'Mismatch: expected=$expected, actual=$actual';
}
}
38 changes: 38 additions & 0 deletions pkg/front_end/testcases/extensions/operators.dart.outline.expect
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
library;
import self as self;
import "dart:core" as core;

class Complex extends core::Object {
final field core::double* real;
final field core::double* imaginary;
const constructor •(core::double* real, core::double* imaginary) → self::Complex*
: self::Complex::real = real, self::Complex::imaginary = imaginary, super core::Object::•()
;
method add(self::Complex* other) → self::Complex*
;
method sub(self::Complex* other) → self::Complex*
;
method negate() → self::Complex*
;
get hashCode() → core::int*
;
operator ==(core::Object* other) → core::bool*
;
method toString() → core::String*
;
}
extension Operators on self::Complex* {
operator + = self::Operators|+;
operator - = self::Operators|-;
operator unary- = self::Operators|unary-;
}
static method Operators|+(final self::Complex* #this, self::Complex* other) → self::Complex*
;
static method Operators|-(final self::Complex* #this, self::Complex* other) → self::Complex*
;
static method Operators|unary-(final self::Complex* #this) → self::Complex*
;
static method main() → dynamic
;
static method expect(dynamic expected, dynamic actual) → dynamic
;
81 changes: 81 additions & 0 deletions pkg/front_end/testcases/extensions/operators.dart.strong.expect
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
library;
import self as self;
import "dart:core" as core;

class Complex extends core::Object {
final field core::double* real;
final field core::double* imaginary;
const constructor •(core::double* real, core::double* imaginary) → self::Complex*
: self::Complex::real = real, self::Complex::imaginary = imaginary, super core::Object::•()
;
method add(self::Complex* other) → self::Complex* {
return new self::Complex::•(this.{self::Complex::real}.{core::double::+}(other.{self::Complex::real}), this.{self::Complex::imaginary}.{core::double::+}(other.{self::Complex::imaginary}));
}
method sub(self::Complex* other) → self::Complex* {
return new self::Complex::•(this.{self::Complex::real}.{core::double::-}(other.{self::Complex::real}), this.{self::Complex::imaginary}.{core::double::-}(other.{self::Complex::imaginary}));
}
method negate() → self::Complex* {
return new self::Complex::•(this.{self::Complex::real}.{core::double::unary-}(), this.{self::Complex::imaginary}.{core::double::unary-}());
}
get hashCode() → core::int*
return this.{self::Complex::real}.{core::num::hashCode}.{core::num::*}(13).{core::num::+}(this.{self::Complex::imaginary}.{core::num::hashCode}.{core::num::*}(19));
operator ==(core::Object* other) → core::bool* {
if(core::identical(this, other))
return true;
return other is self::Complex* && this.{self::Complex::real}.{core::num::==}(other{self::Complex*}.{self::Complex::real}) && this.{self::Complex::imaginary}.{core::num::==}(other{self::Complex*}.{self::Complex::imaginary});
}
method toString() → core::String*
return "Complex(${this.{self::Complex::real}},${this.{self::Complex::imaginary}})";
}
extension Operators on self::Complex* {
operator + = self::Operators|+;
operator - = self::Operators|-;
operator unary- = self::Operators|unary-;
}
static method Operators|+(final self::Complex* #this, self::Complex* other) → self::Complex*
return #this.{self::Complex::add}(other);
static method Operators|-(final self::Complex* #this, self::Complex* other) → self::Complex*
return #this.{self::Complex::sub}(other);
static method Operators|unary-(final self::Complex* #this) → self::Complex*
return #this.{self::Complex::negate}();
static method main() → dynamic {
self::Complex* c_m2 = new self::Complex::•(-2.0, 2.0);
self::Complex* c_m1 = new self::Complex::•(-1.0, 1.0);
self::Complex* c0 = new self::Complex::•(0.0, 0.0);
self::Complex* c1 = new self::Complex::•(1.0, -1.0);
self::Complex* c2 = new self::Complex::•(2.0, -2.0);
self::expect(c_m2, self::Operators|+(c0, c_m2));
self::expect(c_m2, self::Operators|+(c_m2, c0));
self::expect(c_m2, self::Operators|+(c_m1, c_m1));
self::expect(c_m1, self::Operators|+(c0, c_m1));
self::expect(c_m1, self::Operators|+(c_m1, c0));
self::expect(c0, self::Operators|+(c_m2, c2));
self::expect(c0, self::Operators|+(c2, c_m2));
self::expect(c0, self::Operators|+(c_m1, c1));
self::expect(c0, self::Operators|+(c1, c_m1));
self::expect(c0, self::Operators|+(c0, c0));
self::expect(c1, self::Operators|+(c0, c1));
self::expect(c1, self::Operators|+(c1, c0));
self::expect(c2, self::Operators|+(c0, c2));
self::expect(c2, self::Operators|+(c2, c0));
self::expect(c2, self::Operators|+(c1, c1));
self::expect(c_m2, self::Operators|-(c0, c2));
self::expect(c2, self::Operators|-(c2, c0));
self::expect(c_m2, self::Operators|unary-(c2));
self::expect(c_m1, self::Operators|-(c1, c2));
self::expect(c1, self::Operators|-(c2, c1));
self::expect(c_m1, self::Operators|-(c0, c1));
self::expect(c1, self::Operators|-(c1, c0));
self::expect(c_m1, self::Operators|unary-(c1));
self::expect(c0, self::Operators|-(c2, c2));
self::expect(c0, self::Operators|-(c1, c1));
self::expect(c0, self::Operators|-(c0, c0));
self::expect(c0, self::Operators|-(c_m1, c_m1));
self::expect(c0, self::Operators|-(c_m2, c_m2));
self::expect(c0, self::Operators|unary-(c0));
}
static method expect(dynamic expected, dynamic actual) → dynamic {
if(!expected.{core::Object::==}(actual)) {
throw "Mismatch: expected=${expected}, actual=${actual}";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
library;
import self as self;
import "dart:core" as core;

class Complex extends core::Object {
final field core::double* real;
final field core::double* imaginary;
const constructor •(core::double* real, core::double* imaginary) → self::Complex*
: self::Complex::real = real, self::Complex::imaginary = imaginary, super core::Object::•()
;
method add(self::Complex* other) → self::Complex* {
return new self::Complex::•(this.{self::Complex::real}.{core::double::+}(other.{self::Complex::real}), this.{self::Complex::imaginary}.{core::double::+}(other.{self::Complex::imaginary}));
}
method sub(self::Complex* other) → self::Complex* {
return new self::Complex::•(this.{self::Complex::real}.{core::double::-}(other.{self::Complex::real}), this.{self::Complex::imaginary}.{core::double::-}(other.{self::Complex::imaginary}));
}
method negate() → self::Complex* {
return new self::Complex::•(this.{self::Complex::real}.{core::double::unary-}(), this.{self::Complex::imaginary}.{core::double::unary-}());
}
get hashCode() → core::int*
return this.{self::Complex::real}.{core::num::hashCode}.{core::num::*}(13).{core::num::+}(this.{self::Complex::imaginary}.{core::num::hashCode}.{core::num::*}(19));
operator ==(core::Object* other) → core::bool* {
if(core::identical(this, other))
return true;
return other is self::Complex* && this.{self::Complex::real}.{core::num::==}(other{self::Complex*}.{self::Complex::real}) && this.{self::Complex::imaginary}.{core::num::==}(other{self::Complex*}.{self::Complex::imaginary});
}
method toString() → core::String*
return "Complex(${this.{self::Complex::real}},${this.{self::Complex::imaginary}})";
}
extension Operators on self::Complex* {
operator + = self::Operators|+;
operator - = self::Operators|-;
operator unary- = self::Operators|unary-;
}
static method Operators|+(final self::Complex* #this, self::Complex* other) → self::Complex*
return #this.{self::Complex::add}(other);
static method Operators|-(final self::Complex* #this, self::Complex* other) → self::Complex*
return #this.{self::Complex::sub}(other);
static method Operators|unary-(final self::Complex* #this) → self::Complex*
return #this.{self::Complex::negate}();
static method main() → dynamic {
self::Complex* c_m2 = new self::Complex::•(-2.0, 2.0);
self::Complex* c_m1 = new self::Complex::•(-1.0, 1.0);
self::Complex* c0 = new self::Complex::•(0.0, 0.0);
self::Complex* c1 = new self::Complex::•(1.0, -1.0);
self::Complex* c2 = new self::Complex::•(2.0, -2.0);
self::expect(c_m2, self::Operators|+(c0, c_m2));
self::expect(c_m2, self::Operators|+(c_m2, c0));
self::expect(c_m2, self::Operators|+(c_m1, c_m1));
self::expect(c_m1, self::Operators|+(c0, c_m1));
self::expect(c_m1, self::Operators|+(c_m1, c0));
self::expect(c0, self::Operators|+(c_m2, c2));
self::expect(c0, self::Operators|+(c2, c_m2));
self::expect(c0, self::Operators|+(c_m1, c1));
self::expect(c0, self::Operators|+(c1, c_m1));
self::expect(c0, self::Operators|+(c0, c0));
self::expect(c1, self::Operators|+(c0, c1));
self::expect(c1, self::Operators|+(c1, c0));
self::expect(c2, self::Operators|+(c0, c2));
self::expect(c2, self::Operators|+(c2, c0));
self::expect(c2, self::Operators|+(c1, c1));
self::expect(c_m2, self::Operators|-(c0, c2));
self::expect(c2, self::Operators|-(c2, c0));
self::expect(c_m2, self::Operators|unary-(c2));
self::expect(c_m1, self::Operators|-(c1, c2));
self::expect(c1, self::Operators|-(c2, c1));
self::expect(c_m1, self::Operators|-(c0, c1));
self::expect(c1, self::Operators|-(c1, c0));
self::expect(c_m1, self::Operators|unary-(c1));
self::expect(c0, self::Operators|-(c2, c2));
self::expect(c0, self::Operators|-(c1, c1));
self::expect(c0, self::Operators|-(c0, c0));
self::expect(c0, self::Operators|-(c_m1, c_m1));
self::expect(c0, self::Operators|-(c_m2, c_m2));
self::expect(c0, self::Operators|unary-(c0));
}
static method expect(dynamic expected, dynamic actual) → dynamic {
if(!expected.{core::Object::==}(actual)) {
throw "Mismatch: expected=${expected}, actual=${actual}";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pkg/front_end/testcases/extensions/operators.dart:27:18: Context: Possible promotion of other@348
return other is Complex &&
^^
1 change: 1 addition & 0 deletions pkg/front_end/testcases/text_serialization.status
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ extensions/instance_tearoff: TextSerializationFailure
extensions/invalid_explicit_access: TextSerializationFailure
extensions/on_type_inference: TextSerializationFailure
extensions/on_type_variable_inference: TextSerializationFailure
extensions/operators: TextSerializationFailure
extensions/other_kinds: TextSerializationFailure
extensions/static_access: TextSerializationFailure
extensions/static_access_of_instance: TextSerializationFailure
Expand Down

0 comments on commit deb2fcf

Please sign in to comment.