From b91b1f687c1d58d36069e0a4d2cb930271fb7e9b Mon Sep 17 00:00:00 2001 From: Louis Dionne Date: Thu, 25 Jan 2024 10:34:32 -0500 Subject: [PATCH 1/5] [libc++] Avoid -Wzero-as-null-pointer-constant in operator<=> Issue #43670 describes a situation where the following comparison will issue a warning when -Wzero-as-null-pointer-constant is enabled: #include auto b = (1 <=> 2) < 0; This code uses operator<(strong_ordering, Unspecified), which is specified by the Standard to only work with a literal 0. In the library, this is achieved by constructing Unspecified from a pointer, which works but has the downside of triggering the warning. This patch uses an alternative implementation where we require that the operator is used exactly with an int of value 0 (known at compile-time), however that value can technically be an expression like `1 - 1`, which makes us a bit less strict than what's specified in the Standard. Fixes #43670 --- libcxx/include/__compare/ordering.h | 20 +++--- .../reject-other-than-literal-zero.verify.cpp | 70 +++++++++++++++++++ .../cmp.categories.pre/zero_type.verify.cpp | 57 --------------- 3 files changed, 82 insertions(+), 65 deletions(-) create mode 100644 libcxx/test/std/language.support/cmp/cmp.categories.pre/reject-other-than-literal-zero.verify.cpp delete mode 100644 libcxx/test/std/language.support/cmp/cmp.categories.pre/zero_type.verify.cpp diff --git a/libcxx/include/__compare/ordering.h b/libcxx/include/__compare/ordering.h index 2995d381304f0..778cf5dab4ce5 100644 --- a/libcxx/include/__compare/ordering.h +++ b/libcxx/include/__compare/ordering.h @@ -30,14 +30,17 @@ class partial_ordering; class weak_ordering; class strong_ordering; -template -inline constexpr bool __one_of_v = (is_same_v<_Tp, _Args> || ...); - struct _CmpUnspecifiedParam { - _LIBCPP_HIDE_FROM_ABI constexpr _CmpUnspecifiedParam(int _CmpUnspecifiedParam::*) noexcept {} - - template >> - _CmpUnspecifiedParam(_Tp) = delete; + template >> + _LIBCPP_HIDE_FROM_ABI consteval _CmpUnspecifiedParam(_Tp __zero) noexcept { + // If anything other than 0 is provided, the behavior is undefined. + // We catch this case by making this function consteval and making the program ill-formed if + // a value other than 0 is provided. This technically also accepts things that are not + // literal 0s like `1 - 1`. The alternative is to use the fact that a pointer can be + // constructed from literal 0, but this conflicts with `-Wzero-as-null-pointer-constant`. + if (__zero != 0) + __builtin_trap(); + } }; class partial_ordering { @@ -269,7 +272,8 @@ inline constexpr strong_ordering strong_ordering::greater(_OrdResult::__greater) /// The types partial_ordering, weak_ordering, and strong_ordering are /// collectively termed the comparison category types. template -concept __comparison_category = __one_of_v<_Tp, partial_ordering, weak_ordering, strong_ordering>; +concept __comparison_category = + is_same_v<_Tp, partial_ordering> || is_same_v<_Tp, weak_ordering> || is_same_v<_Tp, strong_ordering>; #endif // _LIBCPP_STD_VER >= 20 diff --git a/libcxx/test/std/language.support/cmp/cmp.categories.pre/reject-other-than-literal-zero.verify.cpp b/libcxx/test/std/language.support/cmp/cmp.categories.pre/reject-other-than-literal-zero.verify.cpp new file mode 100644 index 0000000000000..f2b30284d42a6 --- /dev/null +++ b/libcxx/test/std/language.support/cmp/cmp.categories.pre/reject-other-than-literal-zero.verify.cpp @@ -0,0 +1,70 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 + +// In MSVC mode, there's a slightly different number of errors printed for +// each of these, so it doesn't add up to the exact expected count of 18. +// XFAIL: msvc + +// + +// Ensure we reject all cases where an argument other than a literal 0 is used +// for a comparison against a comparison category type. + +// Also ensure that we don't warn about providing a null pointer constant when +// comparing an ordering type against literal 0, since one of the common +// implementation strategies is to use a pointer as the "unspecified type". +// ADDITIONAL_COMPILE_FLAGS: -Wzero-as-null-pointer-constant + +#include + +#include "test_macros.h" + +#define TEST_FAIL(v, op) \ + do { \ + void(v op 0L); \ + void(0L op v); \ + void(v op 1); \ + void(1 op v); \ + void(v op nullptr); \ + void(nullptr op v); \ + } while (false) + +#define TEST_PASS(v, op) \ + do { \ + void(v op 0); \ + void(0 op v); \ + LIBCPP_ONLY(void(v op(1 - 1))); \ + LIBCPP_ONLY(void((1 - 1) op v)); \ + } while (false) + +template +void test_category(T v) { + TEST_FAIL(v, ==); // expected-error 18 {{}} + TEST_FAIL(v, !=); // expected-error 18 {{}} + TEST_FAIL(v, <); // expected-error 18 {{}} + TEST_FAIL(v, <=); // expected-error 18 {{}} + TEST_FAIL(v, >); // expected-error 18 {{}} + TEST_FAIL(v, >=); // expected-error 18 {{}} + TEST_FAIL(v, <=>); // expected-error 18 {{}} + + TEST_PASS(v, ==); + TEST_PASS(v, !=); + TEST_PASS(v, <); + TEST_PASS(v, >); + TEST_PASS(v, <=); + TEST_PASS(v, >=); + TEST_PASS(v, <=>); +} + +void f() { + test_category(std::strong_ordering::equivalent); + test_category(std::weak_ordering::equivalent); + test_category(std::partial_ordering::equivalent); +} diff --git a/libcxx/test/std/language.support/cmp/cmp.categories.pre/zero_type.verify.cpp b/libcxx/test/std/language.support/cmp/cmp.categories.pre/zero_type.verify.cpp deleted file mode 100644 index 8e3c793de4b92..0000000000000 --- a/libcxx/test/std/language.support/cmp/cmp.categories.pre/zero_type.verify.cpp +++ /dev/null @@ -1,57 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -// UNSUPPORTED: c++03, c++11, c++14, c++17 - -// In MSVC mode, there's a slightly different number of errors printed for -// each of these, so it doesn't add up to the exact expected count of 18. -// XFAIL: msvc - -// - -// Ensure we reject all cases where an argument other than a literal 0 is used -// for a comparison against a comparison category type. - -#include - -#define TEST_FAIL(v, op) \ - void(v op 0L); \ - void(0L op v); \ - void(v op nullptr); \ - void(nullptr op v); \ - void(v op(1 - 1)); \ - void((1 - 1) op v) - -#define TEST_PASS(v, op) \ - void(v op 0); \ - void(0 op v) - -template -void test_category(T v) { - TEST_FAIL(v, ==); // expected-error 18 {{}} - TEST_FAIL(v, !=); // expected-error 18 {{}} - TEST_FAIL(v, <); // expected-error 18 {{}} - TEST_FAIL(v, <=); // expected-error 18 {{}} - TEST_FAIL(v, >); // expected-error 18 {{}} - TEST_FAIL(v, >=); // expected-error 18 {{}} - TEST_FAIL(v, <=>); // expected-error 18 {{}} - - TEST_PASS(v, ==); - TEST_PASS(v, !=); - TEST_PASS(v, <); - TEST_PASS(v, >); - TEST_PASS(v, <=); - TEST_PASS(v, >=); - TEST_PASS(v, <=>); -} - -void f() { - test_category(std::strong_ordering::equivalent); - test_category(std::weak_ordering::equivalent); - test_category(std::partial_ordering::equivalent); -} From 3da7bf5980c2c4db8c6bd13012ea6d0fa4614399 Mon Sep 17 00:00:00 2001 From: Louis Dionne Date: Thu, 25 Jan 2024 15:28:09 -0500 Subject: [PATCH 2/5] Use diagnose_if and improve tests --- libcxx/include/__compare/ordering.h | 19 ++++--- libcxx/include/__config | 6 +++ .../reject-other-than-literal-zero.verify.cpp | 49 +++++++++++++++---- 3 files changed, 54 insertions(+), 20 deletions(-) diff --git a/libcxx/include/__compare/ordering.h b/libcxx/include/__compare/ordering.h index 778cf5dab4ce5..274cff61d2b28 100644 --- a/libcxx/include/__compare/ordering.h +++ b/libcxx/include/__compare/ordering.h @@ -31,16 +31,15 @@ class weak_ordering; class strong_ordering; struct _CmpUnspecifiedParam { - template >> - _LIBCPP_HIDE_FROM_ABI consteval _CmpUnspecifiedParam(_Tp __zero) noexcept { - // If anything other than 0 is provided, the behavior is undefined. - // We catch this case by making this function consteval and making the program ill-formed if - // a value other than 0 is provided. This technically also accepts things that are not - // literal 0s like `1 - 1`. The alternative is to use the fact that a pointer can be - // constructed from literal 0, but this conflicts with `-Wzero-as-null-pointer-constant`. - if (__zero != 0) - __builtin_trap(); - } + // If anything other than a literal 0 is provided, the behavior is undefined by the Standard. + // + // We handle this by making this function consteval and making the program ill-formed if + // a value other than 0 is provided. This technically also accepts things that are not + // literal 0s like `1 - 1`. The alternative is to use the fact that a pointer can be + // constructed from literal 0, but this conflicts with `-Wzero-as-null-pointer-constant`. + template > > + _LIBCPP_HIDE_FROM_ABI consteval _CmpUnspecifiedParam(_Tp __zero) noexcept _LIBCPP_DIAGNOSE_ERROR( + __zero != 0, "Only literal 0 is allowed as the operand of a comparison with one of the ordering types") {} }; class partial_ordering { diff --git a/libcxx/include/__config b/libcxx/include/__config index 392053a64a8dc..9fb13450f33b5 100644 --- a/libcxx/include/__config +++ b/libcxx/include/__config @@ -1137,6 +1137,12 @@ typedef __char32_t char32_t; # define _LIBCPP_DIAGNOSE_WARNING(...) # endif +# if __has_attribute(__diagnose_if__) +# define _LIBCPP_DIAGNOSE_ERROR(...) __attribute__((__diagnose_if__(__VA_ARGS__, "error"))) +# else +# define _LIBCPP_DIAGNOSE_ERROR(...) +# endif + // Use a function like macro to imply that it must be followed by a semicolon # if __has_cpp_attribute(fallthrough) # define _LIBCPP_FALLTHROUGH() [[fallthrough]] diff --git a/libcxx/test/std/language.support/cmp/cmp.categories.pre/reject-other-than-literal-zero.verify.cpp b/libcxx/test/std/language.support/cmp/cmp.categories.pre/reject-other-than-literal-zero.verify.cpp index f2b30284d42a6..e001066590938 100644 --- a/libcxx/test/std/language.support/cmp/cmp.categories.pre/reject-other-than-literal-zero.verify.cpp +++ b/libcxx/test/std/language.support/cmp/cmp.categories.pre/reject-other-than-literal-zero.verify.cpp @@ -26,16 +26,29 @@ #include "test_macros.h" -#define TEST_FAIL(v, op) \ +#define TEST_FAIL_INVALID_VALUE(v, op) \ do { \ - void(v op 0L); \ - void(0L op v); \ void(v op 1); \ void(1 op v); \ + } while (false) + +#define TEST_FAIL_INVALID_TYPE(v, op) \ + do { \ + void(v op 0L); \ + void(0L op v); \ + void(v op 0.0); \ + void(0.0 op v); \ void(v op nullptr); \ void(nullptr op v); \ } while (false) +#define TEST_FAIL_NOT_COMPILE_TIME(v, op) \ + do { \ + int i = 0; \ + void(v op i); \ + void(i op v); \ + } while (false) + #define TEST_PASS(v, op) \ do { \ void(v op 0); \ @@ -46,13 +59,29 @@ template void test_category(T v) { - TEST_FAIL(v, ==); // expected-error 18 {{}} - TEST_FAIL(v, !=); // expected-error 18 {{}} - TEST_FAIL(v, <); // expected-error 18 {{}} - TEST_FAIL(v, <=); // expected-error 18 {{}} - TEST_FAIL(v, >); // expected-error 18 {{}} - TEST_FAIL(v, >=); // expected-error 18 {{}} - TEST_FAIL(v, <=>); // expected-error 18 {{}} + TEST_FAIL_INVALID_TYPE(v, ==); // expected-error 18 {{invalid operands to binary expression}} + TEST_FAIL_INVALID_TYPE(v, !=); // expected-error 18 {{invalid operands to binary expression}} + TEST_FAIL_INVALID_TYPE(v, <); // expected-error 18 {{invalid operands to binary expression}} + TEST_FAIL_INVALID_TYPE(v, <=); // expected-error 18 {{invalid operands to binary expression}} + TEST_FAIL_INVALID_TYPE(v, >); // expected-error 18 {{invalid operands to binary expression}} + TEST_FAIL_INVALID_TYPE(v, >=); // expected-error 18 {{invalid operands to binary expression}} + TEST_FAIL_INVALID_TYPE(v, <=>); // expected-error 18 {{invalid operands to binary expression}} + + TEST_FAIL_INVALID_VALUE(v, ==); // expected-error 6 {{Only literal 0 is allowed as the operand}} + TEST_FAIL_INVALID_VALUE(v, !=); // expected-error 6 {{Only literal 0 is allowed as the operand}} + TEST_FAIL_INVALID_VALUE(v, <); // expected-error 6 {{Only literal 0 is allowed as the operand}} + TEST_FAIL_INVALID_VALUE(v, <=); // expected-error 6 {{Only literal 0 is allowed as the operand}} + TEST_FAIL_INVALID_VALUE(v, >); // expected-error 6 {{Only literal 0 is allowed as the operand}} + TEST_FAIL_INVALID_VALUE(v, >=); // expected-error 6 {{Only literal 0 is allowed as the operand}} + TEST_FAIL_INVALID_VALUE(v, <=>); // expected-error 6 {{Only literal 0 is allowed as the operand}} + + TEST_FAIL_NOT_COMPILE_TIME(v, ==); // expected-error 6 {{call to consteval function}} + TEST_FAIL_NOT_COMPILE_TIME(v, !=); // expected-error 6 {{call to consteval function}} + TEST_FAIL_NOT_COMPILE_TIME(v, <); // expected-error 6 {{call to consteval function}} + TEST_FAIL_NOT_COMPILE_TIME(v, <=); // expected-error 6 {{call to consteval function}} + TEST_FAIL_NOT_COMPILE_TIME(v, >); // expected-error 6 {{call to consteval function}} + TEST_FAIL_NOT_COMPILE_TIME(v, >=); // expected-error 6 {{call to consteval function}} + TEST_FAIL_NOT_COMPILE_TIME(v, <=>); // expected-error 6 {{call to consteval function}} TEST_PASS(v, ==); TEST_PASS(v, !=); From 96d64aeb0c13b266cbad7ca441aa011b7ec30bce Mon Sep 17 00:00:00 2001 From: Louis Dionne Date: Mon, 29 Jan 2024 10:56:41 -0500 Subject: [PATCH 3/5] Use __enable_if__ attribute --- libcxx/include/__compare/ordering.h | 15 +++--- libcxx/include/__config | 6 --- .../reject-other-than-literal-zero.verify.cpp | 47 +++++-------------- 3 files changed, 22 insertions(+), 46 deletions(-) diff --git a/libcxx/include/__compare/ordering.h b/libcxx/include/__compare/ordering.h index 274cff61d2b28..bea15456b89ba 100644 --- a/libcxx/include/__compare/ordering.h +++ b/libcxx/include/__compare/ordering.h @@ -33,13 +33,16 @@ class strong_ordering; struct _CmpUnspecifiedParam { // If anything other than a literal 0 is provided, the behavior is undefined by the Standard. // - // We handle this by making this function consteval and making the program ill-formed if - // a value other than 0 is provided. This technically also accepts things that are not - // literal 0s like `1 - 1`. The alternative is to use the fact that a pointer can be - // constructed from literal 0, but this conflicts with `-Wzero-as-null-pointer-constant`. + // The alternative to the `__enable_if__` attribute would be to use the fact that a pointer + // can be constructed from literal 0, but this conflicts with `-Wzero-as-null-pointer-constant`. template > > - _LIBCPP_HIDE_FROM_ABI consteval _CmpUnspecifiedParam(_Tp __zero) noexcept _LIBCPP_DIAGNOSE_ERROR( - __zero != 0, "Only literal 0 is allowed as the operand of a comparison with one of the ordering types") {} + _LIBCPP_HIDE_FROM_ABI consteval _CmpUnspecifiedParam(_Tp __zero) noexcept +# if __has_attribute(__enable_if__) + __attribute__((__enable_if__( + __zero == 0, "Only literal 0 is allowed as the operand of a comparison with one of the ordering types"))) +# endif + { + } }; class partial_ordering { diff --git a/libcxx/include/__config b/libcxx/include/__config index 9fb13450f33b5..392053a64a8dc 100644 --- a/libcxx/include/__config +++ b/libcxx/include/__config @@ -1137,12 +1137,6 @@ typedef __char32_t char32_t; # define _LIBCPP_DIAGNOSE_WARNING(...) # endif -# if __has_attribute(__diagnose_if__) -# define _LIBCPP_DIAGNOSE_ERROR(...) __attribute__((__diagnose_if__(__VA_ARGS__, "error"))) -# else -# define _LIBCPP_DIAGNOSE_ERROR(...) -# endif - // Use a function like macro to imply that it must be followed by a semicolon # if __has_cpp_attribute(fallthrough) # define _LIBCPP_FALLTHROUGH() [[fallthrough]] diff --git a/libcxx/test/std/language.support/cmp/cmp.categories.pre/reject-other-than-literal-zero.verify.cpp b/libcxx/test/std/language.support/cmp/cmp.categories.pre/reject-other-than-literal-zero.verify.cpp index e001066590938..f59f6804b54e9 100644 --- a/libcxx/test/std/language.support/cmp/cmp.categories.pre/reject-other-than-literal-zero.verify.cpp +++ b/libcxx/test/std/language.support/cmp/cmp.categories.pre/reject-other-than-literal-zero.verify.cpp @@ -26,24 +26,19 @@ #include "test_macros.h" -#define TEST_FAIL_INVALID_VALUE(v, op) \ - do { \ - void(v op 1); \ - void(1 op v); \ - } while (false) - -#define TEST_FAIL_INVALID_TYPE(v, op) \ +#define TEST_FAIL(v, op) \ do { \ + /* invalid types */ \ void(v op 0L); \ void(0L op v); \ void(v op 0.0); \ void(0.0 op v); \ void(v op nullptr); \ void(nullptr op v); \ - } while (false) - -#define TEST_FAIL_NOT_COMPILE_TIME(v, op) \ - do { \ + /* invalid value */ \ + void(v op 1); \ + void(1 op v); \ + /* value not known at compile-time */ \ int i = 0; \ void(v op i); \ void(i op v); \ @@ -59,29 +54,13 @@ template void test_category(T v) { - TEST_FAIL_INVALID_TYPE(v, ==); // expected-error 18 {{invalid operands to binary expression}} - TEST_FAIL_INVALID_TYPE(v, !=); // expected-error 18 {{invalid operands to binary expression}} - TEST_FAIL_INVALID_TYPE(v, <); // expected-error 18 {{invalid operands to binary expression}} - TEST_FAIL_INVALID_TYPE(v, <=); // expected-error 18 {{invalid operands to binary expression}} - TEST_FAIL_INVALID_TYPE(v, >); // expected-error 18 {{invalid operands to binary expression}} - TEST_FAIL_INVALID_TYPE(v, >=); // expected-error 18 {{invalid operands to binary expression}} - TEST_FAIL_INVALID_TYPE(v, <=>); // expected-error 18 {{invalid operands to binary expression}} - - TEST_FAIL_INVALID_VALUE(v, ==); // expected-error 6 {{Only literal 0 is allowed as the operand}} - TEST_FAIL_INVALID_VALUE(v, !=); // expected-error 6 {{Only literal 0 is allowed as the operand}} - TEST_FAIL_INVALID_VALUE(v, <); // expected-error 6 {{Only literal 0 is allowed as the operand}} - TEST_FAIL_INVALID_VALUE(v, <=); // expected-error 6 {{Only literal 0 is allowed as the operand}} - TEST_FAIL_INVALID_VALUE(v, >); // expected-error 6 {{Only literal 0 is allowed as the operand}} - TEST_FAIL_INVALID_VALUE(v, >=); // expected-error 6 {{Only literal 0 is allowed as the operand}} - TEST_FAIL_INVALID_VALUE(v, <=>); // expected-error 6 {{Only literal 0 is allowed as the operand}} - - TEST_FAIL_NOT_COMPILE_TIME(v, ==); // expected-error 6 {{call to consteval function}} - TEST_FAIL_NOT_COMPILE_TIME(v, !=); // expected-error 6 {{call to consteval function}} - TEST_FAIL_NOT_COMPILE_TIME(v, <); // expected-error 6 {{call to consteval function}} - TEST_FAIL_NOT_COMPILE_TIME(v, <=); // expected-error 6 {{call to consteval function}} - TEST_FAIL_NOT_COMPILE_TIME(v, >); // expected-error 6 {{call to consteval function}} - TEST_FAIL_NOT_COMPILE_TIME(v, >=); // expected-error 6 {{call to consteval function}} - TEST_FAIL_NOT_COMPILE_TIME(v, <=>); // expected-error 6 {{call to consteval function}} + TEST_FAIL(v, ==); // expected-error 30 {{invalid operands to binary expression}} + TEST_FAIL(v, !=); // expected-error 30 {{invalid operands to binary expression}} + TEST_FAIL(v, <); // expected-error 30 {{invalid operands to binary expression}} + TEST_FAIL(v, <=); // expected-error 30 {{invalid operands to binary expression}} + TEST_FAIL(v, >); // expected-error 30 {{invalid operands to binary expression}} + TEST_FAIL(v, >=); // expected-error 30 {{invalid operands to binary expression}} + TEST_FAIL(v, <=>); // expected-error 30 {{invalid operands to binary expression}} TEST_PASS(v, ==); TEST_PASS(v, !=); From 3c9161f4d3b99396dbaa396a29a8bd33b55f6e6f Mon Sep 17 00:00:00 2001 From: Louis Dionne Date: Mon, 15 Apr 2024 11:30:54 -0400 Subject: [PATCH 4/5] Avoid warning on GCC --- libcxx/include/__compare/ordering.h | 1 + 1 file changed, 1 insertion(+) diff --git a/libcxx/include/__compare/ordering.h b/libcxx/include/__compare/ordering.h index bea15456b89ba..379f3459c681d 100644 --- a/libcxx/include/__compare/ordering.h +++ b/libcxx/include/__compare/ordering.h @@ -42,6 +42,7 @@ struct _CmpUnspecifiedParam { __zero == 0, "Only literal 0 is allowed as the operand of a comparison with one of the ordering types"))) # endif { + (void)__zero; } }; From 49418ba2433172fb3e379eb70924459ae3064044 Mon Sep 17 00:00:00 2001 From: Louis Dionne Date: Mon, 19 Aug 2024 15:11:05 -0400 Subject: [PATCH 5/5] Remove outdated XFAIL for msvc --- .../reject-other-than-literal-zero.verify.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/libcxx/test/std/language.support/cmp/cmp.categories.pre/reject-other-than-literal-zero.verify.cpp b/libcxx/test/std/language.support/cmp/cmp.categories.pre/reject-other-than-literal-zero.verify.cpp index f59f6804b54e9..b6bc4dd4f097a 100644 --- a/libcxx/test/std/language.support/cmp/cmp.categories.pre/reject-other-than-literal-zero.verify.cpp +++ b/libcxx/test/std/language.support/cmp/cmp.categories.pre/reject-other-than-literal-zero.verify.cpp @@ -8,10 +8,6 @@ // UNSUPPORTED: c++03, c++11, c++14, c++17 -// In MSVC mode, there's a slightly different number of errors printed for -// each of these, so it doesn't add up to the exact expected count of 18. -// XFAIL: msvc - // // Ensure we reject all cases where an argument other than a literal 0 is used