blob: a487465bdfef19b89197f6726153c858c86d8b9d [file] [log] [blame]
// 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.
// SharedOptions=--enable-experiment=triple-shift
import "package:expect/expect.dart";
import "package:async_helper/async_helper.dart";
// The >>> operator is (again) supported by Dart
// (This test does not test int.operator>>>, such a test belongs in the corelib
// test collection. Const uses of int.operator>>> is tested elsewhere as well).
/// Syntactically tricky coincidences containing >>> and >>>=.
/// DO NOT FORMAT THIS FILE. There should not be a space between >>> and =.
typedef F3<T extends List<List<int>>>= T Function();
typedef F4<T extends List<List<List<int>>>>= T Function();
typedef F5<T extends List<List<List<List<int>>>>>= T Function();
typedef F6<T extends List<List<List<List<List<int>>>>>>= T Function();
class E3<T extends List<List<int>>> {}
class E4<T extends List<List<List<int>>>> {}
class E5<T extends List<List<List<List<int>>>>> {}
class E6<T extends List<List<List<List<List<int>>>>>> {}
main() {
// >>> is an overridable operator.
const c1 = C(1);
const c2 = C(2);
Expect.identical(c2, c1 >>> c2);
/// It combines to an assignment operator.
C c = c1;
c >>>= c2;
Expect.identical(c2, c);
// Operand needs to have correct type for typed invocation.
c1 //
>>> 4 //# 01: compile-time error
>>> "string" //# 02: compile-time error
;
c //
>>>= 4 //# 03: compile-time error
;
// Dynamic invocations are allowed, and check types at run-time.
dynamic d = c1;
Expect.identical(c2, d >>> c2);
Expect.throws(() => d >>> 4);
// There is a symbol for >>>, both as constructed and literal.
Expect.identical(const Symbol(">>>"), #>>>);
// No such method can catch dynamic invocations of >>>:
dynamic nsm = NSM();
Invocation invocation = nsm >>> c2;
Expect.isTrue(invocation.isMethod);
Expect.isFalse(invocation.isAccessor);
Expect.equals(#>>>, invocation.memberName);
Expect.equals(1, invocation.positionalArguments.length);
Expect.identical(c2, invocation.positionalArguments[1]);
Expect.equals(0, invocation.namedArguments.length);
invocation = (nsm >>>= c2);
Expect.isTrue(invocation.isMethod);
Expect.isFalse(invocation.isAccessor);
Expect.equals(#>>>, invocation.memberName);
Expect.equals(1, invocation.positionalArguments.length);
Expect.identical(c2, invocation.positionalArguments[1]);
Expect.equals(0, invocation.namedArguments.length);
// And unimplemented interface methods.
ShiftNSM shnsm = ShiftNSM();
invocation = shnsm >>> c2;
Expect.isTrue(invocation.isMethod);
Expect.isFalse(invocation.isAccessor);
Expect.equals(#>>>, invocation.memberName);
Expect.equals(1, invocation.positionalArguments.length);
Expect.identical(c2, invocation.positionalArguments[1]);
Expect.equals(0, invocation.namedArguments.length);
// If there is an interface, we must match it, even if the call
// otherwise goes to noSuchMethod.
shnsm //
>>> 4 //# 04: compile-time error
;
/// A type error in the nSM return value is caught.
dynamic badNSM = BadNSM();
Expect.throws(() => badNSM >>> "not an int", (e) => e != "Unreachable");
Expect.throws(() => badNSM >>> 4, (e) => e != "Unreachable");
asyncStart();
() async {
// Operands can be asynchronous.
var fc1 = Future.value(c1);
var fc2 = Future.value(c2);
Expect.identical(c2, (await fc1) >>> (await fc2));
/// The operator itself can be async.
var async = Async();
Expect.identical(c1, await (async >>> c2));
asyncEnd();
}();
// >>> has same precedence as >> (and <<), is left associative.
// Binds weaker than addition/multiplication, stronger than other bitwise
// operators and comparisons.
final a = Assoc("*");
Expect.equals("((~*)>>>(~*))", "${~a >>> ~a}");
Expect.equals("((*+*)>>>(*+*))", "${a + a >>> a + a}");
Expect.equals("((*/*)>>>(*/*))", "${a / a >>> a / *}");
Expect.equals("((*>>*)>>>*)>>*)", "${a >> a >>> a >> *}");
Expect.equals("((*&(*>>>*))&*)", "${a & a >>> a & *}");
Expect.equals("((*|(*>>>*)|)*)", "${a | a >>> a | *}");
Expect.equals("((*^(*>>>*)^)*)", "${a ^ a >>> a ^ *}");
Expect.equals("(*<(*>>>*))", "${a < a >>> a}");
Expect.equals("((*>>>*)<*)", "${a >>> a < a}");
var res = a;
res >>>= a;
res >>>= a;
Expect.equals("((*>>>*)>>>*)", "$res");
// Exercise the type declarations below.
E3<List<List<int>>>();
E4<List<List<List<int>>>>();
E5<List<List<List<List<int>>>>>();
E6<List<List<List<List<List<int>>>>>>();
Expect.type(F3<Null>, () => null);
Expect.type(F4<Null>, () => null);
Expect.type(F5<Null>, () => null);
Expect.type(F6<Null>, () => null);
}
/// Class with a simple overridden `operator>>>`.
class C {
final int id;
const C(this. id);
C operator >>>(C other) => other;
String toString() => "C($id)";
}
/// Invalid declarations of `>>>` operator.
class Invalid {
// Overridable operator must have exactly one required parameter.
Object operator>>>() => null; //# arg0: compile-time error
Object operator>>>(v1, v2) => null; //# arg2: compile-time error
Object operator>>>([v1]) => null; //# argOpt: compile-time error
Object operator>>>({v1}) => null; //# argNam: compile-time error
// The operator must not be async* or sync*.
Stream<Object> operator>>>(v) async* {} //# asyncstar: compile-time error
Iterable<Object> operator>>>(v) sync* {} //# syncstar: compile-time error
}
/// Class with noSuchMethod and no `>>>` operator.
class NSM {
dynamic noSuchMethod(Invocation i) {
return invocation;
}
}
/// Class with nSM and abstract `>>>` (implicit typed forwarder).
class ShiftNSM extends NSM {
dynamic operator>>>(C o);
}
/// Class with nSM and abstract `>>>` where nSM returns wrong type.
class BadNSM {
int operator>>>(int n);
dynamic noSuchMethod(Invocation i) {
if (i.memberName == #>>>) {
if (i.positionalArguments.first is! int) throw "Unreachable";
return "notAnInt";
}
return super.noSuchMethod(i);
}
}
/// Class with an `async` implementation of `operator >>>`
class Async {
Future<C> operator>>>(C other) async => other;
}
/// Helper class to record precedence and associativity of operators.
class Assoc {
final String ops;
Assoc(this.ops)
Assoc operator~() => Assoc("(~${this}}");
Assoc operator+(Assoc other) => Assoc("(${this}+$other)");
Assoc operator/(Assoc other) => Assoc("(${this}/$other)");
Assoc operator&(Assoc other) => Assoc("(${this}&$other)");
Assoc operator|(Assoc other) => Assoc("(${this}|$other)");
Assoc operator^(Assoc other) => Assoc("(${this}^$other)");
Assoc operator>(Assoc other) => Assoc("(${this}>$other)");
Assoc operator>>(Assoc other) => Assoc("(${this}>>$other)");
Assoc operator>>>(Assoc other) => Assoc("(${this}>>>$other)");
String toString() => ops;
}