Skip to content

An explicit destructor call in an assumption or in the operand of __builtin_constant_p ends the lifetime of an object during constant evaluation #503

Open

Description

@daveedvdv-nvidia

During constant evaluation, an explicit destructor call in an evaluation whose result is discarded (the expression of an assumption, or the operand of __builtin_constant_p) ends the lifetime of an object created outside of that evaluation. The object is then treated as uninitialized afterward:

struct T {
  int v = 1;
  constexpr ~T() {}
};

constexpr int assume_dtor() {
  T t;
  [[assume((t.~T(), true))]];
  return t.v;
}
static_assert(assume_dtor() == 1);  // Error: access to uninitialized object.

constexpr int constant_p_dtor() {
  T t;
  (void)__builtin_constant_p((t.~T(), 1));
  return t.v;
}
static_assert(constant_p_dtor() == 1);  // Error: access to uninitialized object.

(Compiled with --c++23 --g++.)

The expression of an assumption is not evaluated ([dcl.attr.assume]), so assume_dtor() should be 1; g++ 16.1 and clang 21 accept it. For __builtin_constant_p, EDG doesn't keep other side effects of the operand: With t.v = 2 instead of t.~T(), both functions return 1 (as with clang; g++ instead performs the side effects of the operand of __builtin_constant_p, so it rejects constant_p_dtor and returns 2 with the assignment). So the end of the lifetime is the one effect of such an evaluation that persists.

This isn't specific to exceptions, but it came up in the review of #495: An explicit call of the destructor of a std::exception_ptr created outside of such an evaluation would end its lifetime the same way.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions