|  | /* Self tests for offset types for GDB, the GNU debugger. | 
|  |  | 
|  | Copyright (C) 2017-2025 Free Software Foundation, Inc. | 
|  |  | 
|  | This file is part of GDB. | 
|  |  | 
|  | This program is free software; you can redistribute it and/or modify | 
|  | it under the terms of the GNU General Public License as published by | 
|  | the Free Software Foundation; either version 3 of the License, or | 
|  | (at your option) any later version. | 
|  |  | 
|  | This program is distributed in the hope that it will be useful, | 
|  | but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | GNU General Public License for more details. | 
|  |  | 
|  | You should have received a copy of the GNU General Public License | 
|  | along with this program.  If not, see <http://www.gnu.org/licenses/>.  */ | 
|  |  | 
|  | #include "gdbsupport/selftest.h" | 
|  | #include "gdbsupport/offset-type.h" | 
|  | #include "gdbsupport/underlying.h" | 
|  | #include "gdbsupport/valid-expr.h" | 
|  |  | 
|  | namespace selftests { | 
|  | namespace offset_type { | 
|  |  | 
|  | DEFINE_OFFSET_TYPE (off_A, unsigned int); | 
|  | DEFINE_OFFSET_TYPE (off_B, unsigned int); | 
|  |  | 
|  | /* First, compile-time tests that: | 
|  |  | 
|  | - make sure that incorrect operations with mismatching types are | 
|  | caught at compile time. | 
|  |  | 
|  | - make sure that the same operations but involving the right types | 
|  | do compile and that they return the correct type. | 
|  | */ | 
|  |  | 
|  | #define CHECK_VALID(VALID, EXPR_TYPE, EXPR)		\ | 
|  | CHECK_VALID_EXPR_2 (off_A, off_B, VALID, EXPR_TYPE, EXPR) | 
|  |  | 
|  | off_A lval_a {}; | 
|  | off_B lval_b {}; | 
|  |  | 
|  | using undrl = std::underlying_type<off_A>::type; | 
|  |  | 
|  | /* Offset +/- underlying.  */ | 
|  |  | 
|  | CHECK_VALID (true,  off_A,  off_A {} + undrl {}); | 
|  | CHECK_VALID (true,  off_A,  off_A {} - undrl {}); | 
|  | CHECK_VALID (true,  off_A,  undrl {} + off_A {}); | 
|  | CHECK_VALID (true,  off_A,  undrl {} - off_A {}); | 
|  |  | 
|  | /* Add offset types.  Both same and different.  */ | 
|  |  | 
|  | CHECK_VALID (false, void,   off_A {} + off_A {}); | 
|  | CHECK_VALID (false, void,   off_A {} + off_B {}); | 
|  |  | 
|  | /* Subtract offset types.  Both same and different.  */ | 
|  |  | 
|  | CHECK_VALID (false, void,   off_B {} - off_A {}); | 
|  | CHECK_VALID (true,  undrl,  off_A {} - off_A {}); | 
|  |  | 
|  | /* Add/assign offset types.  Both same and different.  */ | 
|  |  | 
|  | CHECK_VALID (false, void,   lval_a += off_A {}); | 
|  | CHECK_VALID (false, void,   lval_a += off_B {}); | 
|  | CHECK_VALID (false, void,   lval_a -= off_A {}); | 
|  | CHECK_VALID (false, void,   lval_a -= off_B {}); | 
|  |  | 
|  | /* operator OP+= (offset, underlying), lvalue ref on the lhs. */ | 
|  |  | 
|  | CHECK_VALID (true,  off_A&, lval_a += undrl {}); | 
|  | CHECK_VALID (true,  off_A&, lval_a -= undrl {}); | 
|  |  | 
|  | /* operator OP+= (offset, underlying), rvalue ref on the lhs. */ | 
|  |  | 
|  | CHECK_VALID (false, void,   off_A {} += undrl {}); | 
|  | CHECK_VALID (false, void,   off_A {} -= undrl {}); | 
|  |  | 
|  | /* Rel ops, with same type.  */ | 
|  |  | 
|  | CHECK_VALID (true,  bool,   off_A {} < off_A {}); | 
|  | CHECK_VALID (true,  bool,   off_A {} > off_A {}); | 
|  | CHECK_VALID (true,  bool,   off_A {} <= off_A {}); | 
|  | CHECK_VALID (true,  bool,   off_A {} >= off_A {}); | 
|  |  | 
|  | /* Rel ops, with unrelated offset types.  */ | 
|  |  | 
|  | CHECK_VALID (false, void,   off_A {} < off_B {}); | 
|  | CHECK_VALID (false, void,   off_A {} > off_B {}); | 
|  | CHECK_VALID (false, void,   off_A {} <= off_B {}); | 
|  | CHECK_VALID (false, void,   off_A {} >= off_B {}); | 
|  |  | 
|  | /* Rel ops, with unrelated types.  */ | 
|  |  | 
|  | CHECK_VALID (false, void,   off_A {} < undrl {}); | 
|  | CHECK_VALID (false, void,   off_A {} > undrl {}); | 
|  | CHECK_VALID (false, void,   off_A {} <= undrl {}); | 
|  | CHECK_VALID (false, void,   off_A {} >= undrl {}); | 
|  |  | 
|  | static void | 
|  | run_tests () | 
|  | { | 
|  | /* Test op+ and op-.  */ | 
|  | { | 
|  | constexpr off_A a {}; | 
|  | static_assert (to_underlying (a) == 0, ""); | 
|  |  | 
|  | { | 
|  | constexpr off_A res1 = a + 2; | 
|  | static_assert (to_underlying (res1) == 2, ""); | 
|  |  | 
|  | constexpr off_A res2 = res1 - 1; | 
|  | static_assert (to_underlying (res2) == 1, ""); | 
|  | } | 
|  |  | 
|  | { | 
|  | constexpr off_A res1 = 2 + a; | 
|  | static_assert (to_underlying (res1) == 2, ""); | 
|  |  | 
|  | constexpr off_A res2 = 3 - res1; | 
|  | static_assert (to_underlying (res2) == 1, ""); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Test op+= and op-=.  */ | 
|  | { | 
|  | off_A o {}; | 
|  |  | 
|  | o += 10; | 
|  | SELF_CHECK (to_underlying (o) == 10); | 
|  | o -= 5; | 
|  | SELF_CHECK (to_underlying (o) == 5); | 
|  | } | 
|  |  | 
|  | /* Test op-.  */ | 
|  | { | 
|  | constexpr off_A o1 = (off_A) 10; | 
|  | constexpr off_A o2 = (off_A) 20; | 
|  |  | 
|  | constexpr unsigned int delta = o2 - o1; | 
|  |  | 
|  | static_assert (delta == 10, ""); | 
|  | } | 
|  |  | 
|  | /* Test <, <=, >, >=.  */ | 
|  | { | 
|  | constexpr off_A o1 = (off_A) 10; | 
|  | constexpr off_A o2 = (off_A) 20; | 
|  |  | 
|  | static_assert (o1 < o2, ""); | 
|  | static_assert (!(o2 < o1), ""); | 
|  |  | 
|  | static_assert (o2 > o1, ""); | 
|  | static_assert (!(o1 > o2), ""); | 
|  |  | 
|  | static_assert (o1 <= o2, ""); | 
|  | static_assert (!(o2 <= o1), ""); | 
|  |  | 
|  | static_assert (o2 >= o1, ""); | 
|  | static_assert (!(o1 >= o2), ""); | 
|  |  | 
|  | static_assert (o1 <= o1, ""); | 
|  | static_assert (o1 >= o1, ""); | 
|  | } | 
|  | } | 
|  |  | 
|  | } /* namespace offset_type */ | 
|  | } /* namespace selftests */ | 
|  |  | 
|  | INIT_GDB_FILE (offset_type_selftests) | 
|  | { | 
|  | selftests::register_test ("offset_type", selftests::offset_type::run_tests); | 
|  | } |