简体   繁体   中英

Are function scope static constexpr variable static storage duration

First I'll start by saying the question is maybe wrong, as I am not sure what is the issue with the following code.

#include <array>
#include <cstdint>
#include <iostream>

template <typename T>
struct PixelRGB {
  T r {};
  T g {};
  T b {};
};
using PixelRGBui8 = PixelRGB<uint8_t>;

template <typename Pixel_T>
class FrameRowView {
  public:
  FrameRowView(const uint32_t& width)
      : m_width { width }
  {
  }

  const uint32_t& m_width;
};

double bar(auto& view)
{
  std::array<PixelRGBui8, 128> arrayA = {};
  std::cout << "width: " << view.m_width << " " << arrayA.data() <<  "\n";
  return 42.0;
}

void foo()
{
  static constexpr auto Width = 16;

  auto frameRowPrev = FrameRowView<PixelRGBui8> { Width };
  auto val = bar(frameRowPrev);

  std::cout << "frameRowPrev: " << frameRowPrev.m_width << "\n";
}

int main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[])
{
  foo();
  return EXIT_SUCCESS;
}

and on compiler explorer

Now the expected output is width: 16.

Clang in -O0 and -O3 output the right answer but in -O0 ASAN gives me an error about stack-use-after-scope.

==1==ERROR: AddressSanitizer: stack-use-after-scope on address 0x7fe67e200040 at pc 0x55c0732b8ac1 bp 0x7fff5d95d590 sp 0x7fff5d95d588

READ of size 4 at 0x7fe67e200040 thread T0

 #0 0x55c0732b8ac0 in double bar<FrameRowView<PixelRGB<unsigned char>>>(FrameRowView<PixelRGB<unsigned char>>&) /app/example.cpp:27:34 #1 0x55c0732b8621 in foo() /app/example.cpp:36:14 #2 0x55c0732b874a in main /app/example.cpp:43:3 #3 0x7fe6807190b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x240b2) (BuildId: 9fdb74e7b217d06c93172a8243f8547f947ee6d1) #4 0x55c0731e137d in _start (/app/output.s+0x2137d)

Address 0x7fe67e200040 is located in stack of thread T0 at offset 64 in frame #0 0x55c0732b84ff in foo() /app/example.cpp:32

This frame has 2 object(s):

 [32, 40) 'frameRowPrev' (line 35) [64, 68) 'ref.tmp' (line 35) <== Memory access at offset 64 is inside this variable

HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork (longjmp and C++ exceptions are supported)

MSVC in /O0 output the right answer but in /O2 output garbage. Actually if you play with the size of the array named arrayA in the function bar and set it to 1 the output is actually good.

I am tempted to say it is a false positive on ASAN and a bug from MSVC? But it could be my bug too.

Does anybody know the answer to this riddle?

Because 16 is int by default in C++ standard, Width is an int variable; then you pass it as a const uint32_t& parameter, so possibly it causes a copy to make a temporary uint32_t variable, and pass it to this const reference. After the function ends, this temporary variable is destructed, so your reference refers to an invalid variable, which causes undefined behavior.

See https://godbolt.org/z/GqzvYPW47 , and as you make Width a uint32_t or an int variable, it will call different ctor for rvalue and const lvalue differ there.

If you make Width a uint32_t variable, it will be definitely right.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM