[llvm] [CommandLine] Make options of copyable class types get reset to their provided initial values (PR #173026)
Benjamin Stott via llvm-commits
llvm-commits at lists.llvm.org
Mon Apr 27 06:58:11 PDT 2026
https://github.com/BStott6 updated https://github.com/llvm/llvm-project/pull/173026
>From f52513ee343230e8c70128e8f468fba021e2dc45 Mon Sep 17 00:00:00 2001
From: BStott <Benjamin.Stott at sony.com>
Date: Fri, 19 Dec 2025 14:31:49 +0000
Subject: [PATCH 1/6] Introduce unit test for resetting option of class type to
initial value
---
llvm/unittests/Support/CommandLineTest.cpp | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/llvm/unittests/Support/CommandLineTest.cpp b/llvm/unittests/Support/CommandLineTest.cpp
index 9e8a165fe136f..497978dd817e8 100644
--- a/llvm/unittests/Support/CommandLineTest.cpp
+++ b/llvm/unittests/Support/CommandLineTest.cpp
@@ -2348,4 +2348,15 @@ TEST(CommandLineTest, HelpWithEmptyCategory) {
cl::ResetCommandLineParser();
}
+class CopyableClass {
+public:
+ int Val;
+};
+TEST(CommandLineTest, ResetClassTypeOptionToInitialValue) {
+ CopyableClass InitialValue{42};
+ StackOption<CopyableClass> O("a", cl::init(InitialValue));
+ O.reset();
+ EXPECT_EQ(O.getValue().Val, InitialValue.Val)
+ << "Option should be reset to its initial value.";
+}
} // anonymous namespace
>From e03e366b4e78bdb1cc01bccc347cb5f86717c681 Mon Sep 17 00:00:00 2001
From: BStott <Benjamin.Stott at sony.com>
Date: Fri, 19 Dec 2025 15:24:05 +0000
Subject: [PATCH 2/6] Also test options of scalar types
---
llvm/unittests/Support/CommandLineTest.cpp | 23 ++++++++++++++++++----
1 file changed, 19 insertions(+), 4 deletions(-)
diff --git a/llvm/unittests/Support/CommandLineTest.cpp b/llvm/unittests/Support/CommandLineTest.cpp
index 497978dd817e8..9f8b633630e7c 100644
--- a/llvm/unittests/Support/CommandLineTest.cpp
+++ b/llvm/unittests/Support/CommandLineTest.cpp
@@ -2352,11 +2352,26 @@ class CopyableClass {
public:
int Val;
};
-TEST(CommandLineTest, ResetClassTypeOptionToInitialValue) {
+TEST(CommandLineTest, ResetOptionsToInitialValue) {
+ // Option of scalar type.
+ StackOption<int> O1("opt1", cl::init(12));
+ O1.reset();
+ EXPECT_EQ(O1.getValue(), 12)
+ << "Option should be reset to its initial value.";
+
+ // Option of copyable class type.
CopyableClass InitialValue{42};
- StackOption<CopyableClass> O("a", cl::init(InitialValue));
- O.reset();
- EXPECT_EQ(O.getValue().Val, InitialValue.Val)
+ StackOption<CopyableClass> O2("opt2", cl::init(InitialValue));
+ O2.reset();
+ EXPECT_EQ(O2.getValue().Val, InitialValue.Val)
+ << "Option should be reset to its initial value.";
+
+ // Option of string type (most important case of copyable class).
+ StackOption<std::string> O3("opt3", cl::init("hello"));
+ O3.reset();
+ EXPECT_EQ(O3.getValue(), "hello")
<< "Option should be reset to its initial value.";
+
+ cl::ResetCommandLineParser();
}
} // anonymous namespace
>From 56101faf765402794e5a127574ce5c907b304eb0 Mon Sep 17 00:00:00 2001
From: BStott <Benjamin.Stott at sony.com>
Date: Fri, 19 Dec 2025 15:42:09 +0000
Subject: [PATCH 3/6] [CommandLine] Make options of copyable class types get
reset to their provided initial values
---
llvm/include/llvm/Support/CommandLine.h | 61 ++++++++-----------------
llvm/lib/Support/CommandLine.cpp | 2 -
2 files changed, 20 insertions(+), 43 deletions(-)
diff --git a/llvm/include/llvm/Support/CommandLine.h b/llvm/include/llvm/Support/CommandLine.h
index be754f3c159ca..80f7a47973f37 100644
--- a/llvm/include/llvm/Support/CommandLine.h
+++ b/llvm/include/llvm/Support/CommandLine.h
@@ -547,7 +547,7 @@ template <class DataType> struct OptionValue;
// The default value safely does nothing. Option value printing is only
// best-effort.
-template <class DataType, bool isClass>
+template <class DataType, bool isCopyable>
struct OptionValueBase : GenericOptionValue {
// Temporary storage for argument passing.
using WrapperType = OptionValue<DataType>;
@@ -590,13 +590,24 @@ template <class DataType> class OptionValueCopy : public GenericOptionValue {
return Value;
}
- void setValue(const DataType &V) {
+ template <class DT, std::enable_if_t<std::is_assignable_v<DataType &, DT>,
+ std::nullptr_t> = nullptr>
+ void setValue(const DT &V) {
Valid = true;
Value = V;
}
// Returns whether this instance matches V.
- bool compare(const DataType &V) const { return Valid && (Value == V); }
+ bool compare(const DataType &V) const {
+ // FIXME: With C++23, use `std::equality_comparable` to see if `DataType`
+ // may be a class with `operator==` and, if so, use it instead of silently
+ // returning false.
+ if constexpr (std::is_class_v<DataType>) {
+ return false;
+ } else {
+ return Valid && (Value == V);
+ }
+ }
bool compare(const GenericOptionValue &V) const override {
const OptionValueCopy<DataType> &VC =
@@ -607,9 +618,9 @@ template <class DataType> class OptionValueCopy : public GenericOptionValue {
}
};
-// Non-class option values.
+// Copyable option values.
template <class DataType>
-struct OptionValueBase<DataType, false> : OptionValueCopy<DataType> {
+struct OptionValueBase<DataType, true> : OptionValueCopy<DataType> {
using WrapperType = DataType;
protected:
@@ -622,7 +633,10 @@ struct OptionValueBase<DataType, false> : OptionValueCopy<DataType> {
// Top-level option class.
template <class DataType>
struct OptionValue final
- : OptionValueBase<DataType, std::is_class_v<DataType>> {
+ : OptionValueBase<DataType, std::conjunction_v<
+ std::is_copy_constructible<DataType>,
+ std::is_copy_assignable<DataType>,
+ std::is_default_constructible<DataType>>> {
OptionValue() = default;
OptionValue(const DataType &V) { this->setValue(V); }
@@ -634,42 +648,7 @@ struct OptionValue final
}
};
-// Other safe-to-copy-by-value common option types.
enum boolOrDefault { BOU_UNSET, BOU_TRUE, BOU_FALSE };
-template <>
-struct LLVM_ABI OptionValue<cl::boolOrDefault> final
- : OptionValueCopy<cl::boolOrDefault> {
- using WrapperType = cl::boolOrDefault;
-
- OptionValue() = default;
-
- OptionValue(const cl::boolOrDefault &V) { this->setValue(V); }
-
- OptionValue<cl::boolOrDefault> &operator=(const cl::boolOrDefault &V) {
- setValue(V);
- return *this;
- }
-
-private:
- void anchor() override;
-};
-
-template <>
-struct LLVM_ABI OptionValue<std::string> final : OptionValueCopy<std::string> {
- using WrapperType = StringRef;
-
- OptionValue() = default;
-
- OptionValue(const std::string &V) { this->setValue(V); }
-
- OptionValue<std::string> &operator=(const std::string &V) {
- setValue(V);
- return *this;
- }
-
-private:
- void anchor() override;
-};
//===----------------------------------------------------------------------===//
// Enum valued command line option
diff --git a/llvm/lib/Support/CommandLine.cpp b/llvm/lib/Support/CommandLine.cpp
index 98d80c62466c0..ed78557f73df2 100644
--- a/llvm/lib/Support/CommandLine.cpp
+++ b/llvm/lib/Support/CommandLine.cpp
@@ -86,8 +86,6 @@ template class LLVM_EXPORT_TEMPLATE opt<unsigned>;
// Pin the vtables to this file.
void GenericOptionValue::anchor() {}
-void OptionValue<boolOrDefault>::anchor() {}
-void OptionValue<std::string>::anchor() {}
void Option::anchor() {}
void basic_parser_impl::anchor() {}
void parser<bool>::anchor() {}
>From dde9b1a8c58a056e3548cc6f546f300f7fa74134 Mon Sep 17 00:00:00 2001
From: BStott <Benjamin.Stott at sony.com>
Date: Mon, 22 Dec 2025 10:24:49 +0000
Subject: [PATCH 4/6] Apply suggestions, add test cases for comparing option
values
---
llvm/include/llvm/Support/CommandLine.h | 13 +++---
llvm/unittests/Support/CommandLineTest.cpp | 52 ++++++++++++++++++++++
2 files changed, 57 insertions(+), 8 deletions(-)
diff --git a/llvm/include/llvm/Support/CommandLine.h b/llvm/include/llvm/Support/CommandLine.h
index 80f7a47973f37..0b4979de4a57e 100644
--- a/llvm/include/llvm/Support/CommandLine.h
+++ b/llvm/include/llvm/Support/CommandLine.h
@@ -590,8 +590,8 @@ template <class DataType> class OptionValueCopy : public GenericOptionValue {
return Value;
}
- template <class DT, std::enable_if_t<std::is_assignable_v<DataType &, DT>,
- std::nullptr_t> = nullptr>
+ template <class DT,
+ class = std::enable_if_t<std::is_assignable_v<DataType &, DT>>>
void setValue(const DT &V) {
Valid = true;
Value = V;
@@ -599,13 +599,10 @@ template <class DataType> class OptionValueCopy : public GenericOptionValue {
// Returns whether this instance matches V.
bool compare(const DataType &V) const {
- // FIXME: With C++23, use `std::equality_comparable` to see if `DataType`
- // may be a class with `operator==` and, if so, use it instead of silently
- // returning false.
- if constexpr (std::is_class_v<DataType>) {
- return false;
- } else {
+ if constexpr (has_equality_comparison_v<DataType>) {
return Valid && (Value == V);
+ } else {
+ return false;
}
}
diff --git a/llvm/unittests/Support/CommandLineTest.cpp b/llvm/unittests/Support/CommandLineTest.cpp
index 9f8b633630e7c..bc65ad6145f52 100644
--- a/llvm/unittests/Support/CommandLineTest.cpp
+++ b/llvm/unittests/Support/CommandLineTest.cpp
@@ -2374,4 +2374,56 @@ TEST(CommandLineTest, ResetOptionsToInitialValue) {
cl::ResetCommandLineParser();
}
+
+class WithEqualityComparison {
+public:
+ bool operator==(const WithEqualityComparison &Other) const {
+ return Val == Other.Val;
+ }
+
+ int Val;
+};
+
+class NoEqualityComparison {
+public:
+ int Val;
+};
+
+TEST(CommandLineTest, CompareOptionValues) {
+ // Scalar option.
+ {
+ StackOption<int> O1("opt1", cl::init(42));
+ StackOption<int> O2("opt2", cl::init(42));
+ StackOption<int> O3("opt3", cl::init(3));
+ EXPECT_TRUE(O1.Default.compare(O2.Default));
+ EXPECT_FALSE(O1.Default.compare(O3.Default));
+ cl::ResetCommandLineParser();
+ }
+
+ // Class with equality comparison operator.
+ {
+ StackOption<WithEqualityComparison> O1(
+ "opt1", cl::init(WithEqualityComparison{42}));
+ StackOption<WithEqualityComparison> O2(
+ "opt2", cl::init(WithEqualityComparison{42}));
+ StackOption<WithEqualityComparison> O3("opt3",
+ cl::init(WithEqualityComparison{3}));
+ EXPECT_TRUE(O1.Default.compare(O2.Default));
+ EXPECT_FALSE(O1.Default.compare(O3.Default));
+ cl::ResetCommandLineParser();
+ }
+
+ // Class with no equality comparison operator.
+ {
+ StackOption<NoEqualityComparison> O1("opt1",
+ cl::init(NoEqualityComparison{42}));
+ StackOption<NoEqualityComparison> O2("opt2",
+ cl::init(NoEqualityComparison{42}));
+ StackOption<NoEqualityComparison> O3("opt3",
+ cl::init(NoEqualityComparison{3}));
+ EXPECT_FALSE(O1.Default.compare(O2.Default));
+ EXPECT_FALSE(O1.Default.compare(O3.Default));
+ cl::ResetCommandLineParser();
+ }
+}
} // anonymous namespace
>From 96154d222cda242269f682a74890f1baffed6815 Mon Sep 17 00:00:00 2001
From: BStott <Benjamin.Stott at sony.com>
Date: Fri, 16 Jan 2026 15:10:29 +0000
Subject: [PATCH 5/6] Update test to assign the option a different value before
resetting
---
llvm/unittests/Support/CommandLineTest.cpp | 3 +++
1 file changed, 3 insertions(+)
diff --git a/llvm/unittests/Support/CommandLineTest.cpp b/llvm/unittests/Support/CommandLineTest.cpp
index bc65ad6145f52..c779bce1c7386 100644
--- a/llvm/unittests/Support/CommandLineTest.cpp
+++ b/llvm/unittests/Support/CommandLineTest.cpp
@@ -2355,6 +2355,7 @@ class CopyableClass {
TEST(CommandLineTest, ResetOptionsToInitialValue) {
// Option of scalar type.
StackOption<int> O1("opt1", cl::init(12));
+ O1.setValue(13);
O1.reset();
EXPECT_EQ(O1.getValue(), 12)
<< "Option should be reset to its initial value.";
@@ -2362,12 +2363,14 @@ TEST(CommandLineTest, ResetOptionsToInitialValue) {
// Option of copyable class type.
CopyableClass InitialValue{42};
StackOption<CopyableClass> O2("opt2", cl::init(InitialValue));
+ O2.setValue(CopyableClass{404});
O2.reset();
EXPECT_EQ(O2.getValue().Val, InitialValue.Val)
<< "Option should be reset to its initial value.";
// Option of string type (most important case of copyable class).
StackOption<std::string> O3("opt3", cl::init("hello"));
+ O3.setValue("goodbye");
O3.reset();
EXPECT_EQ(O3.getValue(), "hello")
<< "Option should be reset to its initial value.";
>From e4ed79a3adc376edbe8e2130663b009413be3c65 Mon Sep 17 00:00:00 2001
From: BStott <Benjamin.Stott at sony.com>
Date: Fri, 16 Jan 2026 15:12:45 +0000
Subject: [PATCH 6/6] Update comment on OptionValueCopy::compare to clarify its
behaviour
---
llvm/include/llvm/Support/CommandLine.h | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/llvm/include/llvm/Support/CommandLine.h b/llvm/include/llvm/Support/CommandLine.h
index 0b4979de4a57e..16970aab8598c 100644
--- a/llvm/include/llvm/Support/CommandLine.h
+++ b/llvm/include/llvm/Support/CommandLine.h
@@ -597,7 +597,9 @@ template <class DataType> class OptionValueCopy : public GenericOptionValue {
Value = V;
}
- // Returns whether this instance matches V.
+ // If the option has a value assigned and the data type is equality-comparable
+ // with itself, returns the result of comparing `V` with the stored value.
+ // Otherwise, returns false.
bool compare(const DataType &V) const {
if constexpr (has_equality_comparison_v<DataType>) {
return Valid && (Value == V);
More information about the llvm-commits
mailing list