Aykhan Hagverdili
Aykhan Hagverdili

Reputation: 29985

Is difference of two constexpr instances of __func__ pointers still constexpr?

Is this valid C++?

int main() {
    constexpr auto sz = __func__ - __func__;
    return sz;
}

GCC and MSVC think it's OK, Clang thinks it's not: Compiler Explorer.


All compilers agree that this one is OK: Compiler Explorer.

int main() {
    constexpr auto p = __func__;
    constexpr auto p2 = p;
    constexpr auto sz = p2 - p;
    return sz;
}

Clang again doesn't like this one, but the others are OK with it: Compiler Explorer

int main() {
    constexpr auto p = __func__;
    constexpr auto p2 = __func__;
    constexpr auto sz = p2 - p;
    return sz;
}

What is up here? I think arithmetic on unrelated pointers is undefined behavior but __func__ returns the same pointer, no? I am not sure, so I thought I may test it. If I recall correctly, std::equal_to can compare unrelated pointers without undefined behavior:

#include <functional>

int main() {
    constexpr std::equal_to<const char*> eq{};
    static_assert(eq(__func__, __func__));
}

Clang thinks eq(__func__, __func__) isn't a constant expression, even though std::equal_to::operator() is constexpr. Other compilers don't complain: Compiler Explorer


Clang won't compile this one either. Complains that __func__ == __func__ is not a constant expression: Compiler Explorer

int main() {
    static_assert(__func__ == __func__);
}

Upvotes: 14

Views: 441

Answers (1)

Nicol Bolas
Nicol Bolas

Reputation: 474266

__func__ in C++ is an identifier. In particular, it references a specific object. From [dcl.fct.def.general]/8:

The function-local predefined variable _­_­func_­_­ is defined as if a definition of the form

static const char __func__[] = "function-name";

had been provided, where function-name is an implementation-defined string. It is unspecified whether such a variable has an address distinct from that of any other object in the program.

As a function-local predefined variable, this definition (as if) appears at the beginning of the function block. As such, any uses of __func__ within that block will refer to that variable.

As for the "any other object" part, a variable defines an object. __func__ names the object defined by that variable. Therefore, within a function, all uses of __func__ name the same variable. What is undefined is whether that variable is a distinct object from other objects.

That is, if you're in a function named foo, and you used the literal "foo" somewhere else in the problem, it is not forbidden for an implementation to have the variable __func__ also be the same object that the literal "foo" returns. That is, the standard doesn't require that every function in which __func__ appears must store data separate from the string literal itself.

Now, C++'s "as if" rule allows implementations to deviate from this, but they cannot do it in a way that would be detectable. So, while the variable itself may or may not have a distinct address from other objects, uses of __func__ in the same function must behave as if they are referring to the same object.

Clang does not seem to implement __func__ this way. It appears to implement it as if it returned a prvalue string literal of the function's name. Two distinct string literals don't have to refer to the same object, so subtracting pointers to them is UB. And undefined behavior in a constant expression context is ill-formed.

The only thing that makes me hesitant to say that Clang is 100% wrong here is [temp.arg.nontype]/2:

For a non-type template-parameter of reference or pointer type, the value of the constant expression shall not refer to (or for a pointer type, shall not be the address of):

...

  • a predefined _­_­func_­_­ variable.

See, this seems to allow some fudging by the implementation. That is, while __func__ can technically be a constant expression, you can't use it in a template parameter. It is treated like a string literal, even though it is technically a variable.

So on some level, I would say that the standard is talking out of both sides of its mouth.

Upvotes: 13

Related Questions