mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-16 22:06:36 +00:00

The macro `FUNCTION` is returning the `static` specifier for static templated functions. It's not the case for MSVC. See https://godbolt.org/z/KnhWhqs47 Clang-cl is returning: `__FUNCTION__ static inner::C<class A>::f` `__FUNCTION__ static inner::C<class A>::f` for the reproducer.
1084 lines
30 KiB
C++
1084 lines
30 KiB
C++
// RUN: %clang_cc1 -std=c++1z -fcxx-exceptions -fexceptions -verify %s
|
|
// RUN: %clang_cc1 -std=c++2a -fcxx-exceptions -DUSE_CONSTEVAL -fexceptions -verify %s
|
|
// RUN: %clang_cc1 -std=c++2b -fcxx-exceptions -DUSE_CONSTEVAL -DPAREN_INIT -fexceptions -verify %s
|
|
// RUN: %clang_cc1 -std=c++1z -fcxx-exceptions -fms-extensions -DMS -fexceptions -fms-compatibility -verify %s
|
|
// RUN: %clang_cc1 -std=c++2a -fcxx-exceptions -fms-extensions -DMS -DUSE_CONSTEVAL -fexceptions -fms-compatibility -verify %s
|
|
//
|
|
// RUN: %clang_cc1 -std=c++1z -fcxx-exceptions -fexceptions -fexperimental-new-constant-interpreter -DNEW_INTERP -verify %s
|
|
// RUN: %clang_cc1 -std=c++2a -fcxx-exceptions -DUSE_CONSTEVAL -fexceptions -fexperimental-new-constant-interpreter -DNEW_INTERP -verify %s
|
|
// RUN: %clang_cc1 -std=c++2b -fcxx-exceptions -DUSE_CONSTEVAL -DPAREN_INIT -fexceptions -fexperimental-new-constant-interpreter -DNEW_INTERP -verify %s
|
|
// RUN: %clang_cc1 -std=c++1z -fcxx-exceptions -fms-extensions -DMS -fexceptions -fexperimental-new-constant-interpreter -DNEW_INTERP -fms-compatibility -verify %s
|
|
// RUN: %clang_cc1 -std=c++2a -fcxx-exceptions -fms-extensions -DMS -DUSE_CONSTEVAL -fexceptions -fexperimental-new-constant-interpreter -DNEW_INTERP -verify -fms-compatibility %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;
|
|
|
|
#ifdef USE_CONSTEVAL
|
|
#define SOURCE_LOC_EVAL_KIND consteval
|
|
#else
|
|
#define SOURCE_LOC_EVAL_KIND constexpr
|
|
#endif
|
|
|
|
namespace std {
|
|
class source_location {
|
|
struct __impl;
|
|
|
|
public:
|
|
static SOURCE_LOC_EVAL_KIND 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_FILE_NAME()), const char *>);
|
|
static_assert(is_same<decltype(__builtin_FUNCTION()), const char *>);
|
|
#ifdef MS
|
|
static_assert(is_same<decltype(__builtin_FUNCSIG()), const char *>);
|
|
#endif
|
|
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_FILE_NAME()));
|
|
static_assert(noexcept(__builtin_FUNCTION()));
|
|
#ifdef MS
|
|
static_assert(noexcept(__builtin_FUNCSIG()));
|
|
#endif
|
|
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_FILE_NAME()
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
namespace test_file_name {
|
|
constexpr const char *test_file_name_simple(
|
|
const char *__f = __builtin_FILE_NAME()) {
|
|
return __f;
|
|
}
|
|
void test_function() {
|
|
#line 900
|
|
static_assert(is_equal(test_file_name_simple(), __FILE_NAME__));
|
|
static_assert(is_equal(SLF::test_function_filename(), __FILE_NAME__), "");
|
|
static_assert(is_equal(SLF::test_function_filename_template(42),
|
|
__FILE_NAME__), "");
|
|
|
|
static_assert(is_equal(SLF::test_function_filename_indirect(),
|
|
SLF::global_info_filename), "");
|
|
static_assert(is_equal(SLF::test_function_filename_template_indirect(42),
|
|
SLF::global_info_filename), "");
|
|
|
|
static_assert(test_file_name_simple() != nullptr);
|
|
static_assert(is_equal(test_file_name_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_name;
|
|
constexpr auto Char = F[0];
|
|
static_assert(is_equal(Default.info_file_name, SLF::FILE_NAME), "");
|
|
static_assert(is_equal(InParam.info_file_name, SLF::FILE_NAME), "");
|
|
static_assert(is_equal(InParam.ctor_info_file_name, __FILE_NAME__), "");
|
|
}
|
|
|
|
void test_aggr_class() {
|
|
using Agg = SLF::AggrClass<>;
|
|
constexpr Agg Default{};
|
|
constexpr Agg InitOne{42};
|
|
static_assert(is_equal(Default.init_info_file_name, __FILE_NAME__), "");
|
|
static_assert(is_equal(InitOne.init_info_file_name, __FILE_NAME__), "");
|
|
}
|
|
|
|
} // namespace test_file_name
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// __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(), ""));
|
|
|
|
template <class T>
|
|
class TestBI {
|
|
public:
|
|
TestBI() {
|
|
#ifdef MS
|
|
static_assert(is_equal(__FUNCTION__, "test_func::TestBI<int>::TestBI"));
|
|
#else
|
|
static_assert(is_equal(__FUNCTION__, "TestBI"));
|
|
#endif
|
|
static_assert(is_equal(__func__, "TestBI"));
|
|
}
|
|
};
|
|
|
|
template <class T>
|
|
class TestClass {
|
|
public:
|
|
TestClass() {
|
|
#ifdef MS
|
|
static_assert(is_equal(__FUNCTION__, "test_func::TestClass<class test_func::C>::TestClass"));
|
|
#else
|
|
static_assert(is_equal(__FUNCTION__, "TestClass"));
|
|
#endif
|
|
static_assert(is_equal(__func__, "TestClass"));
|
|
}
|
|
};
|
|
|
|
template <class T>
|
|
class TestStruct {
|
|
public:
|
|
TestStruct() {
|
|
#ifdef MS
|
|
static_assert(is_equal(__FUNCTION__, "test_func::TestStruct<struct test_func::S>::TestStruct"));
|
|
#else
|
|
static_assert(is_equal(__FUNCTION__, "TestStruct"));
|
|
#endif
|
|
static_assert(is_equal(__func__, "TestStruct"));
|
|
}
|
|
};
|
|
|
|
template <class T>
|
|
class TestEnum {
|
|
public:
|
|
TestEnum() {
|
|
#ifdef MS
|
|
static_assert(is_equal(__FUNCTION__, "test_func::TestEnum<enum test_func::E>::TestEnum"));
|
|
#else
|
|
static_assert(is_equal(__FUNCTION__, "TestEnum"));
|
|
#endif
|
|
static_assert(is_equal(__func__, "TestEnum"));
|
|
}
|
|
};
|
|
|
|
class C {};
|
|
struct S {};
|
|
enum E {};
|
|
|
|
TestBI<int> t1;
|
|
TestClass<test_func::C> t2;
|
|
TestStruct<test_func::S> t3;
|
|
TestEnum<test_func::E> t4;
|
|
|
|
class A { int b;};
|
|
namespace inner {
|
|
template <class Ty>
|
|
class C {
|
|
public:
|
|
template <class T>
|
|
static void f(int i) {
|
|
(void)i;
|
|
#ifdef MS
|
|
static_assert(is_equal(__FUNCTION__, "test_func::inner::C<class test_func::A>::f"));
|
|
#else
|
|
static_assert(is_equal(__FUNCTION__, "f"));
|
|
#endif
|
|
}
|
|
template <class T>
|
|
static constexpr void cf(int i) {
|
|
(void)i;
|
|
#ifdef MS
|
|
static_assert(is_equal(__FUNCTION__, "test_func::inner::C<class test_func::A>::cf"));
|
|
#else
|
|
static_assert(is_equal(__FUNCTION__, "cf"));
|
|
#endif
|
|
}
|
|
template <class T>
|
|
static void df(double f) {
|
|
(void)f;
|
|
#ifdef MS
|
|
static_assert(is_equal(__FUNCTION__, "test_func::inner::C<class test_func::A>::df"));
|
|
#else
|
|
static_assert(is_equal(__FUNCTION__, "df"));
|
|
#endif
|
|
}
|
|
template <class T>
|
|
static constexpr void cdf(double f) {
|
|
(void)f;
|
|
#ifdef MS
|
|
static_assert(is_equal(__FUNCTION__, "test_func::inner::C<class test_func::A>::cdf"));
|
|
#else
|
|
static_assert(is_equal(__FUNCTION__, "cdf"));
|
|
#endif
|
|
}
|
|
};
|
|
}
|
|
|
|
void foo() {
|
|
test_func::inner::C<test_func::A>::f<char>(1);
|
|
test_func::inner::C<test_func::A>::cf<char>(1);
|
|
test_func::inner::C<test_func::A>::df<void>(1.0);
|
|
test_func::inner::C<test_func::A>::cdf<void>(1.0);
|
|
}
|
|
|
|
} // namespace test_func
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// __builtin_FUNCSIG()
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifdef MS
|
|
namespace test_funcsig {
|
|
|
|
constexpr const char *test_funcsig_simple(const char *f = __builtin_FUNCSIG()) {
|
|
return f;
|
|
}
|
|
constexpr const char *get_funcsig() {
|
|
return __FUNCSIG__;
|
|
}
|
|
constexpr bool test_funcsig() {
|
|
return is_equal(__FUNCSIG__, test_funcsig_simple()) &&
|
|
!is_equal(get_funcsig(), test_funcsig_simple());
|
|
}
|
|
static_assert(test_funcsig());
|
|
|
|
template <class T>
|
|
constexpr Pair<const char*, const char*> test_funcsig_template(T, const char* f = __builtin_FUNCSIG()) {
|
|
return {f, __builtin_FUNCSIG()};
|
|
}
|
|
template <class T>
|
|
void func_template_tests() {
|
|
constexpr auto P = test_funcsig_template(42);
|
|
static_assert(is_equal(P.first, __FUNCSIG__), "");
|
|
static_assert(!is_equal(P.second, __FUNCSIG__), "");
|
|
}
|
|
template void func_template_tests<int>();
|
|
|
|
template <class = int, class T = const char*>
|
|
struct TestCtor {
|
|
T funcsig = __builtin_FUNCSIG();
|
|
T ctor_funcsig;
|
|
TestCtor() = default;
|
|
template <class F = const char*>
|
|
constexpr TestCtor(int, F f = __builtin_FUNCSIG()) : ctor_funcsig(f) {}
|
|
};
|
|
void ctor_tests() {
|
|
constexpr TestCtor<> Template{42};
|
|
static_assert(is_equal(Template.funcsig, "__cdecl test_funcsig::TestCtor<>::TestCtor(int, F) [T = const char *, F = const char *]"));
|
|
static_assert(is_equal(Template.ctor_funcsig, __FUNCSIG__));
|
|
}
|
|
|
|
constexpr const char* global_funcsig = __builtin_FUNCSIG();
|
|
static_assert(is_equal(global_funcsig, ""));
|
|
|
|
} // namespace test_funcsig
|
|
#endif
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// __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", __builtin_FILE_NAME()));
|
|
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());
|
|
}
|
|
|
|
namespace Lambda {
|
|
#line 8000 "TestLambda.cpp"
|
|
constexpr int nested_lambda(int l = []{
|
|
return SL::current().line();
|
|
}()) {
|
|
return l;
|
|
}
|
|
static_assert(nested_lambda() == __LINE__ - 4);
|
|
|
|
constexpr int lambda_param(int l = [](int l = SL::current().line()) {
|
|
return l;
|
|
}()) {
|
|
return l;
|
|
}
|
|
static_assert(lambda_param() == __LINE__);
|
|
|
|
|
|
}
|
|
|
|
constexpr int compound_literal_fun(int a =
|
|
(int){ SL::current().line() }
|
|
) { return a ;}
|
|
static_assert(compound_literal_fun() == __LINE__);
|
|
|
|
struct CompoundLiteral {
|
|
int a = (int){ SL::current().line() };
|
|
};
|
|
static_assert(CompoundLiteral{}.a == __LINE__);
|
|
|
|
|
|
// FIXME
|
|
// Init captures are subexpressions of the lambda expression
|
|
// so according to the standard immediate invocations in init captures
|
|
// should be evaluated at the call site.
|
|
// However Clang does not yet implement this as it would introduce
|
|
// a fair bit of complexity.
|
|
// We intend to implement that functionality once we find real world
|
|
// use cases that require it.
|
|
constexpr int test_init_capture(int a =
|
|
[b = SL::current().line()] { return b; }()) {
|
|
return a;
|
|
}
|
|
#if defined(USE_CONSTEVAL) && !defined(NEW_INTERP)
|
|
static_assert(test_init_capture() == __LINE__ - 4);
|
|
#else
|
|
static_assert(test_init_capture() == __LINE__ );
|
|
#endif
|
|
|
|
namespace check_immediate_invocations_in_templates {
|
|
|
|
template <typename T = int>
|
|
struct G {
|
|
T line = __builtin_LINE();
|
|
};
|
|
template <typename T>
|
|
struct S {
|
|
int i = G<T>{}.line;
|
|
};
|
|
static_assert(S<int>{}.i != // intentional new line
|
|
S<int>{}.i);
|
|
|
|
template <typename T>
|
|
constexpr int f(int i = G<T>{}.line) {
|
|
return i;
|
|
}
|
|
|
|
static_assert(f<int>() != // intentional new line
|
|
f<int>());
|
|
}
|
|
|
|
#ifdef PAREN_INIT
|
|
namespace GH63903 {
|
|
struct S {
|
|
int _;
|
|
int i = SL::current().line();
|
|
int j = __builtin_LINE();
|
|
};
|
|
// Ensure parent aggregate initialization is consistent with brace
|
|
// aggregate initialization.
|
|
// Note: consteval functions are evaluated where they are used.
|
|
static_assert(S(0).i == __builtin_LINE());
|
|
static_assert(S(0).i == S{0}.i);
|
|
static_assert(S(0).j == S{0}.j);
|
|
static_assert(S(0).j == S{0}.i);
|
|
}
|
|
#endif
|
|
|
|
namespace GH78128 {
|
|
|
|
template<int N>
|
|
constexpr int f() {
|
|
return N;
|
|
}
|
|
|
|
template<typename T>
|
|
void foo() {
|
|
constexpr auto* F1 = std::source_location::current().function();
|
|
static_assert(__builtin_strlen(F1) == f<__builtin_strlen(F1)>());
|
|
|
|
constexpr auto* F2 = __builtin_FUNCTION();
|
|
static_assert(__builtin_strlen(F2) == f<__builtin_strlen(F2)>());
|
|
|
|
#ifdef MS
|
|
constexpr auto* F3 = __builtin_FUNCSIG();
|
|
static_assert(__builtin_strlen(F3) == f<__builtin_strlen(F3)>());
|
|
#endif
|
|
}
|
|
|
|
void test() {
|
|
foo<int>();
|
|
}
|
|
|
|
}
|
|
|
|
namespace GH80630 {
|
|
|
|
#define GH80630_LAMBDA \
|
|
[]( char const* fn ) { \
|
|
static constexpr std::source_location loc = std::source_location::current(); \
|
|
return &loc; \
|
|
}( std::source_location::current().function() )
|
|
|
|
auto f( std::source_location const* loc = GH80630_LAMBDA ) {
|
|
return loc;
|
|
}
|
|
|
|
auto g() {
|
|
return f();
|
|
}
|
|
|
|
}
|
|
|
|
namespace GH92680 {
|
|
|
|
struct IntConstuctible {
|
|
IntConstuctible(std::source_location = std::source_location::current());
|
|
};
|
|
|
|
template <typename>
|
|
auto construct_at(IntConstuctible) -> decltype(IntConstuctible()) {
|
|
return {};
|
|
}
|
|
|
|
void test() {
|
|
construct_at<IntConstuctible>({});
|
|
}
|
|
|
|
}
|
|
|
|
namespace GH106428 {
|
|
|
|
struct add_fn {
|
|
template <typename T>
|
|
constexpr auto operator()(T lhs, T rhs,
|
|
const std::source_location loc = std::source_location::current())
|
|
const -> T
|
|
{
|
|
return lhs + rhs;
|
|
}
|
|
};
|
|
|
|
|
|
template <class _Fp, class... _Args>
|
|
decltype(_Fp{}(0, 0))
|
|
__invoke(_Fp&& __f);
|
|
|
|
template<typename T>
|
|
struct type_identity { using type = T; };
|
|
|
|
template<class Fn>
|
|
struct invoke_result : type_identity<decltype(__invoke(Fn{}))> {};
|
|
|
|
using i = invoke_result<add_fn>::type;
|
|
static_assert(__is_same(i, int));
|
|
|
|
}
|
|
|
|
#if __cplusplus >= 202002L
|
|
|
|
namespace GH81155 {
|
|
struct buff {
|
|
buff(buff &, const char * = __builtin_FUNCTION());
|
|
};
|
|
|
|
template <class Ty>
|
|
Ty declval();
|
|
|
|
template <class Fx>
|
|
auto Call(buff arg) -> decltype(Fx{}(arg));
|
|
|
|
template <typename>
|
|
struct F {};
|
|
|
|
template <class Fx>
|
|
struct InvocableR : F<decltype(Call<Fx>(declval<buff>()))> {
|
|
static constexpr bool value = false;
|
|
};
|
|
|
|
template <class Fx, bool = InvocableR<Fx>::value>
|
|
void Help(Fx) {}
|
|
|
|
void Test() {
|
|
Help([](buff) {});
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
namespace GH67134 {
|
|
template <int loc = std::source_location::current().line()>
|
|
constexpr auto f(std::source_location loc2 = std::source_location::current()) { return loc; }
|
|
|
|
int g = []() -> decltype(f()) { return 0; }();
|
|
|
|
int call() {
|
|
#if __cplusplus >= 202002L
|
|
return []<decltype(f()) = 0>() -> decltype(f()) { return 0; }();
|
|
#endif
|
|
return []() -> decltype(f()) { return 0; }();
|
|
}
|
|
|
|
#if __cplusplus >= 202002L
|
|
template<typename T>
|
|
int Var = requires { []() -> decltype(f()){}; };
|
|
int h = Var<int>;
|
|
#endif
|
|
|
|
|
|
}
|
|
|
|
namespace GH119129 {
|
|
struct X{
|
|
constexpr int foo(std::source_location loc = std::source_location::current()) {
|
|
return loc.line();
|
|
}
|
|
};
|
|
static_assert(X{}.foo() == __LINE__);
|
|
static_assert(X{}.
|
|
foo() == __LINE__);
|
|
static_assert(X{}.
|
|
|
|
|
|
foo() == __LINE__);
|
|
#line 10000
|
|
static_assert(X{}.
|
|
foo() == 10001);
|
|
}
|