From 70de19da13fcd96174146f4830e0c875d2297742 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 13 Oct 2025 11:09:26 +0000 Subject: [PATCH 1/2] Initial plan From e2ee92cb541bc9188e1ee6987b72751f5fe979c8 Mon Sep 17 00:00:00 2001 From: Jeff Bailey Date: Fri, 3 Oct 2025 22:42:07 +0000 Subject: [PATCH 2/2] [libc][cpp] Add generic variant implementation for POD types - Add variant.h supporting 2 and 3 type specializations for POD types - Assumes trivially copyable/destructible types for simplified implementation - Add comprehensive variant_test.cpp with tests for construction, assignment, type-safe access, and move semantics - Place variant and variant_test in correct alphabetical order in CMakeLists.txt - Remove variant test from time_zone_posix_test.cpp (moved to proper location) This provides a type-safe alternative to raw unions for use in libc components. --- libc/src/__support/CPP/CMakeLists.txt | 11 + libc/src/__support/CPP/variant.h | 256 +++++++++++++++++++ libc/test/src/__support/CPP/CMakeLists.txt | 10 + libc/test/src/__support/CPP/variant_test.cpp | 119 +++++++++ 4 files changed, 396 insertions(+) create mode 100644 libc/src/__support/CPP/variant.h create mode 100644 libc/test/src/__support/CPP/variant_test.cpp diff --git a/libc/src/__support/CPP/CMakeLists.txt b/libc/src/__support/CPP/CMakeLists.txt index bdfbc6151c773..e13b32dd1d367 100644 --- a/libc/src/__support/CPP/CMakeLists.txt +++ b/libc/src/__support/CPP/CMakeLists.txt @@ -187,6 +187,17 @@ add_header_library( utility/move.h ) +add_header_library( + variant + HDRS + variant.h + DEPENDS + .type_traits + .utility + libc.src.__support.macros.attributes + libc.src.__support.macros.config +) + add_header_library( atomic HDRS diff --git a/libc/src/__support/CPP/variant.h b/libc/src/__support/CPP/variant.h new file mode 100644 index 0000000000000..3bddea73e2d82 --- /dev/null +++ b/libc/src/__support/CPP/variant.h @@ -0,0 +1,256 @@ +//===-- Standalone implementation of std::variant ---------------*- C++ -*-===// +// +// 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_LIBC_SRC___SUPPORT_CPP_VARIANT_H +#define LLVM_LIBC_SRC___SUPPORT_CPP_VARIANT_H + +#include "src/__support/CPP/type_traits.h" +#include "src/__support/CPP/utility.h" +#include "src/__support/macros/attributes.h" +#include "src/__support/macros/config.h" + +namespace LIBC_NAMESPACE_DECL { +namespace cpp { + +// Simple variant implementation for POD types. +// This implementation assumes all types are trivially copyable and destructible. +// Supports common cases of 2 and 3 types. + +// Forward declaration +template +class variant; + +// Specialization for 2 types +template +class variant { +private: + static constexpr int storage_size = (sizeof(T1) > sizeof(T2)) ? sizeof(T1) : sizeof(T2); + static constexpr int storage_align = (alignof(T1) > alignof(T2)) ? alignof(T1) : alignof(T2); + + alignas(storage_align) char storage_[storage_size]; + int index_; + +public: + // Default constructor - initializes with first type default-constructed + LIBC_INLINE constexpr variant() : index_(0) { + *reinterpret_cast(storage_) = T1{}; + } + + // Constructor from T1 + LIBC_INLINE constexpr variant(const T1& value) : index_(0) { + *reinterpret_cast(storage_) = value; + } + + // Constructor from T2 + LIBC_INLINE constexpr variant(const T2& value) : index_(1) { + *reinterpret_cast(storage_) = value; + } + + // Copy constructor + LIBC_INLINE constexpr variant(const variant& other) : index_(other.index_) { + if (index_ == 0) { + *reinterpret_cast(storage_) = *reinterpret_cast(other.storage_); + } else { + *reinterpret_cast(storage_) = *reinterpret_cast(other.storage_); + } + } + + // Move constructor + LIBC_INLINE constexpr variant(variant&& other) : index_(other.index_) { + if (index_ == 0) { + *reinterpret_cast(storage_) = move(*reinterpret_cast(other.storage_)); + } else { + *reinterpret_cast(storage_) = move(*reinterpret_cast(other.storage_)); + } + } + + // Assignment operators + LIBC_INLINE constexpr variant& operator=(const variant& other) { + if (this != &other) { + index_ = other.index_; + if (index_ == 0) { + *reinterpret_cast(storage_) = *reinterpret_cast(other.storage_); + } else { + *reinterpret_cast(storage_) = *reinterpret_cast(other.storage_); + } + } + return *this; + } + + LIBC_INLINE constexpr variant& operator=(variant&& other) { + if (this != &other) { + index_ = other.index_; + if (index_ == 0) { + *reinterpret_cast(storage_) = move(*reinterpret_cast(other.storage_)); + } else { + *reinterpret_cast(storage_) = move(*reinterpret_cast(other.storage_)); + } + } + return *this; + } + + // Get the index of the currently active type + LIBC_INLINE constexpr int index() const { return index_; } + + // Type-safe access to stored value + template + LIBC_INLINE constexpr T& get() & { + if constexpr (is_same_v) { + return *reinterpret_cast(storage_); + } else if constexpr (is_same_v) { + return *reinterpret_cast(storage_); + } + } + + template + LIBC_INLINE constexpr const T& get() const & { + if constexpr (is_same_v) { + return *reinterpret_cast(storage_); + } else if constexpr (is_same_v) { + return *reinterpret_cast(storage_); + } + } +}; + +// Specialization for 3 types +template +class variant { +private: + static constexpr int storage_size = (sizeof(T1) > sizeof(T2)) ? + ((sizeof(T1) > sizeof(T3)) ? sizeof(T1) : sizeof(T3)) : + ((sizeof(T2) > sizeof(T3)) ? sizeof(T2) : sizeof(T3)); + + static constexpr int storage_align = (alignof(T1) > alignof(T2)) ? + ((alignof(T1) > alignof(T3)) ? alignof(T1) : alignof(T3)) : + ((alignof(T2) > alignof(T3)) ? alignof(T2) : alignof(T3)); + + alignas(storage_align) char storage_[storage_size]; + int index_; + +public: + // Default constructor + LIBC_INLINE constexpr variant() : index_(0) { + *reinterpret_cast(storage_) = T1{}; + } + + // Constructors + LIBC_INLINE constexpr variant(const T1& value) : index_(0) { + *reinterpret_cast(storage_) = value; + } + + LIBC_INLINE constexpr variant(const T2& value) : index_(1) { + *reinterpret_cast(storage_) = value; + } + + LIBC_INLINE constexpr variant(const T3& value) : index_(2) { + *reinterpret_cast(storage_) = value; + } + + // Copy constructor + LIBC_INLINE constexpr variant(const variant& other) : index_(other.index_) { + if (index_ == 0) { + *reinterpret_cast(storage_) = *reinterpret_cast(other.storage_); + } else if (index_ == 1) { + *reinterpret_cast(storage_) = *reinterpret_cast(other.storage_); + } else { + *reinterpret_cast(storage_) = *reinterpret_cast(other.storage_); + } + } + + // Move constructor + LIBC_INLINE constexpr variant(variant&& other) : index_(other.index_) { + if (index_ == 0) { + *reinterpret_cast(storage_) = move(*reinterpret_cast(other.storage_)); + } else if (index_ == 1) { + *reinterpret_cast(storage_) = move(*reinterpret_cast(other.storage_)); + } else { + *reinterpret_cast(storage_) = move(*reinterpret_cast(other.storage_)); + } + } + + // Assignment operators + LIBC_INLINE constexpr variant& operator=(const variant& other) { + if (this != &other) { + index_ = other.index_; + if (index_ == 0) { + *reinterpret_cast(storage_) = *reinterpret_cast(other.storage_); + } else if (index_ == 1) { + *reinterpret_cast(storage_) = *reinterpret_cast(other.storage_); + } else { + *reinterpret_cast(storage_) = *reinterpret_cast(other.storage_); + } + } + return *this; + } + + LIBC_INLINE constexpr variant& operator=(variant&& other) { + if (this != &other) { + index_ = other.index_; + if (index_ == 0) { + *reinterpret_cast(storage_) = move(*reinterpret_cast(other.storage_)); + } else if (index_ == 1) { + *reinterpret_cast(storage_) = move(*reinterpret_cast(other.storage_)); + } else { + *reinterpret_cast(storage_) = move(*reinterpret_cast(other.storage_)); + } + } + return *this; + } + + // Get the index of the currently active type + LIBC_INLINE constexpr int index() const { return index_; } + + // Type-safe access to stored value + template + LIBC_INLINE constexpr T& get() & { + if constexpr (is_same_v) { + return *reinterpret_cast(storage_); + } else if constexpr (is_same_v) { + return *reinterpret_cast(storage_); + } else if constexpr (is_same_v) { + return *reinterpret_cast(storage_); + } + } + + template + LIBC_INLINE constexpr const T& get() const & { + if constexpr (is_same_v) { + return *reinterpret_cast(storage_); + } else if constexpr (is_same_v) { + return *reinterpret_cast(storage_); + } else if constexpr (is_same_v) { + return *reinterpret_cast(storage_); + } + } +}; + +// Free functions for type-safe access +template +LIBC_INLINE constexpr T& get(variant& v) { + return v.template get(); +} + +template +LIBC_INLINE constexpr const T& get(const variant& v) { + return v.template get(); +} + +template +LIBC_INLINE constexpr T& get(variant& v) { + return v.template get(); +} + +template +LIBC_INLINE constexpr const T& get(const variant& v) { + return v.template get(); +} + +} // namespace cpp +} // namespace LIBC_NAMESPACE_DECL + +#endif // LLVM_LIBC_SRC___SUPPORT_CPP_VARIANT_H \ No newline at end of file diff --git a/libc/test/src/__support/CPP/CMakeLists.txt b/libc/test/src/__support/CPP/CMakeLists.txt index 430cd7b136c69..3560c1260bd9e 100644 --- a/libc/test/src/__support/CPP/CMakeLists.txt +++ b/libc/test/src/__support/CPP/CMakeLists.txt @@ -171,6 +171,16 @@ add_libc_test( libc.src.__support.CPP.type_traits ) +add_libc_test( + variant_test + SUITE + libc-cpp-utils-tests + SRCS + variant_test.cpp + DEPENDS + libc.src.__support.CPP.variant +) + if(LIBC_COMPILER_HAS_EXT_VECTOR_TYPE) add_libc_test( simd_test diff --git a/libc/test/src/__support/CPP/variant_test.cpp b/libc/test/src/__support/CPP/variant_test.cpp new file mode 100644 index 0000000000000..ee74d42518fef --- /dev/null +++ b/libc/test/src/__support/CPP/variant_test.cpp @@ -0,0 +1,119 @@ +//===-- Unittests for Variant --------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "src/__support/CPP/variant.h" +#include "test/UnitTest/Test.h" + +using LIBC_NAMESPACE::cpp::variant; +using LIBC_NAMESPACE::cpp::get; + +// Simple POD types for testing +struct SimpleStruct { + int value; + + SimpleStruct() : value(0) {} + SimpleStruct(int v) : value(v) {} + + bool operator==(const SimpleStruct& other) const { + return value == other.value; + } +}; + +TEST(LlvmLibcVariantTest, TwoTypeVariant) { + // Test default construction + variant v1; + ASSERT_EQ(v1.index(), 0); + ASSERT_EQ(get(v1), 0); + + // Test construction from first type + variant v2(42); + ASSERT_EQ(v2.index(), 0); + ASSERT_EQ(get(v2), 42); + + // Test construction from second type + variant v3(3.14); + ASSERT_EQ(v3.index(), 1); + ASSERT_EQ(get(v3), 3.14); + + // Test copy construction + variant v4(v2); + ASSERT_EQ(v4.index(), 0); + ASSERT_EQ(get(v4), 42); + + // Test assignment + v1 = v3; + ASSERT_EQ(v1.index(), 1); + ASSERT_EQ(get(v1), 3.14); + + // Test member get + int& int_ref = v2.get(); + ASSERT_EQ(int_ref, 42); + int_ref = 99; + ASSERT_EQ(get(v2), 99); +} + +TEST(LlvmLibcVariantTest, ThreeTypeVariant) { + // Test default construction + variant v1; + ASSERT_EQ(v1.index(), 0); + ASSERT_EQ(get(v1), 0); + + // Test construction from first type + variant v2(42); + ASSERT_EQ(v2.index(), 0); + ASSERT_EQ(get(v2), 42); + + // Test construction from second type + variant v3(3.14); + ASSERT_EQ(v3.index(), 1); + ASSERT_EQ(get(v3), 3.14); + + // Test construction from third type + SimpleStruct s(123); + variant v4(s); + ASSERT_EQ(v4.index(), 2); + ASSERT_EQ(get(v4).value, 123); + + // Test copy construction + variant v5(v4); + ASSERT_EQ(v5.index(), 2); + ASSERT_EQ(get(v5).value, 123); + + // Test assignment + v1 = v3; + ASSERT_EQ(v1.index(), 1); + ASSERT_EQ(get(v1), 3.14); + + // Test modifying through reference + SimpleStruct& struct_ref = v4.get(); + struct_ref.value = 456; + ASSERT_EQ(get(v4).value, 456); +} + +TEST(LlvmLibcVariantTest, MoveSemantics) { + // Test move construction + variant v1(42); + variant v2(LIBC_NAMESPACE::cpp::move(v1)); + ASSERT_EQ(v2.index(), 0); + ASSERT_EQ(get(v2), 42); + + // Test move assignment + variant v3(3.14); + v2 = LIBC_NAMESPACE::cpp::move(v3); + ASSERT_EQ(v2.index(), 1); + ASSERT_EQ(get(v2), 3.14); +} + +TEST(LlvmLibcVariantTest, ConstAccess) { + const variant v(42); + ASSERT_EQ(v.index(), 0); + ASSERT_EQ(get(v), 42); + + const int& const_ref = v.get(); + ASSERT_EQ(const_ref, 42); +} \ No newline at end of file