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