[llvm] [Option] Add BoolOrDefault for OptionalBoolField and DefaultOnOffField (PR #228835)
via llvm-commits
llvm-commits at lists.llvm.org
Sat Oct 3 23:15:12 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-tablegen
Author: Fangrui Song (MaskRay)
<details>
<summary>Changes</summary>
The members of OptionalBoolField and DefaultOnOffField are
std::optional<bool>, on which `if (X)` tests whether the option is
given, not its value. Make them a new 1-byte `enum class BoolOrDefault :
uint8_t { Default, True, False }`, which does not convert to bool, and
read them with `valueOr(X, Default)`.
Aided by Opus 5.5
---
Full diff: https://github.com/llvm/llvm-project/pull/228835.diff
8 Files Affected:
- (modified) llvm/docs/CommandLine.md (+2-2)
- (added) llvm/include/llvm/ADT/BoolOrDefault.h (+29)
- (modified) llvm/include/llvm/Option/LibraryOptions.h (+9)
- (modified) llvm/include/llvm/Option/LibraryOptions.td (+11-9)
- (modified) llvm/lib/MC/MCAsmInfo.cpp (+4-4)
- (modified) llvm/lib/MC/MCAsmInfoXCOFF.cpp (+1-1)
- (modified) llvm/unittests/Option/LibraryOptionsTest.cpp (+15-8)
- (modified) llvm/utils/TableGen/Basic/OptionParserEmitter.cpp (+1)
``````````diff
diff --git a/llvm/docs/CommandLine.md b/llvm/docs/CommandLine.md
index a6d6429439453..bdb25833caf73 100644
--- a/llvm/docs/CommandLine.md
+++ b/llvm/docs/CommandLine.md
@@ -1586,9 +1586,9 @@ A member is named after its option, `enable_foo` for `-enable-foo`; a named `def
The `BoolField` is set by `-enable-foo` or `-enable-foo=true|false|1|0`.
A `ValueField`, of an integer type, `float`, `double`, or `StringRef`, is set by `-foo-threshold=8` or `-foo-threshold 8`.
-An `OptionalBoolField` is a `std::optional<bool>` that stays `std::nullopt` unless the option is given, replacing `cl::boolOrDefault`.
+An `OptionalBoolField` is a `BoolOrDefault` that stays `Default` unless the option is given, replacing `cl::boolOrDefault`; read it with `valueOr(X, Default)`.
An `EnumField` maps each of its comma-separated values to an enumerator, replacing `cl::values`: `defm : EnumField<"foo-mode", "FooMode", "FooMode::Fast", "fast,safe", ["FooMode::Fast", "FooMode::Safe"], "The mode">;` accepts `-foo-mode=fast` and `-foo-mode=safe`.
-A `DefaultOnOffField` is a `std::optional<bool>` set by `=Default`, `=Enable`, or `=Disable`.
+A `DefaultOnOffField` is a `BoolOrDefault` set by `=Default`, `=Enable`, or `=Disable`.
Both accept `--` for `-`.
Only `-help-hidden` lists the options, like `cl::Hidden`.
diff --git a/llvm/include/llvm/ADT/BoolOrDefault.h b/llvm/include/llvm/ADT/BoolOrDefault.h
new file mode 100644
index 0000000000000..4b5829cad1480
--- /dev/null
+++ b/llvm/include/llvm/ADT/BoolOrDefault.h
@@ -0,0 +1,29 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_ADT_BOOLORDEFAULT_H
+#define LLVM_ADT_BOOLORDEFAULT_H
+
+#include <cstdint>
+
+namespace llvm {
+
+/// A bool, or Default when the caller's default applies, e.g. for an option
+/// that is not given. Unlike std::optional<bool>, it does not convert to bool,
+/// so `if (X)` cannot be mistaken for a test of the value.
+enum class BoolOrDefault : uint8_t { Default, True, False };
+
+/// Returns \p Default if \p X is BoolOrDefault::Default, and otherwise the bool
+/// \p X holds.
+constexpr bool valueOr(BoolOrDefault X, bool Default) {
+ return X == BoolOrDefault::Default ? Default : X == BoolOrDefault::True;
+}
+
+} // namespace llvm
+
+#endif // LLVM_ADT_BOOLORDEFAULT_H
diff --git a/llvm/include/llvm/Option/LibraryOptions.h b/llvm/include/llvm/Option/LibraryOptions.h
index 06209d5d56a40..5164267b21611 100644
--- a/llvm/include/llvm/Option/LibraryOptions.h
+++ b/llvm/include/llvm/Option/LibraryOptions.h
@@ -15,6 +15,7 @@
#ifndef LLVM_OPTION_LIBRARYOPTIONS_H
#define LLVM_OPTION_LIBRARYOPTIONS_H
+#include "llvm/ADT/BoolOrDefault.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Option/Arg.h"
#include "llvm/Option/OptTable.h"
@@ -38,6 +39,14 @@ inline bool parseArgValue(StringRef S, bool &V) {
return true;
}
+inline bool parseArgValue(StringRef S, BoolOrDefault &V) {
+ bool B;
+ if (!parseArgValue(S, B))
+ return false;
+ V = B ? BoolOrDefault::True : BoolOrDefault::False;
+ return true;
+}
+
inline bool parseArgValue(StringRef S, StringRef &V) {
V = S;
return true;
diff --git a/llvm/include/llvm/Option/LibraryOptions.td b/llvm/include/llvm/Option/LibraryOptions.td
index 68a92e5687ca1..eafa89084b17a 100644
--- a/llvm/include/llvm/Option/LibraryOptions.td
+++ b/llvm/include/llvm/Option/LibraryOptions.td
@@ -38,14 +38,14 @@ multiclass BoolField<string name, code default, string help> {
def NAME#_EQ : Joined<["-", "--"], name#"=">, Field<NAME, "bool", default>;
}
-// A std::optional<bool> member, std::nullopt unless -<name> or
-// -<name>=<true|false|1|0> is given.
+// A BoolOrDefault member, Default unless -<name> or -<name>=<true|false|1|0>
+// is given.
multiclass OptionalBoolField<string name, string help> {
def NAME : Flag<["-", "--"], name>,
- Field<NAME, "std::optional<bool>", "std::nullopt">, FieldValue<"true">,
- HelpText<help>;
+ Field<NAME, "llvm::BoolOrDefault", "llvm::BoolOrDefault::Default">,
+ FieldValue<"llvm::BoolOrDefault::True">, HelpText<help>;
def NAME#_EQ : Joined<["-", "--"], name#"=">,
- Field<NAME, "std::optional<bool>", "std::nullopt">;
+ Field<NAME, "llvm::BoolOrDefault", "llvm::BoolOrDefault::Default">;
}
// A member of the enumeration <type> set by -<name>=<value> or -<name> <value>,
@@ -59,11 +59,13 @@ multiclass EnumField<string name, string type, code default, string values,
def NAME : Separate<["-", "--"], name>, Alias<!cast<Option>(NAME#"_EQ")>;
}
-// A std::optional<bool> member, std::nullopt unless -<name>=Enable or
-// -<name>=Disable is given.
+// A BoolOrDefault member, Default unless -<name>=Enable or -<name>=Disable is
+// given.
multiclass DefaultOnOffField<string name, string help> {
- defm NAME : EnumField<name, "std::optional<bool>", "std::nullopt",
- "Default,Enable,Disable", ["std::nullopt", "true", "false"], help>;
+ defm NAME : EnumField<name, "llvm::BoolOrDefault",
+ "llvm::BoolOrDefault::Default", "Default,Enable,Disable",
+ ["llvm::BoolOrDefault::Default", "llvm::BoolOrDefault::True",
+ "llvm::BoolOrDefault::False"], help>;
}
// A member of the C++ type <type> set by -<name>=<value> or -<name> <value>.
diff --git a/llvm/lib/MC/MCAsmInfo.cpp b/llvm/lib/MC/MCAsmInfo.cpp
index 3b9f96739eda1..24b593c380a3b 100644
--- a/llvm/lib/MC/MCAsmInfo.cpp
+++ b/llvm/lib/MC/MCAsmInfo.cpp
@@ -26,10 +26,10 @@ using namespace llvm;
MCAsmInfo::MCAsmInfo(const MCTargetOptions &Options) : TargetOptions(Options) {
const MCCLOptions &CLOpts = MCCLOptions::Global;
- if (CLOpts.dwarf_extended_loc)
- SupportsExtendedDwarfLocDirective = *CLOpts.dwarf_extended_loc;
- if (CLOpts.use_leb128_directives)
- HasLEB128Directives = *CLOpts.use_leb128_directives;
+ SupportsExtendedDwarfLocDirective =
+ valueOr(CLOpts.dwarf_extended_loc, SupportsExtendedDwarfLocDirective);
+ HasLEB128Directives =
+ valueOr(CLOpts.use_leb128_directives, HasLEB128Directives);
if (Options.BinutilsVersion.first > 0)
BinutilsVersion = Options.BinutilsVersion;
}
diff --git a/llvm/lib/MC/MCAsmInfoXCOFF.cpp b/llvm/lib/MC/MCAsmInfoXCOFF.cpp
index 8bf80212aa029..b1922fe104a2c 100644
--- a/llvm/lib/MC/MCAsmInfoXCOFF.cpp
+++ b/llvm/lib/MC/MCAsmInfoXCOFF.cpp
@@ -23,7 +23,7 @@ MCAsmInfoXCOFF::MCAsmInfoXCOFF(const MCTargetOptions &Options)
InternalSymbolPrefix = "L..";
SupportsQuotedNames = false;
- if (!MCCLOptions::Global.use_leb128_directives)
+ if (MCCLOptions::Global.use_leb128_directives == BoolOrDefault::Default)
HasLEB128Directives = false;
ZeroDirective = "\t.space\t";
AsciiDirective = nullptr; // not supported
diff --git a/llvm/unittests/Option/LibraryOptionsTest.cpp b/llvm/unittests/Option/LibraryOptionsTest.cpp
index 1ca9d3320d79c..4242c33082278 100644
--- a/llvm/unittests/Option/LibraryOptionsTest.cpp
+++ b/llvm/unittests/Option/LibraryOptionsTest.cpp
@@ -35,9 +35,9 @@ TEST(LibraryOptionsTest, Apply) {
EXPECT_EQ(O.count, 3u);
EXPECT_EQ(O.limit, std::nullopt);
EXPECT_EQ(O.mode, test::Mode::A);
- EXPECT_EQ(O.override, std::nullopt);
+ EXPECT_EQ(O.override, BoolOrDefault::Default);
EXPECT_EQ(O.ratio, 0.5);
- EXPECT_EQ(O.tristate, std::nullopt);
+ EXPECT_EQ(O.tristate, BoolOrDefault::Default);
EXPECT_EQ(O.Path, "p");
auto Apply = [&](std::initializer_list<const char *> Argv) {
@@ -57,9 +57,9 @@ TEST(LibraryOptionsTest, Apply) {
EXPECT_EQ(O.count, 7u);
EXPECT_EQ(O.limit, 0u);
EXPECT_EQ(O.mode, test::Mode::B);
- EXPECT_EQ(O.override, true);
+ EXPECT_EQ(O.override, BoolOrDefault::True);
EXPECT_EQ(O.ratio, 0.25);
- EXPECT_EQ(O.tristate, false);
+ EXPECT_EQ(O.tristate, BoolOrDefault::False);
EXPECT_EQ(O.Path, "a=b");
EXPECT_THAT(Apply({"-lib-enable=false"}), testing::Each(true));
EXPECT_FALSE(O.enable);
@@ -67,10 +67,10 @@ TEST(LibraryOptionsTest, Apply) {
Apply({"-lib-enable=1", "-lib-override=false", "-lib-tristate=Enable"}),
testing::Each(true));
EXPECT_TRUE(O.enable);
- EXPECT_EQ(O.override, false);
- EXPECT_EQ(O.tristate, true);
+ EXPECT_EQ(O.override, BoolOrDefault::False);
+ EXPECT_EQ(O.tristate, BoolOrDefault::True);
EXPECT_THAT(Apply({"-lib-tristate=Default"}), testing::Each(true));
- EXPECT_EQ(O.tristate, std::nullopt);
+ EXPECT_EQ(O.tristate, BoolOrDefault::Default);
// A rejected value leaves the member unchanged.
EXPECT_THAT(Apply({"-lib-enable=2", "-lib-count=-1", "-lib-limit=x",
@@ -80,10 +80,17 @@ TEST(LibraryOptionsTest, Apply) {
EXPECT_EQ(O.count, 7u);
EXPECT_EQ(O.limit, 0u);
EXPECT_EQ(O.mode, test::Mode::B);
- EXPECT_EQ(O.override, false);
+ EXPECT_EQ(O.override, BoolOrDefault::False);
EXPECT_EQ(O.ratio, 0.25);
}
+TEST(LibraryOptionsTest, BoolOrDefault) {
+ EXPECT_TRUE(valueOr(BoolOrDefault::Default, true));
+ EXPECT_FALSE(valueOr(BoolOrDefault::Default, false));
+ EXPECT_TRUE(valueOr(BoolOrDefault::True, false));
+ EXPECT_FALSE(valueOr(BoolOrDefault::False, true));
+}
+
// What cl:: sees of the struct, without cl::.
TEST(LibraryOptionsTest, Parser) {
opt::LibraryOptionsParser P(
diff --git a/llvm/utils/TableGen/Basic/OptionParserEmitter.cpp b/llvm/utils/TableGen/Basic/OptionParserEmitter.cpp
index b4487c47edacb..fd0edb21bcc43 100644
--- a/llvm/utils/TableGen/Basic/OptionParserEmitter.cpp
+++ b/llvm/utils/TableGen/Basic/OptionParserEmitter.cpp
@@ -315,6 +315,7 @@ static void emitOptionsStruct(const Record &Struct,
StringRef Name = Struct.getName();
OS << "\n#ifdef OPTIONS_STRUCT_DECL\n#undef OPTIONS_STRUCT_DECL\n";
+ OS << "#include \"llvm/ADT/BoolOrDefault.h\"\n";
OS << "#include \"llvm/ADT/StringRef.h\"\n\n";
StringRef Namespace = Struct.getValueAsString("Namespace");
OS << "namespace llvm {\nnamespace opt {\nclass Arg;\n"
``````````
</details>
https://github.com/llvm/llvm-project/pull/228835
More information about the llvm-commits
mailing list