diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b5f18b1d58..d79cfa2b082 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 4a9290a1d22..de5ca6eb259 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 @@ class JSBool extends Interceptor implements bool { @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 423242f1ea9..d20db53663a 100644 --- a/pkg/expect/lib/expect.dart +++ b/pkg/expect/lib/expect.dart @@ -543,7 +543,13 @@ class Expect { // 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 @@ class Expect { static void type(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(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 6eba5be305d..96ef8c84c9b 100644 --- a/pkg/front_end/messages.status +++ b/pkg/front_end/messages.status @@ -330,9 +330,6 @@ ThisAsIdentifier/example: Fail 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 93b5751dc6c..a9fcb75e8e8 100644 --- a/sdk/lib/_internal/js_runtime/lib/interceptors.dart +++ b/sdk/lib/_internal/js_runtime/lib/interceptors.dart @@ -16,6 +16,7 @@ import 'dart:_js_helper' JSSyntaxRegExp, Primitives, argumentErrorValue, + checkBool, checkInt, checkNull, checkNum, @@ -364,6 +365,12 @@ class JSBool extends Interceptor implements bool { // 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 2aeca44d916..980b1c5feb7 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 @@ class bool { * 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 @@ class bool { 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 b3d404f96cc..455498abd38 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 @@ LibTest/math/Rectangle/operator_equality_A04_t01: CompileTimeError # Uppercase c # 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 f41c498346c..a701a20435a 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 00000000000..3d5e3de39bd --- /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 69a741e5ac0..7176febd47c 100644 --- a/tests/corelib_2/core_runtime_types_test.dart +++ b/tests/corelib_2/core_runtime_types_test.dart @@ -46,13 +46,16 @@ class CoreRuntimeTypesTest { assertListEquals(a, b); } - static assertTypeError(void f()) { - Expect.throws( + static assertTypeError(void f(), [String message]) { + Expect.throws( 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 @@ class CoreRuntimeTypesTest { 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]); - } - if (objs[i] != 1) { - testUnaryOperatorErrors(objs[i]); + testBinaryOperatorErrors(objs[j], 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 @@ class CoreRuntimeTypesTest { 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 9c8758358f4..8467f2f8c81 100644 --- a/tests/corelib_2/corelib_2.status +++ b/tests/corelib_2/corelib_2.status @@ -20,6 +20,7 @@ bigint_from_test: RuntimeError # Issue 32589 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_reserved_word_test/12: RuntimeError # Issues 11669 and 31936 - throwing c 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