[libcxx-commits] [libcxx] [libc++][ratio] Avoids accepting unrelated types. (PR #80491)

via libcxx-commits libcxx-commits at lists.llvm.org
Fri Feb 2 12:48:04 PST 2024


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Mark de Wever (mordante)

<details>
<summary>Changes</summary>

The arithemtic and comparison operations are ill-formed when R1 or R2 is not a std::ratio.

Fixes: https://github.com/llvm/llvm-project/issues/63753

---
Full diff: https://github.com/llvm/llvm-project/pull/80491.diff


4 Files Affected:

- (modified) libcxx/include/ratio (+36-6) 
- (added) libcxx/test/std/utilities/ratio/ratio.arithmetic/R1_R2_requirement.verify.cpp (+50) 
- (added) libcxx/test/std/utilities/ratio/ratio.comparison/R1_R2_requirement.verify.cpp (+67) 
- (added) libcxx/test/std/utilities/ratio/ratio.comparison/R1_R2_requirement_v.verify.cpp (+75) 


``````````diff
diff --git a/libcxx/include/ratio b/libcxx/include/ratio
index 3b11a2aa5bf6e..8563a0a1113ce 100644
--- a/libcxx/include/ratio
+++ b/libcxx/include/ratio
@@ -289,6 +289,9 @@ private:
   static const intmax_t __gcd_n1_d2 = __static_gcd<_R1::num, _R2::den>::value;
   static const intmax_t __gcd_d1_n2 = __static_gcd<_R1::den, _R2::num>::value;
 
+  static_assert(__is_ratio<_R1>::value, "[ratio.general]/2 requires R1 to be a specialication of the ratio template");
+  static_assert(__is_ratio<_R2>::value, "[ratio.general]/2 requires R2 to be a specialication of the ratio template");
+
 public:
   typedef typename ratio< __ll_mul<_R1::num / __gcd_n1_d2, _R2::num / __gcd_d1_n2>::value,
                           __ll_mul<_R2::den / __gcd_n1_d2, _R1::den / __gcd_d1_n2>::value >::type type;
@@ -312,6 +315,9 @@ private:
   static const intmax_t __gcd_n1_n2 = __static_gcd<_R1::num, _R2::num>::value;
   static const intmax_t __gcd_d1_d2 = __static_gcd<_R1::den, _R2::den>::value;
 
+  static_assert(__is_ratio<_R1>::value, "[ratio.general]/2 requires R1 to be a specialication of the ratio template");
+  static_assert(__is_ratio<_R2>::value, "[ratio.general]/2 requires R2 to be a specialication of the ratio template");
+
 public:
   typedef typename ratio< __ll_mul<_R1::num / __gcd_n1_n2, _R2::den / __gcd_d1_d2>::value,
                           __ll_mul<_R2::num / __gcd_n1_n2, _R1::den / __gcd_d1_d2>::value >::type type;
@@ -335,6 +341,9 @@ private:
   static const intmax_t __gcd_n1_n2 = __static_gcd<_R1::num, _R2::num>::value;
   static const intmax_t __gcd_d1_d2 = __static_gcd<_R1::den, _R2::den>::value;
 
+  static_assert(__is_ratio<_R1>::value, "[ratio.general]/2 requires R1 to be a specialication of the ratio template");
+  static_assert(__is_ratio<_R2>::value, "[ratio.general]/2 requires R2 to be a specialication of the ratio template");
+
 public:
   typedef typename ratio_multiply<
       ratio<__gcd_n1_n2, _R1::den / __gcd_d1_d2>,
@@ -361,6 +370,9 @@ private:
   static const intmax_t __gcd_n1_n2 = __static_gcd<_R1::num, _R2::num>::value;
   static const intmax_t __gcd_d1_d2 = __static_gcd<_R1::den, _R2::den>::value;
 
+  static_assert(__is_ratio<_R1>::value, "[ratio.general]/2 requires R1 to be a specialication of the ratio template");
+  static_assert(__is_ratio<_R2>::value, "[ratio.general]/2 requires R2 to be a specialication of the ratio template");
+
 public:
   typedef typename ratio_multiply<
       ratio<__gcd_n1_n2, _R1::den / __gcd_d1_d2>,
@@ -384,10 +396,16 @@ struct _LIBCPP_TEMPLATE_VIS ratio_subtract : public __ratio_subtract<_R1, _R2>::
 // ratio_equal
 
 template <class _R1, class _R2>
-struct _LIBCPP_TEMPLATE_VIS ratio_equal : _BoolConstant<(_R1::num == _R2::num && _R1::den == _R2::den)> {};
+struct _LIBCPP_TEMPLATE_VIS ratio_equal : _BoolConstant<(_R1::num == _R2::num && _R1::den == _R2::den)> {
+  static_assert(__is_ratio<_R1>::value, "[ratio.general]/2 requires R1 to be a specialication of the ratio template");
+  static_assert(__is_ratio<_R2>::value, "[ratio.general]/2 requires R2 to be a specialication of the ratio template");
+};
 
 template <class _R1, class _R2>
-struct _LIBCPP_TEMPLATE_VIS ratio_not_equal : _BoolConstant<!ratio_equal<_R1, _R2>::value> {};
+struct _LIBCPP_TEMPLATE_VIS ratio_not_equal : _BoolConstant<!ratio_equal<_R1, _R2>::value> {
+  static_assert(__is_ratio<_R1>::value, "[ratio.general]/2 requires R1 to be a specialication of the ratio template");
+  static_assert(__is_ratio<_R2>::value, "[ratio.general]/2 requires R2 to be a specialication of the ratio template");
+};
 
 // ratio_less
 
@@ -441,16 +459,28 @@ struct __ratio_less<_R1, _R2, -1LL, -1LL> {
 };
 
 template <class _R1, class _R2>
-struct _LIBCPP_TEMPLATE_VIS ratio_less : _BoolConstant<__ratio_less<_R1, _R2>::value> {};
+struct _LIBCPP_TEMPLATE_VIS ratio_less : _BoolConstant<__ratio_less<_R1, _R2>::value> {
+  static_assert(__is_ratio<_R1>::value, "[ratio.general]/2 requires R1 to be a specialication of the ratio template");
+  static_assert(__is_ratio<_R2>::value, "[ratio.general]/2 requires R2 to be a specialication of the ratio template");
+};
 
 template <class _R1, class _R2>
-struct _LIBCPP_TEMPLATE_VIS ratio_less_equal : _BoolConstant<!ratio_less<_R2, _R1>::value> {};
+struct _LIBCPP_TEMPLATE_VIS ratio_less_equal : _BoolConstant<!ratio_less<_R2, _R1>::value> {
+  static_assert(__is_ratio<_R1>::value, "[ratio.general]/2 requires R1 to be a specialication of the ratio template");
+  static_assert(__is_ratio<_R2>::value, "[ratio.general]/2 requires R2 to be a specialication of the ratio template");
+};
 
 template <class _R1, class _R2>
-struct _LIBCPP_TEMPLATE_VIS ratio_greater : _BoolConstant<ratio_less<_R2, _R1>::value> {};
+struct _LIBCPP_TEMPLATE_VIS ratio_greater : _BoolConstant<ratio_less<_R2, _R1>::value> {
+  static_assert(__is_ratio<_R1>::value, "[ratio.general]/2 requires R1 to be a specialication of the ratio template");
+  static_assert(__is_ratio<_R2>::value, "[ratio.general]/2 requires R2 to be a specialication of the ratio template");
+};
 
 template <class _R1, class _R2>
-struct _LIBCPP_TEMPLATE_VIS ratio_greater_equal : _BoolConstant<!ratio_less<_R1, _R2>::value> {};
+struct _LIBCPP_TEMPLATE_VIS ratio_greater_equal : _BoolConstant<!ratio_less<_R1, _R2>::value> {
+  static_assert(__is_ratio<_R1>::value, "[ratio.general]/2 requires R1 to be a specialication of the ratio template");
+  static_assert(__is_ratio<_R2>::value, "[ratio.general]/2 requires R2 to be a specialication of the ratio template");
+};
 
 template <class _R1, class _R2>
 struct __ratio_gcd {
diff --git a/libcxx/test/std/utilities/ratio/ratio.arithmetic/R1_R2_requirement.verify.cpp b/libcxx/test/std/utilities/ratio/ratio.arithmetic/R1_R2_requirement.verify.cpp
new file mode 100644
index 0000000000000..93fdd9c02b3f6
--- /dev/null
+++ b/libcxx/test/std/utilities/ratio/ratio.arithmetic/R1_R2_requirement.verify.cpp
@@ -0,0 +1,50 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <ratio>
+//
+// [ratio.general]/2
+//   Throughout subclause [ratio], the names of template parameters are
+//   used to express type requirements. If a template parameter is named
+//   R1 or R2, and the template argument is not a specialization of the
+//   ratio template, the program is ill-formed.
+//
+// test ratio_multiply
+
+#include <ratio>
+
+struct R {
+  constexpr static int num = 1;
+  constexpr static int den = 1;
+};
+
+using r = std::ratio<1, 1>;
+
+namespace add {
+using r_r = std::ratio_add<r, r>::type;
+using R_r = std::ratio_add<R, r>::type; // expected-error@*:* {{R1 to be a specialication of the ratio template}}
+using r_R = std::ratio_add<r, R>::type; // expected-error@*:* {{R2 to be a specialication of the ratio template}}
+} // namespace add
+
+namespace subtract {
+using r_r = std::ratio_subtract<r, r>::type;
+using R_r = std::ratio_subtract<R, r>::type; // expected-error@*:* {{R1 to be a specialication of the ratio template}}
+using r_R = std::ratio_subtract<r, R>::type; // expected-error@*:* {{R2 to be a specialication of the ratio template}}
+} // namespace subtract
+
+namespace multiply {
+using r_r = std::ratio_multiply<r, r>::type;
+using R_r = std::ratio_multiply<R, r>::type; // expected-error@*:* {{R1 to be a specialication of the ratio template}}
+using r_R = std::ratio_multiply<r, R>::type; // expected-error@*:* {{R2 to be a specialication of the ratio template}}
+} // namespace multiply
+
+namespace divide {
+using r_r = std::ratio_divide<r, r>::type;
+using R_r = std::ratio_divide<R, r>::type; // expected-error@*:* {{R1 to be a specialication of the ratio template}}
+using r_R = std::ratio_divide<r, R>::type; // expected-error@*:* {{R2 to be a specialication of the ratio template}}
+} // namespace divide
diff --git a/libcxx/test/std/utilities/ratio/ratio.comparison/R1_R2_requirement.verify.cpp b/libcxx/test/std/utilities/ratio/ratio.comparison/R1_R2_requirement.verify.cpp
new file mode 100644
index 0000000000000..a19477ea763a2
--- /dev/null
+++ b/libcxx/test/std/utilities/ratio/ratio.comparison/R1_R2_requirement.verify.cpp
@@ -0,0 +1,67 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// <ratio>
+//
+// [ratio.general]/2
+//   Throughout subclause [ratio], the names of template parameters are
+//   used to express type requirements. If a template parameter is named
+//   R1 or R2, and the template argument is not a specialization of the
+//   ratio template, the program is ill-formed.
+//
+// Since std::ratio_xxx_v uses the same instantiations only one error
+// will be generated. These values are tested in a separate test.
+
+#include <ratio>
+
+struct R {
+  constexpr static int num = 1;
+  constexpr static int den = 1;
+};
+
+using r = std::ratio<1, 1>;
+
+namespace equal {
+using r_r = std::ratio_equal<r, r>::type;
+using R_r = std::ratio_equal<R, r>::type; // expected-error@*:* {{R1 to be a specialication of the ratio template}}
+using r_R = std::ratio_equal<r, R>::type; // expected-error@*:* {{R2 to be a specialication of the ratio template}}
+} // namespace equal
+
+namespace not_equal {
+using r_r = std::ratio_not_equal<r, r>::type;
+using R_r = std::ratio_not_equal<R, r>::type; // expected-error@*:* {{R1 to be a specialication of the ratio template}}
+using r_R = std::ratio_not_equal<r, R>::type; // expected-error@*:* {{R2 to be a specialication of the ratio template}}
+} // namespace not_equal
+
+namespace less {
+using r_r = std::ratio_less<r, r>::type;
+using R_r = std::ratio_less<R, r>::type; // expected-error@*:* {{R1 to be a specialication of the ratio template}}
+using r_R = std::ratio_less<r, R>::type; // expected-error@*:* {{R2 to be a specialication of the ratio template}}
+} // namespace less
+
+namespace less_equal {
+using r_r = std::ratio_less_equal<r, r>::type;
+using R_r = std::ratio_less_equal<R, r>::type; // expected-error@*:* {{R1 to be a specialication of the ratio template}}
+using r_R = std::ratio_less_equal<r, R>::type; // expected-error@*:* {{R2 to be a specialication of the ratio template}}
+} // namespace less_equal
+
+namespace greater {
+using r_r = std::ratio_greater<r, r>::type;
+using R_r = std::ratio_greater<R, r>::type; // expected-error@*:* {{R1 to be a specialication of the ratio template}}
+using r_R = std::ratio_greater<r, R>::type; // expected-error@*:* {{R2 to be a specialication of the ratio template}}
+} // namespace greater
+
+namespace greater_equal {
+using r_r = std::ratio_greater_equal<r, r>::type;
+using R_r =
+    std::ratio_greater_equal<R, r>::type; // expected-error@*:* {{R1 to be a specialication of the ratio template}}
+using r_R =
+    std::ratio_greater_equal<r, R>::type; // expected-error@*:* {{R2 to be a specialication of the ratio template}}
+} // namespace greater_equal
diff --git a/libcxx/test/std/utilities/ratio/ratio.comparison/R1_R2_requirement_v.verify.cpp b/libcxx/test/std/utilities/ratio/ratio.comparison/R1_R2_requirement_v.verify.cpp
new file mode 100644
index 0000000000000..d89f614558a6d
--- /dev/null
+++ b/libcxx/test/std/utilities/ratio/ratio.comparison/R1_R2_requirement_v.verify.cpp
@@ -0,0 +1,75 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <ratio>
+//
+// [ratio.general]/2
+//   Throughout subclause [ratio], the names of template parameters are
+//   used to express type requirements. If a template parameter is named
+//   R1 or R2, and the template argument is not a specialization of the
+//   ratio template, the program is ill-formed.
+//
+// Since std::ratio_xxx uses the same instantiations only one error
+// will be generated. These types are tested in a separate test.
+
+#include <ratio>
+
+struct R {
+  constexpr static int num = 1;
+  constexpr static int den = 1;
+};
+
+using r = std::ratio<1, 1>;
+
+namespace equal {
+constexpr bool r_r_v = std::ratio_equal_v<r, r>;
+constexpr bool R_r_v =
+    std::ratio_equal_v<R, r>; // expected-error@*:* {{R1 to be a specialication of the ratio template}}
+constexpr bool r_R_v =
+    std::ratio_equal_v<r, R>; // expected-error@*:* {{R2 to be a specialication of the ratio template}}
+} // namespace equal
+
+namespace not_equal {
+constexpr bool r_r_v = std::ratio_not_equal_v<r, r>;
+constexpr bool R_r_v =
+    std::ratio_not_equal_v<R, r>; // expected-error@*:* {{R1 to be a specialication of the ratio template}}
+constexpr bool r_R_v =
+    std::ratio_not_equal_v<r, R>; // expected-error@*:* {{R2 to be a specialication of the ratio template}}
+} // namespace not_equal
+
+namespace less {
+constexpr bool r_r_v = std::ratio_less_v<r, r>;
+constexpr bool R_r_v =
+    std::ratio_less_v<R, r>; // expected-error@*:* {{R1 to be a specialication of the ratio template}}
+constexpr bool r_R_v =
+    std::ratio_less_v<r, R>; // expected-error@*:* {{R2 to be a specialication of the ratio template}}
+} // namespace less
+
+namespace less_equal {
+constexpr bool r_r_v = std::ratio_less_equal_v<r, r>;
+constexpr bool R_r_v =
+    std::ratio_less_equal_v<R, r>; // expected-error@*:* {{R1 to be a specialication of the ratio template}}
+constexpr bool r_R_v =
+    std::ratio_less_equal_v<r, R>; // expected-error@*:* {{R2 to be a specialication of the ratio template}}
+} // namespace less_equal
+
+namespace greater {
+constexpr bool r_r_v = std::ratio_greater_v<r, r>;
+constexpr bool R_r_v =
+    std::ratio_greater_v<R, r>; // expected-error@*:* {{R1 to be a specialication of the ratio template}}
+constexpr bool r_R_v =
+    std::ratio_greater_v<r, R>; // expected-error@*:* {{R2 to be a specialication of the ratio template}}
+} // namespace greater
+
+namespace greater_equal {
+constexpr bool r_r_v = std::ratio_greater_equal_v<r, r>;
+constexpr bool R_r_v =
+    std::ratio_greater_equal_v<R, r>; // expected-error@*:* {{R1 to be a specialication of the ratio template}}
+constexpr bool r_R_v =
+    std::ratio_greater_equal_v<r, R>; // expected-error@*:* {{R2 to be a specialication of the ratio template}}
+} // namespace greater_equal

``````````

</details>


https://github.com/llvm/llvm-project/pull/80491


More information about the libcxx-commits mailing list