Skip to content

Commit fe0d277

Browse files
authored
[libc++][ratio] Avoids accepting unrelated types. (#80491)
The arithmetic and comparison operators are ill-formed when R1 or R2 is not a std::ratio. Fixes: #63753
1 parent 8033749 commit fe0d277

File tree

4 files changed

+242
-6
lines changed

4 files changed

+242
-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: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
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+
#include <ratio>
18+
19+
struct invalid {
20+
static const int num = 1;
21+
static const int den = 1;
22+
};
23+
24+
using valid = std::ratio<1, 1>;
25+
26+
namespace add {
27+
using valid_valid = std::ratio_add<valid, valid>::type;
28+
using invalid_valid =
29+
std::ratio_add<invalid, valid>::type; // expected-error@*:* {{R1 to be a specialisation of the ratio template}}
30+
using valid_invalid =
31+
std::ratio_add<valid, invalid>::type; // expected-error@*:* {{R2 to be a specialisation of the ratio template}}
32+
} // namespace add
33+
34+
namespace subtract {
35+
using valid_valid = std::ratio_subtract<valid, valid>::type;
36+
using invalid_valid =
37+
std::ratio_subtract<invalid, valid>::type; // expected-error@*:* {{R1 to be a specialisation of the ratio template}}
38+
using valid_invalid =
39+
std::ratio_subtract<valid, invalid>::type; // expected-error@*:* {{R2 to be a specialisation of the ratio template}}
40+
} // namespace subtract
41+
42+
namespace multiply {
43+
using valid_valid = std::ratio_multiply<valid, valid>::type;
44+
using invalid_valid =
45+
std::ratio_multiply<invalid, valid>::type; // expected-error@*:* {{R1 to be a specialisation of the ratio template}}
46+
using valid_invalid =
47+
std::ratio_multiply<valid, invalid>::type; // expected-error@*:* {{R2 to be a specialisation of the ratio template}}
48+
} // namespace multiply
49+
50+
namespace divide {
51+
using valid_valid = std::ratio_divide<valid, valid>::type;
52+
using invalid_valid =
53+
std::ratio_divide<invalid, valid>::type; // expected-error@*:* {{R1 to be a specialisation of the ratio template}}
54+
using valid_invalid =
55+
std::ratio_divide<valid, invalid>::type; // expected-error@*:* {{R2 to be a specialisation of the ratio template}}
56+
} // namespace divide
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
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 all std::ratio_xxx_v variables use the same instantiation, only one
18+
// error will be generated. These values are tested in a separate test.
19+
20+
#include <ratio>
21+
22+
struct invalid {
23+
static const int num = 1;
24+
static const int den = 1;
25+
};
26+
27+
using valid = std::ratio<1, 1>;
28+
29+
namespace equal {
30+
using valid_valid = std::ratio_equal<valid, valid>::type;
31+
using invalid_valid =
32+
std::ratio_equal<invalid, valid>::type; // expected-error@*:* {{R1 to be a specialisation of the ratio template}}
33+
using valid_invalid =
34+
std::ratio_equal<valid, invalid>::type; // expected-error@*:* {{R2 to be a specialisation of the ratio template}}
35+
} // namespace equal
36+
37+
namespace not_equal {
38+
using valid_valid = std::ratio_not_equal<valid, valid>::type;
39+
using invalid_valid =
40+
std::ratio_not_equal<invalid,
41+
valid>::type; // expected-error@*:* {{R1 to be a specialisation of the ratio template}}
42+
using valid_invalid =
43+
std::ratio_not_equal<valid,
44+
invalid>::type; // expected-error@*:* {{R2 to be a specialisation of the ratio template}}
45+
} // namespace not_equal
46+
47+
namespace less {
48+
using valid_valid = std::ratio_less<valid, valid>::type;
49+
using invalid_valid =
50+
std::ratio_less<invalid, valid>::type; // expected-error@*:* {{R1 to be a specialisation of the ratio template}}
51+
using valid_invalid =
52+
std::ratio_less<valid, invalid>::type; // expected-error@*:* {{R2 to be a specialisation of the ratio template}}
53+
} // namespace less
54+
55+
namespace less_equal {
56+
using valid_valid = std::ratio_less_equal<valid, valid>::type;
57+
using invalid_valid =
58+
std::ratio_less_equal<invalid,
59+
valid>::type; // expected-error@*:* {{R1 to be a specialisation of the ratio template}}
60+
using valid_invalid =
61+
std::ratio_less_equal<valid,
62+
invalid>::type; // expected-error@*:* {{R2 to be a specialisation of the ratio template}}
63+
} // namespace less_equal
64+
65+
namespace greater {
66+
using valid_valid = std::ratio_greater<valid, valid>::type;
67+
using invalid_valid =
68+
std::ratio_greater<invalid, valid>::type; // expected-error@*:* {{R1 to be a specialisation of the ratio template}}
69+
using valid_invalid =
70+
std::ratio_greater<valid, invalid>::type; // expected-error@*:* {{R2 to be a specialisation of the ratio template}}
71+
} // namespace greater
72+
73+
namespace greater_equal {
74+
using valid_valid = std::ratio_greater_equal<valid, valid>::type;
75+
using invalid_valid =
76+
std::ratio_greater_equal<invalid,
77+
valid>::type; // expected-error@*:* {{R1 to be a specialisation of the ratio template}}
78+
using valid_invalid =
79+
std::ratio_greater_equal<valid,
80+
invalid>::type; // expected-error@*:* {{R2 to be a specialisation of the ratio template}}
81+
} // namespace greater_equal
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
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++14
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 all std::ratio_xxx_v variables use the same instantiation, only one
20+
// error will be generated. These values are tested in a separate test.
21+
22+
#include <ratio>
23+
24+
struct invalid {
25+
constexpr static int num = 1;
26+
constexpr static int den = 1;
27+
};
28+
29+
using valid = std::ratio<1, 1>;
30+
31+
void test() {
32+
// equal
33+
(void)std::ratio_equal_v<valid, valid>;
34+
(void)std::ratio_equal_v<invalid, valid>; // expected-error@*:* {{R1 to be a specialisation of the ratio template}}
35+
(void)std::ratio_equal_v<valid, invalid>; // expected-error@*:* {{R2 to be a specialisation of the ratio template}}
36+
37+
// not_equal
38+
(void)std::ratio_not_equal_v<valid, valid>;
39+
(void)std::ratio_not_equal_v<invalid,
40+
valid>; // expected-error@*:* {{R1 to be a specialisation of the ratio template}}
41+
(void)std::ratio_not_equal_v<valid,
42+
invalid>; // expected-error@*:* {{R2 to be a specialisation of the ratio template}}
43+
44+
// less
45+
(void)std::ratio_less_v<valid, valid>;
46+
(void)std::ratio_less_v<invalid, valid>; // expected-error@*:* {{R1 to be a specialisation of the ratio template}}
47+
(void)std::ratio_less_v<valid, invalid>; // expected-error@*:* {{R2 to be a specialisation of the ratio template}}
48+
49+
// less_equal
50+
(void)std::ratio_less_equal_v<valid, valid>;
51+
(void)std::ratio_less_equal_v<invalid,
52+
valid>; // expected-error@*:* {{R1 to be a specialisation of the ratio template}}
53+
(void)std::ratio_less_equal_v<valid,
54+
invalid>; // expected-error@*:* {{R2 to be a specialisation of the ratio template}}
55+
56+
// greater
57+
(void)std::ratio_greater_v<valid, valid>;
58+
(void)std::ratio_greater_v<invalid, valid>; // expected-error@*:* {{R1 to be a specialisation of the ratio template}}
59+
(void)std::ratio_greater_v<valid, invalid>; // expected-error@*:* {{R2 to be a specialisation of the ratio template}}
60+
61+
// greater_equal
62+
(void)std::ratio_greater_equal_v<valid, valid>;
63+
64+
(void)std::ratio_greater_equal_v<invalid,
65+
valid>; // expected-error@*:* {{R1 to be a specialisation of the ratio template}}
66+
67+
(void)std::ratio_greater_equal_v<valid,
68+
invalid>; // expected-error@*:* {{R2 to be a specialisation of the ratio template}}
69+
}

0 commit comments

Comments
 (0)