Bug 106889 - __builtin_strlen fails for some constexpr constructs
Summary: __builtin_strlen fails for some constexpr constructs
Status: UNCONFIRMED
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 10.4.1
: P3 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords:
: 119866 (view as bug list)
Depends on:
Blocks: constexpr
  Show dependency treegraph
 
Reported: 2022-09-08 09:46 UTC by m.cencora
Modified: 2025-04-18 19:53 UTC (History)
3 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description m.cencora 2022-09-08 09:46:18 UTC
Compiling following program on any gcc version, with C++14 or newer standard fails with
"'__builtin_strlen(...)' is not a constant expression" for some of the static asserts.

It compiles successfuly on clang.


g++ -std=c++14

constexpr unsigned constexpr_strlen(const char* str)
{
#ifdef WORKAROUND
    if (!__builtin_constant_p(str))
    {
        unsigned i = 0;
        while (str[i] != '\0')
        {
           ++i;
        }
        return i;
    }
#endif
    return __builtin_strlen(str);
}

struct StaticString
{
    constexpr StaticString(const char (&src)[4])
        : buf{src[0], src[1], src[2], src[3]}
    {}

    constexpr const char* data() const
    {
        return &buf[0];
    }

    char buf[4];
};

constexpr StaticString createStaticString()
{
    return {"foo"};
}

constexpr const char *s1 = "foo";
constexpr const char s2[] = "foo";
constexpr StaticString s3{"foo"};
constexpr StaticString s4 = s2;
constexpr StaticString s5 = s4;
constexpr StaticString s6 = createStaticString();

void test()
{
    constexpr const char *s7 = "foo";
    constexpr const char s8[] = "foo";
    constexpr StaticString s9{"foo"};
    constexpr StaticString s10 = s8;
    constexpr StaticString s11 = s10;
    constexpr StaticString s12 = createStaticString();

    static constexpr const char *s13 = "foo";
    static constexpr const char s14[] = "foo";
    static constexpr StaticString s15{"foo"};
    static constexpr StaticString s16 = s14;
    static constexpr StaticString s17 = s6;
    static constexpr StaticString s18 = createStaticString();

    static_assert(constexpr_strlen(s1) == 3, "");
    static_assert(constexpr_strlen(s2) == 3, "");
    static_assert(constexpr_strlen(s3.data()) == 3, "");
    static_assert(constexpr_strlen(s4.data()) == 3, ""); // ERROR
    static_assert(constexpr_strlen(s5.data()) == 3, ""); // ERROR 
    static_assert(constexpr_strlen(s6.data()) == 3, ""); // ERROR
    static_assert(constexpr_strlen(s7) == 3, "");
    static_assert(constexpr_strlen(s8) == 3, ""); // ERROR
    static_assert(constexpr_strlen(s9.data()) == 3, ""); // ERROR
    static_assert(constexpr_strlen(s10.data()) == 3, ""); // ERROR
    static_assert(constexpr_strlen(s11.data()) == 3, ""); // ERROR
    static_assert(constexpr_strlen(s12.data()) == 3, ""); // ERROR
    static_assert(constexpr_strlen(s13) == 3, "");
    static_assert(constexpr_strlen(s14) == 3, "");
    static_assert(constexpr_strlen(s15.data()) == 3, "");
    static_assert(constexpr_strlen(s16.data()) == 3, ""); // ERROR
    static_assert(constexpr_strlen(s17.data()) == 3, ""); // ERROR
    static_assert(constexpr_strlen(s18.data()) == 3, ""); // ERROR
}
Comment 1 Jonathan Wakely 2022-09-08 16:48:32 UTC
You can either use char_traits<char>::length (which is actually required to work in constexpr) or use __builtin_is_constant_evaluated() instead of your workaround.
Comment 2 Jakub Jelinek 2022-09-08 16:54:11 UTC
__builtin_strlen like many other string/memory builtins simply tries the middle-end folding and gives up otherwise.  So if it is called on a string literal, it is folded, but if it would require reading the bytes of the string one by one, it will most likely fail.
This has been discussed in some other PR that we should have a helper that allows constexpr.cc to read a single character and as fallback for the string builtins call those in a loop and essentially evaluate the builtins as a non-optimized trivial C++ implementation of those builtins.
I think std::strlen isn't constexpr even in latest C++, so all of this is permitted by the standard...
Comment 3 Jonathan Wakely 2022-09-08 18:28:50 UTC
(In reply to Jakub Jelinek from comment #2)
> I think std::strlen isn't constexpr even in latest C++, so all of this is
> permitted by the standard...

Right, which is why I recommend using char_traits::length instead.
Comment 4 Drea Pinski 2025-04-18 19:53:09 UTC
*** Bug 119866 has been marked as a duplicate of this bug. ***