Skip to content

[libc++][ratio] Avoids accepting unrelated types. #80491

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Feb 11, 2024

Conversation

mordante
Copy link
Member

@mordante mordante commented Feb 2, 2024

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

Fixes: #63753

@mordante mordante requested a review from a team as a code owner February 2, 2024 20:47
@llvmbot llvmbot added the libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. label Feb 2, 2024
@llvmbot
Copy link
Member

llvmbot commented Feb 2, 2024

@llvm/pr-subscribers-libcxx

Author: Mark de Wever (mordante)

Changes

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

Fixes: #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 --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

Copy link
Contributor

@hawkinsw hawkinsw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hope that this helps!!

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

Fixes: llvm#63753
@mordante mordante force-pushed the review/ratio_type_requirement branch from d2bb633 to e982e81 Compare February 3, 2024 19:20
Copy link

github-actions bot commented Feb 10, 2024

✅ With the latest revision this PR passed the C/C++ code formatter.

@mordante mordante force-pushed the review/ratio_type_requirement branch from 67401b9 to bfe8796 Compare February 10, 2024 14:16
@mordante mordante force-pushed the review/ratio_type_requirement branch from bfe8796 to 1c18400 Compare February 10, 2024 16:27
@mordante mordante merged commit fe0d277 into llvm:main Feb 11, 2024
@mordante mordante deleted the review/ratio_type_requirement branch February 11, 2024 12:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

<ratio>: The std::ratio meta arithmetic can accept non-std::ratio
4 participants