Skip to content

Commit e982e81

Browse files
committed
[libc++][ratio] Avoids accepting unrelated types.
The arithemtic and comparison operations are ill-formed when R1 or R2 is not a std::ratio. Fixes: #63753
1 parent dd0356d commit e982e81

File tree

4 files changed

+228
-6
lines changed

4 files changed

+228
-6
lines changed

libcxx/include/ratio

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,9 @@ private:
289289
static const intmax_t __gcd_n1_d2 = __static_gcd<_R1::num, _R2::den>::value;
290290
static const intmax_t __gcd_d1_n2 = __static_gcd<_R1::den, _R2::num>::value;
291291

292+
static_assert(__is_ratio<_R1>::value, "[ratio.general]/2 requires R1 to be a specialisation of the ratio template");
293+
static_assert(__is_ratio<_R2>::value, "[ratio.general]/2 requires R2 to be a specialisation of the ratio template");
294+
292295
public:
293296
typedef typename ratio< __ll_mul<_R1::num / __gcd_n1_d2, _R2::num / __gcd_d1_n2>::value,
294297
__ll_mul<_R2::den / __gcd_n1_d2, _R1::den / __gcd_d1_n2>::value >::type type;
@@ -312,6 +315,9 @@ private:
312315
static const intmax_t __gcd_n1_n2 = __static_gcd<_R1::num, _R2::num>::value;
313316
static const intmax_t __gcd_d1_d2 = __static_gcd<_R1::den, _R2::den>::value;
314317

318+
static_assert(__is_ratio<_R1>::value, "[ratio.general]/2 requires R1 to be a specialisation of the ratio template");
319+
static_assert(__is_ratio<_R2>::value, "[ratio.general]/2 requires R2 to be a specialisation of the ratio template");
320+
315321
public:
316322
typedef typename ratio< __ll_mul<_R1::num / __gcd_n1_n2, _R2::den / __gcd_d1_d2>::value,
317323
__ll_mul<_R2::num / __gcd_n1_n2, _R1::den / __gcd_d1_d2>::value >::type type;
@@ -335,6 +341,9 @@ private:
335341
static const intmax_t __gcd_n1_n2 = __static_gcd<_R1::num, _R2::num>::value;
336342
static const intmax_t __gcd_d1_d2 = __static_gcd<_R1::den, _R2::den>::value;
337343

344+
static_assert(__is_ratio<_R1>::value, "[ratio.general]/2 requires R1 to be a specialisation of the ratio template");
345+
static_assert(__is_ratio<_R2>::value, "[ratio.general]/2 requires R2 to be a specialisation of the ratio template");
346+
338347
public:
339348
typedef typename ratio_multiply<
340349
ratio<__gcd_n1_n2, _R1::den / __gcd_d1_d2>,
@@ -361,6 +370,9 @@ private:
361370
static const intmax_t __gcd_n1_n2 = __static_gcd<_R1::num, _R2::num>::value;
362371
static const intmax_t __gcd_d1_d2 = __static_gcd<_R1::den, _R2::den>::value;
363372

373+
static_assert(__is_ratio<_R1>::value, "[ratio.general]/2 requires R1 to be a specialisation of the ratio template");
374+
static_assert(__is_ratio<_R2>::value, "[ratio.general]/2 requires R2 to be a specialisation of the ratio template");
375+
364376
public:
365377
typedef typename ratio_multiply<
366378
ratio<__gcd_n1_n2, _R1::den / __gcd_d1_d2>,
@@ -384,10 +396,16 @@ struct _LIBCPP_TEMPLATE_VIS ratio_subtract : public __ratio_subtract<_R1, _R2>::
384396
// ratio_equal
385397

386398
template <class _R1, class _R2>
387-
struct _LIBCPP_TEMPLATE_VIS ratio_equal : _BoolConstant<(_R1::num == _R2::num && _R1::den == _R2::den)> {};
399+
struct _LIBCPP_TEMPLATE_VIS ratio_equal : _BoolConstant<(_R1::num == _R2::num && _R1::den == _R2::den)> {
400+
static_assert(__is_ratio<_R1>::value, "[ratio.general]/2 requires R1 to be a specialisation of the ratio template");
401+
static_assert(__is_ratio<_R2>::value, "[ratio.general]/2 requires R2 to be a specialisation of the ratio template");
402+
};
388403

389404
template <class _R1, class _R2>
390-
struct _LIBCPP_TEMPLATE_VIS ratio_not_equal : _BoolConstant<!ratio_equal<_R1, _R2>::value> {};
405+
struct _LIBCPP_TEMPLATE_VIS ratio_not_equal : _BoolConstant<!ratio_equal<_R1, _R2>::value> {
406+
static_assert(__is_ratio<_R1>::value, "[ratio.general]/2 requires R1 to be a specialisation of the ratio template");
407+
static_assert(__is_ratio<_R2>::value, "[ratio.general]/2 requires R2 to be a specialisation of the ratio template");
408+
};
391409

392410
// ratio_less
393411

@@ -441,16 +459,28 @@ struct __ratio_less<_R1, _R2, -1LL, -1LL> {
441459
};
442460

443461
template <class _R1, class _R2>
444-
struct _LIBCPP_TEMPLATE_VIS ratio_less : _BoolConstant<__ratio_less<_R1, _R2>::value> {};
462+
struct _LIBCPP_TEMPLATE_VIS ratio_less : _BoolConstant<__ratio_less<_R1, _R2>::value> {
463+
static_assert(__is_ratio<_R1>::value, "[ratio.general]/2 requires R1 to be a specialisation of the ratio template");
464+
static_assert(__is_ratio<_R2>::value, "[ratio.general]/2 requires R2 to be a specialisation of the ratio template");
465+
};
445466

446467
template <class _R1, class _R2>
447-
struct _LIBCPP_TEMPLATE_VIS ratio_less_equal : _BoolConstant<!ratio_less<_R2, _R1>::value> {};
468+
struct _LIBCPP_TEMPLATE_VIS ratio_less_equal : _BoolConstant<!ratio_less<_R2, _R1>::value> {
469+
static_assert(__is_ratio<_R1>::value, "[ratio.general]/2 requires R1 to be a specialisation of the ratio template");
470+
static_assert(__is_ratio<_R2>::value, "[ratio.general]/2 requires R2 to be a specialisation of the ratio template");
471+
};
448472

449473
template <class _R1, class _R2>
450-
struct _LIBCPP_TEMPLATE_VIS ratio_greater : _BoolConstant<ratio_less<_R2, _R1>::value> {};
474+
struct _LIBCPP_TEMPLATE_VIS ratio_greater : _BoolConstant<ratio_less<_R2, _R1>::value> {
475+
static_assert(__is_ratio<_R1>::value, "[ratio.general]/2 requires R1 to be a specialisation of the ratio template");
476+
static_assert(__is_ratio<_R2>::value, "[ratio.general]/2 requires R2 to be a specialisation of the ratio template");
477+
};
451478

452479
template <class _R1, class _R2>
453-
struct _LIBCPP_TEMPLATE_VIS ratio_greater_equal : _BoolConstant<!ratio_less<_R1, _R2>::value> {};
480+
struct _LIBCPP_TEMPLATE_VIS ratio_greater_equal : _BoolConstant<!ratio_less<_R1, _R2>::value> {
481+
static_assert(__is_ratio<_R1>::value, "[ratio.general]/2 requires R1 to be a specialisation of the ratio template");
482+
static_assert(__is_ratio<_R2>::value, "[ratio.general]/2 requires R2 to be a specialisation of the ratio template");
483+
};
454484

455485
template <class _R1, class _R2>
456486
struct __ratio_gcd {
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
// <ratio>
10+
//
11+
// [ratio.general]/2
12+
// Throughout subclause [ratio], the names of template parameters are
13+
// used to express type requirements. If a template parameter is named
14+
// R1 or R2, and the template argument is not a specialization of the
15+
// ratio template, the program is ill-formed.
16+
//
17+
// test ratio_multiply
18+
19+
#include <ratio>
20+
21+
struct R {
22+
static const int num = 1;
23+
static const int den = 1;
24+
};
25+
26+
using r = std::ratio<1, 1>;
27+
28+
namespace add {
29+
using r_r = std::ratio_add<r, r>::type;
30+
using R_r = std::ratio_add<R, r>::type; // expected-error@*:* {{R1 to be a specialisation of the ratio template}}
31+
using r_R = std::ratio_add<r, R>::type; // expected-error@*:* {{R2 to be a specialisation of the ratio template}}
32+
} // namespace add
33+
34+
namespace subtract {
35+
using r_r = std::ratio_subtract<r, r>::type;
36+
using R_r = std::ratio_subtract<R, r>::type; // expected-error@*:* {{R1 to be a specialisation of the ratio template}}
37+
using r_R = std::ratio_subtract<r, R>::type; // expected-error@*:* {{R2 to be a specialisation of the ratio template}}
38+
} // namespace subtract
39+
40+
namespace multiply {
41+
using r_r = std::ratio_multiply<r, r>::type;
42+
using R_r = std::ratio_multiply<R, r>::type; // expected-error@*:* {{R1 to be a specialisation of the ratio template}}
43+
using r_R = std::ratio_multiply<r, R>::type; // expected-error@*:* {{R2 to be a specialisation of the ratio template}}
44+
} // namespace multiply
45+
46+
namespace divide {
47+
using r_r = std::ratio_divide<r, r>::type;
48+
using R_r = std::ratio_divide<R, r>::type; // expected-error@*:* {{R1 to be a specialisation of the ratio template}}
49+
using r_R = std::ratio_divide<r, R>::type; // expected-error@*:* {{R2 to be a specialisation of the ratio template}}
50+
} // namespace divide
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
// <ratio>
10+
//
11+
// [ratio.general]/2
12+
// Throughout subclause [ratio], the names of template parameters are
13+
// used to express type requirements. If a template parameter is named
14+
// R1 or R2, and the template argument is not a specialization of the
15+
// ratio template, the program is ill-formed.
16+
//
17+
// Since std::ratio_xxx_v uses the same instantiations only one error
18+
// will be generated. These values are tested in a separate test.
19+
20+
#include <ratio>
21+
22+
struct R {
23+
static const int num = 1;
24+
static const int den = 1;
25+
};
26+
27+
using r = std::ratio<1, 1>;
28+
29+
namespace equal {
30+
using r_r = std::ratio_equal<r, r>::type;
31+
using R_r = std::ratio_equal<R, r>::type; // expected-error@*:* {{R1 to be a specialisation of the ratio template}}
32+
using r_R = std::ratio_equal<r, R>::type; // expected-error@*:* {{R2 to be a specialisation of the ratio template}}
33+
} // namespace equal
34+
35+
namespace not_equal {
36+
using r_r = std::ratio_not_equal<r, r>::type;
37+
using R_r = std::ratio_not_equal<R, r>::type; // expected-error@*:* {{R1 to be a specialisation of the ratio template}}
38+
using r_R = std::ratio_not_equal<r, R>::type; // expected-error@*:* {{R2 to be a specialisation of the ratio template}}
39+
} // namespace not_equal
40+
41+
namespace less {
42+
using r_r = std::ratio_less<r, r>::type;
43+
using R_r = std::ratio_less<R, r>::type; // expected-error@*:* {{R1 to be a specialisation of the ratio template}}
44+
using r_R = std::ratio_less<r, R>::type; // expected-error@*:* {{R2 to be a specialisation of the ratio template}}
45+
} // namespace less
46+
47+
namespace less_equal {
48+
using r_r = std::ratio_less_equal<r, r>::type;
49+
using R_r = std::ratio_less_equal<R, r>::type; // expected-error@*:* {{R1 to be a specialisation of the ratio template}}
50+
using r_R = std::ratio_less_equal<r, R>::type; // expected-error@*:* {{R2 to be a specialisation of the ratio template}}
51+
} // namespace less_equal
52+
53+
namespace greater {
54+
using r_r = std::ratio_greater<r, r>::type;
55+
using R_r = std::ratio_greater<R, r>::type; // expected-error@*:* {{R1 to be a specialisation of the ratio template}}
56+
using r_R = std::ratio_greater<r, R>::type; // expected-error@*:* {{R2 to be a specialisation of the ratio template}}
57+
} // namespace greater
58+
59+
namespace greater_equal {
60+
using r_r = std::ratio_greater_equal<r, r>::type;
61+
using R_r =
62+
std::ratio_greater_equal<R, r>::type; // expected-error@*:* {{R1 to be a specialisation of the ratio template}}
63+
using r_R =
64+
std::ratio_greater_equal<r, R>::type; // expected-error@*:* {{R2 to be a specialisation of the ratio template}}
65+
} // namespace greater_equal
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
// UNSUPPORTED: c++03, c++11, c++17
10+
11+
// <ratio>
12+
//
13+
// [ratio.general]/2
14+
// Throughout subclause [ratio], the names of template parameters are
15+
// used to express type requirements. If a template parameter is named
16+
// R1 or R2, and the template argument is not a specialization of the
17+
// ratio template, the program is ill-formed.
18+
//
19+
// Since std::ratio_xxx uses the same instantiations only one error
20+
// will be generated. These types are tested in a separate test.
21+
22+
#include <ratio>
23+
24+
struct R {
25+
constexpr static int num = 1;
26+
constexpr static int den = 1;
27+
};
28+
29+
using r = std::ratio<1, 1>;
30+
31+
namespace equal {
32+
constexpr bool r_r_v = std::ratio_equal_v<r, r>;
33+
constexpr bool R_r_v =
34+
std::ratio_equal_v<R, r>; // expected-error@*:* {{R1 to be a specialisation of the ratio template}}
35+
constexpr bool r_R_v =
36+
std::ratio_equal_v<r, R>; // expected-error@*:* {{R2 to be a specialisation of the ratio template}}
37+
} // namespace equal
38+
39+
namespace not_equal {
40+
constexpr bool r_r_v = std::ratio_not_equal_v<r, r>;
41+
constexpr bool R_r_v =
42+
std::ratio_not_equal_v<R, r>; // expected-error@*:* {{R1 to be a specialisation of the ratio template}}
43+
constexpr bool r_R_v =
44+
std::ratio_not_equal_v<r, R>; // expected-error@*:* {{R2 to be a specialisation of the ratio template}}
45+
} // namespace not_equal
46+
47+
namespace less {
48+
constexpr bool r_r_v = std::ratio_less_v<r, r>;
49+
constexpr bool R_r_v =
50+
std::ratio_less_v<R, r>; // expected-error@*:* {{R1 to be a specialisation of the ratio template}}
51+
constexpr bool r_R_v =
52+
std::ratio_less_v<r, R>; // expected-error@*:* {{R2 to be a specialisation of the ratio template}}
53+
} // namespace less
54+
55+
namespace less_equal {
56+
constexpr bool r_r_v = std::ratio_less_equal_v<r, r>;
57+
constexpr bool R_r_v =
58+
std::ratio_less_equal_v<R, r>; // expected-error@*:* {{R1 to be a specialisation of the ratio template}}
59+
constexpr bool r_R_v =
60+
std::ratio_less_equal_v<r, R>; // expected-error@*:* {{R2 to be a specialisation of the ratio template}}
61+
} // namespace less_equal
62+
63+
namespace greater {
64+
constexpr bool r_r_v = std::ratio_greater_v<r, r>;
65+
constexpr bool R_r_v =
66+
std::ratio_greater_v<R, r>; // expected-error@*:* {{R1 to be a specialisation of the ratio template}}
67+
constexpr bool r_R_v =
68+
std::ratio_greater_v<r, R>; // expected-error@*:* {{R2 to be a specialisation of the ratio template}}
69+
} // namespace greater
70+
71+
namespace greater_equal {
72+
constexpr bool r_r_v = std::ratio_greater_equal_v<r, r>;
73+
constexpr bool R_r_v =
74+
std::ratio_greater_equal_v<R, r>; // expected-error@*:* {{R1 to be a specialisation of the ratio template}}
75+
constexpr bool r_R_v =
76+
std::ratio_greater_equal_v<r, R>; // expected-error@*:* {{R2 to be a specialisation of the ratio template}}
77+
} // namespace greater_equal

0 commit comments

Comments
 (0)