Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

explicitly noexcept destructor incorrectly not constexpr #59854

Open
CaseyCarter opened this issue Jan 6, 2023 · 5 comments
Open

explicitly noexcept destructor incorrectly not constexpr #59854

CaseyCarter opened this issue Jan 6, 2023 · 5 comments
Labels
bug Indicates an unexpected problem or unintended behavior clang:frontend Language frontend issues, e.g. anything involving "Sema"

Comments

@CaseyCarter
Copy link
Member

CaseyCarter commented Jan 6, 2023

This well-formed C++20 TU:

#include <vector>

template <class E>
struct expected {
  constexpr explicit expected(std::initializer_list<int> il) : unex(il) {}
  constexpr ~expected() 
#ifndef MEOW
    noexcept 
#endif
  { unex.~E(); }
  constexpr explicit operator bool() const { return true; }
  union { E unex; };
};

struct Data {
  std::vector<int> vec_;
  constexpr Data(std::initializer_list<int> il) : vec_(il) {}
};

static_assert(expected<Data>({1, 2, 3}));

fails to compile if MEOW isn't defined (https://godbolt.org/z/qx1nEzvzW):

<source>:20:15: error: static assertion expression is not an integral constant expression
static_assert(expected<Data>({1, 2, 3}));
              ^~~~~~~~~~~~~~~~~~~~~~~~~
<source>:20:15: note: subexpression not valid in a constant expression
<source>:20:15: note: in call to '&expected<Data>({1, 2, 3})->~expected()'
1 error generated.

The repro is admittedly not library-free, but it does reproduce with all of libc++, libstdc++, and MSVCSTL - and not with GCC or MSVC - so I'm reasonably confident the bug is in the compiler and not the implementation of vector.

@CaseyCarter CaseyCarter added bug Indicates an unexpected problem or unintended behavior clang:frontend Language frontend issues, e.g. anything involving "Sema" labels Jan 6, 2023
@llvmbot
Copy link
Member

llvmbot commented Jan 6, 2023

@llvm/issue-subscribers-clang-frontend

@llvmbot
Copy link
Member

llvmbot commented Jan 6, 2023

@llvm/issue-subscribers-bug

@shafik
Copy link
Collaborator

shafik commented Jan 9, 2023

I am not sure who is correct here but I am curious do you except the non-anonymous union case to be the same:

#include <vector>

template <typename T>
union U {
  constexpr explicit U(std::initializer_list<int> il) : t(il) {}
  T t;
};

template <class E>
struct expected {
  constexpr explicit expected(std::initializer_list<int> il) : unex(il) {}
  constexpr ~expected() 
#ifndef MEOW
    noexcept 
#endif
  { unex.~E(); }
  constexpr explicit operator bool() const { return true; }
  U<E> unex;
};

struct Data {
  std::vector<int> vec_;
  constexpr Data(std::initializer_list<int> il) : vec_(il) {}
};

static_assert(expected<Data>({1, 2, 3}));

godbolt: https://godbolt.org/z/5q15jK7hn

gcc then agrees with clang that this is ill-formed. If you don't think this is the same case can you give me the specific sections of the draft standard that you believe apply to your original case?

@CaseyCarter
Copy link
Member Author

CaseyCarter commented Jan 9, 2023

(EDIT: to be perfectly clear, I do not expect the named union case to behave similarly.) They're obviously unrelated, the error conditions are different. Your named union example is ill-formed because the named union has a deleted destructor. In the anonymous union case, ~expected effectively is the union's destructor.

The discrepancy here is that Clang believes the destructor call is valid in a constant expression when it has no noexcept-specifier, but not when it does have a noexcept-specifier regardless of whether it's noexcept(true) (equivalently plain noexcept) or noexcept(false) (https://godbolt.org/z/7M3s3c1bv).

@frederick-vs-ja
Copy link
Contributor

I am not sure who is correct here but I am curious do you except the non-anonymous union case to be the same:

CWG2761 clarified that these two cases are not the same.

Reduced to a library-free version (https://godbolt.org/z/TGo3P5cGv):

struct my_nontrivial {
    my_nontrivial() = default;
    constexpr ~my_nontrivial() {} // non-trivial
};

struct my_data {
  my_data() = default;
#ifdef EXPLICITLY_DEFAUTLING
  ~my_data() = default;
#endif

  my_nontrivial dat_;
};

template <class E>
struct expected {
  expected() = default;
  constexpr ~expected() 
#ifndef MEOW
    noexcept
#endif
  { unex.~E(); }
  constexpr explicit operator bool() const { return true; }
  union { E unex = E(); };
};

static_assert(expected<my_data>{});

It seems that this bug is trigged when the E::~E is implicitly declared but non-trivial.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Indicates an unexpected problem or unintended behavior clang:frontend Language frontend issues, e.g. anything involving "Sema"
Projects
None yet
Development

No branches or pull requests

4 participants