[llvm] [orc-rt] Make Expected's move operations conditionally noexcept. (PR #226393)

Lang Hames via llvm-commits llvm-commits at lists.llvm.org
Fri Sep 25 01:11:42 PDT 2026


https://github.com/lhames created https://github.com/llvm/llvm-project/pull/226393

Expected<T>'s move constructor and move assignment operator are now noexcept whenever T's storage type is nothrow-move-constructible. Error's move operations are already noexcept, so this is the only condition under which moving an Expected can throw.

This allows callables that take Expected<T> by value to be stored in noexcept-qualified move_only_functions: is_nothrow_invocable includes the initialization of by-value parameters, which previously used Expected's potentially-throwing move constructor.

>From 1a077faf9a3c79dfebe0175626dcbb3493e90caa Mon Sep 17 00:00:00 2001
From: Lang Hames <lhames at gmail.com>
Date: Fri, 25 Sep 2026 17:50:57 +1000
Subject: [PATCH] [orc-rt] Make Expected's move operations conditionally
 noexcept.

Expected<T>'s move constructor and move assignment operator are now noexcept
whenever T's storage type is nothrow-move-constructible. Error's move
operations are already noexcept, so this is the only condition under which
moving an Expected can throw.

This allows callables that take Expected<T> by value to be stored in
noexcept-qualified move_only_functions: is_nothrow_invocable includes the
initialization of by-value parameters, which previously used Expected's
potentially-throwing move constructor.
---
 orc-rt/include/orc-rt/support/Error.h  |  8 ++++++--
 orc-rt/test/unit/support/ErrorTest.cpp | 22 ++++++++++++++++++++++
 2 files changed, 28 insertions(+), 2 deletions(-)

diff --git a/orc-rt/include/orc-rt/support/Error.h b/orc-rt/include/orc-rt/support/Error.h
index 3ff55b8cac74cf..b9cb1952c5ded9 100644
--- a/orc-rt/include/orc-rt/support/Error.h
+++ b/orc-rt/include/orc-rt/support/Error.h
@@ -403,7 +403,10 @@ template <typename T> class [[nodiscard]] Expected {
   }
 
   /// Move-construct an Expected<T> from an Expected<OtherT>.
-  Expected(Expected &&Other) { moveConstruct(std::move(Other)); }
+  Expected(Expected &&Other) noexcept(
+      std::is_nothrow_move_constructible_v<storage_type>) {
+    moveConstruct(std::move(Other));
+  }
 
   /// Move construct an Expected<T> value from an Expected<OtherT>, where OtherT
   /// must be convertible to T.
@@ -423,7 +426,8 @@ template <typename T> class [[nodiscard]] Expected {
   }
 
   /// Move-assign from another Expected<T>.
-  Expected &operator=(Expected &&Other) {
+  Expected &operator=(Expected &&Other) noexcept(
+      std::is_nothrow_move_constructible_v<storage_type>) {
     moveAssign(std::move(Other));
     return *this;
   }
diff --git a/orc-rt/test/unit/support/ErrorTest.cpp b/orc-rt/test/unit/support/ErrorTest.cpp
index 92b71dc67fd0ff..1f077592c3b969 100644
--- a/orc-rt/test/unit/support/ErrorTest.cpp
+++ b/orc-rt/test/unit/support/ErrorTest.cpp
@@ -484,6 +484,28 @@ TEST(ErrorTest, ExpectedExpected) {
   }
 }
 
+// Test that Expected<T>'s move operations are noexcept iff T's move
+// constructor is.
+namespace {
+struct ThrowingMove {
+  ThrowingMove(ThrowingMove &&) noexcept(false);
+  ThrowingMove &operator=(ThrowingMove &&) noexcept(false);
+};
+} // namespace
+
+static_assert(std::is_nothrow_move_constructible_v<Expected<int>>);
+static_assert(std::is_nothrow_move_assignable_v<Expected<int>>);
+static_assert(std::is_nothrow_move_constructible_v<Expected<int &>>);
+static_assert(std::is_nothrow_move_assignable_v<Expected<int &>>);
+static_assert(std::is_nothrow_move_constructible_v<Expected<Error>>);
+static_assert(std::is_nothrow_move_assignable_v<Expected<Error>>);
+static_assert(std::is_nothrow_move_constructible_v<Expected<Expected<int>>>);
+static_assert(std::is_nothrow_move_assignable_v<Expected<Expected<int>>>);
+static_assert(!std::is_nothrow_move_constructible_v<Expected<ThrowingMove>>);
+static_assert(!std::is_nothrow_move_assignable_v<Expected<ThrowingMove>>);
+static_assert(
+    !std::is_nothrow_move_constructible_v<Expected<Expected<ThrowingMove>>>);
+
 // Test that the ExitOnError utility works as expected.
 TEST(ErrorTest, CantFailSuccess) {
   cantFail(Error::success());



More information about the llvm-commits mailing list