| // 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[0]); |
| 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[0]); |
| 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[0]); |
| 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 >>> c1)); |
| |
| var asyncStar = AsyncStar(); |
| int count = 0; |
| await for (var v in asyncStar >>> c1) { |
| count++; |
| Expect.identical(c1, v); |
| } |
| Expect.equals(1, count); |
| asyncEnd(); |
| }(); |
| |
| { |
| var syncStar = SyncStar(); |
| int count = 0; |
| for (var v in syncStar >>> c1) { |
| count++; |
| Expect.identical(c1, v); |
| } |
| Expect.equals(1, count); |
| } |
| |
| // >>> 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 / a}"); |
| Expect.equals("(((*>>*)>>>*)>>*)", "${a >> a >>> a >> a}"); |
| Expect.equals("((*&(*>>>*))&*)", "${a & a >>> a & a}"); |
| Expect.equals("((*|(*>>>*))|*)", "${a | a >>> a | a}"); |
| Expect.equals("((*^(*>>>*))^*)", "${a ^ 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<List<List<int>>>>(() => <List<int>>[]); |
| Expect.type<F4<List<List<List<int>>>>>(() => <List<List<int>>>[]); |
| Expect.type<F5<List<List<List<List<int>>>>>>(() => <List<List<List<int>>>>[]); |
| Expect.type<F6<List<List<List<List<List<int>>>>>>>( |
| () => <List<List<List<List<int>>>>>[]); |
| } |
| |
| /// 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 |
| } |
| |
| /// Class with noSuchMethod and no `>>>` operator. |
| class NSM { |
| dynamic noSuchMethod(Invocation invocation) { |
| 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 value) async => value; |
| } |
| |
| /// Class with an `async*` implementation of `operator >>>` |
| class AsyncStar { |
| Stream<C> operator >>>(C value) async* { |
| yield value; |
| } |
| } |
| |
| /// Class with a `sync*` implementation of `operator >>>` |
| class SyncStar { |
| Iterable<C> operator >>>(C value) sync* { |
| yield value; |
| } |
| } |
| |
| /// 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)"); |
| Assoc operator <(Assoc other) => Assoc("(${this}<$other)"); |
| Assoc operator >=(Assoc other) => Assoc("(${this}>=$other)"); |
| Assoc operator <=(Assoc other) => Assoc("(${this}<=$other)"); |
| String toString() => ops; |
| } |