[llvm] [libsycl] Add constructors for context (PR #214538)

via llvm-commits llvm-commits at lists.llvm.org
Tue Sep 8 15:55:39 PDT 2026


https://github.com/Robertkq updated https://github.com/llvm/llvm-project/pull/214538

>From 19ace57f105d0f9c0752f0a02a0a27481080bd65 Mon Sep 17 00:00:00 2001
From: Robertkq <robertvuia06 at gmail.com>
Date: Thu, 6 Aug 2026 20:17:40 +0300
Subject: [PATCH 1/6] Add public ctors for context

---
 libsycl/docs/index.md                   |  2 +-
 libsycl/include/sycl/__impl/context.hpp | 76 +++++++++++++++++++++++++
 libsycl/src/context.cpp                 | 55 ++++++++++++++++++
 libsycl/src/detail/context_impl.cpp     |  4 +-
 libsycl/src/detail/context_impl.hpp     |  1 +
 5 files changed, 134 insertions(+), 4 deletions(-)

diff --git a/libsycl/docs/index.md b/libsycl/docs/index.md
index 189120782366b..d75b912ef502f 100644
--- a/libsycl/docs/index.md
+++ b/libsycl/docs/index.md
@@ -102,7 +102,7 @@ which doesn't currently support Windows.
 
 - device selection: to add compatibility with old SYCL 1.2.1 device selectors, still part of SYCL 2020 specification
 
-- `context`: to implement get_info, properties & public constructors once context support is added to liboffload
+- `context`: to implement get_info, properties once context support is added to liboffload
 
 - `queue`:
 
diff --git a/libsycl/include/sycl/__impl/context.hpp b/libsycl/include/sycl/__impl/context.hpp
index 3acc91fad9647..9c7c72b85a06a 100644
--- a/libsycl/include/sycl/__impl/context.hpp
+++ b/libsycl/include/sycl/__impl/context.hpp
@@ -16,8 +16,10 @@
 #ifndef _LIBSYCL___IMPL_CONTEXT_HPP
 #define _LIBSYCL___IMPL_CONTEXT_HPP
 
+#include <sycl/__impl/async_handler.hpp>
 #include <sycl/__impl/backend.hpp>
 #include <sycl/__impl/info/desc_base.hpp>
+#include <sycl/__impl/property_list.hpp>
 
 #include <sycl/__impl/detail/config.hpp>
 #include <sycl/__impl/detail/obj_utils.hpp>
@@ -40,6 +42,80 @@ using is_context_info_desc_t = typename is_info_desc<T, context>::return_type;
 // SYCL 2020 4.6.3. Context class
 class _LIBSYCL_EXPORT context {
 public:
+  /// @brief Constructs a SYCL context instance using an instance of
+  /// default_selector.
+  ///
+  /// @param propList SYCL properties to be associated with the context.
+  explicit context(const property_list &propList = {});
+
+  /// @brief Constructs a SYCL context instance using an instance of
+  /// device_selector.
+  ///
+  /// @param asyncHandler Async handler to be used for asynchronous error
+  /// reporting.
+  /// @param propList SYCL properties to be associated with the context.
+  explicit context(async_handler asyncHandler,
+                   const property_list &propList = {});
+
+  /// @brief Constructs a SYCL context instance using the provided device.
+  /// The context will be associated with the platform of the provided device.
+  ///
+  /// @param dev is an instance of SYCL device
+  /// @param propList SYCL properties to be associated with the context.
+  explicit context(const device &dev, const property_list &propList = {});
+
+  /// @brief Constructs a SYCL context instance using the provided device.
+  /// The context will be associated with the platform of the provided device.
+  ///
+  /// @param dev is an instance of SYCL device
+  /// @param asyncHandler Async handler to be used for asynchronous error
+  /// reporting.
+  /// @param propList SYCL properties to be associated with the context.
+  explicit context(const device &dev, async_handler asyncHandler,
+                   const property_list &propList = {});
+
+  /// @brief Constructs a SYCL context instance using the provided platform.
+  /// The context will be associated with the provided platform and with each
+  /// SYCL device that is associated with the Platform.
+  ///
+  /// @param plt is an instance of SYCL platform
+  /// @param propList SYCL properties to be associated with the context.
+  explicit context(const platform &plt, const property_list &propList = {});
+
+  /// @brief Constructs a SYCL context instance using the provided platform.
+  /// The context will be associated with the provided platform and with each
+  /// SYCL device that is associated with the Platform.
+  ///
+  /// @param plt is an instance of SYCL platform
+  /// @param asyncHandler Async handler to be used for asynchronous error
+  /// reporting.
+  /// @param propList SYCL properties to be associated with the context.
+  explicit context(const platform &plt, async_handler asyncHandler,
+                   const property_list &propList = {});
+
+  /// @brief Constructs a SYCL context instance using the provided list of
+  /// devices. The context will be associated with each SYCL device in the
+  /// deviceList. This requires that all devices in the deviceList are
+  /// associated with the same platform.
+  ///
+  /// @param deviceList is a vector of SYCL devices
+  /// @param propList SYCL properties to be associated with the context.
+  explicit context(const std::vector<device> &deviceList,
+                   const property_list &propList = {});
+
+  /// @brief Constructs a SYCL context instance using the provided list of
+  /// devices. The context will be associated with each SYCL device in the
+  /// deviceList. This requires that all devices in the deviceList are
+  /// associated with the same platform.
+  ///
+  /// @param deviceList is a vector of SYCL devices
+  /// @param asyncHandler Async handler to be used for asynchronous error
+  /// reporting.
+  /// @param propList SYCL properties to be associated with the context.
+  explicit context(const std::vector<device> &deviceList,
+                   async_handler asyncHandler,
+                   const property_list &propList = {});
+
   context(const context &rhs) = default;
 
   context(context &&rhs) = default;
diff --git a/libsycl/src/context.cpp b/libsycl/src/context.cpp
index 3241d6fd47b67..b9c6d8a7b0da5 100644
--- a/libsycl/src/context.cpp
+++ b/libsycl/src/context.cpp
@@ -6,15 +6,70 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include <sycl/__impl/context.hpp>
 #include <sycl/__impl/device.hpp>
+#include <sycl/__impl/exception.hpp>
 
 #include <detail/context_impl.hpp>
 #include <detail/platform_impl.hpp>
 
+#include <algorithm>
 #include <cassert>
+#include <vector>
 
 _LIBSYCL_BEGIN_NAMESPACE_SYCL
 
+context::context(const property_list &propList) : context(device{}, propList) {}
+
+context::context(async_handler asyncHandler, const property_list &propList)
+    : context(device{}, asyncHandler, propList) {}
+
+context::context(const device &dev, const property_list &propList)
+    : context(std::vector<device>{dev}, propList) {}
+
+context::context(const device &dev, async_handler asyncHandler,
+                 const property_list &propList)
+    : context(std::vector<device>{dev}, asyncHandler, propList) {}
+
+context::context(const platform &plt, const property_list &propList)
+    : context(plt.get_devices(), propList) {}
+
+context::context(const platform &plt, async_handler asyncHandler,
+                 const property_list &propList)
+    : context(plt.get_devices(), asyncHandler, propList) {}
+
+context::context(const std::vector<device> &deviceList,
+                 const property_list &propList)
+    : context(deviceList, detail::defaultAsyncHandler, propList) {}
+
+context::context(const std::vector<device> &deviceList,
+                 async_handler asyncHandler, const property_list &propList)
+    : impl() {
+  if (deviceList.empty()) {
+    throw exception(make_error_code(errc::invalid),
+                    "Device list must not be empty");
+  }
+
+  const auto &platform = deviceList[0].get_platform();
+  if (std::any_of(deviceList.begin(), deviceList.end(),
+                  [&platform](const device &dev) {
+                    return dev.get_platform() != platform;
+                  })) {
+    throw exception(make_error_code(errc::invalid),
+                    "All devices must be associated with the same platform");
+  }
+
+  std::vector<detail::DeviceImpl *> deviceImpls;
+  deviceImpls.reserve(deviceList.size());
+  for (const device &dev : deviceList) {
+    deviceImpls.push_back(detail::ImplUtils::getSyclObjImpl(dev));
+  }
+
+  impl = detail::ContextImpl::create(std::move(deviceImpls), asyncHandler,
+                                     propList);
+  assert(impl && "ContextImpl creation failed");
+}
+
 backend context::get_backend() const noexcept { return impl->getBackend(); }
 
 platform context::get_platform() const {
diff --git a/libsycl/src/detail/context_impl.cpp b/libsycl/src/detail/context_impl.cpp
index 66a63131b49bb..9f05250cc283c 100644
--- a/libsycl/src/detail/context_impl.cpp
+++ b/libsycl/src/detail/context_impl.cpp
@@ -16,9 +16,7 @@ namespace detail {
 ContextImpl::ContextImpl(std::vector<DeviceImpl *> &&DeviceList,
                          const async_handler &AsyncHandler,
                          const property_list &PropList, Private)
-    : MAsyncHandler(AsyncHandler), MDevices(DeviceList) {
-  (void)PropList;
-
+    : MAsyncHandler(AsyncHandler), MDevices(DeviceList), MPropList(PropList) {
   assert(!MDevices.empty() && "Device list must not be empty");
 
   std::vector<ol_device_handle_t> DeviceIds;
diff --git a/libsycl/src/detail/context_impl.hpp b/libsycl/src/detail/context_impl.hpp
index ec9c8d10aca7e..99caed18e42d7 100644
--- a/libsycl/src/detail/context_impl.hpp
+++ b/libsycl/src/detail/context_impl.hpp
@@ -86,6 +86,7 @@ class ContextImpl : public std::enable_shared_from_this<ContextImpl> {
   const async_handler MAsyncHandler;
   const std::vector<DeviceImpl *> MDevices;
   ol_context_handle_t MOffloadContext{};
+  property_list MPropList;
 };
 
 } // namespace detail

>From 04440d0c24a20962d853bf635ffdb1caed6d76ab Mon Sep 17 00:00:00 2001
From: Robertkq <robertvuia06 at gmail.com>
Date: Sun, 16 Aug 2026 15:17:43 +0300
Subject: [PATCH 2/6] Add E2E & unit tests for context ctors

---
 libsycl/src/detail/context_impl.cpp         |  3 +-
 libsycl/test/basic/context.cpp              | 62 ++++++++++++++++++
 libsycl/unittests/CMakeLists.txt            |  1 +
 libsycl/unittests/context/CMakeLists.txt    |  3 +
 libsycl/unittests/context/context_ctors.cpp | 71 +++++++++++++++++++++
 5 files changed, 139 insertions(+), 1 deletion(-)
 create mode 100644 libsycl/test/basic/context.cpp
 create mode 100644 libsycl/unittests/context/CMakeLists.txt
 create mode 100644 libsycl/unittests/context/context_ctors.cpp

diff --git a/libsycl/src/detail/context_impl.cpp b/libsycl/src/detail/context_impl.cpp
index 9f05250cc283c..b0618d2a5ab0e 100644
--- a/libsycl/src/detail/context_impl.cpp
+++ b/libsycl/src/detail/context_impl.cpp
@@ -16,7 +16,8 @@ namespace detail {
 ContextImpl::ContextImpl(std::vector<DeviceImpl *> &&DeviceList,
                          const async_handler &AsyncHandler,
                          const property_list &PropList, Private)
-    : MAsyncHandler(AsyncHandler), MDevices(DeviceList), MPropList(PropList) {
+    : MAsyncHandler(AsyncHandler), MDevices(std::move(DeviceList)),
+      MPropList(PropList) {
   assert(!MDevices.empty() && "Device list must not be empty");
 
   std::vector<ol_device_handle_t> DeviceIds;
diff --git a/libsycl/test/basic/context.cpp b/libsycl/test/basic/context.cpp
new file mode 100644
index 0000000000000..cfb3ac471542e
--- /dev/null
+++ b/libsycl/test/basic/context.cpp
@@ -0,0 +1,62 @@
+// REQUIRES: any-device
+// RUN: %clangxx -fsycl %s -o %t.out
+// RUN: %t.out
+
+#include <iostream>
+
+#include <sycl/sycl.hpp>
+
+using namespace sycl;
+
+void return_fail() {
+  std::cout << "Failed" << std::endl;
+  exit(1);
+}
+
+void dummyAsyncHandler(sycl::exception_list) {}
+
+void check(const context &ctx) {
+  auto devices = ctx.get_devices();
+  if (devices.empty()) {
+    std::cout << "Context has no devices" << std::endl;
+    return_fail();
+  }
+
+  auto plt = ctx.get_platform();
+  for (const auto &dev : devices) {
+    if (dev.get_platform() != plt) {
+      std::cout << "Device platform does not match context platform"
+                << std::endl;
+      return_fail();
+    }
+  }
+  auto backend = ctx.get_backend();
+  for (const auto &dev : devices) {
+    if (dev.get_backend() != backend) {
+      std::cout << "Device backend does not match context backend" << std::endl;
+      return_fail();
+    }
+  }
+}
+
+int main() {
+  context ctx;
+  check(ctx);
+
+  device dev;
+  context ctx2(dev);
+  check(ctx2);
+
+  device dev2;
+
+  platform plt = dev.get_platform();
+  context ctx3(plt);
+  check(ctx3);
+
+  context ctx4({dev, dev2}, dummyAsyncHandler,
+               {/* explicit properties list */});
+  check(ctx4);
+
+  std::cout << "Passed" << std::endl;
+  return 0;
+}
diff --git a/libsycl/unittests/CMakeLists.txt b/libsycl/unittests/CMakeLists.txt
index 349818cedb597..02e89b714abdb 100644
--- a/libsycl/unittests/CMakeLists.txt
+++ b/libsycl/unittests/CMakeLists.txt
@@ -13,6 +13,7 @@ add_subdirectory(event)
 add_subdirectory(platform)
 add_subdirectory(program_manager)
 add_subdirectory(queue)
+add_subdirectory(context)
 
 # The unit test executables are GoogleTest binaries that are discovered and run
 # by lit, mirroring how liboffload runs its unit tests.
diff --git a/libsycl/unittests/context/CMakeLists.txt b/libsycl/unittests/context/CMakeLists.txt
new file mode 100644
index 0000000000000..9e599e3bcd589
--- /dev/null
+++ b/libsycl/unittests/context/CMakeLists.txt
@@ -0,0 +1,3 @@
+add_sycl_unittest(ContextTests
+    context_ctors.cpp
+)
diff --git a/libsycl/unittests/context/context_ctors.cpp b/libsycl/unittests/context/context_ctors.cpp
new file mode 100644
index 0000000000000..8cb97efcb9124
--- /dev/null
+++ b/libsycl/unittests/context/context_ctors.cpp
@@ -0,0 +1,71 @@
+#include <mock/helpers.hpp>
+
+#include <sycl/__impl/context.hpp>
+#include <sycl/__impl/device.hpp>
+#include <sycl/__impl/exception.hpp>
+#include <sycl/__impl/platform.hpp>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <utility>
+
+using namespace sycl;
+using namespace ::testing;
+
+void dummyAsyncHandler(exception_list) {}
+
+TEST(Context, DefaultConstructor) {
+  mock::MockWrapper Mock;
+
+  std::ignore = device{};
+
+  EXPECT_CALL(Mock.get(), olCreateContext(_, _, _)).Times(1);
+  EXPECT_CALL(Mock.get(), olDestroyContext(_)).Times(1);
+
+  context Ctx;
+}
+
+TEST(Context, DeviceConstructor) {
+  mock::MockWrapper Mock;
+  device Dev;
+
+  EXPECT_CALL(Mock.get(), olCreateContext(_, _, _)).Times(1);
+  EXPECT_CALL(Mock.get(), olDestroyContext(_)).Times(1);
+
+  context Ctx(Dev);
+}
+
+TEST(Context, DeviceConstructorWithAsyncHandler) {
+  mock::MockWrapper Mock;
+  device Dev;
+  async_handler AsyncHandler = dummyAsyncHandler;
+
+  EXPECT_CALL(Mock.get(), olCreateContext(_, _, _)).Times(1);
+  EXPECT_CALL(Mock.get(), olDestroyContext(_)).Times(1);
+
+  context Ctx(Dev, AsyncHandler);
+}
+
+TEST(Context, PlatformConstructor) {
+  mock::MockWrapper Mock;
+  device Dev;
+  platform Plt = Dev.get_platform();
+
+  EXPECT_CALL(Mock.get(), olCreateContext(_, _, _)).Times(1);
+  EXPECT_CALL(Mock.get(), olDestroyContext(_)).Times(1);
+
+  context Ctx(Plt);
+}
+
+TEST(Context, DeviceListConstructor) {
+  mock::MockWrapper Mock;
+  device Dev1;
+  device Dev2;
+  async_handler AsyncHandler = dummyAsyncHandler;
+
+  EXPECT_CALL(Mock.get(), olCreateContext(_, _, _)).Times(1);
+  EXPECT_CALL(Mock.get(), olDestroyContext(_)).Times(1);
+
+  context Ctx({Dev1, Dev2}, AsyncHandler);
+}
\ No newline at end of file

>From 623500902d914af86c682b070d60ab5beff1024a Mon Sep 17 00:00:00 2001
From: Robertkq <robertvuia06 at gmail.com>
Date: Sun, 16 Aug 2026 15:21:24 +0300
Subject: [PATCH 3/6] add missing eof in unittests

---
 libsycl/unittests/context/context_ctors.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libsycl/unittests/context/context_ctors.cpp b/libsycl/unittests/context/context_ctors.cpp
index 8cb97efcb9124..19f6a515733eb 100644
--- a/libsycl/unittests/context/context_ctors.cpp
+++ b/libsycl/unittests/context/context_ctors.cpp
@@ -68,4 +68,4 @@ TEST(Context, DeviceListConstructor) {
   EXPECT_CALL(Mock.get(), olDestroyContext(_)).Times(1);
 
   context Ctx({Dev1, Dev2}, AsyncHandler);
-}
\ No newline at end of file
+}

>From 56ec0c5cc39d1b20dab1d4b83184d15114037f4f Mon Sep 17 00:00:00 2001
From: Robertkq <robertvuia06 at gmail.com>
Date: Wed, 2 Sep 2026 20:54:02 +0300
Subject: [PATCH 4/6] Better documentation, cleanup & addressing reviews

---
 libsycl/include/sycl/__impl/context.hpp     | 100 ++++++++++++--------
 libsycl/include/sycl/__impl/platform.hpp    |   2 +-
 libsycl/src/context.cpp                     |  33 +------
 libsycl/src/detail/context_impl.cpp         |   5 +-
 libsycl/src/detail/context_impl.hpp         |   1 -
 libsycl/src/platform.cpp                    |   1 +
 libsycl/test/basic/context.cpp              |   4 -
 libsycl/unittests/context/context_ctors.cpp |  15 +++
 8 files changed, 81 insertions(+), 80 deletions(-)

diff --git a/libsycl/include/sycl/__impl/context.hpp b/libsycl/include/sycl/__impl/context.hpp
index 9c7c72b85a06a..5c4a463197d95 100644
--- a/libsycl/include/sycl/__impl/context.hpp
+++ b/libsycl/include/sycl/__impl/context.hpp
@@ -18,7 +18,10 @@
 
 #include <sycl/__impl/async_handler.hpp>
 #include <sycl/__impl/backend.hpp>
+#include <sycl/__impl/device.hpp>
+#include <sycl/__impl/exception.hpp>
 #include <sycl/__impl/info/desc_base.hpp>
+#include <sycl/__impl/platform.hpp>
 #include <sycl/__impl/property_list.hpp>
 
 #include <sycl/__impl/detail/config.hpp>
@@ -30,8 +33,6 @@
 _LIBSYCL_BEGIN_NAMESPACE_SYCL
 
 class context;
-class device;
-class platform;
 
 namespace detail {
 class ContextImpl;
@@ -42,76 +43,91 @@ using is_context_info_desc_t = typename is_info_desc<T, context>::return_type;
 // SYCL 2020 4.6.3. Context class
 class _LIBSYCL_EXPORT context {
 public:
-  /// @brief Constructs a SYCL context instance using an instance of
-  /// default_selector.
+  /// Constructs a SYCL context instance using the device selected by
+  /// default_selector_v.
   ///
-  /// @param propList SYCL properties to be associated with the context.
-  explicit context(const property_list &propList = {});
+  /// \param propList SYCL properties to be associated with the context.
+  explicit context(const property_list &propList = {})
+      : context(device{}, propList) {}
 
-  /// @brief Constructs a SYCL context instance using an instance of
-  /// device_selector.
+  /// Constructs a SYCL context instance using the device selected by
+  /// default_selector_v.
   ///
-  /// @param asyncHandler Async handler to be used for asynchronous error
+  /// \param asyncHandler Async handler to be used for asynchronous error
   /// reporting.
-  /// @param propList SYCL properties to be associated with the context.
+  /// \param propList SYCL properties to be associated with the context.
   explicit context(async_handler asyncHandler,
-                   const property_list &propList = {});
+                   const property_list &propList = {})
+      : context(device{}, asyncHandler, propList) {}
 
-  /// @brief Constructs a SYCL context instance using the provided device.
+  /// Constructs a SYCL context instance using the provided device.
   /// The context will be associated with the platform of the provided device.
   ///
-  /// @param dev is an instance of SYCL device
-  /// @param propList SYCL properties to be associated with the context.
-  explicit context(const device &dev, const property_list &propList = {});
+  /// \param dev is an instance of SYCL device
+  /// \param propList SYCL properties to be associated with the context.
+  explicit context(const device &dev, const property_list &propList = {})
+      : context(std::vector<device>{dev}, propList) {}
 
-  /// @brief Constructs a SYCL context instance using the provided device.
+  /// Constructs a SYCL context instance using the provided device.
   /// The context will be associated with the platform of the provided device.
   ///
-  /// @param dev is an instance of SYCL device
-  /// @param asyncHandler Async handler to be used for asynchronous error
+  /// \param dev is an instance of SYCL device
+  /// \param asyncHandler Async handler to be used for asynchronous error
   /// reporting.
-  /// @param propList SYCL properties to be associated with the context.
+  /// \param propList SYCL properties to be associated with the context.
   explicit context(const device &dev, async_handler asyncHandler,
-                   const property_list &propList = {});
+                   const property_list &propList = {})
+      : context(std::vector<device>{dev}, asyncHandler, propList) {}
 
-  /// @brief Constructs a SYCL context instance using the provided platform.
-  /// The context will be associated with the provided platform and with each
-  /// SYCL device that is associated with the Platform.
+  /// Constructs a SYCL context instance that contains all of the devices
+  /// in the platform plt.
+  ///
+  /// \param plt is an instance of SYCL platform
+  /// \param propList SYCL properties to be associated with the context.
   ///
-  /// @param plt is an instance of SYCL platform
-  /// @param propList SYCL properties to be associated with the context.
-  explicit context(const platform &plt, const property_list &propList = {});
+  /// \throws an exception with code errc::invalid if the platform plt has no
+  /// devices.
+  explicit context(const platform &plt, const property_list &propList = {})
+      : context(plt.get_devices(), propList) {}
 
-  /// @brief Constructs a SYCL context instance using the provided platform.
-  /// The context will be associated with the provided platform and with each
-  /// SYCL device that is associated with the Platform.
+  /// Constructs a SYCL context instance that contains all of the devices
+  /// in the platform plt.
   ///
-  /// @param plt is an instance of SYCL platform
-  /// @param asyncHandler Async handler to be used for asynchronous error
+  /// \param plt is an instance of SYCL platform
+  /// \param asyncHandler Async handler to be used for asynchronous error
   /// reporting.
-  /// @param propList SYCL properties to be associated with the context.
+  /// \param propList SYCL properties to be associated with the context.
+  ///
+  /// \throws an exception with code errc::invalid if the platform plt has no
+  /// devices.
   explicit context(const platform &plt, async_handler asyncHandler,
-                   const property_list &propList = {});
+                   const property_list &propList = {})
+      : context(plt.get_devices(), asyncHandler, propList) {}
 
-  /// @brief Constructs a SYCL context instance using the provided list of
+  /// Constructs a SYCL context instance using the provided list of
   /// devices. The context will be associated with each SYCL device in the
   /// deviceList. This requires that all devices in the deviceList are
   /// associated with the same platform.
   ///
-  /// @param deviceList is a vector of SYCL devices
-  /// @param propList SYCL properties to be associated with the context.
+  /// \param deviceList is a vector of SYCL devices
+  /// \param propList SYCL properties to be associated with the context.
+  ///
+  /// \throws an exception with code errc::invalid if the deviceList is empty.
   explicit context(const std::vector<device> &deviceList,
-                   const property_list &propList = {});
+                   const property_list &propList = {})
+      : context(deviceList, detail::defaultAsyncHandler, propList) {}
 
-  /// @brief Constructs a SYCL context instance using the provided list of
+  /// Constructs a SYCL context instance using the provided list of
   /// devices. The context will be associated with each SYCL device in the
   /// deviceList. This requires that all devices in the deviceList are
   /// associated with the same platform.
   ///
-  /// @param deviceList is a vector of SYCL devices
-  /// @param asyncHandler Async handler to be used for asynchronous error
+  /// \param deviceList is a vector of SYCL devices
+  /// \param asyncHandler Async handler to be used for asynchronous error
   /// reporting.
-  /// @param propList SYCL properties to be associated with the context.
+  /// \param propList SYCL properties to be associated with the context.
+  ///
+  /// \throws an exception with code errc::invalid if the deviceList is empty.
   explicit context(const std::vector<device> &deviceList,
                    async_handler asyncHandler,
                    const property_list &propList = {});
@@ -124,6 +140,8 @@ class _LIBSYCL_EXPORT context {
 
   context &operator=(context &&rhs) = default;
 
+  ~context() = default;
+
   friend bool operator==(const context &lhs, const context &rhs) {
     return lhs.impl == rhs.impl;
   }
diff --git a/libsycl/include/sycl/__impl/platform.hpp b/libsycl/include/sycl/__impl/platform.hpp
index 991380025c236..8512e53a11385 100644
--- a/libsycl/include/sycl/__impl/platform.hpp
+++ b/libsycl/include/sycl/__impl/platform.hpp
@@ -17,7 +17,6 @@
 
 #include <sycl/__impl/aspect.hpp>
 #include <sycl/__impl/backend.hpp>
-#include <sycl/__impl/context.hpp>
 #include <sycl/__impl/detail/config.hpp>
 #include <sycl/__impl/detail/obj_utils.hpp>
 #include <sycl/__impl/info/device_type.hpp>
@@ -30,6 +29,7 @@
 
 _LIBSYCL_BEGIN_NAMESPACE_SYCL
 
+class context;
 class device;
 
 namespace detail {
diff --git a/libsycl/src/context.cpp b/libsycl/src/context.cpp
index b9c6d8a7b0da5..c8bf75c9140e6 100644
--- a/libsycl/src/context.cpp
+++ b/libsycl/src/context.cpp
@@ -19,32 +19,8 @@
 
 _LIBSYCL_BEGIN_NAMESPACE_SYCL
 
-context::context(const property_list &propList) : context(device{}, propList) {}
-
-context::context(async_handler asyncHandler, const property_list &propList)
-    : context(device{}, asyncHandler, propList) {}
-
-context::context(const device &dev, const property_list &propList)
-    : context(std::vector<device>{dev}, propList) {}
-
-context::context(const device &dev, async_handler asyncHandler,
-                 const property_list &propList)
-    : context(std::vector<device>{dev}, asyncHandler, propList) {}
-
-context::context(const platform &plt, const property_list &propList)
-    : context(plt.get_devices(), propList) {}
-
-context::context(const platform &plt, async_handler asyncHandler,
-                 const property_list &propList)
-    : context(plt.get_devices(), asyncHandler, propList) {}
-
-context::context(const std::vector<device> &deviceList,
-                 const property_list &propList)
-    : context(deviceList, detail::defaultAsyncHandler, propList) {}
-
 context::context(const std::vector<device> &deviceList,
-                 async_handler asyncHandler, const property_list &propList)
-    : impl() {
+                 async_handler asyncHandler, const property_list &propList) {
   if (deviceList.empty()) {
     throw exception(make_error_code(errc::invalid),
                     "Device list must not be empty");
@@ -59,15 +35,10 @@ context::context(const std::vector<device> &deviceList,
                     "All devices must be associated with the same platform");
   }
 
-  std::vector<detail::DeviceImpl *> deviceImpls;
-  deviceImpls.reserve(deviceList.size());
-  for (const device &dev : deviceList) {
-    deviceImpls.push_back(detail::ImplUtils::getSyclObjImpl(dev));
-  }
+  auto deviceImpls = detail::getSyclObjImpls(deviceList);
 
   impl = detail::ContextImpl::create(std::move(deviceImpls), asyncHandler,
                                      propList);
-  assert(impl && "ContextImpl creation failed");
 }
 
 backend context::get_backend() const noexcept { return impl->getBackend(); }
diff --git a/libsycl/src/detail/context_impl.cpp b/libsycl/src/detail/context_impl.cpp
index b0618d2a5ab0e..a5cb0680cbed6 100644
--- a/libsycl/src/detail/context_impl.cpp
+++ b/libsycl/src/detail/context_impl.cpp
@@ -16,8 +16,9 @@ namespace detail {
 ContextImpl::ContextImpl(std::vector<DeviceImpl *> &&DeviceList,
                          const async_handler &AsyncHandler,
                          const property_list &PropList, Private)
-    : MAsyncHandler(AsyncHandler), MDevices(std::move(DeviceList)),
-      MPropList(PropList) {
+    : MAsyncHandler(AsyncHandler), MDevices(std::move(DeviceList)) {
+  // TODO: Remove this when property_list is implemented
+  std::ignore = PropList;
   assert(!MDevices.empty() && "Device list must not be empty");
 
   std::vector<ol_device_handle_t> DeviceIds;
diff --git a/libsycl/src/detail/context_impl.hpp b/libsycl/src/detail/context_impl.hpp
index 99caed18e42d7..ec9c8d10aca7e 100644
--- a/libsycl/src/detail/context_impl.hpp
+++ b/libsycl/src/detail/context_impl.hpp
@@ -86,7 +86,6 @@ class ContextImpl : public std::enable_shared_from_this<ContextImpl> {
   const async_handler MAsyncHandler;
   const std::vector<DeviceImpl *> MDevices;
   ol_context_handle_t MOffloadContext{};
-  property_list MPropList;
 };
 
 } // namespace detail
diff --git a/libsycl/src/platform.cpp b/libsycl/src/platform.cpp
index b5bddc82e0d9a..9464257f25ba5 100644
--- a/libsycl/src/platform.cpp
+++ b/libsycl/src/platform.cpp
@@ -6,6 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include <sycl/__impl/context.hpp>
 #include <sycl/__impl/device.hpp>
 #include <sycl/__impl/platform.hpp>
 
diff --git a/libsycl/test/basic/context.cpp b/libsycl/test/basic/context.cpp
index cfb3ac471542e..10f8842c47289 100644
--- a/libsycl/test/basic/context.cpp
+++ b/libsycl/test/basic/context.cpp
@@ -17,10 +17,6 @@ void dummyAsyncHandler(sycl::exception_list) {}
 
 void check(const context &ctx) {
   auto devices = ctx.get_devices();
-  if (devices.empty()) {
-    std::cout << "Context has no devices" << std::endl;
-    return_fail();
-  }
 
   auto plt = ctx.get_platform();
   for (const auto &dev : devices) {
diff --git a/libsycl/unittests/context/context_ctors.cpp b/libsycl/unittests/context/context_ctors.cpp
index 19f6a515733eb..ff1b328df3271 100644
--- a/libsycl/unittests/context/context_ctors.cpp
+++ b/libsycl/unittests/context/context_ctors.cpp
@@ -18,6 +18,7 @@ void dummyAsyncHandler(exception_list) {}
 TEST(Context, DefaultConstructor) {
   mock::MockWrapper Mock;
 
+  // TODO: remove once context is properly implemented
   std::ignore = device{};
 
   EXPECT_CALL(Mock.get(), olCreateContext(_, _, _)).Times(1);
@@ -69,3 +70,17 @@ TEST(Context, DeviceListConstructor) {
 
   context Ctx({Dev1, Dev2}, AsyncHandler);
 }
+
+TEST(Context, DeviceListConstructorThrowsOnEmptyList) {
+  mock::MockWrapper Mock;
+  async_handler AsyncHandler = dummyAsyncHandler;
+
+  EXPECT_CALL(Mock.get(), olCreateContext(_, _, _)).Times(0);
+
+  try {
+    context Ctx(std::vector<device>{}, AsyncHandler);
+    FAIL() << "Expected sycl::exception";
+  } catch (const exception &Ex) {
+    EXPECT_EQ(Ex.code(), make_error_code(errc::invalid));
+  }
+}

>From 0c33b09da0f21a8a62d323ba91e8dfd11c6440d9 Mon Sep 17 00:00:00 2001
From: Robertkq <robertvuia06 at gmail.com>
Date: Wed, 9 Sep 2026 01:21:06 +0300
Subject: [PATCH 5/6] Fix context error code and drop a stale include and
 comment

---
 libsycl/src/context.cpp                     | 14 --------------
 libsycl/src/detail/context_impl.cpp         |  8 ++++++--
 libsycl/src/platform.cpp                    |  1 -
 libsycl/unittests/context/context_ctors.cpp |  7 ++++++-
 4 files changed, 12 insertions(+), 18 deletions(-)

diff --git a/libsycl/src/context.cpp b/libsycl/src/context.cpp
index c8bf75c9140e6..1f1be69e89139 100644
--- a/libsycl/src/context.cpp
+++ b/libsycl/src/context.cpp
@@ -21,20 +21,6 @@ _LIBSYCL_BEGIN_NAMESPACE_SYCL
 
 context::context(const std::vector<device> &deviceList,
                  async_handler asyncHandler, const property_list &propList) {
-  if (deviceList.empty()) {
-    throw exception(make_error_code(errc::invalid),
-                    "Device list must not be empty");
-  }
-
-  const auto &platform = deviceList[0].get_platform();
-  if (std::any_of(deviceList.begin(), deviceList.end(),
-                  [&platform](const device &dev) {
-                    return dev.get_platform() != platform;
-                  })) {
-    throw exception(make_error_code(errc::invalid),
-                    "All devices must be associated with the same platform");
-  }
-
   auto deviceImpls = detail::getSyclObjImpls(deviceList);
 
   impl = detail::ContextImpl::create(std::move(deviceImpls), asyncHandler,
diff --git a/libsycl/src/detail/context_impl.cpp b/libsycl/src/detail/context_impl.cpp
index a5cb0680cbed6..2360c481eaef0 100644
--- a/libsycl/src/detail/context_impl.cpp
+++ b/libsycl/src/detail/context_impl.cpp
@@ -28,8 +28,12 @@ ContextImpl::ContextImpl(std::vector<DeviceImpl *> &&DeviceList,
     DeviceIds.push_back(D->getOLHandle());
   }
 
-  callAndThrow(olCreateContext, DeviceIds.size(), DeviceIds.data(),
-               &MOffloadContext);
+  auto Result = callNoCheck(olCreateContext, DeviceIds.size(), DeviceIds.data(),
+                            &MOffloadContext);
+  if (isFailed(Result))
+    throw sycl::exception(make_error_code(errc::invalid),
+                          "Failed to create SYCL context: " +
+                              formatCodeString(Result));
 }
 
 ContextImpl::~ContextImpl() {
diff --git a/libsycl/src/platform.cpp b/libsycl/src/platform.cpp
index 9464257f25ba5..b5bddc82e0d9a 100644
--- a/libsycl/src/platform.cpp
+++ b/libsycl/src/platform.cpp
@@ -6,7 +6,6 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include <sycl/__impl/context.hpp>
 #include <sycl/__impl/device.hpp>
 #include <sycl/__impl/platform.hpp>
 
diff --git a/libsycl/unittests/context/context_ctors.cpp b/libsycl/unittests/context/context_ctors.cpp
index ff1b328df3271..a718c9b403ca9 100644
--- a/libsycl/unittests/context/context_ctors.cpp
+++ b/libsycl/unittests/context/context_ctors.cpp
@@ -18,6 +18,8 @@ void dummyAsyncHandler(exception_list) {}
 TEST(Context, DefaultConstructor) {
   mock::MockWrapper Mock;
 
+  // This line triggers lazy platform discovery, which we want to suppress
+  // from the expected calls of the mock lib.
   // TODO: remove once context is properly implemented
   std::ignore = device{};
 
@@ -75,7 +77,10 @@ TEST(Context, DeviceListConstructorThrowsOnEmptyList) {
   mock::MockWrapper Mock;
   async_handler AsyncHandler = dummyAsyncHandler;
 
-  EXPECT_CALL(Mock.get(), olCreateContext(_, _, _)).Times(0);
+  EXPECT_CALL(Mock.get(), olCreateContext(0, _, _))
+      .Times(1)
+      .WillOnce(Return(
+          mock::getMockLiboffload().makeEmptyStrError(OL_ERRC_INVALID_SIZE)));
 
   try {
     context Ctx(std::vector<device>{}, AsyncHandler);

>From ac3185123d6f3bbaa9b4e1eeb401ed506b4a0a9b Mon Sep 17 00:00:00 2001
From: Robertkq <robertvuia06 at gmail.com>
Date: Wed, 9 Sep 2026 01:42:27 +0300
Subject: [PATCH 6/6] Omit explicit \param in favor of \p

---
 libsycl/include/sycl/__impl/context.hpp | 68 +++++--------------------
 1 file changed, 14 insertions(+), 54 deletions(-)

diff --git a/libsycl/include/sycl/__impl/context.hpp b/libsycl/include/sycl/__impl/context.hpp
index 5c4a463197d95..8a9fea4296850 100644
--- a/libsycl/include/sycl/__impl/context.hpp
+++ b/libsycl/include/sycl/__impl/context.hpp
@@ -43,91 +43,51 @@ using is_context_info_desc_t = typename is_info_desc<T, context>::return_type;
 // SYCL 2020 4.6.3. Context class
 class _LIBSYCL_EXPORT context {
 public:
-  /// Constructs a SYCL context instance using the device selected by
+  /// Constructs a SYCL context using the device selected by
   /// default_selector_v.
-  ///
-  /// \param propList SYCL properties to be associated with the context.
   explicit context(const property_list &propList = {})
       : context(device{}, propList) {}
 
-  /// Constructs a SYCL context instance using the device selected by
+  /// Constructs a SYCL context using the device selected by
   /// default_selector_v.
-  ///
-  /// \param asyncHandler Async handler to be used for asynchronous error
-  /// reporting.
-  /// \param propList SYCL properties to be associated with the context.
   explicit context(async_handler asyncHandler,
                    const property_list &propList = {})
       : context(device{}, asyncHandler, propList) {}
 
-  /// Constructs a SYCL context instance using the provided device.
-  /// The context will be associated with the platform of the provided device.
-  ///
-  /// \param dev is an instance of SYCL device
-  /// \param propList SYCL properties to be associated with the context.
+  /// Constructs a SYCL context associated with the platform of \p dev.
   explicit context(const device &dev, const property_list &propList = {})
       : context(std::vector<device>{dev}, propList) {}
 
-  /// Constructs a SYCL context instance using the provided device.
-  /// The context will be associated with the platform of the provided device.
-  ///
-  /// \param dev is an instance of SYCL device
-  /// \param asyncHandler Async handler to be used for asynchronous error
-  /// reporting.
-  /// \param propList SYCL properties to be associated with the context.
+  /// Constructs a SYCL context associated with the platform of \p dev.
   explicit context(const device &dev, async_handler asyncHandler,
                    const property_list &propList = {})
       : context(std::vector<device>{dev}, asyncHandler, propList) {}
 
-  /// Constructs a SYCL context instance that contains all of the devices
-  /// in the platform plt.
+  /// Constructs a SYCL context containing all devices in \p plt.
   ///
-  /// \param plt is an instance of SYCL platform
-  /// \param propList SYCL properties to be associated with the context.
-  ///
-  /// \throws an exception with code errc::invalid if the platform plt has no
-  /// devices.
+  /// \throws an exception with code errc::invalid if \p plt has no devices.
   explicit context(const platform &plt, const property_list &propList = {})
       : context(plt.get_devices(), propList) {}
 
-  /// Constructs a SYCL context instance that contains all of the devices
-  /// in the platform plt.
-  ///
-  /// \param plt is an instance of SYCL platform
-  /// \param asyncHandler Async handler to be used for asynchronous error
-  /// reporting.
-  /// \param propList SYCL properties to be associated with the context.
+  /// Constructs a SYCL context containing all devices in \p plt.
   ///
-  /// \throws an exception with code errc::invalid if the platform plt has no
-  /// devices.
+  /// \throws an exception with code errc::invalid if \p plt has no devices.
   explicit context(const platform &plt, async_handler asyncHandler,
                    const property_list &propList = {})
       : context(plt.get_devices(), asyncHandler, propList) {}
 
-  /// Constructs a SYCL context instance using the provided list of
-  /// devices. The context will be associated with each SYCL device in the
-  /// deviceList. This requires that all devices in the deviceList are
-  /// associated with the same platform.
+  /// Constructs a SYCL context associated with each device in \p deviceList.
+  /// All devices in \p deviceList must belong to the same platform.
   ///
-  /// \param deviceList is a vector of SYCL devices
-  /// \param propList SYCL properties to be associated with the context.
-  ///
-  /// \throws an exception with code errc::invalid if the deviceList is empty.
+  /// \throws an exception with code errc::invalid if \p deviceList is empty.
   explicit context(const std::vector<device> &deviceList,
                    const property_list &propList = {})
       : context(deviceList, detail::defaultAsyncHandler, propList) {}
 
-  /// Constructs a SYCL context instance using the provided list of
-  /// devices. The context will be associated with each SYCL device in the
-  /// deviceList. This requires that all devices in the deviceList are
-  /// associated with the same platform.
-  ///
-  /// \param deviceList is a vector of SYCL devices
-  /// \param asyncHandler Async handler to be used for asynchronous error
-  /// reporting.
-  /// \param propList SYCL properties to be associated with the context.
+  /// Constructs a SYCL context associated with each device in \p deviceList.
+  /// All devices in \p deviceList must belong to the same platform.
   ///
-  /// \throws an exception with code errc::invalid if the deviceList is empty.
+  /// \throws an exception with code errc::invalid if \p deviceList is empty.
   explicit context(const std::vector<device> &deviceList,
                    async_handler asyncHandler,
                    const property_list &propList = {});



More information about the llvm-commits mailing list