Bug 102284 - Can access object outside of its lifetime during constant evaluation
Summary: Can access object outside of its lifetime during constant evaluation
Status: NEW
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 12.0
: P3 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords: accepts-invalid
: 117255 (view as bug list)
Depends on:
Blocks: constexpr 110367
  Show dependency treegraph
 
Reported: 2021-09-10 20:57 UTC by Johel Ernesto Guerrero Peña
Modified: 2025-07-15 09:41 UTC (History)
5 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2022-01-17 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Johel Ernesto Guerrero Peña 2021-09-10 20:57:21 UTC
This is prohibited at https://eel.is/c++draft/basic.life#7.1.
See https://godbolt.org/z/exKnnT935.
```C++
#include <memory>
struct X { int x; };
int main() {
  []() consteval {
    {
      X x{};
      x.~X();
      ++x.x;
      std::construct_at(&x);
    }
    {
      union { X x{}; };
      x.~X();
      ++x.x;
    }
  }();
}
```
Comment 1 Johel Ernesto Guerrero Peña 2021-09-10 21:23:04 UTC
There's also https://eel.is/c++draft/basic.life#9.sentence-1 to consider.
See https://godbolt.org/z/P97Kaqhv8.
```C++
struct X { int x; };
int main() {
  []() consteval {
    X x{};
    x.~X();
  }();
}
```
Comment 2 Johel Ernesto Guerrero Peña 2021-09-10 21:24:07 UTC
Fixed that: https://godbolt.org/z/YGf4GTP5P.
```C++
struct X { constexpr ~X() { } };
int main() {
  []() consteval {
    X x{};
    x.~X();
  }();
}
```
Comment 3 Drea Pinski 2021-09-10 21:26:43 UTC
I think there might be a dup of this bug.
Comment 4 Johel Ernesto Guerrero Peña 2021-09-11 01:04:31 UTC
If there is, I confirmed it's not in https://gcc.gnu.org/bugzilla/show_bug.cgi?id=55004.
Comment 5 Drea Pinski 2022-01-17 11:12:41 UTC
Confirmed.
Comment 6 Johel Ernesto Guerrero Peña 2022-09-24 20:11:23 UTC
Another example.

Simplified: https://godbolt.org/z/z781c56PM.

```C++
struct ratio {
  int numerator;
};

template<auto> struct constant;

template<auto F> concept constant_invocable = requires { typename constant<(F(), 0)>; };

static_assert(not constant_invocable<[] {
  ratio r;
  (void)int{r.*&ratio::numerator};
}>);
```

```
<source>:9:15: error: static assertion failed
    9 | static_assert(not constant_invocable<[] {
      |               ^~~~~~~~~~~~~~~~~~~~~~~~~~~
   10 |   ratio r;
      |   ~~~~~~~~     
   11 |   (void)int{r.*&ratio::numerator};
      |   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   12 | }>);
      | ~~             
Compiler returned: 1
```

Found while opening https://github.com/llvm/llvm-project/issues/57958: https://godbolt.org/z/KWfvEnaae.

```C++
#include <concepts>
#include <cstdint>
#include <functional>
#include <type_traits>

struct ratio {
  std::intmax_t numerator;
  std::intmax_t denominator{1};
};

template<auto F> concept constant_invocable = requires { typename std::integral_constant<int, (F(), 0)>; };

template<auto P> requires std::is_member_object_pointer_v<decltype(P)>
inline constexpr bool default_initialization_leaves_uninitialized =
  []<class T, class M>(M T::*) {
    return not constant_invocable<[]() {
      T v;
      (void)M{v.*P};
    }>;
  }(P);

static_assert(default_initialization_leaves_uninitialized<&ratio::numerator>);
static_assert(not default_initialization_leaves_uninitialized<&ratio::denominator>);
```

```
<source>:22:15: error: static assertion failed
   22 | static_assert(default_initialization_leaves_uninitialized<&ratio::numerator>);
      |               ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Compiler returned: 1
```
Comment 7 Johel Ernesto Guerrero Peña 2022-09-27 01:40:46 UTC
This may be related. Assigning to another member doesn't end the lifetime of the active one: https://godbolt.org/z/eMGY5ehnb.

```C++
#include <memory>

struct symbol { };

constexpr symbol one{};

struct unit {
  const struct symbol* symbol = &one;

  struct not_one_t { };

  union to_the {
    not_one_t not_one = {}; // Omits ¹ from NTTP.
    int i;

    to_the() = default;
    constexpr to_the(int i) : i{i} {
      if (i == 1) {
        // std::destroy_at(&this->i); // On GCC, doesn't help.
        not_one = {};
        // std::construct_at(&not_one); // On GCC, initializes `not_one`, doesn't end the lifetime of `i`.
      }
    }
  } exponent = {};

  constexpr unit(const struct symbol& s)
    : symbol{&s} { }
  constexpr unit(const struct symbol& s, auto e)
    : symbol{&s}, exponent{e} { }
};

template<auto> void print() { }

int main() {
  constexpr auto U = unit{one, 1};
  print<U>();
  print<unit{one}>();
  static_assert(&U.exponent.not_one);
  // static_assert(U.exponent.i == 1); // Works on GCC (and MSVC).
}
```
Comment 8 Drea Pinski 2024-10-22 07:45:12 UTC
*** Bug 117255 has been marked as a duplicate of this bug. ***
Comment 9 Jonathan Wakely 2025-07-10 12:27:36 UTC
I think this probably needs to be fixed for std::is_within_lifetime (Bug 110367) to work correctly.
Comment 10 Tomasz Kamiński 2025-07-15 07:54:28 UTC
This also makes following ill-formed:
```
struct A {
    union { int x = 10; };
};

using Int = int;
constexpr A create1()
{
    A a;
    a.x.~Int();
    return a;
}

constexpr A a1 = create1();
```
Comment 11 GCC Commits 2025-07-15 09:41:04 UTC
The master branch has been updated by Jonathan Wakely <redi@gcc.gnu.org>:

https://gcc.gnu.org/g:a63b663c7bf77e38d90cebe993a3de8a548f8d66

commit r16-2256-ga63b663c7bf77e38d90cebe993a3de8a548f8d66
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Thu Jul 10 14:12:44 2025 +0100

    libstdc++: Ensure that ranges::destroy destroys in constexpr [PR121024]
    
    The new test is currently marked as XFAIL because PR c++/102284 means
    that GCC doesn't notice that the lifetimes have ended.
    
    libstdc++-v3/ChangeLog:
    
            PR libstdc++/121024
            * include/bits/ranges_uninitialized.h (ranges::destroy): Do not
            optimize away trivial destructors during constant evaluation.
            (ranges::destroy_n): Likewise.
            * testsuite/20_util/specialized_algorithms/destroy/121024.cc:
            New test.
    
    Reviewed-by: Tomasz KamiÅski <tkaminsk@redhat.com>