// 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 Function(); typedef F4>>>= T Function(); typedef F5>>>>= T Function(); typedef F6>>>>>= T Function(); class E3>> {} class E4>>> {} class E5>>>> {} class E6>>>>> {} 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>>(); E4>>>(); E5>>>>(); E6>>>>>(); Expect.type>(() => null); Expect.type>(() => null); Expect.type>(() => null); Expect.type>(() => 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 } /// 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 operator >>>(C value) async => value; } /// Class with an `async*` implementation of `operator >>>` class AsyncStar { Stream operator >>>(C value) async* { yield value; } } /// Class with a `sync*` implementation of `operator >>>` class SyncStar { Iterable 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; }