diff --git a/tests/language/regress/regress52243_test.dart b/tests/language/regress/regress52243_test.dart new file mode 100644 index 00000000000..8a2ba869559 --- /dev/null +++ b/tests/language/regress/regress52243_test.dart @@ -0,0 +1,219 @@ +// Copyright (c) 2023, 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"; + +/// Regression test for the issue discovered by +/// https://github.com/dart-lang/sdk/issues/52243. +/// +/// In DDC the types [num], [int], [double], [String], and [bool] used in bounds +/// caused an "optimization" in type tests that was incorrect when passing a +/// subtype as the type argument. + +bool isWithNumBound(x) => x is T; +T asWithNumBound(x) => x as T; +bool isWithNullableNumBound(x) => x is T; +T asWithNullableNumBound(x) => x as T; +bool isWithIntBound(x) => x is T; +T asWithIntBound(x) => x as T; +bool isWithNullableIntBound(x) => x is T; +T asWithNullableIntBound(x) => x as T; +bool isWithDoubleBound(x) => x is T; +T asWithDoubleBound(x) => x as T; +bool isWithNullableDoubleBound(x) => x is T; +T asWithNullableDoubleBound(x) => x as T; +bool isWithStringBound(x) => x is T; +T asWithStringBound(x) => x as T; +bool isWithNullableStringBound(x) => x is T; +T asWithNullableStringBound(x) => x as T; +bool isWithBoolBound(x) => x is T; +T asWithBoolBound(x) => x as T; +bool isWithNullableBoolBound(x) => x is T; +T asWithNullableBoolBound(x) => x as T; + +expectTypeErrorWhenSoundOrValue(T Function(T) computation, T value) { + hasSoundNullSafety + ? Expect.throws(() => computation(value)) + : Expect.equals(value, computation(value)); +} + +main() { + // NOTE: JavaScript platforms can't determine that 1 is not a double so any + // test combination that would exercise that pattern are excluded here. + Expect.isTrue(isWithNumBound(1)); + Expect.isTrue(isWithNumBound(1)); + Expect.isFalse(isWithNumBound(1)); + Expect.isTrue(isWithNumBound(1.2)); + Expect.isFalse(isWithNumBound(1.2)); + Expect.isTrue(isWithNumBound(1.2)); + Expect.isFalse(isWithNumBound(1.2)); + + Expect.equals(1, asWithNumBound(1)); + Expect.equals(1, asWithNumBound(1)); + Expect.throws(() => asWithNumBound(1)); + Expect.equals(1.2, asWithNumBound(1.2)); + Expect.throws(() => asWithNumBound(1.2)); + Expect.equals(1.2, asWithNumBound(1.2)); + Expect.throws(() => asWithNumBound(1.2)); + + Expect.isTrue(isWithNullableNumBound(1)); + Expect.isTrue(isWithNullableNumBound(1)); + Expect.isFalse(isWithNullableNumBound(1)); + Expect.isTrue(isWithNullableNumBound(1)); + Expect.isTrue(isWithNullableNumBound(1)); + Expect.isFalse(isWithNullableNumBound(1)); + Expect.isTrue(isWithNullableNumBound(1.2)); + Expect.isFalse(isWithNullableNumBound(1.2)); + Expect.isTrue(isWithNullableNumBound(1.2)); + Expect.isFalse(isWithNullableNumBound(1.2)); + Expect.isTrue(isWithNullableNumBound(1.2)); + Expect.isFalse(isWithNullableNumBound(1.2)); + Expect.isTrue(isWithNullableNumBound(1.2)); + Expect.isFalse(isWithNullableNumBound(1.2)); + Expect.isFalse(isWithNullableNumBound(null)); + Expect.isFalse(isWithNullableNumBound(null)); + Expect.isFalse(isWithNullableNumBound(null)); + Expect.isFalse(isWithNullableNumBound(null)); + Expect.isTrue(isWithNullableNumBound(null)); + Expect.isTrue(isWithNullableNumBound(null)); + Expect.isTrue(isWithNullableNumBound(null)); + Expect.isTrue(isWithNullableNumBound(null)); + + Expect.equals(1, asWithNullableNumBound(1)); + Expect.equals(1, asWithNullableNumBound(1)); + Expect.throws(() => asWithNullableNumBound(1)); + Expect.equals(1, asWithNullableNumBound(1)); + Expect.equals(1, asWithNullableNumBound(1)); + Expect.throws(() => asWithNullableNumBound(1)); + Expect.equals(1.2, asWithNullableNumBound(1.2)); + Expect.throws(() => asWithNullableNumBound(1.2)); + Expect.equals(1.2, asWithNullableNumBound(1.2)); + Expect.throws(() => asWithNullableNumBound(1.2)); + Expect.equals(1.2, asWithNullableNumBound(1.2)); + Expect.throws(() => asWithNullableNumBound(1.2)); + Expect.equals(1.2, asWithNullableNumBound(1.2)); + Expect.throws(() => asWithNullableNumBound(1.2)); + expectTypeErrorWhenSoundOrValue(asWithNullableNumBound, null); + expectTypeErrorWhenSoundOrValue(asWithNullableNumBound, null); + expectTypeErrorWhenSoundOrValue(asWithNullableNumBound, null); + expectTypeErrorWhenSoundOrValue(asWithNullableNumBound, null); + Expect.equals(null, asWithNullableNumBound(null)); + Expect.equals(null, asWithNullableNumBound(null)); + Expect.equals(null, asWithNullableNumBound(null)); + Expect.equals(null, asWithNullableNumBound(null)); + + Expect.isTrue(isWithIntBound(1)); + Expect.isFalse(isWithIntBound(1)); + Expect.isFalse(isWithIntBound(1.2)); + Expect.isFalse(isWithIntBound(1.2)); + + Expect.equals(1, asWithIntBound(1)); + Expect.throws(() => asWithIntBound(1)); + Expect.throws(() => asWithIntBound(1.2)); + Expect.throws(() => asWithIntBound(1.2)); + + Expect.isTrue(isWithNullableIntBound(1)); + Expect.isFalse(isWithNullableIntBound(1)); + Expect.isTrue(isWithNullableIntBound(1)); + Expect.isFalse(isWithNullableIntBound(1)); + Expect.isFalse(isWithNullableIntBound(1.2)); + Expect.isFalse(isWithNullableIntBound(1.2)); + Expect.isFalse(isWithNullableIntBound(1.2)); + Expect.isFalse(isWithNullableIntBound(1.2)); + Expect.isFalse(isWithNullableIntBound(null)); + Expect.isFalse(isWithNullableIntBound(null)); + Expect.isTrue(isWithNullableIntBound(null)); + Expect.isTrue(isWithNullableIntBound(null)); + + Expect.equals(1, asWithNullableIntBound(1)); + Expect.throws(() => asWithNullableIntBound(1)); + Expect.equals(1, asWithNullableIntBound(1)); + Expect.throws(() => asWithNullableIntBound(1)); + Expect.throws(() => asWithNullableIntBound(1.2)); + Expect.throws(() => asWithNullableIntBound(1.2)); + Expect.throws(() => asWithNullableIntBound(1.2)); + Expect.throws(() => asWithNullableIntBound(1.2)); + expectTypeErrorWhenSoundOrValue(asWithNullableIntBound, null); + expectTypeErrorWhenSoundOrValue(asWithNullableIntBound, null); + Expect.equals(null, asWithNullableIntBound(null)); + Expect.equals(null, asWithNullableIntBound(null)); + + Expect.isFalse(isWithDoubleBound(1)); + Expect.isTrue(isWithDoubleBound(1.2)); + Expect.isFalse(isWithDoubleBound(1.2)); + + Expect.throws(() => asWithDoubleBound(1)); + Expect.equals(1.2, asWithDoubleBound(1.2)); + Expect.throws(() => asWithDoubleBound(1.2)); + + Expect.isFalse(isWithNullableDoubleBound(1)); + Expect.isFalse(isWithNullableDoubleBound(1)); + Expect.isTrue(isWithNullableDoubleBound(1.2)); + Expect.isFalse(isWithNullableDoubleBound(1.2)); + Expect.isTrue(isWithNullableDoubleBound(1.2)); + Expect.isFalse(isWithNullableDoubleBound(1.2)); + Expect.isFalse(isWithNullableDoubleBound(null)); + Expect.isFalse(isWithNullableDoubleBound(null)); + Expect.isTrue(isWithNullableDoubleBound(null)); + Expect.isTrue(isWithNullableDoubleBound(null)); + + Expect.throws(() => asWithNullableDoubleBound(1)); + Expect.throws(() => asWithNullableDoubleBound(1)); + Expect.equals(1.2, asWithNullableDoubleBound(1.2)); + Expect.throws(() => asWithNullableDoubleBound(1.2)); + Expect.equals(1.2, asWithNullableDoubleBound(1.2)); + Expect.throws(() => asWithNullableDoubleBound(1.2)); + expectTypeErrorWhenSoundOrValue(asWithNullableDoubleBound, null); + expectTypeErrorWhenSoundOrValue(asWithNullableDoubleBound, null); + Expect.equals(null, asWithNullableDoubleBound(null)); + Expect.equals(null, asWithNullableDoubleBound(null)); + + Expect.isTrue(isWithStringBound('hello')); + Expect.isFalse(isWithStringBound('hello')); + + Expect.equals('hello', asWithStringBound('hello')); + Expect.throws(() => asWithStringBound('hello')); + + Expect.isTrue(isWithNullableStringBound('hello')); + Expect.isFalse(isWithNullableStringBound('hello')); + Expect.isTrue(isWithNullableStringBound('hello')); + Expect.isFalse(isWithNullableStringBound('hello')); + Expect.isFalse(isWithNullableStringBound(null)); + Expect.isFalse(isWithNullableStringBound(null)); + Expect.isTrue(isWithNullableStringBound(null)); + Expect.isTrue(isWithNullableStringBound(null)); + + Expect.equals('hello', asWithNullableStringBound('hello')); + Expect.throws(() => asWithNullableStringBound('hello')); + Expect.equals('hello', asWithNullableStringBound('hello')); + Expect.throws(() => asWithNullableStringBound('hello')); + expectTypeErrorWhenSoundOrValue(asWithNullableStringBound, null); + expectTypeErrorWhenSoundOrValue(asWithNullableStringBound, null); + Expect.equals(null, asWithNullableStringBound(null)); + Expect.equals(null, asWithNullableStringBound(null)); + + Expect.isTrue(isWithBoolBound(true)); + Expect.isFalse(isWithBoolBound(true)); + + Expect.equals(true, asWithBoolBound(true)); + Expect.throws(() => asWithBoolBound(true)); + + Expect.isTrue(isWithNullableBoolBound(true)); + Expect.isFalse(isWithNullableBoolBound(true)); + Expect.isTrue(isWithNullableBoolBound(true)); + Expect.isFalse(isWithNullableBoolBound(true)); + Expect.isFalse(isWithNullableBoolBound(null)); + Expect.isFalse(isWithNullableBoolBound(null)); + Expect.isTrue(isWithNullableBoolBound(null)); + Expect.isTrue(isWithNullableBoolBound(null)); + + Expect.equals(true, asWithNullableBoolBound(true)); + Expect.throws(() => asWithNullableBoolBound(true)); + Expect.equals(true, asWithNullableBoolBound(true)); + Expect.throws(() => asWithNullableBoolBound(true)); + expectTypeErrorWhenSoundOrValue(asWithNullableBoolBound, null); + expectTypeErrorWhenSoundOrValue(asWithNullableBoolBound, null); + Expect.equals(null, asWithNullableBoolBound(null)); + Expect.equals(null, asWithNullableBoolBound(null)); +} diff --git a/tests/language_2/regress/regress52243_test.dart b/tests/language_2/regress/regress52243_test.dart new file mode 100644 index 00000000000..c9b721b5f5f --- /dev/null +++ b/tests/language_2/regress/regress52243_test.dart @@ -0,0 +1,105 @@ +// Copyright (c) 2023, 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. + +// @dart = 2.9 + +import "package:expect/expect.dart"; + +/// Regression test for the issue discovered by +/// https://github.com/dart-lang/sdk/issues/52243. +/// +/// In DDC the types [num], [int], [double], [String], and [bool] used in bounds +/// caused an "optimization" in type tests that was incorrect when passing a +/// subtype as the type argument. + +bool isWithNumBound(x) => x is T; +T asWithNumBound(x) => x as T; +bool isWithIntBound(x) => x is T; +T asWithIntBound(x) => x as T; +bool isWithDoubleBound(x) => x is T; +T asWithDoubleBound(x) => x as T; +bool isWithStringBound(x) => x is T; +T asWithStringBound(x) => x as T; +bool isWithBoolBound(x) => x is T; +T asWithBoolBound(x) => x as T; + +main() { + Expect.isTrue(isWithNumBound(1)); + Expect.isTrue(isWithNumBound(1)); + // NOTE: Web backends can't determine that 1 is not a double so that test is + // excluded here. + Expect.isFalse(isWithNumBound(1)); + Expect.isTrue(isWithNumBound(1.2)); + Expect.isFalse(isWithNumBound(1.2)); + Expect.isTrue(isWithNumBound(1.2)); + Expect.isFalse(isWithNumBound(1.2)); + Expect.isFalse(isWithNumBound(null)); + Expect.isFalse(isWithNumBound(null)); + Expect.isFalse(isWithNumBound(null)); + Expect.isTrue(isWithNumBound(null)); + + Expect.equals(1, asWithNumBound(1)); + Expect.equals(1, asWithNumBound(1)); + // NOTE: Web backends can't determine that 1 is not a double so that test is + // excluded here. + Expect.throws(() => asWithNumBound(1)); + Expect.equals(1.2, asWithNumBound(1.2)); + Expect.throws(() => asWithNumBound(1.2)); + Expect.equals(1.2, asWithNumBound(1.2)); + Expect.throws(() => asWithNumBound(1.2)); + Expect.equals(null, asWithNumBound(null)); + Expect.equals(null, asWithNumBound(null)); + Expect.equals(null, asWithNumBound(null)); + Expect.equals(null, asWithNumBound(null)); + + Expect.isTrue(isWithIntBound(1)); + Expect.isFalse(isWithIntBound(1)); + Expect.isFalse(isWithIntBound(1.2)); + Expect.isFalse(isWithIntBound(1.2)); + Expect.isFalse(isWithIntBound(null)); + Expect.isTrue(isWithIntBound(null)); + + Expect.equals(1, asWithIntBound(1)); + Expect.throws(() => asWithIntBound(1)); + Expect.throws(() => asWithIntBound(1.2)); + Expect.throws(() => asWithIntBound(1.2)); + Expect.equals(null, asWithIntBound(null)); + Expect.equals(null, asWithIntBound(null)); + + // NOTE: Web backends can't determine that 1 is not a double so that test is + // excluded here. + Expect.isFalse(isWithDoubleBound(1)); + Expect.isTrue(isWithDoubleBound(1.2)); + Expect.isFalse(isWithDoubleBound(1.2)); + Expect.isFalse(isWithDoubleBound(null)); + Expect.isTrue(isWithDoubleBound(null)); + + // NOTE: Web backends can't determine that 1 is not a double so that test is + // excluded here. + Expect.throws(() => asWithDoubleBound(1)); + Expect.equals(1.2, asWithDoubleBound(1.2)); + Expect.throws(() => asWithDoubleBound(1.2)); + Expect.equals(null, asWithDoubleBound(null)); + Expect.equals(null, asWithDoubleBound(null)); + + Expect.isTrue(isWithStringBound('hello')); + Expect.isFalse(isWithStringBound('hello')); + Expect.isFalse(isWithStringBound(null)); + Expect.isTrue(isWithStringBound(null)); + + Expect.equals('hello', asWithStringBound('hello')); + Expect.throws(() => asWithStringBound('hello')); + Expect.equals(null, asWithStringBound(null)); + Expect.equals(null, asWithStringBound(null)); + + Expect.isTrue(isWithBoolBound(true)); + Expect.isFalse(isWithBoolBound(true)); + Expect.isFalse(isWithBoolBound(null)); + Expect.isTrue(isWithBoolBound(null)); + + Expect.equals(true, asWithBoolBound(true)); + Expect.throws(() => asWithBoolBound(true)); + Expect.equals(null, asWithBoolBound(null)); + Expect.equals(null, asWithBoolBound(null)); +}