Skip to content

Commit 973d721

Browse files
jonmeowzygoloid
andauthored
Some more edits to EnumBase and EnumMaskBase (#6054)
Adds a unit test, and some smaller edits: - Remove the `=` when defining names, in order to change `}` placement by clang-format on uses. - context: #6053 (comment) - I believe with `EnumBase` that keeping the `=` had been a deliberate choice, so this PR is intended to confirm that removing it is okay. - Delete `EnumMaskBase::name` - context: #6053 (comment) - We can't just do nothing because `EnumBase::name` uses indexing that's incompatible with `EnumMaskBase`. - Some small comment cleanups. - Tests don't need to be in the `Carbon` namespace anymore, macros work fine in other namespaces, but it's still the right namespace. - Documentation on `EnumBase::name` seems to be referring to a prior structure, wherein we had a macro defining the function instead of the `Names` array. --------- Co-authored-by: Richard Smith <[email protected]>
1 parent 1d19fa3 commit 973d721

17 files changed

+202
-52
lines changed

.clang-format

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ SpaceBeforeParens: ControlStatementsExceptControlMacros
1717
IfMacros:
1818
[
1919
'CARBON_DEFINE_RAW_ENUM_CLASS',
20+
'CARBON_DEFINE_ENUM_CLASS_NAMES',
2021
'CARBON_DEFINE_RAW_ENUM_MASK',
22+
'CARBON_DEFINE_ENUM_MASK_NAMES',
2123
'CARBON_KIND_SWITCH',
2224
]
2325
StatementMacros: ['ABSTRACT']

common/BUILD

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,18 @@ cc_library(
168168
],
169169
)
170170

171+
cc_test(
172+
name = "enum_mask_base_test",
173+
size = "small",
174+
srcs = ["enum_mask_base_test.cpp"],
175+
deps = [
176+
":enum_mask_base",
177+
":raw_string_ostream",
178+
"//testing/base:gtest_main",
179+
"@googletest//:gtest",
180+
],
181+
)
182+
171183
cc_library(
172184
name = "error",
173185
hdrs = ["error.h"],

common/enum_base.h

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#ifndef CARBON_COMMON_ENUM_BASE_H_
66
#define CARBON_COMMON_ENUM_BASE_H_
77

8+
#include <compare>
89
#include <type_traits>
910

1011
#include "common/ostream.h"
@@ -53,7 +54,7 @@ namespace Carbon::Internal {
5354
//
5455
// In `my_kind.cpp`:
5556
// ```
56-
// CARBON_DEFINE_ENUM_CLASS_NAMES(MyKind) = {
57+
// CARBON_DEFINE_ENUM_CLASS_NAMES(MyKind) {
5758
// #define CARBON_MY_KIND(Name) CARBON_ENUM_CLASS_NAME_STRING(Name)
5859
// #include ".../my_kind.def"
5960
// };
@@ -129,15 +130,19 @@ class EnumBase : public Printable<DerivedT> {
129130
explicit operator bool() const = delete;
130131

131132
// Returns the name of this value.
132-
//
133-
// This method will be automatically defined using the static `names` string
134-
// table in the base class, which is in turn will be populated for each
135-
// derived type using the macro helpers in this file.
136133
auto name() const -> llvm::StringRef { return Names[AsInt()]; }
137134

138135
// Prints this value using its name.
139136
auto Print(llvm::raw_ostream& out) const -> void { out << name(); }
140137

138+
// Don't support comparison of enums by default.
139+
friend auto operator<(DerivedT lhs, DerivedT rhs) -> bool = delete;
140+
friend auto operator<=(DerivedT lhs, DerivedT rhs) -> bool = delete;
141+
friend auto operator>(DerivedT lhs, DerivedT rhs) -> bool = delete;
142+
friend auto operator>=(DerivedT lhs, DerivedT rhs) -> bool = delete;
143+
friend auto operator<=>(DerivedT lhs, DerivedT rhs)
144+
-> std::partial_ordering = delete;
145+
141146
protected:
142147
// The default constructor is explicitly defaulted (and constexpr) as a
143148
// protected constructor to allow derived classes to be constructed but not
@@ -215,7 +220,7 @@ class EnumBase : public Printable<DerivedT> {
215220
// `clang-format` has a bug with spacing around `->` returns in macros. See
216221
// https://bugs.llvm.org/show_bug.cgi?id=48320 for details.
217222
#define CARBON_DEFINE_ENUM_CLASS_NAMES(EnumClassName) \
218-
constexpr llvm::StringLiteral Internal::EnumClassName##Data::Names[]
223+
constexpr llvm::StringLiteral Internal::EnumClassName##Data::Names[] =
219224

220225
// Use this within the names array initializer to generate a string for each
221226
// name.

common/enum_base_test.cpp

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ class TestKind : public CARBON_ENUM_BASE(TestKind) {
3030
CARBON_ENUM_CONSTANT_DEFINITION(TestKind, Name)
3131
#include "common/enum_base_test.def"
3232

33-
CARBON_DEFINE_ENUM_CLASS_NAMES(TestKind) = {
33+
CARBON_DEFINE_ENUM_CLASS_NAMES(TestKind) {
3434
#define CARBON_ENUM_BASE_TEST_KIND(Name) CARBON_ENUM_CLASS_NAME_STRING(Name)
3535
#include "common/enum_base_test.def"
3636
};
@@ -79,23 +79,15 @@ TEST(EnumBaseTest, Switch) {
7979
TEST(EnumBaseTest, Comparison) {
8080
TestKind kind = TestKind::Beep;
8181

82-
// Make sure all the different comparisons work, and also to work with
82+
// Make sure all the different comparisons work, and also work with
8383
// GoogleTest expectations.
8484
EXPECT_EQ(TestKind::Beep, kind);
8585
EXPECT_NE(TestKind::Boop, kind);
86-
EXPECT_LT(kind, TestKind::Boop);
87-
EXPECT_GT(TestKind::Burr, kind);
88-
EXPECT_LE(kind, TestKind::Beep);
89-
EXPECT_GE(TestKind::Beep, kind);
9086

9187
// These should also all be constexpr.
9288
constexpr TestKind Kind2 = TestKind::Beep;
9389
static_assert(Kind2 == TestKind::Beep);
9490
static_assert(Kind2 != TestKind::Boop);
95-
static_assert(Kind2 < TestKind::Boop);
96-
static_assert(!(Kind2 > TestKind::Burr));
97-
static_assert(Kind2 <= TestKind::Beep);
98-
static_assert(!(Kind2 >= TestKind::Burr));
9991
}
10092

10193
TEST(EnumBaseTest, IntConversion) {

common/enum_mask_base.h

Lines changed: 14 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
namespace Carbon::Internal {
1414

1515
// CRTP-style base class similar to `EnumBase`, but supporting mask enums.
16+
// Enumerator values are consecutive bit shifts (1 << 0, 1 << 1, 1 << 2, 1 << 3,
17+
// ...).
1618
//
1719
// Users must be in the `Carbon` namespace and should look like the following.
1820
//
@@ -24,7 +26,7 @@ namespace Carbon::Internal {
2426
// X(Enumerator3) \
2527
// ...
2628
//
27-
// CARBON_DEFINE_RAW_ENUM_MASK(MyKind) {
29+
// CARBON_DEFINE_RAW_ENUM_MASK(MyKind, uint32_t) {
2830
// CARBON_MY_KIND(CARBON_RAW_ENUM_MASK_ENUMERATOR)
2931
// };
3032
//
@@ -43,8 +45,9 @@ namespace Carbon::Internal {
4345
//
4446
// In `my_kind.cpp`:
4547
// ```
46-
// CARBON_DEFINE_ENUM_MASK_NAMES(MyKind) = {
47-
// CARBON_MY_KIND(CARBON_ENUM_MASK_NAME_STRING)};
48+
// CARBON_DEFINE_ENUM_MASK_NAMES(MyKind) {
49+
// CARBON_MY_KIND(CARBON_ENUM_MASK_NAME_STRING)
50+
// };
4851
// ```
4952
template <typename DerivedT, typename EnumT, const llvm::StringLiteral Names[]>
5053
class EnumMaskBase : public EnumBase<DerivedT, EnumT, Names> {
@@ -67,6 +70,8 @@ class EnumMaskBase : public EnumBase<DerivedT, EnumT, Names> {
6770
// Removes entries from the mask.
6871
auto Remove(DerivedT other) -> void { *this = *this & ~other; }
6972

73+
constexpr auto empty() const -> bool { return this->AsInt() == 0; }
74+
7075
constexpr auto operator|(DerivedT other) const -> DerivedT {
7176
return DerivedT::FromInt(this->AsInt() | other.AsInt());
7277
}
@@ -79,21 +84,9 @@ class EnumMaskBase : public EnumBase<DerivedT, EnumT, Names> {
7984
return DerivedT::FromInt(~this->AsInt());
8085
}
8186

82-
constexpr auto empty() const -> bool { return this->AsInt() == 0; }
83-
84-
// Returns the name of this value. Requires it to be a single value, not
85-
// combined.
86-
//
87-
// This method will be automatically defined using the static `names` string
88-
// table in the base class, which is in turn will be populated for each
89-
// derived type using the macro helpers in this file.
90-
//
91-
// This shadows EnumBase::name.
92-
auto name() const -> llvm::StringRef {
93-
CARBON_CHECK(std::has_single_bit(this->AsInt()), "Not a single bit: {0}",
94-
this->AsInt());
95-
return Names[std::bit_width(this->AsInt())];
96-
}
87+
// Use `Print` for mask entries. This hides `EnumBase::name`; it's not
88+
// compatible with `EnumMaskBase`.
89+
auto name() const -> llvm::StringRef = delete;
9790

9891
// Prints this value as a `|`-separated list of mask entries, or `None`.
9992
//
@@ -119,9 +112,9 @@ constexpr const DerivedT& EnumMaskBase<DerivedT, EnumT, Names>::None =
119112

120113
} // namespace Carbon::Internal
121114

122-
// Use this before defining a class that derives from `EnumBase` to begin the
123-
// definition of the raw `enum class`. It should be followed by the body of that
124-
// raw enum class.
115+
// Use this before defining a class that derives from `EnumMaskBase` to begin
116+
// the definition of the raw `enum class`. It should be followed by the body of
117+
// that raw enum class.
125118
#define CARBON_DEFINE_RAW_ENUM_MASK(EnumMaskName, UnderlyingType) \
126119
namespace Internal { \
127120
struct EnumMaskName##Data { \

common/enum_mask_base_test.cpp

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
2+
// Exceptions. See /LICENSE for license information.
3+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
4+
5+
#include "common/enum_mask_base.h"
6+
7+
#include <gtest/gtest.h>
8+
9+
#include "common/raw_string_ostream.h"
10+
11+
namespace Carbon {
12+
namespace {
13+
14+
#define CARBON_TEST_KIND(X) \
15+
X(Beep) \
16+
X(Boop) \
17+
X(Burr)
18+
19+
CARBON_DEFINE_RAW_ENUM_MASK(TestKind, uint8_t) {
20+
CARBON_TEST_KIND(CARBON_RAW_ENUM_MASK_ENUMERATOR)
21+
};
22+
23+
class TestKind : public CARBON_ENUM_MASK_BASE(TestKind) {
24+
public:
25+
CARBON_TEST_KIND(CARBON_ENUM_MASK_CONSTANT_DECL)
26+
27+
using EnumMaskBase::AsInt;
28+
using EnumMaskBase::FromInt;
29+
};
30+
31+
#define CARBON_TEST_KIND_WITH_TYPE(X) \
32+
CARBON_ENUM_MASK_CONSTANT_DEFINITION(TestKind, X)
33+
CARBON_TEST_KIND(CARBON_TEST_KIND_WITH_TYPE)
34+
#undef CARBON_TEST_KIND_WITH_TYPE
35+
36+
CARBON_DEFINE_ENUM_MASK_NAMES(TestKind) {
37+
CARBON_TEST_KIND(CARBON_ENUM_MASK_NAME_STRING)
38+
};
39+
40+
static_assert(sizeof(TestKind) == sizeof(uint8_t),
41+
"Class size doesn't match enum size!");
42+
43+
TEST(EnumMaskBaseTest, Printing) {
44+
RawStringOstream stream;
45+
46+
TestKind kind = TestKind::Beep;
47+
stream << kind;
48+
EXPECT_EQ("Beep", stream.TakeStr());
49+
50+
kind = TestKind::Boop;
51+
stream << kind;
52+
EXPECT_EQ("Boop", stream.TakeStr());
53+
54+
stream << TestKind::Beep;
55+
EXPECT_EQ("Beep", stream.TakeStr());
56+
57+
stream << (TestKind::Beep | TestKind::Burr);
58+
EXPECT_EQ("Beep|Burr", stream.TakeStr());
59+
}
60+
61+
// This just ensures it compiles, it's not validating what's printed.
62+
TEST(EnumMaskBaseTest, PrintToGoogletest) {
63+
EXPECT_TRUE(true) << TestKind::Beep;
64+
}
65+
66+
TEST(EnumMaskBaseTest, Switch) {
67+
TestKind kind = TestKind::Boop;
68+
69+
switch (kind) {
70+
case TestKind::Beep: {
71+
FAIL() << "Beep case selected!";
72+
break;
73+
}
74+
case TestKind::Boop: {
75+
EXPECT_EQ(kind, TestKind::Boop);
76+
break;
77+
}
78+
case TestKind::Burr: {
79+
FAIL() << "Burr case selected!";
80+
break;
81+
}
82+
}
83+
}
84+
85+
TEST(EnumMaskBaseTest, Equality) {
86+
TestKind kind = TestKind::Beep;
87+
88+
// Make sure all the different comparisons work, and also work with
89+
// GoogleTest expectations.
90+
EXPECT_EQ(TestKind::Beep, kind);
91+
EXPECT_NE(TestKind::Boop, kind);
92+
93+
// These should also all be constexpr.
94+
constexpr TestKind Kind2 = TestKind::Beep;
95+
static_assert(Kind2 == TestKind::Beep);
96+
static_assert(Kind2 != TestKind::Boop);
97+
}
98+
99+
TEST(EnumMaskBaseTest, AddRemove) {
100+
TestKind kind = TestKind::Beep;
101+
EXPECT_EQ(kind, TestKind::Beep);
102+
kind.Add(TestKind::Beep);
103+
EXPECT_EQ(kind, TestKind::Beep);
104+
kind.Add(TestKind::Burr);
105+
EXPECT_EQ(kind, TestKind::Beep | TestKind::Burr);
106+
kind.Remove(TestKind::Beep);
107+
EXPECT_EQ(kind, TestKind::Burr);
108+
kind.Remove(TestKind::Beep);
109+
EXPECT_EQ(kind, TestKind::Burr);
110+
kind.Remove(TestKind::Burr);
111+
EXPECT_EQ(kind, TestKind::None);
112+
}
113+
114+
TEST(EnumMaskBaseTest, HasAnyOf) {
115+
static_assert(TestKind::Beep.HasAnyOf(TestKind::Beep));
116+
static_assert(TestKind::Beep.HasAnyOf(TestKind::Beep | TestKind::Burr));
117+
static_assert(!TestKind::Beep.HasAnyOf(TestKind::Burr));
118+
}
119+
120+
TEST(EnumMaskBaseTest, MaskOperations) {
121+
TestKind kind =
122+
TestKind::Beep | (TestKind::Burr & (TestKind::Burr | TestKind::Beep));
123+
EXPECT_EQ(kind, TestKind::Beep | TestKind::Burr);
124+
125+
// These should also all be constexpr.
126+
static_assert((TestKind::Beep & TestKind::Burr) == TestKind::None);
127+
static_assert((TestKind::Beep | TestKind::Burr) != TestKind::None);
128+
static_assert(TestKind::Beep == ~~TestKind::Beep);
129+
}
130+
131+
TEST(EnumMaskBaseTest, IntConversion) {
132+
EXPECT_EQ(1, TestKind::Beep.AsInt());
133+
EXPECT_EQ(2, TestKind::Boop.AsInt());
134+
EXPECT_EQ(4, TestKind::Burr.AsInt());
135+
136+
EXPECT_EQ(TestKind::Beep, TestKind::FromInt(1));
137+
EXPECT_EQ(TestKind::Boop, TestKind::FromInt(2));
138+
EXPECT_EQ(TestKind::Burr, TestKind::FromInt(4));
139+
}
140+
141+
} // namespace
142+
} // namespace Carbon

toolchain/base/llvm_tools.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
namespace Carbon {
1717

18-
CARBON_DEFINE_ENUM_CLASS_NAMES(LLVMTool) = {
18+
CARBON_DEFINE_ENUM_CLASS_NAMES(LLVMTool) {
1919
#define CARBON_LLVM_TOOL(Identifier, Name, BinName, MainFn) Name,
2020
#include "toolchain/base/llvm_tools.def"
2121
};

toolchain/check/generic.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,9 @@
2323

2424
namespace Carbon::Check {
2525

26-
CARBON_DEFINE_ENUM_MASK_NAMES(DependentInstKind) = {
27-
CARBON_DEPENDENT_INST_KIND(CARBON_ENUM_MASK_NAME_STRING)};
26+
CARBON_DEFINE_ENUM_MASK_NAMES(DependentInstKind) {
27+
CARBON_DEPENDENT_INST_KIND(CARBON_ENUM_MASK_NAME_STRING)
28+
};
2829

2930
static auto MakeSelfSpecificId(Context& context, SemIR::GenericId generic_id)
3031
-> SemIR::SpecificId;

toolchain/check/keyword_modifier_set.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66

77
namespace Carbon::Check {
88

9-
CARBON_DEFINE_ENUM_MASK_NAMES(KeywordModifierSet) = {
10-
CARBON_KEYWORD_MODIFIER_SET(CARBON_ENUM_MASK_NAME_STRING)};
9+
CARBON_DEFINE_ENUM_MASK_NAMES(KeywordModifierSet) {
10+
CARBON_KEYWORD_MODIFIER_SET(CARBON_ENUM_MASK_NAME_STRING)
11+
};
1112

1213
} // namespace Carbon::Check

toolchain/diagnostics/diagnostic_kind.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
namespace Carbon::Diagnostics {
88

9-
CARBON_DEFINE_ENUM_CLASS_NAMES(Kind) = {
9+
CARBON_DEFINE_ENUM_CLASS_NAMES(Kind) {
1010
#define CARBON_DIAGNOSTIC_KIND(Name) CARBON_ENUM_CLASS_NAME_STRING(Name)
1111
#include "toolchain/diagnostics/diagnostic_kind.def"
1212
};

0 commit comments

Comments
 (0)