//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//

// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20

// clang-cl and cl currently don't support [[no_unique_address]]
// XFAIL: msvc

// test [[no_unique_address]] is applied to the union
// In particular, we ensure that we reuse tail padding in the T
// to store the discriminator whenever we can.

#include <__type_traits/datasizeof.h>
#include <expected>
#include <optional>
#include <memory>

struct Empty {};

struct A {
  int x_;
  int y_;
};

struct B : public A {
  int z_;
  short z2_;
  virtual ~B() = default;
};

struct BoolWithPadding {
  explicit operator bool() { return b; }

private:
  alignas(1024) bool b = false;
};

static_assert(sizeof(std::expected<void, Empty>) == sizeof(bool));
static_assert(sizeof(std::expected<void, A>) == 2 * sizeof(int) + alignof(std::expected<void, A>));
static_assert(sizeof(std::expected<void, B>) == sizeof(B));

// Check that `expected`'s datasize is large enough for the parameter type(s).
static_assert(sizeof(std::expected<void, BoolWithPadding>) ==
              std::__datasizeof_v<std::expected<void, BoolWithPadding>>);

// In this case, there should be tail padding in the `expected` because `A`
// itself does _not_ have tail padding.
static_assert(sizeof(std::expected<void, A>) > std::__datasizeof_v<std::expected<void, A>>);

// Test with some real types.
static_assert(sizeof(std::expected<void, std::optional<int>>) == 8);
static_assert(std::__datasizeof_v<std::expected<void, std::optional<int>>> == 8);

static_assert(sizeof(std::expected<void, int>) == 8);
static_assert(std::__datasizeof_v<std::expected<void, int>> == 5);

// clang-format off
static_assert(std::__datasizeof_v<int> == 4);
static_assert(std::__datasizeof_v<std::expected<void, int>> == 5);
static_assert(std::__datasizeof_v<std::expected<void, std::expected<void, int>>> == 8);
static_assert(std::__datasizeof_v<std::expected<void, std::expected<void, std::expected<void, int>>>> == 9);
static_assert(std::__datasizeof_v<std::expected<void, std::expected<void, std::expected<void, std::expected<void, int>>>>> == 12);
// clang-format on
