[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