In which context default argument of immediate function is substituted in C++20 (on source location example)?
Asked Answered
G

1

6

In C++20 a new feature was added to get the source location information: https://en.cppreference.com/w/cpp/utility/source_location

Here is a slightly modified example from that page, where an addition immediate function loc is used to get the source location:

#include <iostream>
#include <string_view>
#include <source_location>
 
consteval auto loc(std::source_location x = std::source_location::current() ) { return x; }

void log(const std::string_view message,
         const std::source_location location = loc()) {
    std::cout << "file: "
              << location.file_name() << "("
              << location.line() << ":"
              << location.column() << ") `"
              << location.function_name() << "`: "
              << message << '\n';
}
 
template <typename T> void fun(T x) { log(x); }
 
int main(int, char*[]) {
    log("Hello world!");
    fun("Hello C++20!");
}

In the latest MSVC 2019 it prints as in the original example from cppreference.com:

file: main.cpp(25:5) `main`: Hello world!
file: main.cpp(20:5) `fun`: Hello C++20!

But in GCC the same line is indicated twice in the output:

file: /app/example.cpp(8:51) ``: Hello world!
file: /app/example.cpp(8:51) ``: Hello C++20!

demo: https://gcc.godbolt.org/z/nqE4cr9d4

Which of the compilers is right here?

And if define loc function not as immediate one:

auto loc(std::source_location x = std::source_location::current() ) { return x; }

then the output of GCC changes and resembles the original example:

file: /app/example.cpp(20:8) `int main(int, char**)`: Hello world!
file: /app/example.cpp(17:42) `void fun(T) [with T = const char*]`: Hello C++20!

While MSVC refuses to compile it with the error:

error C7595: 'std::source_location::current': call to immediate function is not a constant expression

demo: https://gcc.godbolt.org/z/vorW4f9ax

Please also suggest, which compiler is right in not-immediate case as well?

Grearson answered 13/8, 2021 at 10:25 Comment(1)
It is undefined behavior unless the standard says how it should behave in such cases. You make the code harder to read to anyone who don't know what loc does. Declare log function as intended using std::source_location::current() as the default value.Phillis
T
3

This is explained in 17.8.2.1.2 [support.srcloc.class] (emphasis mine):

Remarks: Any call to current that appears as a default member initializer (11.4), or as a subexpression thereof, should correspond to the location of the constructor definition or aggregate initialization that uses the default member initializer. Any call to current that appears as a default argument (9.3.3.6), or as a subexpression thereof, should correspond to the location of the invocation of the function that uses the default argument (7.6.1.2).

From this, I deduce that GCC is right.

When the call to current happens in line 5, it returns a source_location object that "correspond[s] to the location of the invocation of the function (in this case the function loc) that uses the default argument".

In this case the invocation location is 8:51 (the expression const std::source_location location = loc()).

Why the function name is empty is explained by the following:

  1. 17.8.2.1.1.1 (Table 38) tells us that the function name should be "such as in" __func__.
Element Value
function_name_ A name of the current function such as in __func__ (9.5.1) if any, an empty string otherwise.
  1. 9.5.1.8 Example shows that if __func__ appears as a default argument, the name is undefined. I know that examples are nonnormative text, but this clearly describes the intent:

[Example:

struct S {
S() : s(__func__) { } // OK
const char* s;
};
void f(const char* s = __func__); // error: __func__ is undeclared

— end example]

Tisbee answered 13/8, 2021 at 11:42 Comment(6)
[dcl.fct.def.general]/7 explains (normatively) that __func__ is usable only in the function-body; this way the function type is known should the implementation want to include it in the name.Comenius
This doesn’t address the non-consteval loc case (which I don’t think should change anything).Comenius
@DavisHerring I know it doesn't address the second issue, but I wanted to at least help with the first question, as I wasn't able to find an answer for the second one yet. I thought that maybe the issue in g++ was caused by inlining and MSVC had a bug. However, no matter what I did - marking the function as noinline or using -O0 - nothing changed.Tisbee
I believe the quoted remark relates to default initialization of class members and not to default values of a function argument. But good idea, let us consider this example as well. Here GCC prints the third distinct result, pointing now to function log: gcc.godbolt.org/z/GsW1586n1Grearson
@Grearson The excerpt from 17.8.2.1.2 [support.srcloc.class] refers to both in-class initialization and default function arguments. The emphasized part says that it refers to "Any call to current that appears as a default argument (9.3.3.6), or as a subexpression thereof". The referenced section 9.3.3.6 is [dcl.fct.default], which should dispel any ambiguity whether the excerpt applies to the situation in question.Tisbee
The described behavior in the question was with GCC 12, while it was changed in GCC 13, which now resembles MSVC 2019: gcc.godbolt.org/z/Tazfd8K4MGrearson

© 2022 - 2024 — McMap. All rights reserved.