hsutter / cppfront

A personal experimental C++ Syntax 2 -> Syntax 1 compiler

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

[BUG] Cannot emit `static constexpr` function local variables

bluetarpmedia opened this issue · comments

Describe the bug
I can't find a Cpp2 syntax to emit a static constexpr local variable. static constexpr variables are useful (particularly in debug/non-optimised builds) for look-up tables in a function where you want the data initialised once and also prefer to keep the variable close to its use in the function, rather than declaring it globally.

See:

To Reproduce
Run cppfront on this code:

main: () -> int = {
    ints: std::array == (11, 22, 33);
    it: == std::find(ints.cbegin(), ints.cend(), 33);
    return it*;
}

It lowers to:

auto main() -> int{
    std::array constexpr ints{ 11, 22, 33 };
    auto constexpr it = std::find(CPP2_UFCS(cbegin)(ints), CPP2_UFCS(cend)(ints), 33);
    return *cpp2::impl::assert_not_null(it); 
}

which produces a C++ compiler error:

main.cpp2:3:20: error: constexpr variable 'it' must be initialized by a constant expression
    3 |     auto constexpr it = std::find(CPP2_UFCS(cbegin)(ints), CPP2_UFCS(cend)(ints), 33);
      |                    ^    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
main.cpp2:3:20: note: pointer to subobject of 'ints' is not a constant expression
main.cpp2:2:26: note: address of non-static constexpr variable 'ints' may differ on each invocation of the enclosing function; add 'static' to give it a constant address
    2 |     std::array constexpr ints{ 11, 22, 33 };
      |                          ^
      |     static 

Repro on Godbolt

Why static constexpr

Here's a C++ example demonstrating why static constexpr is useful:

Side-by-side repro on Godbolt

With static constexpr

int main()
{
    static constexpr std::array ints = {11, 22, 33};
    constexpr auto it = std::find(ints.cbegin(), ints.cend(), 33);
    return *it;
}
MSVC Release `/O2`
main    PROC                                            ; COMDAT
        mov     eax, 33                             ; 00000021H
        ret     0
main    ENDP
MSVC Debug `/Od`
std::array<int,3> const `main'::`2'::ints DD 0bH      ; `main'::`2'::ints
        DD      016H
        DD      021H

it$ = 32
main    PROC
$LN3:
        sub     rsp, 56                             ; 00000038H
        lea     rax, OFFSET FLAT:std::array<int,3> const `main'::`2'::ints
        add     rax, 8
        mov     QWORD PTR it$[rsp], rax
        lea     rcx, QWORD PTR it$[rsp]
        call    int const & std::_Array_const_iterator<int,3>::operator*(void)const  ; std::_Array_const_iterator<int,3>::operator*
        mov     eax, DWORD PTR [rax]
        add     rsp, 56                             ; 00000038H
        ret     0
main    ENDP

Without static constexpr

int main()
{
    std::array ints = {11, 22, 33};
    auto it = std::find(ints.cbegin(), ints.cend(), 33);
    return *it;
}
MSVC Release `/O2`
ints$ = 32
__$ArrayPad$ = 48
main    PROC                                            ; COMDAT
$LN41:
        sub     rsp, 72                             ; 00000048H
        mov     rax, QWORD PTR __security_cookie
        xor     rax, rsp
        mov     QWORD PTR __$ArrayPad$[rsp], rax
        mov     r8d, 33                             ; 00000021H
        mov     DWORD PTR ints$[rsp], 11
        lea     rdx, QWORD PTR ints$[rsp+12]
        mov     DWORD PTR ints$[rsp+4], 22
        lea     rcx, QWORD PTR ints$[rsp]
        mov     DWORD PTR ints$[rsp+8], 33                ; 00000021H
        call    __std_find_trivial_4
        mov     eax, DWORD PTR [rax]
        mov     rcx, QWORD PTR __$ArrayPad$[rsp]
        xor     rcx, rsp
        call    __security_check_cookie
        add     rsp, 72                             ; 00000048H
        ret     0
main    ENDP
MSVC Debug `/Od`
voltbl  SEGMENT
        DDSymXIndex:    FLAT:main
voltbl  ENDS

$T1 = 32
$T2 = 40
$T3 = 48
it$ = 56
$T4 = 64
$T5 = 72
ints$ = 80
__$ArrayPad$ = 96
main    PROC
$LN3:
        sub     rsp, 120                      ; 00000078H
        mov     rax, QWORD PTR __security_cookie
        xor     rax, rsp
        mov     QWORD PTR __$ArrayPad$[rsp], rax
        mov     DWORD PTR ints$[rsp], 11
        mov     DWORD PTR ints$[rsp+4], 22
        mov     DWORD PTR ints$[rsp+8], 33                ; 00000021H
        mov     DWORD PTR $T1[rsp], 33                    ; 00000021H
        lea     rdx, QWORD PTR $T4[rsp]
        lea     rcx, QWORD PTR ints$[rsp]
        call    std::_Array_const_iterator<int,3> std::array<int,3>::cend(void)const  ; std::array<int,3>::cend
        mov     rax, QWORD PTR [rax]
        mov     QWORD PTR $T2[rsp], rax
        lea     rdx, QWORD PTR $T5[rsp]
        lea     rcx, QWORD PTR ints$[rsp]
        call    std::_Array_const_iterator<int,3> std::array<int,3>::cbegin(void)const  ; std::array<int,3>::cbegin
        mov     rax, QWORD PTR [rax]
        mov     QWORD PTR $T3[rsp], rax
        lea     r9, QWORD PTR $T1[rsp]
        mov     r8, QWORD PTR $T2[rsp]
        mov     rdx, QWORD PTR $T3[rsp]
        lea     rcx, QWORD PTR it$[rsp]
        call    std::_Array_const_iterator<int,3> std::find<std::_Array_const_iterator<int,3>,int>(std::_Array_const_iterator<int,3>,std::_Array_const_iterator<int,3>,int const &) ; std::find<std::_Array_const_iterator<int,3>,int>
        lea     rcx, QWORD PTR it$[rsp]
        call    int const & std::_Array_const_iterator<int,3>::operator*(void)const  ; std::_Array_const_iterator<int,3>::operator*
        mov     eax, DWORD PTR [rax]
        mov     rcx, QWORD PTR __$ArrayPad$[rsp]
        xor     rcx, rsp
        call    __security_check_cookie
        add     rsp, 120                      ; 00000078H
        ret     0
main    ENDP

Thanks! More generally, Cpp2 doesn't yet support "magic statics" / static local variables, whether constexpr or not. I think your request is to support a "magic statics" equivalent.

I was dragging my feet on supporting that because call_once is still available for single-lazy-initialization semantics, so I was waiting to see if there was demand for the feature. However, call_once doesn't let you do the initialization of the constexpr value you want.

So it seems that a "static" or "once" or similar qualifier or similar is wanted, that lowers to magic statics...