Issue #178324 was actually fixed by #187755 We lost the "declaration does not declare anything" warning since the regression was introduced, but that was because: 1) Since #78436 we treat __builtin_FUNCSIG in a dependent context effectivelly as if it contained a template parameter. 2) Our decltype implementation treats eexpressions containing template parameters as if they were completely opaque (but alas this goes against the spec, which says in [temp.type]p4 this should be looking only at type dependence). 3) Since the decltype is opaque, we don't know what lookup will find, so we can't issue the warning because we don't know if we are going to end up with a type or an expression. Fixes #178324
1094 lines
30 KiB
C++
1094 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);
|
|
}
|
|
|
|
#ifdef MS
|
|
namespace GH178324 {
|
|
struct a {
|
|
using e = int;
|
|
};
|
|
void current(const char * = __builtin_FUNCSIG());
|
|
template <class> void c() { decltype(a(current()))::e; }
|
|
} // namespace GH178324
|
|
#endif
|