Skip to content

Commit fb65b17

Browse files
author
Erich Keane
committed
[NFCI] Refactor how KeywordStatus is calculated
The getKeywordStatus function is a horrible mess of inter-dependent 'if' statements that depend significantly on the ORDER of the checks. This patch removes the dependency on order by checking each set-flag only once. It does this by looping through each of the set bits, and checks each individual flag for its effect, then combines them at the end. This might slow down startup performance slightly, as there are only a few hundred keywords, and a vast majority will only get checked 1x still. This patch ALSO removes the KEYWORD_CONCEPTS flag, because it has since become synonymous with C++20. Differential Revision: https://reviews.llvm.org/D131007
1 parent 84831bd commit fb65b17

File tree

3 files changed

+132
-73
lines changed

3 files changed

+132
-73
lines changed

clang/include/clang/Basic/TokenKinds.def

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,7 @@
99
// This file defines the TokenKind database. This includes normal tokens like
1010
// tok::ampamp (corresponding to the && token) as well as keywords for various
1111
// languages. Users of this file must optionally #define the TOK, KEYWORD,
12-
// CXX11_KEYWORD, CONCEPTS_KEYWORD, ALIAS, or PPKEYWORD macros to make use of
13-
// this file.
12+
// CXX11_KEYWORD, ALIAS, or PPKEYWORD macros to make use of this file.
1413
//
1514
//===----------------------------------------------------------------------===//
1615

@@ -29,9 +28,6 @@
2928
#ifndef CXX20_KEYWORD
3029
#define CXX20_KEYWORD(X,Y) KEYWORD(X,KEYCXX20|(Y))
3130
#endif
32-
#ifndef CONCEPTS_KEYWORD
33-
#define CONCEPTS_KEYWORD(X) CXX20_KEYWORD(X,KEYCONCEPTS)
34-
#endif
3531
#ifndef COROUTINES_KEYWORD
3632
#define COROUTINES_KEYWORD(X) CXX20_KEYWORD(X,KEYCOROUTINES)
3733
#endif
@@ -259,8 +255,6 @@ PUNCTUATOR(caretcaret, "^^")
259255
// KEYNOCXX - This is a keyword in every non-C++ dialect.
260256
// KEYCXX11 - This is a C++ keyword introduced to C++ in C++11
261257
// KEYCXX20 - This is a C++ keyword introduced to C++ in C++20
262-
// KEYCONCEPTS - This is a keyword if the C++ extensions for concepts
263-
// are enabled.
264258
// KEYMODULES - This is a keyword if the C++ extensions for modules
265259
// are enabled.
266260
// KEYGNU - This is a keyword if GNU extensions are enabled
@@ -390,10 +384,6 @@ CXX11_KEYWORD(nullptr , 0)
390384
CXX11_KEYWORD(static_assert , KEYMSCOMPAT)
391385
CXX11_KEYWORD(thread_local , 0)
392386

393-
// C++20 keywords
394-
CONCEPTS_KEYWORD(concept)
395-
CONCEPTS_KEYWORD(requires)
396-
397387
// C++20 / coroutines TS keywords
398388
COROUTINES_KEYWORD(co_await)
399389
COROUTINES_KEYWORD(co_return)
@@ -406,6 +396,9 @@ MODULES_KEYWORD(import)
406396
// C++20 keywords.
407397
CXX20_KEYWORD(consteval , 0)
408398
CXX20_KEYWORD(constinit , 0)
399+
CXX20_KEYWORD(concept , 0)
400+
CXX20_KEYWORD(requires , 0)
401+
409402
// Not a CXX20_KEYWORD because it is disabled by -fno-char8_t.
410403
KEYWORD(char8_t , CHAR8SUPPORT)
411404

@@ -935,7 +928,6 @@ ANNOTATION(header_unit)
935928
#undef TYPE_TRAIT_2
936929
#undef TYPE_TRAIT_1
937930
#undef TYPE_TRAIT
938-
#undef CONCEPTS_KEYWORD
939931
#undef CXX20_KEYWORD
940932
#undef CXX11_KEYWORD
941933
#undef KEYWORD

clang/lib/Basic/IdentifierTable.cpp

Lines changed: 122 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ IdentifierTable::IdentifierTable(const LangOptions &LangOpts,
8282
// Constants for TokenKinds.def
8383
namespace {
8484

85-
enum {
85+
enum TokenKey : unsigned {
8686
KEYC99 = 0x1,
8787
KEYCXX = 0x2,
8888
KEYCXX11 = 0x4,
@@ -99,70 +99,146 @@ namespace {
9999
WCHARSUPPORT = 0x2000,
100100
HALFSUPPORT = 0x4000,
101101
CHAR8SUPPORT = 0x8000,
102-
KEYCONCEPTS = 0x10000,
103-
KEYOBJC = 0x20000,
104-
KEYZVECTOR = 0x40000,
105-
KEYCOROUTINES = 0x80000,
106-
KEYMODULES = 0x100000,
107-
KEYCXX20 = 0x200000,
108-
KEYOPENCLCXX = 0x400000,
109-
KEYMSCOMPAT = 0x800000,
110-
KEYSYCL = 0x1000000,
111-
KEYCUDA = 0x2000000,
102+
KEYOBJC = 0x10000,
103+
KEYZVECTOR = 0x20000,
104+
KEYCOROUTINES = 0x40000,
105+
KEYMODULES = 0x80000,
106+
KEYCXX20 = 0x100000,
107+
KEYOPENCLCXX = 0x200000,
108+
KEYMSCOMPAT = 0x400000,
109+
KEYSYCL = 0x800000,
110+
KEYCUDA = 0x1000000,
112111
KEYMAX = KEYCUDA, // The maximum key
113112
KEYALLCXX = KEYCXX | KEYCXX11 | KEYCXX20,
114113
KEYALL = (KEYMAX | (KEYMAX-1)) & ~KEYNOMS18 &
115114
~KEYNOOPENCL // KEYNOMS18 and KEYNOOPENCL are used to exclude.
116115
};
117116

118-
/// How a keyword is treated in the selected standard.
117+
/// How a keyword is treated in the selected standard. This enum is ordered
118+
/// intentionally so that the value that 'wins' is the most 'permissive'.
119119
enum KeywordStatus {
120+
KS_Unknown, // Not yet calculated. Used when figuring out the status.
120121
KS_Disabled, // Disabled
122+
KS_Future, // Is a keyword in future standard
121123
KS_Extension, // Is an extension
122124
KS_Enabled, // Enabled
123-
KS_Future // Is a keyword in future standard
124125
};
125126

126127
} // namespace
127128

129+
// This works on a single TokenKey flag and checks the LangOpts to get the
130+
// KeywordStatus based exclusively on this flag, so that it can be merged in
131+
// getKeywordStatus. Most should be enabled/disabled, but some might imply
132+
// 'future' versions, or extensions. Returns 'unknown' unless this is KNOWN to
133+
// be disabled, and the calling function makes it 'disabled' if no other flag
134+
// changes it. This is necessary for the KEYNOCXX and KEYNOOPENCL flags.
135+
static KeywordStatus getKeywordStatusHelper(const LangOptions &LangOpts,
136+
TokenKey Flag) {
137+
// Flag is a single bit version of TokenKey (that is, not
138+
// KEYALL/KEYALLCXX/etc), so we can check with == throughout this function.
139+
assert((Flag & ~(Flag - 1)) == Flag && "Multiple bits set?");
140+
141+
switch (Flag) {
142+
case KEYC99:
143+
// FIXME: This should have KS_Future logic here, but that can only happen if
144+
// getFutureCompatDiagKind ALSO gets updated. This is safe, since C mode is
145+
// ALWAYS implied.
146+
return LangOpts.C99 ? KS_Enabled : KS_Unknown;
147+
case KEYC11:
148+
// FIXME: This should have KS_Future logic here, but that can only happen if
149+
// getFutureCompatDiagKind ALSO gets updated. This is safe, since C mode is
150+
// ALWAYS implied.
151+
return LangOpts.C11 ? KS_Enabled : KS_Unknown;
152+
case KEYCXX:
153+
return LangOpts.CPlusPlus ? KS_Enabled : KS_Unknown;
154+
case KEYCXX11:
155+
if (LangOpts.CPlusPlus11)
156+
return KS_Enabled;
157+
return LangOpts.CPlusPlus ? KS_Future : KS_Unknown;
158+
case KEYCXX20:
159+
if (LangOpts.CPlusPlus20)
160+
return KS_Enabled;
161+
return LangOpts.CPlusPlus ? KS_Future : KS_Unknown;
162+
case KEYGNU:
163+
return LangOpts.GNUKeywords ? KS_Extension : KS_Unknown;
164+
case KEYMS:
165+
return LangOpts.MicrosoftExt ? KS_Extension : KS_Unknown;
166+
case BOOLSUPPORT:
167+
return LangOpts.Bool ? KS_Enabled : KS_Unknown;
168+
case KEYALTIVEC:
169+
return LangOpts.AltiVec ? KS_Enabled : KS_Unknown;
170+
case KEYBORLAND:
171+
return LangOpts.Borland ? KS_Extension : KS_Unknown;
172+
case KEYOPENCLC:
173+
return LangOpts.OpenCL && !LangOpts.OpenCLCPlusPlus ? KS_Enabled
174+
: KS_Unknown;
175+
case WCHARSUPPORT:
176+
return LangOpts.WChar ? KS_Enabled : KS_Unknown;
177+
case HALFSUPPORT:
178+
return LangOpts.Half ? KS_Enabled : KS_Unknown;
179+
case CHAR8SUPPORT:
180+
if (LangOpts.Char8) return KS_Enabled;
181+
if (LangOpts.CPlusPlus20) return KS_Unknown;
182+
return KS_Future;
183+
case KEYOBJC:
184+
// We treat bridge casts as objective-C keywords so we can warn on them
185+
// in non-arc mode.
186+
return LangOpts.ObjC ? KS_Enabled : KS_Unknown;
187+
case KEYZVECTOR:
188+
return LangOpts.ZVector ? KS_Enabled : KS_Unknown;
189+
case KEYCOROUTINES:
190+
return LangOpts.Coroutines ? KS_Enabled : KS_Unknown;
191+
case KEYMODULES:
192+
return LangOpts.ModulesTS ? KS_Enabled : KS_Unknown;
193+
case KEYOPENCLCXX:
194+
return LangOpts.OpenCLCPlusPlus ? KS_Enabled : KS_Unknown;
195+
case KEYMSCOMPAT:
196+
return LangOpts.MSVCCompat ? KS_Enabled : KS_Unknown;
197+
case KEYSYCL:
198+
return LangOpts.isSYCL() ? KS_Enabled : KS_Unknown;
199+
case KEYCUDA:
200+
return LangOpts.CUDA ? KS_Enabled : KS_Unknown;
201+
case KEYNOCXX:
202+
// This is enabled in all non-C++ modes, but might be enabled for other
203+
// reasons as well.
204+
return LangOpts.CPlusPlus ? KS_Unknown : KS_Enabled;
205+
case KEYNOOPENCL:
206+
// The disable behavior for this is handled in getKeywordStatus.
207+
return KS_Unknown;
208+
case KEYNOMS18:
209+
// The disable behavior for this is handled in getKeywordStatus.
210+
return KS_Unknown;
211+
default:
212+
llvm_unreachable("Unknown KeywordStatus flag");
213+
}
214+
}
215+
128216
/// Translates flags as specified in TokenKinds.def into keyword status
129217
/// in the given language standard.
130218
static KeywordStatus getKeywordStatus(const LangOptions &LangOpts,
131219
unsigned Flags) {
220+
// KEYALL means always enabled, so special case this one.
132221
if (Flags == KEYALL) return KS_Enabled;
133-
if (LangOpts.CPlusPlus && (Flags & KEYCXX)) return KS_Enabled;
134-
if (LangOpts.CPlusPlus11 && (Flags & KEYCXX11)) return KS_Enabled;
135-
if (LangOpts.CPlusPlus20 && (Flags & KEYCXX20)) return KS_Enabled;
136-
if (LangOpts.C99 && (Flags & KEYC99)) return KS_Enabled;
137-
if (LangOpts.GNUKeywords && (Flags & KEYGNU)) return KS_Extension;
138-
if (LangOpts.MicrosoftExt && (Flags & KEYMS)) return KS_Extension;
139-
if (LangOpts.MSVCCompat && (Flags & KEYMSCOMPAT)) return KS_Enabled;
140-
if (LangOpts.Borland && (Flags & KEYBORLAND)) return KS_Extension;
141-
if (LangOpts.Bool && (Flags & BOOLSUPPORT)) return KS_Enabled;
142-
if (LangOpts.Half && (Flags & HALFSUPPORT)) return KS_Enabled;
143-
if (LangOpts.WChar && (Flags & WCHARSUPPORT)) return KS_Enabled;
144-
if (LangOpts.Char8 && (Flags & CHAR8SUPPORT)) return KS_Enabled;
145-
if (LangOpts.AltiVec && (Flags & KEYALTIVEC)) return KS_Enabled;
146-
if (LangOpts.ZVector && (Flags & KEYZVECTOR)) return KS_Enabled;
147-
if (LangOpts.OpenCL && !LangOpts.OpenCLCPlusPlus && (Flags & KEYOPENCLC))
148-
return KS_Enabled;
149-
if (LangOpts.OpenCLCPlusPlus && (Flags & KEYOPENCLCXX)) return KS_Enabled;
150-
if (!LangOpts.CPlusPlus && (Flags & KEYNOCXX)) return KS_Enabled;
151-
if (LangOpts.C11 && (Flags & KEYC11)) return KS_Enabled;
152-
// We treat bridge casts as objective-C keywords so we can warn on them
153-
// in non-arc mode.
154-
if (LangOpts.ObjC && (Flags & KEYOBJC)) return KS_Enabled;
155-
if (LangOpts.CPlusPlus20 && (Flags & KEYCONCEPTS)) return KS_Enabled;
156-
if (LangOpts.Coroutines && (Flags & KEYCOROUTINES)) return KS_Enabled;
157-
if (LangOpts.ModulesTS && (Flags & KEYMODULES)) return KS_Enabled;
158-
if (LangOpts.CPlusPlus && (Flags & KEYALLCXX)) return KS_Future;
159-
if (LangOpts.CPlusPlus && !LangOpts.CPlusPlus20 && (Flags & CHAR8SUPPORT))
160-
return KS_Future;
161-
if (LangOpts.isSYCL() && (Flags & KEYSYCL))
162-
return KS_Enabled;
163-
if (LangOpts.CUDA && (Flags & KEYCUDA))
164-
return KS_Enabled;
165-
return KS_Disabled;
222+
// These are tests that need to 'always win', as they are special in that they
223+
// disable based on certain conditions.
224+
if (LangOpts.OpenCL && (Flags & KEYNOOPENCL)) return KS_Disabled;
225+
if (LangOpts.MSVCCompat && (Flags & KEYNOMS18) &&
226+
!LangOpts.isCompatibleWithMSVC(LangOptions::MSVC2015))
227+
return KS_Disabled;
228+
229+
KeywordStatus CurStatus = KS_Unknown;
230+
231+
while (Flags != 0) {
232+
unsigned CurFlag = Flags & ~(Flags - 1);
233+
Flags = Flags & ~CurFlag;
234+
CurStatus = std::max(
235+
CurStatus,
236+
getKeywordStatusHelper(LangOpts, static_cast<TokenKey>(CurFlag)));
237+
}
238+
239+
if (CurStatus == KS_Unknown)
240+
return KS_Disabled;
241+
return CurStatus;
166242
}
167243

168244
/// AddKeyword - This method is used to associate a token ID with specific
@@ -173,15 +249,6 @@ static void AddKeyword(StringRef Keyword,
173249
const LangOptions &LangOpts, IdentifierTable &Table) {
174250
KeywordStatus AddResult = getKeywordStatus(LangOpts, Flags);
175251

176-
// Don't add this keyword under MSVCCompat.
177-
if (LangOpts.MSVCCompat && (Flags & KEYNOMS18) &&
178-
!LangOpts.isCompatibleWithMSVC(LangOptions::MSVC2015))
179-
return;
180-
181-
// Don't add this keyword under OpenCL.
182-
if (LangOpts.OpenCL && (Flags & KEYNOOPENCL))
183-
return;
184-
185252
// Don't add this keyword if disabled in this language.
186253
if (AddResult == KS_Disabled) return;
187254

clang/test/Lexer/keywords_test.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// RUN: %clang_cc1 -std=c++03 -fsyntax-only %s
22
// RUN: %clang_cc1 -std=c++11 -DCXX11 -fsyntax-only %s
3-
// RUN: %clang_cc1 -std=c++2a -DCXX11 -DCXX2A -fsyntax-only %s
3+
// RUN: %clang_cc1 -std=c++20 -DCXX11 -DCXX20 -fsyntax-only %s
44
// RUN: %clang_cc1 -std=c++03 -fdeclspec -DDECLSPEC -fsyntax-only %s
55
// RUN: %clang_cc1 -std=c++03 -fms-extensions -DDECLSPEC -fsyntax-only %s
66
// RUN: %clang_cc1 -std=c++03 -fborland-extensions -DDECLSPEC -fsyntax-only %s
@@ -19,10 +19,10 @@
1919
#define NOT_KEYWORD(NAME) _Static_assert(__is_identifier(NAME), #NAME)
2020
#define IS_TYPE(NAME) void is_##NAME##_type() { int f(NAME); }
2121

22-
#if defined(CXX2A)
23-
#define CONCEPTS_KEYWORD(NAME) IS_KEYWORD(NAME)
22+
#if defined(CXX20)
23+
#define CXX20_KEYWORD(NAME) IS_KEYWORD(NAME)
2424
#else
25-
#define CONCEPTS_KEYWORD(NAME) NOT_KEYWORD(NAME)
25+
#define CXX20_KEYWORD(NAME) NOT_KEYWORD(NAME)
2626
#endif
2727

2828
#ifdef DECLSPEC
@@ -59,8 +59,8 @@ IS_KEYWORD(static_assert);
5959
CXX11_KEYWORD(thread_local);
6060

6161
// Concepts keywords
62-
CONCEPTS_KEYWORD(concept);
63-
CONCEPTS_KEYWORD(requires);
62+
CXX20_KEYWORD(concept);
63+
CXX20_KEYWORD(requires);
6464

6565
// __declspec extension
6666
DECLSPEC_KEYWORD(__declspec);

0 commit comments

Comments
 (0)