This builtin returns the address of a global instance of the `std::source_location::__impl` type, which must be defined (with an appropriate shape) before calling the builtin. It will be used to implement std::source_location in libc++ in a future change. The builtin is compatible with GCC's implementation, and libstdc++'s usage. An intentional divergence is that GCC declares the builtin's return type to be `const void*` (for ease-of-implementation reasons), while Clang uses the actual type, `const std::source_location::__impl*`. In order to support this new functionality, I've also added a new 'UnnamedGlobalConstantDecl'. This artificial Decl is modeled after MSGuidDecl, and is used to represent a generic concept of an lvalue constant with global scope, deduplicated by its value. It's possible that MSGuidDecl itself, or some of the other similar sorts of things in Clang might be able to be refactored onto this more-generic concept, but there's enough special-case weirdness in MSGuidDecl that I gave up attempting to share code there, at least for now. Finally, for compatibility with libstdc++'s <source_location> header, I've added a second exception to the "cannot cast from void* to T* in constant evaluation" rule. This seems a bit distasteful, but feels like the best available option. Reviewers: aaron.ballman, erichkeane Differential Revision: https://reviews.llvm.org/D120159
596 lines
17 KiB
C++
596 lines
17 KiB
C++
// RUN: %clang_cc1 -std=c++1z -fcxx-exceptions -fexceptions -verify %s
|
|
// expected-no-diagnostics
|
|
|
|
#define assert(...) ((__VA_ARGS__) ? ((void)0) : throw 42)
|
|
#define CURRENT_FROM_MACRO() SL::current()
|
|
#define FORWARD(...) __VA_ARGS__
|
|
|
|
template <unsigned>
|
|
struct Printer;
|
|
|
|
namespace std {
|
|
class source_location {
|
|
struct __impl;
|
|
|
|
public:
|
|
static constexpr source_location current(const __impl *__p = __builtin_source_location()) noexcept {
|
|
source_location __loc;
|
|
__loc.__m_impl = __p;
|
|
return __loc;
|
|
}
|
|
constexpr source_location() = default;
|
|
constexpr source_location(source_location const &) = default;
|
|
constexpr unsigned int line() const noexcept { return __m_impl ? __m_impl->_M_line : 0; }
|
|
constexpr unsigned int column() const noexcept { return __m_impl ? __m_impl->_M_column : 0; }
|
|
constexpr const char *file() const noexcept { return __m_impl ? __m_impl->_M_file_name : ""; }
|
|
constexpr const char *function() const noexcept { return __m_impl ? __m_impl->_M_function_name : ""; }
|
|
|
|
private:
|
|
// Note: The type name "std::source_location::__impl", and its constituent
|
|
// field-names are required by __builtin_source_location().
|
|
struct __impl {
|
|
const char *_M_file_name;
|
|
const char *_M_function_name;
|
|
unsigned _M_line;
|
|
unsigned _M_column;
|
|
};
|
|
const __impl *__m_impl = nullptr;
|
|
|
|
public:
|
|
using public_impl_alias = __impl;
|
|
};
|
|
} // namespace std
|
|
|
|
using SL = std::source_location;
|
|
|
|
#include "Inputs/source-location-file.h"
|
|
namespace SLF = source_location_file;
|
|
|
|
constexpr bool is_equal(const char *LHS, const char *RHS) {
|
|
while (*LHS != 0 && *RHS != 0) {
|
|
if (*LHS != *RHS)
|
|
return false;
|
|
++LHS;
|
|
++RHS;
|
|
}
|
|
return *LHS == 0 && *RHS == 0;
|
|
}
|
|
|
|
template <class T>
|
|
constexpr T identity(T t) {
|
|
return t;
|
|
}
|
|
|
|
template <class T, class U>
|
|
struct Pair {
|
|
T first;
|
|
U second;
|
|
};
|
|
|
|
template <class T, class U>
|
|
constexpr bool is_same = false;
|
|
template <class T>
|
|
constexpr bool is_same<T, T> = true;
|
|
|
|
// test types
|
|
static_assert(is_same<decltype(__builtin_LINE()), unsigned>);
|
|
static_assert(is_same<decltype(__builtin_COLUMN()), unsigned>);
|
|
static_assert(is_same<decltype(__builtin_FILE()), const char *>);
|
|
static_assert(is_same<decltype(__builtin_FUNCTION()), const char *>);
|
|
static_assert(is_same<decltype(__builtin_source_location()), const std::source_location::public_impl_alias *>);
|
|
|
|
// test noexcept
|
|
static_assert(noexcept(__builtin_LINE()));
|
|
static_assert(noexcept(__builtin_COLUMN()));
|
|
static_assert(noexcept(__builtin_FILE()));
|
|
static_assert(noexcept(__builtin_FUNCTION()));
|
|
static_assert(noexcept(__builtin_source_location()));
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// __builtin_LINE()
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
namespace test_line {
|
|
static_assert(SL::current().line() == __LINE__);
|
|
static_assert(SL::current().line() == CURRENT_FROM_MACRO().line());
|
|
|
|
static constexpr SL GlobalS = SL::current();
|
|
|
|
static_assert(GlobalS.line() == __LINE__ - 2);
|
|
|
|
// clang-format off
|
|
constexpr bool test_line_fn() {
|
|
constexpr SL S = SL::current();
|
|
static_assert(S.line() == (__LINE__ - 1), "");
|
|
// The start of the call expression to `current()` begins at the token `SL`
|
|
constexpr int ExpectLine = __LINE__ + 3;
|
|
constexpr SL S2
|
|
=
|
|
SL // Call expression starts here
|
|
::
|
|
current
|
|
(
|
|
|
|
)
|
|
;
|
|
static_assert(S2.line() == ExpectLine, "");
|
|
|
|
static_assert(
|
|
FORWARD(
|
|
__builtin_LINE
|
|
(
|
|
)
|
|
)
|
|
== __LINE__ - 1, "");
|
|
static_assert(\
|
|
\
|
|
__builtin_LINE()\
|
|
\
|
|
== __LINE__ - 2, "");
|
|
static_assert(\
|
|
_\
|
|
_builtin_LINE()
|
|
== __LINE__ - 2, "");
|
|
|
|
return true;
|
|
}
|
|
// clang-format on
|
|
static_assert(test_line_fn());
|
|
|
|
static_assert(__builtin_LINE() == __LINE__, "");
|
|
|
|
constexpr int baz() { return 101; }
|
|
|
|
constexpr int test_line_fn_simple(int z = baz(), int x = __builtin_LINE()) {
|
|
return x;
|
|
}
|
|
void bar() {
|
|
static_assert(test_line_fn_simple() == __LINE__, "");
|
|
static_assert(test_line_fn_simple() == __LINE__, "");
|
|
}
|
|
|
|
struct CallExpr {
|
|
constexpr int operator()(int x = __builtin_LINE()) const { return x; }
|
|
};
|
|
constexpr CallExpr get_call() { return CallExpr{}; }
|
|
static_assert(get_call()() == __LINE__, "");
|
|
|
|
template <class T>
|
|
constexpr bool test_line_fn_template(T Expect, int L = __builtin_LINE()) {
|
|
return Expect == L;
|
|
}
|
|
static_assert(test_line_fn_template(__LINE__));
|
|
|
|
struct InMemInit {
|
|
constexpr bool check(int expect) const {
|
|
return info.line() == expect;
|
|
}
|
|
SL info = SL::current();
|
|
InMemInit() = default;
|
|
constexpr InMemInit(int) {}
|
|
};
|
|
static_assert(InMemInit{}.check(__LINE__ - 3), "");
|
|
static_assert(InMemInit{42}.check(__LINE__ - 3), "");
|
|
|
|
template <class T, class U = SL>
|
|
struct InMemInitTemplate {
|
|
constexpr bool check(int expect) const {
|
|
return info.line() == expect;
|
|
}
|
|
U info = U::current();
|
|
InMemInitTemplate() = default;
|
|
constexpr InMemInitTemplate(T) {}
|
|
constexpr InMemInitTemplate(T, T) : info(U::current()) {}
|
|
template <class V = U> constexpr InMemInitTemplate(T, T, T, V info = U::current())
|
|
: info(info) {}
|
|
};
|
|
void test_mem_init_template() {
|
|
constexpr int line_offset = 8;
|
|
static_assert(InMemInitTemplate<int>{}.check(__LINE__ - line_offset), "");
|
|
static_assert(InMemInitTemplate<unsigned>{42}.check(__LINE__ - line_offset), "");
|
|
static_assert(InMemInitTemplate<unsigned>{42, 42}.check(__LINE__ - line_offset), "");
|
|
static_assert(InMemInitTemplate<unsigned>{42, 42, 42}.check(__LINE__), "");
|
|
}
|
|
|
|
struct AggInit {
|
|
int x;
|
|
int y = __builtin_LINE();
|
|
constexpr bool check(int expect) const {
|
|
return y == expect;
|
|
}
|
|
};
|
|
constexpr AggInit AI{42};
|
|
static_assert(AI.check(__LINE__ - 1), "");
|
|
|
|
template <class T, class U = SL>
|
|
struct AggInitTemplate {
|
|
constexpr bool check(int expect) const {
|
|
return expect == info.line();
|
|
}
|
|
T x;
|
|
U info = U::current();
|
|
};
|
|
|
|
template <class T, class U = SL>
|
|
constexpr U test_fn_template(T, U u = U::current()) {
|
|
return u;
|
|
}
|
|
void fn_template_tests() {
|
|
static_assert(test_fn_template(42).line() == __LINE__, "");
|
|
}
|
|
|
|
struct TestMethodTemplate {
|
|
template <class T, class U = SL, class U2 = SL>
|
|
constexpr U get(T, U u = U::current(), U2 u2 = identity(U2::current())) const {
|
|
assert(u.line() == u2.line());
|
|
return u;
|
|
}
|
|
};
|
|
void method_template_tests() {
|
|
static_assert(TestMethodTemplate{}.get(42).line() == __LINE__, "");
|
|
}
|
|
|
|
struct InStaticInit {
|
|
static constexpr int LINE = __LINE__;
|
|
static constexpr const int x1 = __builtin_LINE();
|
|
static constexpr const int x2 = identity(__builtin_LINE());
|
|
static const int x3;
|
|
const int x4 = __builtin_LINE();
|
|
int x5 = __builtin_LINE();
|
|
};
|
|
const int InStaticInit::x3 = __builtin_LINE();
|
|
static_assert(InStaticInit::x1 == InStaticInit::LINE + 1, "");
|
|
static_assert(InStaticInit::x2 == InStaticInit::LINE + 2, "");
|
|
|
|
template <class T, int N = __builtin_LINE(), int Expect = -1>
|
|
constexpr void check_fn_template_param(T) {
|
|
constexpr int RealExpect = Expect == -1 ? __LINE__ - 2 : Expect;
|
|
static_assert(N == RealExpect);
|
|
}
|
|
template void check_fn_template_param(int);
|
|
template void check_fn_template_param<long, 42, 42>(long);
|
|
|
|
#line 100
|
|
struct AggBase {
|
|
#line 200
|
|
int x = __builtin_LINE();
|
|
int y = __builtin_LINE();
|
|
int z = __builtin_LINE();
|
|
};
|
|
#line 300
|
|
struct AggDer : AggBase {
|
|
};
|
|
#line 400
|
|
static_assert(AggDer{}.x == 400, "");
|
|
|
|
struct ClassBase {
|
|
#line 400
|
|
int x = __builtin_LINE();
|
|
int y = 0;
|
|
int z = 0;
|
|
#line 500
|
|
ClassBase() = default;
|
|
constexpr ClassBase(int yy, int zz = __builtin_LINE())
|
|
: y(yy), z(zz) {}
|
|
};
|
|
struct ClassDer : ClassBase {
|
|
#line 600
|
|
ClassDer() = default;
|
|
constexpr ClassDer(int yy) : ClassBase(yy) {}
|
|
constexpr ClassDer(int yy, int zz) : ClassBase(yy, zz) {}
|
|
};
|
|
#line 700
|
|
static_assert(ClassDer{}.x == 500, "");
|
|
static_assert(ClassDer{42}.x == 501, "");
|
|
static_assert(ClassDer{42}.z == 601, "");
|
|
static_assert(ClassDer{42, 42}.x == 501, "");
|
|
|
|
struct ClassAggDer : AggBase {
|
|
#line 800
|
|
ClassAggDer() = default;
|
|
constexpr ClassAggDer(int, int x = __builtin_LINE()) : AggBase{x} {}
|
|
};
|
|
static_assert(ClassAggDer{}.x == 100, "");
|
|
|
|
} // namespace test_line
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// __builtin_FILE()
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
namespace test_file {
|
|
constexpr const char *test_file_simple(const char *__f = __builtin_FILE()) {
|
|
return __f;
|
|
}
|
|
void test_function() {
|
|
#line 900
|
|
static_assert(is_equal(test_file_simple(), __FILE__));
|
|
static_assert(is_equal(SLF::test_function().file(), __FILE__), "");
|
|
static_assert(is_equal(SLF::test_function_template(42).file(), __FILE__), "");
|
|
|
|
static_assert(is_equal(SLF::test_function_indirect().file(), SLF::global_info.file()), "");
|
|
static_assert(is_equal(SLF::test_function_template_indirect(42).file(), SLF::global_info.file()), "");
|
|
|
|
static_assert(test_file_simple() != nullptr);
|
|
static_assert(!is_equal(test_file_simple(), "source_location.cpp"));
|
|
}
|
|
|
|
void test_class() {
|
|
#line 315
|
|
using SLF::TestClass;
|
|
constexpr TestClass Default;
|
|
constexpr TestClass InParam{42};
|
|
constexpr TestClass Template{42, 42};
|
|
constexpr auto *F = Default.info.file();
|
|
constexpr auto Char = F[0];
|
|
static_assert(is_equal(Default.info.file(), SLF::FILE), "");
|
|
static_assert(is_equal(InParam.info.file(), SLF::FILE), "");
|
|
static_assert(is_equal(InParam.ctor_info.file(), __FILE__), "");
|
|
}
|
|
|
|
void test_aggr_class() {
|
|
using Agg = SLF::AggrClass<>;
|
|
constexpr Agg Default{};
|
|
constexpr Agg InitOne{42};
|
|
static_assert(is_equal(Default.init_info.file(), __FILE__), "");
|
|
static_assert(is_equal(InitOne.init_info.file(), __FILE__), "");
|
|
}
|
|
|
|
} // namespace test_file
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// __builtin_FUNCTION()
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
namespace test_func {
|
|
|
|
constexpr const char *test_func_simple(const char *__f = __builtin_FUNCTION()) {
|
|
return __f;
|
|
}
|
|
constexpr const char *get_function() {
|
|
return __func__;
|
|
}
|
|
constexpr bool test_function() {
|
|
return is_equal(__func__, test_func_simple()) &&
|
|
!is_equal(get_function(), test_func_simple());
|
|
}
|
|
static_assert(test_function());
|
|
|
|
template <class T, class U = SL>
|
|
constexpr Pair<U, U> test_func_template(T, U u = U::current()) {
|
|
static_assert(is_equal(__PRETTY_FUNCTION__, U::current().function()));
|
|
return {u, U::current()};
|
|
}
|
|
template <class T>
|
|
void func_template_tests() {
|
|
constexpr auto P = test_func_template(42);
|
|
//static_assert(is_equal(P.first.function(), __func__), "");
|
|
//static_assert(!is_equal(P.second.function(), __func__), "");
|
|
}
|
|
template void func_template_tests<int>();
|
|
|
|
template <class = int, class T = SL>
|
|
struct TestCtor {
|
|
T info = T::current();
|
|
T ctor_info;
|
|
TestCtor() = default;
|
|
template <class U = SL>
|
|
constexpr TestCtor(int, U u = U::current()) : ctor_info(u) {}
|
|
};
|
|
void ctor_tests() {
|
|
constexpr TestCtor<> Default;
|
|
constexpr TestCtor<> Template{42};
|
|
static const char *XYZZY = Template.info.function();
|
|
static_assert(is_equal(Default.info.function(), "test_func::TestCtor<>::TestCtor() [T = std::source_location]"));
|
|
static_assert(is_equal(Default.ctor_info.function(), ""));
|
|
static_assert(is_equal(Template.info.function(), "test_func::TestCtor<>::TestCtor(int, U) [T = std::source_location, U = std::source_location]"));
|
|
static_assert(is_equal(Template.ctor_info.function(), __PRETTY_FUNCTION__));
|
|
}
|
|
|
|
constexpr SL global_sl = SL::current();
|
|
static_assert(is_equal(global_sl.function(), ""));
|
|
|
|
} // namespace test_func
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// __builtin_COLUMN()
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
namespace test_column {
|
|
|
|
// clang-format off
|
|
constexpr bool test_column_fn() {
|
|
constexpr SL S = SL::current();
|
|
static_assert(S.line() == (__LINE__ - 1), "");
|
|
constexpr int Indent = 4;
|
|
{
|
|
// The start of the call expression to `current()` begins at the token `SL`
|
|
constexpr int ExpectCol = Indent + 3;
|
|
constexpr SL S2
|
|
=
|
|
SL // Call expression starts here
|
|
::
|
|
current
|
|
(
|
|
|
|
)
|
|
;
|
|
static_assert(S2.column() == ExpectCol, "");
|
|
}
|
|
{
|
|
constexpr int ExpectCol = 2;
|
|
constexpr int C =
|
|
__builtin_COLUMN // Expect call expression to start here
|
|
();
|
|
static_assert(C == ExpectCol);
|
|
}
|
|
return true;
|
|
}
|
|
#line 420
|
|
static_assert(test_column_fn());
|
|
|
|
// Test that the column matches the start of the call expression 'SL::current()'
|
|
static_assert(SL::current().column() == __builtin_strlen("static_assert(S"));
|
|
struct TestClass {
|
|
int x = __builtin_COLUMN();
|
|
TestClass() = default; /* indented to 3 spaces for testing */
|
|
constexpr TestClass(int, int o = __builtin_COLUMN()) : x(o) {}
|
|
};
|
|
struct TestAggClass {
|
|
int x = __builtin_COLUMN();
|
|
};
|
|
constexpr bool test_class() {
|
|
|
|
auto check = [](int V, const char* S, int indent = 4) {
|
|
assert(V == (__builtin_strlen(S) + indent));
|
|
};
|
|
{
|
|
TestClass t{};
|
|
check(t.x, " T", 0); // Start of default constructor decl.
|
|
}
|
|
{
|
|
TestClass t1
|
|
{42};
|
|
check(t1.x, "TestClass t"); // Start of variable being constructed.
|
|
}
|
|
{
|
|
TestAggClass t { };
|
|
check(t.x, "TestAggClass t { }");
|
|
}
|
|
{
|
|
TestAggClass t = { };
|
|
check(t.x, "TestAggClass t = { }");
|
|
}
|
|
return true;
|
|
}
|
|
static_assert(test_class());
|
|
// clang-format on
|
|
} // namespace test_column
|
|
|
|
// Test [reflection.src_loc.creation]p2
|
|
// > The value should be affected by #line (C++14 16.4) in the same manner as
|
|
// > for __LINE__ and __FILE__.
|
|
namespace test_pragma_line {
|
|
constexpr int StartLine = 42;
|
|
#line 42
|
|
static_assert(__builtin_LINE() == StartLine);
|
|
static_assert(__builtin_LINE() == StartLine + 1);
|
|
static_assert(SL::current().line() == StartLine + 2);
|
|
#line 44 "test_file.c"
|
|
static_assert(is_equal("test_file.c", __FILE__));
|
|
static_assert(is_equal("test_file.c", __builtin_FILE()));
|
|
static_assert(is_equal("test_file.c", SL::current().file()));
|
|
static_assert(is_equal("test_file.c", SLF::test_function().file()));
|
|
static_assert(is_equal(SLF::FILE, SLF::test_function_indirect().file()));
|
|
} // end namespace test_pragma_line
|
|
|
|
namespace test_out_of_line_init {
|
|
#line 4000 "test_out_of_line_init.cpp"
|
|
constexpr unsigned get_line(unsigned n = __builtin_LINE()) { return n; }
|
|
constexpr const char *get_file(const char *f = __builtin_FILE()) { return f; }
|
|
constexpr const char *get_func(const char *f = __builtin_FUNCTION()) { return f; }
|
|
#line 4100 "A.cpp"
|
|
struct A {
|
|
int n = __builtin_LINE();
|
|
int n2 = get_line();
|
|
const char *f = __builtin_FILE();
|
|
const char *f2 = get_file();
|
|
const char *func = __builtin_FUNCTION();
|
|
const char *func2 = get_func();
|
|
SL info = SL::current();
|
|
};
|
|
#line 4200 "B.cpp"
|
|
struct B {
|
|
A a = {};
|
|
};
|
|
#line 4300 "test_passed.cpp"
|
|
constexpr B b = {};
|
|
static_assert(b.a.n == 4300, "");
|
|
static_assert(b.a.n2 == 4300, "");
|
|
static_assert(b.a.info.line() == 4300, "");
|
|
static_assert(is_equal(b.a.f, "test_passed.cpp"));
|
|
static_assert(is_equal(b.a.f2, "test_passed.cpp"));
|
|
static_assert(is_equal(b.a.info.file(), "test_passed.cpp"));
|
|
static_assert(is_equal(b.a.func, ""));
|
|
static_assert(is_equal(b.a.func2, ""));
|
|
static_assert(is_equal(b.a.info.function(), ""));
|
|
|
|
constexpr bool test_in_func() {
|
|
#line 4400 "test_func_passed.cpp"
|
|
constexpr B b = {};
|
|
static_assert(b.a.n == 4400, "");
|
|
static_assert(b.a.n2 == 4400, "");
|
|
static_assert(b.a.info.line() == 4400, "");
|
|
static_assert(is_equal(b.a.f, "test_func_passed.cpp"));
|
|
static_assert(is_equal(b.a.f2, "test_func_passed.cpp"));
|
|
static_assert(is_equal(b.a.info.file(), "test_func_passed.cpp"));
|
|
static_assert(is_equal(b.a.func, "test_in_func"));
|
|
static_assert(is_equal(b.a.func2, "test_in_func"));
|
|
static_assert(is_equal(b.a.info.function(), "bool test_out_of_line_init::test_in_func()"));
|
|
return true;
|
|
}
|
|
static_assert(test_in_func());
|
|
|
|
} // end namespace test_out_of_line_init
|
|
|
|
namespace test_global_scope {
|
|
#line 5000 "test_global_scope.cpp"
|
|
constexpr unsigned get_line(unsigned n = __builtin_LINE()) { return n; }
|
|
constexpr const char *get_file(const char *f = __builtin_FILE()) { return f; }
|
|
constexpr const char *get_func(const char *f = __builtin_FUNCTION()) { return f; }
|
|
#line 5100
|
|
struct InInit {
|
|
unsigned l = get_line();
|
|
const char *f = get_file();
|
|
const char *func = get_func();
|
|
|
|
#line 5200 "in_init.cpp"
|
|
constexpr InInit() {}
|
|
};
|
|
#line 5300
|
|
constexpr InInit II;
|
|
|
|
static_assert(II.l == 5200, "");
|
|
static_assert(is_equal(II.f, "in_init.cpp"));
|
|
static_assert(is_equal(II.func, "InInit"));
|
|
|
|
#line 5400
|
|
struct AggInit {
|
|
unsigned l = get_line();
|
|
const char *f = get_file();
|
|
const char *func = get_func();
|
|
};
|
|
#line 5500 "brace_init.cpp"
|
|
constexpr AggInit AI = {};
|
|
static_assert(AI.l == 5500);
|
|
static_assert(is_equal(AI.f, "brace_init.cpp"));
|
|
static_assert(is_equal(AI.func, ""));
|
|
|
|
} // namespace test_global_scope
|
|
|
|
namespace TestFuncInInit {
|
|
#line 6000 "InitClass.cpp"
|
|
struct Init {
|
|
SL info;
|
|
#line 6100 "InitCtor.cpp"
|
|
constexpr Init(SL info = SL::current()) : info(info) {}
|
|
};
|
|
#line 6200 "InitGlobal.cpp"
|
|
constexpr Init I;
|
|
static_assert(I.info.line() == 6200);
|
|
static_assert(is_equal(I.info.file(), "InitGlobal.cpp"));
|
|
|
|
} // namespace TestFuncInInit
|
|
|
|
namespace TestConstexprContext {
|
|
#line 7000 "TestConstexprContext.cpp"
|
|
constexpr const char* foo() { return __builtin_FILE(); }
|
|
#line 7100 "Bar.cpp"
|
|
constexpr const char* bar(const char* x = foo()) { return x; }
|
|
constexpr bool test() {
|
|
static_assert(is_equal(bar(), "TestConstexprContext.cpp"));
|
|
return true;
|
|
}
|
|
static_assert(test());
|
|
}
|