mirror of
https://github.com/dart-lang/sdk
synced 2024-09-18 21:31:20 +00:00
e7fab5eacc
Change-Id: Iafdf3e56a3e69b4d945799c541795e1fd6901178 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/108806 Commit-Queue: Lasse R.H. Nielsen <lrn@google.com> Reviewed-by: Erik Ernst <eernst@google.com>
230 lines
7.4 KiB
Dart
230 lines
7.4 KiB
Dart
// 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<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
|
|
}
|
|
|
|
/// 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;
|
|
}
|