Add operators `&`, `|` and `^` to `bool`.
Change-Id: Idd6472f239445914c1ff1ab68fc7b38fa6b320ae
Reviewed-on: https://dart-review.googlesource.com/25240
Commit-Queue: Lasse R.H. Nielsen <lrn@google.com>
Reviewed-by: Leaf Petersen <leafp@google.com>
Reviewed-by: Lasse R.H. Nielsen <lrn@google.com>
Reviewed-by: Florian Loitsch <floitsch@google.com>
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7b5f18b1..d79cfa2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -20,6 +20,7 @@
be unmodifiable incorrectly allowed new methods added in Dart 2 to
succeed.
* Exported `Future` and `Stream` from `dart:core`.
+* Added operators `&`, `|` and `^` to `bool`.
#### `dart:async`
diff --git a/pkg/dev_compiler/tool/input_sdk/private/interceptors.dart b/pkg/dev_compiler/tool/input_sdk/private/interceptors.dart
index 4a9290a..de5ca6e 100644
--- a/pkg/dev_compiler/tool/input_sdk/private/interceptors.dart
+++ b/pkg/dev_compiler/tool/input_sdk/private/interceptors.dart
@@ -42,6 +42,15 @@
@notNull
int get hashCode => this ? (2 * 3 * 23 * 3761) : (269 * 811);
+ @notNull
+ bool operator &(@nullCheck bool other) => JS('bool', "# && #", other, this);
+
+ @notNull
+ bool operator |(@nullCheck bool other) => JS('bool', "# || #", other, this);
+
+ @notNull
+ bool operator ^(@nullCheck bool other) => !identical(this, other);
+
Type get runtimeType => bool;
}
diff --git a/pkg/expect/lib/expect.dart b/pkg/expect/lib/expect.dart
index 423242f..d20db53 100644
--- a/pkg/expect/lib/expect.dart
+++ b/pkg/expect/lib/expect.dart
@@ -543,7 +543,13 @@
// A test failure doesn't count as throwing.
if (e is ExpectException) rethrow;
if (e is T && (check == null || check(e))) return;
- _fail("Expect.throws$msg: Unexpected '$e'\n$s");
+ // Throws something unexpected.
+ String type = "";
+ if (T != dynamic && T != Object) {
+ type = "<$T>";
+ }
+ _fail("Expect.throws$type$msg: "
+ "Unexpected '${Error.safeToString(e)}'\n$s");
}
_fail('Expect.throws$msg fails: Did not throw');
}
@@ -602,14 +608,16 @@
static void type<T>(Object object, [String reason]) {
if (object is T) return;
String msg = _getMessage(reason);
- _fail("Expect.type($object is $T$msg) fails, was ${object.runtimeType}");
+ _fail("Expect.type($object is $T$msg) fails "
+ "on ${Error.safeToString(object)}");
}
/// Checks that [object] does not have type [T].
static void notType<T>(Object object, [String reason]) {
if (object is! T) return;
String msg = _getMessage(reason);
- _fail("Expect.type($object is! $T$msg) fails, was ${object.runtimeType}");
+ _fail("Expect.type($object is! $T$msg) fails"
+ "on ${Error.safeToString(object)}");
}
static String _getMessage(String reason) =>
diff --git a/pkg/front_end/messages.status b/pkg/front_end/messages.status
index 6eba5be..96ef8c84 100644
--- a/pkg/front_end/messages.status
+++ b/pkg/front_end/messages.status
@@ -330,9 +330,6 @@
ThisOrSuperAccessInFieldInitializer/example: Fail
TooFewArguments/example: Fail
TooManyArguments/example: Fail
-TopLevelOperator/script1: Fail
-TopLevelOperator/script2: Fail
-TopLevelOperator/script3: Fail
TypeAfterVar/example: Fail
TypeArgumentMismatch/example: Fail
TypeArgumentsOnTypeVariable/script1: Fail
diff --git a/sdk/lib/_internal/js_runtime/lib/interceptors.dart b/sdk/lib/_internal/js_runtime/lib/interceptors.dart
index 93b5751..a9fcb75 100644
--- a/sdk/lib/_internal/js_runtime/lib/interceptors.dart
+++ b/sdk/lib/_internal/js_runtime/lib/interceptors.dart
@@ -16,6 +16,7 @@
JSSyntaxRegExp,
Primitives,
argumentErrorValue,
+ checkBool,
checkInt,
checkNull,
checkNum,
@@ -364,6 +365,12 @@
// Note: if you change this, also change the function [S].
String toString() => JS('String', r'String(#)', this);
+ bool operator &(bool other) => JS('bool', "# && #", checkBool(other), this);
+
+ bool operator |(bool other) => JS('bool', "# || #", checkBool(other), this);
+
+ bool operator ^(bool other) => !identical(this, checkBool(other));
+
// The values here are SMIs, co-prime and differ about half of the bit
// positions, including the low bit, so they are different mod 2^k.
int get hashCode => this ? (2 * 3 * 23 * 3761) : (269 * 811);
diff --git a/sdk/lib/core/bool.dart b/sdk/lib/core/bool.dart
index 2aeca44..980b1c5 100644
--- a/sdk/lib/core/bool.dart
+++ b/sdk/lib/core/bool.dart
@@ -5,7 +5,7 @@
part of dart.core;
/**
- * The reserved words [:true:] and [:false:] denote objects that are the only
+ * The reserved words `true` and `false` denote objects that are the only two
* instances of this class.
*
* It is a compile-time error for a class to attempt to extend or implement
@@ -23,21 +23,22 @@
* the result is the [defaultValue].
*
* The result is the same as would be returned by:
- *
- * (const String.fromEnvironment(name) == "true")
- * ? true
- * : (const String.fromEnvironment(name) == "false")
- * ? false
- * : defaultValue
- *
+ * ```dart
+ * (const String.fromEnvironment(name) == "true")
+ * ? true
+ * : (const String.fromEnvironment(name) == "false")
+ * ? false
+ * : defaultValue
+ * ```
* Example:
- *
- * const loggingFlag = const bool.fromEnvironment("logging");
- *
+ * ```dart
+ * const loggingFlag = const bool.fromEnvironment("logging");
+ * ```
* If you want to use a different truth-string than `"true"`, you can use the
* [String.fromEnvironment] constructor directly:
- *
- * const isLoggingOn = (const String.fromEnvironment("logging") == "on");
+ * ```dart
+ * const isLoggingOn = (const String.fromEnvironment("logging") == "on");
+ * ```
*/
// The .fromEnvironment() constructors are special in that we do not want
// users to call them using "new". We prohibit that by giving them bodies
@@ -50,9 +51,24 @@
external int get hashCode;
+ /// The logical conjuncton ("and") of this and [other].
+ ///
+ /// Returns `true` if both this and [other] are `true`, and `false` otherwise.
+ //TODO(lrn): Remove "as bool" in Dart 2.
+ bool operator &(bool other) => (other as bool) && this;
+
+ /// The logical disjunction ("inclusive or") of this and [other].
+ ///
+ /// Returns `true` if either this or [other] is `true`, and `false` otherwise.
+ bool operator |(bool other) => (other as bool) || this;
+
+ /// The logical exclusive disjuction ("exclusive or") of this and [other].
+ ///
+ /// Returns whether this and [other] are neither both `true` nor both `false`.
+ bool operator ^(bool other) => !(other as bool) == this;
+
/**
- * Returns [:"true":] if the receiver is [:true:], or [:"false":] if the
- * receiver is [:false:].
+ * Returns either `"true"` for `true` and `"false"` for `false`.
*/
String toString() {
return this ? "true" : "false";
diff --git a/tests/co19/co19-co19.status b/tests/co19/co19-co19.status
index b3d404f..455498a 100644
--- a/tests/co19/co19-co19.status
+++ b/tests/co19/co19-co19.status
@@ -10,6 +10,7 @@
# Tests that produce compile-time errors even in legacy mode.
[ $runtime == none ]
+Language/Expressions/Bitwise_Expressions/syntax_t01/02: Skip
Language/Variables/constant_initialization_t03: CompileTimeError # Uppercase constants removed
Language/Variables/constant_variable_t09: CompileTimeError # Uppercase constants removed
LibTest/core/double/operator_GE_A01_t03: CompileTimeError # Uppercase constants removed
@@ -42,6 +43,7 @@
# Tests that fail either at compile-time or at runtime.
[ $runtime != none ]
+Language/Expressions/Bitwise_Expressions/syntax_t01/02: Skip
Language/Expressions/Numbers/static_type_of_double_t01: RuntimeError # Uppercase constants removed
Language/Expressions/Object_Identity/constant_objects_t01: RuntimeError # Uppercase constants removed
Language/Expressions/Object_Identity/double_t02: RuntimeError # Uppercase constants removed
diff --git a/tests/corelib/corelib.status b/tests/corelib/corelib.status
index f41c498..a701a20 100644
--- a/tests/corelib/corelib.status
+++ b/tests/corelib/corelib.status
@@ -2,6 +2,7 @@
# 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.
+core_runtime_types_test: RuntimeError # Does not expect bool to have operators.
maps_test: Skip # Maps class no longer exists
[ $compiler == dart2analyzer ]
diff --git a/tests/corelib_2/bool_operator_test.dart b/tests/corelib_2/bool_operator_test.dart
new file mode 100644
index 0000000..3d5e3de
--- /dev/null
+++ b/tests/corelib_2/bool_operator_test.dart
@@ -0,0 +1,46 @@
+// Copyright (c) 2017, 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.
+
+import "package:expect/expect.dart";
+
+main() {
+ void test(bool b1, bool b2) {
+ var and1 = b1 && b2;
+ var and2 = b1 & b2;
+ var and3 = b1 ? b2 ? true : false : false;
+ var or1 = b1 || b2;
+ var or2 = b1 | b2;
+ var or3 = b1 ? true : b2 ? true : false;
+ var xor1 = b1 != b2;
+ var xor2 = b1 ^ b2;
+ var xor3 = b1 ? b2 ? false : true : b2 ? true : false;
+ var nb1 = !b1;
+ var nb2 = !b2;
+ Expect.equals(and3, and1);
+ Expect.equals(and3, and2);
+ Expect.equals(or3, or1);
+ Expect.equals(or3, or2);
+ Expect.equals(xor3, xor1);
+ Expect.equals(xor3, xor2);
+ Expect.notEquals(nb1, b1);
+ Expect.notEquals(nb2, b2);
+ }
+
+ test(true, false);
+ test(true, true);
+ test(false, true);
+ test(false, false);
+
+ Expect.isTrue(true || (throw "unreachable"));
+ Expect.throws(() => false || (throw "unreachable"));
+
+ Expect.isFalse(false && (throw "unreachable"));
+ Expect.throws(() => true && (throw "unreachable"));
+
+ Expect.throws(() => true | (throw "unreachable"));
+ Expect.throws(() => false | (throw "unreachable"));
+
+ Expect.throws(() => true & (throw "unreachable"));
+ Expect.throws(() => false & (throw "unreachable"));
+}
diff --git a/tests/corelib_2/core_runtime_types_test.dart b/tests/corelib_2/core_runtime_types_test.dart
index 69a741e..7176feb 100644
--- a/tests/corelib_2/core_runtime_types_test.dart
+++ b/tests/corelib_2/core_runtime_types_test.dart
@@ -46,13 +46,16 @@
assertListEquals(a, b);
}
- static assertTypeError(void f()) {
- Expect.throws(
+ static assertTypeError(void f(), [String message]) {
+ Expect.throws<Error>(
f,
(exception) =>
(exception is TypeError) ||
+ (exception is CastError) ||
+ (exception is AssertionError) ||
(exception is NoSuchMethodError) ||
- (exception is ArgumentError));
+ (exception is ArgumentError),
+ message);
}
static testBooleanOperators() {
@@ -94,46 +97,49 @@
for (var i = 0; i < objs.length; i++) {
for (var j = i + 1; j < objs.length; j++) {
testBinaryOperatorErrors(objs[i], objs[j]);
- // Allow "String * int".
- if (j > 2) testBinaryOperatorErrors(objs[j], objs[i]);
+ testBinaryOperatorErrors(objs[j], objs[i]);
}
- if (objs[i] != 1) {
- testUnaryOperatorErrors(objs[i]);
- }
+ testUnaryOperatorErrors(objs[i]);
}
}
static testBinaryOperatorErrors(x, y) {
assertTypeError(() {
- x - y;
- });
+ x + y;
+ }, "$x+$y");
assertTypeError(() {
- x * y;
- });
+ x - y;
+ }, "$x-$y");
+ // String.* is the only non-same-type binary operator we have.
+ if (x is! String && y is! int) {
+ assertTypeError(() {
+ x * y;
+ }, "$x*$y");
+ }
assertTypeError(() {
x / y;
- });
+ }, "$x/$y");
assertTypeError(() {
x | y;
- });
+ }, "$x|$y");
assertTypeError(() {
x ^ y;
- });
+ }, "$x^$y");
assertTypeError(() {
x & y;
- });
+ }, "$x&$y");
assertTypeError(() {
x << y;
- });
+ }, "$x<<$y");
assertTypeError(() {
x >> y;
- });
+ }, "$x>>$y");
assertTypeError(() {
x ~/ y;
- });
+ }, "$x~/$y");
assertTypeError(() {
x % y;
- });
+ }, "$x%$y");
testComparisonOperatorErrors(x, y);
}
@@ -143,27 +149,34 @@
assertEquals(x != y, true);
assertTypeError(() {
x < y;
- });
+ }, "$x<$y");
assertTypeError(() {
x <= y;
- });
+ }, "$x<=$y");
assertTypeError(() {
x > y;
- });
+ }, "$x>$y");
assertTypeError(() {
x >= y;
- });
+ }, "$x>=$y");
}
static testUnaryOperatorErrors(x) {
- // TODO(jimhug): Add guard for 'is num' when 'is' is working
- assertTypeError(() {
- ~x;
- });
- assertTypeError(() {
- -x;
- });
- // TODO(jimhug): Add check for !x as an error when x is not a bool
+ if (x is! int) {
+ assertTypeError(() {
+ ~x;
+ }, "~$x");
+ }
+ if (x is! num) {
+ assertTypeError(() {
+ -x;
+ }, "-$x");
+ }
+ if (x is! bool) {
+ assertTypeError(() {
+ !x;
+ }, "!$x");
+ }
}
static testRationalMethods() {
diff --git a/tests/corelib_2/corelib_2.status b/tests/corelib_2/corelib_2.status
index 9c87583..8467f2f 100644
--- a/tests/corelib_2/corelib_2.status
+++ b/tests/corelib_2/corelib_2.status
@@ -20,6 +20,7 @@
bigint_test: CompileTimeError, OK # Error if web int literal cannot be represented exactly, see http://dartbug.com/33351
bit_twiddling_test/int64: CompileTimeError, OK # Error if web int literal cannot be represented exactly, see http://dartbug.com/33351
compare_to2_test: CompileTimeError, OK # Error if web int literal cannot be represented exactly, see http://dartbug.com/33351
+core_runtime_types_test: RuntimeError # Issue 34147
date_time11_test: RuntimeError, Pass # Fails when US is on winter time, issue 31285.
date_time_test: CompileTimeError, OK # Error if web int literal cannot be represented exactly, see http://dartbug.com/33351
double_ceil_test/int64: CompileTimeError, OK # Error if web int literal cannot be represented exactly, see http://dartbug.com/33351
@@ -288,6 +289,9 @@
symbol_test/none: RuntimeError # Issues 11669 and 31936 - throwing const constructors.
unicode_test: RuntimeError # Issue 18061: German double S.
+[ $compiler != fasta && !$strong ]
+core_runtime_types_test: SkipByDesign
+
[ $compiler == none && $runtime == vm ]
from_environment_const_type_undefined_test/09: MissingCompileTimeError
from_environment_const_type_undefined_test/11: MissingCompileTimeError