EricWF updated this revision to Diff 115161. EricWF marked an inline comment as done. EricWF added a comment.
- Reimplement without rewriting the AST and instead during the substitution during constant expression evaluation and code gen. I still haven't implemented Richards suggestion to reduce the size of `source_location` struct, but I think that would be better done as a separate patch. These builtins still need to be implemented, and to act like their GNU counterparts. I haven't cleaned this patch up yet, but I plan to do so tomorrow. There are going to be silly mistakes until then. https://reviews.llvm.org/D37035 Files: docs/LanguageExtensions.rst include/clang/AST/Decl.h include/clang/AST/Expr.h include/clang/AST/ExprCXX.h include/clang/AST/RecursiveASTVisitor.h include/clang/AST/Stmt.h include/clang/Basic/StmtNodes.td include/clang/Basic/TokenKinds.def include/clang/Sema/Sema.h include/clang/Serialization/ASTBitCodes.h lib/AST/ASTImporter.cpp lib/AST/Expr.cpp lib/AST/ExprCXX.cpp lib/AST/ExprClassification.cpp lib/AST/ExprConstant.cpp lib/AST/ItaniumMangle.cpp lib/AST/StmtPrinter.cpp lib/AST/StmtProfile.cpp lib/CodeGen/CGExpr.cpp lib/CodeGen/CGExprAgg.cpp lib/CodeGen/CGExprComplex.cpp lib/CodeGen/CGExprScalar.cpp lib/CodeGen/CodeGenFunction.h lib/Parse/ParseExpr.cpp lib/Sema/SemaDeclCXX.cpp lib/Sema/SemaExceptionSpec.cpp lib/Sema/SemaExpr.cpp lib/Sema/TreeTransform.h lib/Serialization/ASTReaderStmt.cpp lib/Serialization/ASTWriterStmt.cpp lib/StaticAnalyzer/Core/ExprEngine.cpp test/CodeGenCXX/builtin_FUNCTION.cpp test/CodeGenCXX/builtin_LINE.cpp test/CodeGenCXX/debug-info-line.cpp test/Parser/builtin_source_location.c test/Sema/source_location.c test/SemaCXX/Inputs/source-location-file.h test/SemaCXX/loc2.cpp test/SemaCXX/source_location.cpp test/SemaCXX/test.cpp
Index: test/SemaCXX/test.cpp =================================================================== --- /dev/null +++ test/SemaCXX/test.cpp @@ -0,0 +1,35 @@ +struct SL { + static constexpr SL current( + const char* f = __builtin_FUNCTION(), + unsigned l = __builtin_LINE()) { + return {f, l}; + } + const char* function; + unsigned line; +}; + +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, class U = SL> +constexpr const char* test_func_template(T, U u = U::current()) { + static_assert(is_equal(U::current().function, __func__)); + static_assert(is_equal(U::current().function, "")); + static_assert(U::current().line== __LINE__); + static_assert(U::current().line== __LINE__); + + return u.function; +} +template <class T> +void func_template_tests() { + constexpr auto P = test_func_template(42); +} +template void func_template_tests<int>(); Index: test/SemaCXX/source_location.cpp =================================================================== --- /dev/null +++ test/SemaCXX/source_location.cpp @@ -0,0 +1,475 @@ +// 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__ + +namespace std { +namespace experimental { +struct source_location { +private: + unsigned int __m_line = 0; + unsigned int __m_col = 0; + const char *__m_file = nullptr; + const char *__m_func = nullptr; +public: + static constexpr source_location current( + const char *__file = __builtin_FILE(), + const char *__func = __builtin_FUNCTION(), + unsigned int __line = __builtin_LINE(), + unsigned int __col = __builtin_COLUMN()) noexcept { + source_location __loc; + __loc.__m_line = __line; + __loc.__m_col = __col; + __loc.__m_file = __file; + __loc.__m_func = __func; + return __loc; + } + constexpr source_location() = default; + constexpr source_location(source_location const &) = default; + constexpr unsigned int line() const noexcept { return __m_line; } + constexpr unsigned int column() const noexcept { return __m_col; } + constexpr const char *file() const noexcept { return __m_file; } + constexpr const char *function() const noexcept { return __m_func; } +}; +} // namespace experimental +} // namespace std + +using SL = std::experimental::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 *>); + +// test noexcept +static_assert(noexcept(__builtin_LINE())); +static_assert(noexcept(__builtin_COLUMN())); +static_assert(noexcept(__builtin_FILE())); +static_assert(noexcept(__builtin_FUNCTION())); + +//===----------------------------------------------------------------------===// +// __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 313 + using SLF::TestClass; + constexpr TestClass Default; + constexpr TestClass InParam{42}; + constexpr TestClass Template{42, 42}; + 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(__func__, 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_assert(!is_equal(Default.info.function(), __func__)); + static_assert(is_equal(Default.info.function(), "TestCtor")); + static_assert(is_equal(Template.info.function(), "TestCtor")); + static_assert(is_equal(Template.ctor_info.function(), __func__)); +} + +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 Index: test/SemaCXX/loc2.cpp =================================================================== --- /dev/null +++ test/SemaCXX/loc2.cpp @@ -0,0 +1,78 @@ +// 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__ + +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 *>); + +// test noexcept +static_assert(noexcept(__builtin_LINE())); +static_assert(noexcept(__builtin_COLUMN())); +static_assert(noexcept(__builtin_FILE())); +static_assert(noexcept(__builtin_FUNCTION())); + +#line 500 +constexpr const int x = __builtin_LINE(); +const int x_static = __builtin_LINE(); +static_assert(x == 500, ""); + +#line 600 "loc2.cpp" +constexpr const char* y = __builtin_FILE(); +const char* const yy = __builtin_FILE(); +const char* const yyy = __FILE__; +static_assert(is_equal(y, "loc2.cpp"), ""); +static_assert(!is_equal(y, "abc"), ""); + +#line 10 +const volatile void* volatile sink = nullptr; +void foo() { + sink = &yy; + sink = &yyy; + sink = __FUNCTION__; + sink = __FILE__; + constexpr const char *_ = __FUNCTION__; +} + +constexpr unsigned in_default(int x = __builtin_LINE()) { + return x; +} +#line 700 +static_assert(in_default() == 700); + +constexpr unsigned in_body() { +#line 800 + return __builtin_LINE(); +} +static_assert(in_body() == 800, ""); Index: test/SemaCXX/Inputs/source-location-file.h =================================================================== --- /dev/null +++ test/SemaCXX/Inputs/source-location-file.h @@ -0,0 +1,43 @@ + +// NOTE: source_location.cpp must include this file after defining +// std::source_location. +namespace source_location_file { + +constexpr const char *FILE = __FILE__; +constexpr SL global_info = SL::current(); + +constexpr SL test_function(SL v = SL::current()) { + return v; +} + +constexpr SL test_function_indirect() { + return test_function(); +} + +template <class T, class U = SL> +constexpr U test_function_template(T, U u = U::current()) { + return u; +} + +template <class T, class U = SL> +constexpr U test_function_template_indirect(T t) { + return test_function_template(t); +} + +struct TestClass { + SL info = SL::current(); + SL ctor_info; + TestClass() = default; + constexpr TestClass(int, SL cinfo = SL::current()) : ctor_info(cinfo) {} + template <class T, class U = SL> + constexpr TestClass(int, T, U u = U::current()) : ctor_info(u) {} +}; + +template <class T = SL> +struct AggrClass { + int x; + T info; + T init_info = T::current(); +}; + +} // namespace source_location_file Index: test/Sema/source_location.c =================================================================== --- /dev/null +++ test/Sema/source_location.c @@ -0,0 +1,32 @@ +// RUN: %clang_cc1 -std=c90 -fconst-strings -DCONST_STRINGS -verify %s +// RUN: %clang_cc1 -std=c90 -verify %s + +// expected-no-diagnostics + +#define IsEqual(L, R) (__builtin_strcmp(L, R) == 0) + +const char *const FILE = __builtin_FILE(); +const char *const FUNC = __builtin_FUNCTION(); +const unsigned LINE = __builtin_LINE(); +const unsigned COL = __builtin_COLUMN(); + +#ifndef CONST_STRINGS +char *const NCFILE = __builtin_FILE(); +char *const NCFUNC = __builtin_FUNCTION(); +#endif + +#ifdef CONST_STRINGS +_Static_assert(IsEqual(__builtin_FILE(), __FILE__), ""); +_Static_assert(__builtin_LINE() == __LINE__, ""); +_Static_assert(IsEqual("", __builtin_FUNCTION()), ""); + +#line 42 "my_file.c" +_Static_assert(__builtin_LINE() == 42, ""); +_Static_assert(IsEqual(__builtin_FILE(), "my_file.c"), ""); + +_Static_assert(__builtin_COLUMN() == __builtin_strlen("_Static_assert(_"), ""); + +void foo() { + _Static_assert(IsEqual(__builtin_FUNCTION(), "foo"), ""); +} +#endif // CONST_STRINGS Index: test/Parser/builtin_source_location.c =================================================================== --- /dev/null +++ test/Parser/builtin_source_location.c @@ -0,0 +1,19 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +int main() { + int line = __builtin_LINE(); + __builtin_LINE(42); // expected-error {{expected ')'}} + __builtin_LINE(double); // expected-error {{expected ')'}} + + int column = __builtin_COLUMN(); + __builtin_COLUMN(42); // expected-error {{expected ')'}} + __builtin_COLUMN(double); // expected-error {{expected ')'}} + + const char *func = __builtin_FUNCTION(); + __builtin_FUNCTION(42); // expected-error {{expected ')'}} + __builtin_FUNCTION(double); // expected-error {{expected ')'}} + + const char *file = __builtin_FILE(); + __builtin_FILE(42); // expected-error {{expected ')'}} + __builtin_FILE(double); // expected-error {{expected ')'}} +} Index: test/CodeGenCXX/debug-info-line.cpp =================================================================== --- test/CodeGenCXX/debug-info-line.cpp +++ test/CodeGenCXX/debug-info-line.cpp @@ -291,6 +291,13 @@ f24_a(); } +// CHECK-LABEL: define +void f25_a(int x = __builtin_LINE()) {} +void f25() { + // CHECK: call void @_Z5f25_ai(i32 2700) +#line 2700 + f25_a(); +} // CHECK: [[DBG_F1]] = !DILocation(line: 100, // CHECK: [[DBG_FOO_VALUE]] = !DILocation(line: 200, // CHECK: [[DBG_FOO_REF]] = !DILocation(line: 202, Index: test/CodeGenCXX/builtin_LINE.cpp =================================================================== --- /dev/null +++ test/CodeGenCXX/builtin_LINE.cpp @@ -0,0 +1,71 @@ +// RUN: %clang_cc1 -std=c++1z -fblocks %s -triple %itanium_abi_triple -emit-llvm -o - | FileCheck %s + +extern "C" int sink; +extern "C" const volatile void* volatile ptr_sink = nullptr; + +struct Tag1 {}; +struct Tag2 {}; +struct Tag3 {}; +struct Tag4 {}; + +constexpr int get_line_constexpr(int l = __builtin_LINE()) { + return l; +} + +int get_line_nonconstexpr(int l = __builtin_LINE()) { + return l; +} + +// CHECK: @global_one = global i32 [[@LINE+1]], align 4 +int global_one = __builtin_LINE(); +// CHECK-NEXT: @global_two = global i32 [[@LINE+1]], align 4 +int global_two = get_line_constexpr(); +// CHECK: @_ZL12global_three = internal constant i32 [[@LINE+1]], align 4 +const int global_three(get_line_constexpr()); + +// CHECK-LABEL: define internal void @__cxx_global_var_init +// CHECK: %call = call i32 @_Z21get_line_nonconstexpri(i32 [[@LINE+2]]) +// CHECK-NEXT: store i32 %call, i32* @global_four, align 4 +int global_four = get_line_nonconstexpr(); + +struct InClassInit { + int Init = __builtin_LINE(); + InClassInit() = default; + constexpr InClassInit(Tag1, int l = __builtin_LINE()) : Init(l) {} + constexpr InClassInit(Tag2) : Init(__builtin_LINE()) {} + InClassInit(Tag3, int l = __builtin_LINE()) : Init(l) {} + InClassInit(Tag4) : Init(__builtin_LINE()) {} + + static void test_class(); +}; +// CHECK-LABEL: define void @_ZN11InClassInit10test_classEv() +void InClassInit::test_class() { + // CHECK: call void @_ZN11InClassInitC1Ev(%struct.InClassInit* %test_one) + InClassInit test_one; + // CHECK-NEXT: call void @_ZN11InClassInitC1E4Tag1i(%struct.InClassInit* %test_two, i32 [[@LINE+1]]) + InClassInit test_two{Tag1{}}; + // CHECK-NEXT: call void @_ZN11InClassInitC1E4Tag2(%struct.InClassInit* %test_three) + InClassInit test_three{Tag2{}}; + // CHECK-NEXT: call void @_ZN11InClassInitC1E4Tag3i(%struct.InClassInit* %test_four, i32 [[@LINE+1]]) + InClassInit test_four(Tag3{}); + // CHECK-NEXT: call void @_ZN11InClassInitC1E4Tag4(%struct.InClassInit* %test_five) + InClassInit test_five{Tag4{}}; +} + +int get_line(int l = __builtin_LINE()) { + return l; +} + +// CHECK-LABEL: define void @_Z13get_line_testv() +void get_line_test() { + // CHECK: %[[CALL:.+]] = call i32 @_Z8get_linei(i32 [[@LINE+2]]) + // CHECK-NEXT: store i32 %[[CALL]], i32* @sink, align 4 + sink = get_line(); + // CHECK-NEXT: store i32 [[@LINE+1]], i32* @sink, align 4 + sink = __builtin_LINE(); + ptr_sink = &global_three; +} + +void foo() { + const int N[] = {__builtin_LINE(), get_line_constexpr()}; +} Index: test/CodeGenCXX/builtin_FUNCTION.cpp =================================================================== --- /dev/null +++ test/CodeGenCXX/builtin_FUNCTION.cpp @@ -0,0 +1,47 @@ +// RUN: %clang_cc1 -std=c++1z -fblocks %s -triple %itanium_abi_triple -emit-llvm -o - | FileCheck %s + +namespace test_func { + +constexpr const char *test_default_arg(const char *f = __builtin_FUNCTION()) { + return f; +} +// CHECK: @[[EMPTY_STR:.+]] = private unnamed_addr constant [1 x i8] zeroinitializer, align 1 + +// CHECK: @_ZN9test_func6globalE = global i8* getelementptr inbounds ([1 x i8], [1 x i8]* @[[EMPTY_STR]], i32 0, i32 0), align 8 +const char *global = test_default_arg(); + +// CHECK: @_ZN9test_func10global_twoE = global i8* getelementptr inbounds ([1 x i8], [1 x i8]* @[[EMPTY_STR]], i32 0, i32 0), align 8 +const char *global_two = __builtin_FUNCTION(); + +const char * const global_three = test_default_arg(); + +// CHECK: @[[STR_ONE:.+]] = private unnamed_addr constant [14 x i8] c"test_func_one\00", align 1 +// CHECK: @[[STR_TWO:.+]] = private unnamed_addr constant [14 x i8] c"test_func_two\00", align 1 +// CHECK: @[[STR_THREE:.+]] = private unnamed_addr constant [20 x i8] c"do_default_arg_test\00", align 1 + +// CHECK: define i8* @_ZN9test_func13test_func_oneEv() +// CHECK: ret i8* getelementptr inbounds ([14 x i8], [14 x i8]* @[[STR_ONE]], i32 0, i32 0) +const char *test_func_one() { + return __builtin_FUNCTION(); +} + +// CHECK: define i8* @_ZN9test_func13test_func_twoEv() +// CHECK: ret i8* getelementptr inbounds ([14 x i8], [14 x i8]* @[[STR_TWO]], i32 0, i32 0) +const char *test_func_two() { + return __builtin_FUNCTION(); +} + +// CHECK: define void @_ZN9test_func19do_default_arg_testEv() +// CHECK: %call = call i8* @_ZN9test_func16test_default_argEPKc(i8* getelementptr inbounds ([20 x i8], [20 x i8]* @[[STR_THREE]], i32 0, i32 0)) +void do_default_arg_test() { + test_default_arg(); +} + +template <class> +void test_template(const char *f = __builtin_FUNCTION()) { + (void)__builtin_FUNCTION(); +} +void do_template_test() { + test_template<int>(); +} +} // namespace test_func Index: lib/StaticAnalyzer/Core/ExprEngine.cpp =================================================================== --- lib/StaticAnalyzer/Core/ExprEngine.cpp +++ lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -1021,6 +1021,7 @@ case Stmt::NoInitExprClass: case Stmt::SizeOfPackExprClass: case Stmt::StringLiteralClass: + case Stmt::SourceLocExprClass: case Stmt::ObjCStringLiteralClass: case Stmt::CXXPseudoDestructorExprClass: case Stmt::SubstNonTypeTemplateParmExprClass: Index: lib/Serialization/ASTWriterStmt.cpp =================================================================== --- lib/Serialization/ASTWriterStmt.cpp +++ lib/Serialization/ASTWriterStmt.cpp @@ -843,6 +843,15 @@ Code = serialization::EXPR_VA_ARG; } +void ASTStmtWriter::VisitSourceLocExpr(SourceLocExpr *E) { + VisitExpr(E); + Record.AddDeclRef(cast_or_null<Decl>(E->getParentContext())); + Record.AddSourceLocation(E->getLocStart()); + Record.AddSourceLocation(E->getLocEnd()); + Record.push_back(E->getIdentType()); + Code = serialization::EXPR_SOURCE_LOC; +} + void ASTStmtWriter::VisitAddrLabelExpr(AddrLabelExpr *E) { VisitExpr(E); Record.AddSourceLocation(E->getAmpAmpLoc()); @@ -1386,13 +1395,15 @@ void ASTStmtWriter::VisitCXXDefaultArgExpr(CXXDefaultArgExpr *E) { VisitExpr(E); Record.AddDeclRef(E->getParam()); + Record.AddDeclRef(cast_or_null<Decl>(E->getUsedContext())); Record.AddSourceLocation(E->getUsedLocation()); Code = serialization::EXPR_CXX_DEFAULT_ARG; } void ASTStmtWriter::VisitCXXDefaultInitExpr(CXXDefaultInitExpr *E) { VisitExpr(E); Record.AddDeclRef(E->getField()); + Record.AddDeclRef(cast_or_null<Decl>(E->getUsedContext())); Record.AddSourceLocation(E->getExprLoc()); Code = serialization::EXPR_CXX_DEFAULT_INIT; } Index: lib/Serialization/ASTReaderStmt.cpp =================================================================== --- lib/Serialization/ASTReaderStmt.cpp +++ lib/Serialization/ASTReaderStmt.cpp @@ -862,6 +862,14 @@ E->setIsMicrosoftABI(Record.readInt()); } +void ASTStmtReader::VisitSourceLocExpr(SourceLocExpr *E) { + VisitExpr(E); + E->setParentContext(ReadDeclAs<DeclContext>()); + E->setLocStart(ReadSourceLocation()); + E->setLocEnd(ReadSourceLocation()); + E->setIdentType(static_cast<SourceLocExpr::IdentType>(Record.readInt())); +} + void ASTStmtReader::VisitAddrLabelExpr(AddrLabelExpr *E) { VisitExpr(E); E->setAmpAmpLoc(ReadSourceLocation()); @@ -1376,12 +1384,14 @@ void ASTStmtReader::VisitCXXDefaultArgExpr(CXXDefaultArgExpr *E) { VisitExpr(E); E->Param = ReadDeclAs<ParmVarDecl>(); + E->UsedContext = ReadDeclAs<DeclContext>(); E->Loc = ReadSourceLocation(); } void ASTStmtReader::VisitCXXDefaultInitExpr(CXXDefaultInitExpr *E) { VisitExpr(E); E->Field = ReadDeclAs<FieldDecl>(); + E->UsedContext = ReadDeclAs<DeclContext>(); E->Loc = ReadSourceLocation(); } @@ -3342,6 +3352,10 @@ S = new (Context) VAArgExpr(Empty); break; + case EXPR_SOURCE_LOC: + S = new (Context) SourceLocExpr(Empty); + break; + case EXPR_ADDR_LABEL: S = new (Context) AddrLabelExpr(Empty); break; Index: lib/Sema/TreeTransform.h =================================================================== --- lib/Sema/TreeTransform.h +++ lib/Sema/TreeTransform.h @@ -2660,9 +2660,9 @@ /// By default, builds a new default-argument expression, which does not /// require any semantic analysis. Subclasses may override this routine to /// provide different behavior. - ExprResult RebuildCXXDefaultArgExpr(SourceLocation Loc, - ParmVarDecl *Param) { - return CXXDefaultArgExpr::Create(getSema().Context, Loc, Param); + ExprResult RebuildCXXDefaultArgExpr(SourceLocation Loc, ParmVarDecl *Param) { + return CXXDefaultArgExpr::Create(getSema().Context, Loc, Param, + getSema().CurContext); } /// \brief Build a new C++11 default-initialization expression. @@ -2672,7 +2672,8 @@ /// routine to provide different behavior. ExprResult RebuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) { - return CXXDefaultInitExpr::Create(getSema().Context, Loc, Field); + return CXXDefaultInitExpr::Create(getSema().Context, Loc, Field, + getSema().CurContext); } /// \brief Build a new C++ zero-initialization expression. @@ -2928,6 +2929,18 @@ RParenLoc, Length, PartialArgs); } + /// \brief Build a new expression representing a call to a source location + /// builtin. + /// + /// By default, performs semantic analysis to build the new expression. + /// Subclasses may override this routine to provide different behavior. + ExprResult RebuildSourceLocExpr(SourceLocExpr::IdentType Type, + SourceLocation BuiltinLoc, + SourceLocation RPLoc, + DeclContext *ParentContext) { + return getSema().BuildSourceLocExpr(Type, BuiltinLoc, RPLoc, ParentContext); + } + /// \brief Build a new Objective-C boxed expression. /// /// By default, performs semantic analysis to build the new expression. @@ -9793,6 +9806,20 @@ } template <typename Derived> +ExprResult TreeTransform<Derived>::TransformSourceLocExpr(SourceLocExpr *E) { + bool NeedRebuildFunc = E->getIdentType() == SourceLocExpr::Function && + isa<FunctionDecl>(getSema().CurContext) && + getSema().CurContext != E->getParentContext(); + + if (!getDerived().AlwaysRebuild() && !NeedRebuildFunc) + return E; + + return getDerived().RebuildSourceLocExpr(E->getIdentType(), E->getLocStart(), + E->getLocEnd(), + getSema().CurContext); +} + +template<typename Derived> ExprResult TreeTransform<Derived>::TransformCUDAKernelCallExpr(CUDAKernelCallExpr *E) { // Transform the callee. @@ -10026,8 +10053,8 @@ if (!Param) return ExprError(); - if (!getDerived().AlwaysRebuild() && - Param == E->getParam()) + if (!getDerived().AlwaysRebuild() && Param == E->getParam() && + E->getUsedContext() == SemaRef.CurContext) return E; return getDerived().RebuildCXXDefaultArgExpr(E->getUsedLocation(), Param); @@ -10042,7 +10069,8 @@ if (!Field) return ExprError(); - if (!getDerived().AlwaysRebuild() && Field == E->getField()) + if (!getDerived().AlwaysRebuild() && Field == E->getField() && + E->getUsedContext() == SemaRef.CurContext) return E; return getDerived().RebuildCXXDefaultInitExpr(E->getExprLoc(), Field); Index: lib/Sema/SemaExpr.cpp =================================================================== --- lib/Sema/SemaExpr.cpp +++ lib/Sema/SemaExpr.cpp @@ -4588,7 +4588,7 @@ FunctionDecl *FD, ParmVarDecl *Param) { if (CheckCXXDefaultArgExpr(CallLoc, FD, Param)) return ExprError(); - return CXXDefaultArgExpr::Create(Context, CallLoc, Param); + return CXXDefaultArgExpr::Create(Context, CallLoc, Param, CurContext); } Sema::VariadicCallType @@ -4846,8 +4846,7 @@ } else { assert(Param && "can't use default arguments without a known callee"); - ExprResult ArgExpr = - BuildCXXDefaultArgExpr(CallLoc, FDecl, Param); + ExprResult ArgExpr = BuildCXXDefaultArgExpr(CallLoc, FDecl, Param); if (ArgExpr.isInvalid()) return true; @@ -12869,6 +12868,22 @@ return new (Context) GNUNullExpr(Ty, TokenLoc); } +ExprResult Sema::ActOnSourceLocExpr(SourceLocExpr::IdentType Type, + SourceLocation BuiltinLoc, + SourceLocation RPLoc) { + return BuildSourceLocExpr(Type, BuiltinLoc, RPLoc, CurContext); +} + +ExprResult Sema::BuildSourceLocExpr(SourceLocExpr::IdentType Type, + SourceLocation BuiltinLoc, + SourceLocation RPLoc, + DeclContext *ParentContext) { + + return new (Context) SourceLocExpr( + Type, BuiltinLoc, RPLoc, + SourceLocExpr::BuildSourceLocExprType(Context, Type), ParentContext); +} + bool Sema::ConversionToObjCStringLiteralCheck(QualType DstType, Expr *&Exp, bool Diagnose) { if (!getLangOpts().ObjC1) Index: lib/Sema/SemaExceptionSpec.cpp =================================================================== --- lib/Sema/SemaExceptionSpec.cpp +++ lib/Sema/SemaExceptionSpec.cpp @@ -1271,6 +1271,7 @@ case Expr::PredefinedExprClass: case Expr::SizeOfPackExprClass: case Expr::StringLiteralClass: + case Expr::SourceLocExprClass: // These expressions can never throw. return CT_Cannot; Index: lib/Sema/SemaDeclCXX.cpp =================================================================== --- lib/Sema/SemaDeclCXX.cpp +++ lib/Sema/SemaDeclCXX.cpp @@ -12402,7 +12402,7 @@ // If we already have the in-class initializer nothing needs to be done. if (Field->getInClassInitializer()) - return CXXDefaultInitExpr::Create(Context, Loc, Field); + return CXXDefaultInitExpr::Create(Context, Loc, Field, CurContext); // If we might have already tried and failed to instantiate, don't try again. if (Field->isInvalidDecl()) @@ -12442,7 +12442,7 @@ Field->setInvalidDecl(); return ExprError(); } - return CXXDefaultInitExpr::Create(Context, Loc, Field); + return CXXDefaultInitExpr::Create(Context, Loc, Field, CurContext); } // DR1351: Index: lib/Parse/ParseExpr.cpp =================================================================== --- lib/Parse/ParseExpr.cpp +++ lib/Parse/ParseExpr.cpp @@ -619,6 +619,10 @@ /// [GNU] '__builtin_offsetof' '(' type-name ',' offsetof-member-designator')' /// [GNU] '__builtin_choose_expr' '(' assign-expr ',' assign-expr ',' /// assign-expr ')' +/// [GNU] '__builtin_FILE' '(' ')' +/// [GNU] '__builtin_FUNCTION' '(' ')' +/// [GNU] '__builtin_LINE' '(' ')' +/// [CLANG] '__builtin_COLUMN' '(' ')' /// [GNU] '__builtin_types_compatible_p' '(' type-name ',' type-name ')' /// [GNU] '__null' /// [OBJC] '[' objc-message-expr ']' @@ -1072,6 +1076,10 @@ case tok::kw___builtin_choose_expr: case tok::kw___builtin_astype: // primary-expression: [OCL] as_type() case tok::kw___builtin_convertvector: + case tok::kw___builtin_COLUMN: + case tok::kw___builtin_FILE: + case tok::kw___builtin_FUNCTION: + case tok::kw___builtin_LINE: return ParseBuiltinPrimaryExpression(); case tok::kw___null: return Actions.ActOnGNUNullExpr(ConsumeToken()); @@ -1992,6 +2000,10 @@ /// [GNU] '__builtin_choose_expr' '(' assign-expr ',' assign-expr ',' /// assign-expr ')' /// [GNU] '__builtin_types_compatible_p' '(' type-name ',' type-name ')' +/// [GNU] '__builtin_FILE' '(' ')' +/// [GNU] '__builtin_FUNCTION' '(' ')' +/// [GNU] '__builtin_LINE' '(' ')' +/// [CLANG] '__builtin_COLUMN' '(' ')' /// [OCL] '__builtin_astype' '(' assignment-expression ',' type-name ')' /// /// [GNU] offsetof-member-designator: @@ -2211,6 +2223,33 @@ ConsumeParen()); break; } + case tok::kw___builtin_COLUMN: + case tok::kw___builtin_FILE: + case tok::kw___builtin_FUNCTION: + case tok::kw___builtin_LINE: { + // Attempt to consume the r-paren. + if (Tok.isNot(tok::r_paren)) { + Diag(Tok, diag::err_expected) << tok::r_paren; + SkipUntil(tok::r_paren, StopAtSemi); + return ExprError(); + } + SourceLocExpr::IdentType Type = [&] { + switch (T) { + case tok::kw___builtin_FILE: + return SourceLocExpr::File; + case tok::kw___builtin_FUNCTION: + return SourceLocExpr::Function; + case tok::kw___builtin_LINE: + return SourceLocExpr::Line; + case tok::kw___builtin_COLUMN: + return SourceLocExpr::Column; + default: + llvm_unreachable("invalid keyword"); + } + }(); + Res = Actions.ActOnSourceLocExpr(Type, StartLoc, ConsumeParen()); + break; + } } if (Res.isInvalid()) Index: lib/CodeGen/CodeGenFunction.h =================================================================== --- lib/CodeGen/CodeGenFunction.h +++ lib/CodeGen/CodeGenFunction.h @@ -1247,6 +1247,55 @@ SourceLocation LastStopPoint; public: +public: + class SourceLocExprScopeBase; + + SourceLocExprScopeBase *CurCXXDefaultArgScope = nullptr; + SourceLocExprScopeBase *CurCXXDefaultInitScope = nullptr; + + class SourceLocExprScopeBase { + bool isSameCurCodeDecl() const { return CGF.CurCodeDecl == CurCodeDecl; } + + static bool ShouldEnable(CodeGenFunction &CGF) { + return CGF.CurCXXDefaultArgScope == nullptr || + !CGF.CurCXXDefaultArgScope->isSameCurCodeDecl(); + } + + SourceLocExprScopeBase(CodeGenFunction &CGF, SourceLocation Loc, + const DeclContext *CurContext, + SourceLocExprScopeBase **Dest, bool Enable) + : CGF(CGF), Loc(Loc), CurContext(CurContext), Dest(Dest), OldVal(*Dest), + CurCodeDecl(CGF.CurCodeDecl), Enable(Enable) { + if (Enable) + *Dest = this; + } + + protected: + SourceLocExprScopeBase(CodeGenFunction &CGF, const CXXDefaultArgExpr *E) + : SourceLocExprScopeBase(CGF, E->getUsedLocation(), E->getUsedContext(), + &CGF.CurCXXDefaultArgScope, + ShouldEnable(CGF)) {} + + SourceLocExprScopeBase(CodeGenFunction &CGF, const CXXDefaultInitExpr *E) + : SourceLocExprScopeBase(CGF, E->getLocStart(), E->getUsedContext(), + &CGF.CurCXXDefaultInitScope, true) {} + ~SourceLocExprScopeBase() { + if (Enable) + *Dest = OldVal; + } + + public: + CodeGenFunction &CGF; + SourceLocation Loc; + const DeclContext *CurContext; + + private: + SourceLocExprScopeBase **Dest; + SourceLocExprScopeBase *OldVal; + const Decl *CurCodeDecl; + bool Enable; + }; + /// A scope within which we are constructing the fields of an object which /// might use a CXXDefaultInitExpr. This stashes away a 'this' value to use /// if we need to evaluate a CXXDefaultInitExpr within the evaluation. @@ -1267,10 +1316,12 @@ /// The scope of a CXXDefaultInitExpr. Within this scope, the value of 'this' /// is overridden to be the object under construction. - class CXXDefaultInitExprScope { + class CXXDefaultInitExprScope : public SourceLocExprScopeBase { + using Base = SourceLocExprScopeBase; + public: - CXXDefaultInitExprScope(CodeGenFunction &CGF) - : CGF(CGF), OldCXXThisValue(CGF.CXXThisValue), + CXXDefaultInitExprScope(CodeGenFunction &CGF, const CXXDefaultInitExpr *E) + : Base(CGF, E), OldCXXThisValue(CGF.CXXThisValue), OldCXXThisAlignment(CGF.CXXThisAlignment) { CGF.CXXThisValue = CGF.CXXDefaultInitExprThis.getPointer(); CGF.CXXThisAlignment = CGF.CXXDefaultInitExprThis.getAlignment(); @@ -1281,11 +1332,18 @@ } public: - CodeGenFunction &CGF; llvm::Value *OldCXXThisValue; CharUnits OldCXXThisAlignment; }; + class CXXDefaultArgExprScope : public SourceLocExprScopeBase { + using Base = SourceLocExprScopeBase; + + public: + CXXDefaultArgExprScope(CodeGenFunction &CGF, const CXXDefaultArgExpr *E) + : Base(CGF, E) {} + }; + /// The scope of an ArrayInitLoopExpr. Within this scope, the value of the /// current loop index is overridden. class ArrayInitLoopExprScope { Index: lib/CodeGen/CGExprScalar.cpp =================================================================== --- lib/CodeGen/CGExprScalar.cpp +++ lib/CodeGen/CGExprScalar.cpp @@ -577,12 +577,33 @@ Value *VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *E) { return EmitLoadOfLValue(E); } - + Value *VisitSourceLocExpr(SourceLocExpr *SLE) { + auto &Ctx = CGF.getContext(); + SourceLocation Loc = SLE->getLocation(); + const DeclContext *DC = SLE->getParentContext(); + if (auto *DIS = CGF.CurCXXDefaultInitScope) { + Loc = DIS->Loc; + DC = DIS->CurContext; + } + else if (auto *DAS = CGF.CurCXXDefaultArgScope) { + Loc = DAS->Loc; + DC = DAS->CurContext; + } + if (SLE->isLineOrColumn()) { + auto Val = SLE->getIntValue(Ctx, Loc); + return Builder.getInt(Val); + } else { + StringLiteral *Str = SLE->getStringValue(Ctx, Loc, DC); + return CGF.EmitArrayToPointerDecay(Str).getPointer(); + } + llvm_unreachable("missing return"); + } Value *VisitCXXDefaultArgExpr(CXXDefaultArgExpr *DAE) { + CodeGenFunction::CXXDefaultArgExprScope Scope(CGF, DAE); return Visit(DAE->getExpr()); } Value *VisitCXXDefaultInitExpr(CXXDefaultInitExpr *DIE) { - CodeGenFunction::CXXDefaultInitExprScope Scope(CGF); + CodeGenFunction::CXXDefaultInitExprScope Scope(CGF, DIE); return Visit(DIE->getExpr()); } Value *VisitCXXThisExpr(CXXThisExpr *TE) { Index: lib/CodeGen/CGExprComplex.cpp =================================================================== --- lib/CodeGen/CGExprComplex.cpp +++ lib/CodeGen/CGExprComplex.cpp @@ -210,10 +210,11 @@ return Visit(E->getSubExpr()); } ComplexPairTy VisitCXXDefaultArgExpr(CXXDefaultArgExpr *DAE) { + CodeGenFunction::CXXDefaultArgExprScope Scope(CGF, DAE); return Visit(DAE->getExpr()); } ComplexPairTy VisitCXXDefaultInitExpr(CXXDefaultInitExpr *DIE) { - CodeGenFunction::CXXDefaultInitExprScope Scope(CGF); + CodeGenFunction::CXXDefaultInitExprScope Scope(CGF, DIE); return Visit(DIE->getExpr()); } ComplexPairTy VisitExprWithCleanups(ExprWithCleanups *E) { Index: lib/CodeGen/CGExprAgg.cpp =================================================================== --- lib/CodeGen/CGExprAgg.cpp +++ lib/CodeGen/CGExprAgg.cpp @@ -159,10 +159,11 @@ void VisitImplicitValueInitExpr(ImplicitValueInitExpr *E); void VisitNoInitExpr(NoInitExpr *E) { } // Do nothing. void VisitCXXDefaultArgExpr(CXXDefaultArgExpr *DAE) { + CodeGenFunction::CXXDefaultArgExprScope(CGF, DAE); Visit(DAE->getExpr()); } void VisitCXXDefaultInitExpr(CXXDefaultInitExpr *DIE) { - CodeGenFunction::CXXDefaultInitExprScope Scope(CGF); + CodeGenFunction::CXXDefaultInitExprScope Scope(CGF, DIE); Visit(DIE->getExpr()); } void VisitCXXBindTemporaryExpr(CXXBindTemporaryExpr *E); Index: lib/CodeGen/CGExpr.cpp =================================================================== --- lib/CodeGen/CGExpr.cpp +++ lib/CodeGen/CGExpr.cpp @@ -1169,11 +1169,15 @@ return LV; } - case Expr::CXXDefaultArgExprClass: - return EmitLValue(cast<CXXDefaultArgExpr>(E)->getExpr()); + case Expr::CXXDefaultArgExprClass: { + auto *DAE = cast<CXXDefaultArgExpr>(E); + CXXDefaultArgExprScope Scope(*this, DAE); + return EmitLValue(DAE->getExpr()); + } case Expr::CXXDefaultInitExprClass: { - CXXDefaultInitExprScope Scope(*this); - return EmitLValue(cast<CXXDefaultInitExpr>(E)->getExpr()); + auto *DIE = cast<CXXDefaultInitExpr>(E); + CXXDefaultInitExprScope Scope(*this, DIE); + return EmitLValue(DIE->getExpr()); } case Expr::CXXTypeidExprClass: return EmitCXXTypeidLValue(cast<CXXTypeidExpr>(E)); Index: lib/AST/StmtProfile.cpp =================================================================== --- lib/AST/StmtProfile.cpp +++ lib/AST/StmtProfile.cpp @@ -1811,6 +1811,11 @@ VisitExpr(E); } +void StmtProfiler::VisitSourceLocExpr(const SourceLocExpr *E) { + VisitExpr(E); + // FIXME(EricWF) Something to do? +} + void StmtProfiler::VisitObjCStringLiteral(const ObjCStringLiteral *S) { VisitExpr(S); } Index: lib/AST/StmtPrinter.cpp =================================================================== --- lib/AST/StmtPrinter.cpp +++ lib/AST/StmtPrinter.cpp @@ -1308,6 +1308,10 @@ // Expr printing methods. //===----------------------------------------------------------------------===// +void StmtPrinter::VisitSourceLocExpr(SourceLocExpr *Node) { + OS << Node->getBuiltinStr() << "()"; +} + void StmtPrinter::VisitDeclRefExpr(DeclRefExpr *Node) { if (auto *OCED = dyn_cast<OMPCapturedExprDecl>(Node->getDecl())) { OCED->getInit()->IgnoreImpCasts()->printPretty(OS, nullptr, Policy); Index: lib/AST/ItaniumMangle.cpp =================================================================== --- lib/AST/ItaniumMangle.cpp +++ lib/AST/ItaniumMangle.cpp @@ -3429,7 +3429,7 @@ case Expr::AsTypeExprClass: case Expr::PseudoObjectExprClass: case Expr::AtomicExprClass: - { + case Expr::SourceLocExprClass: { if (!NullOut) { // As bad as this diagnostic is, it's better than crashing. DiagnosticsEngine &Diags = Context.getDiags(); Index: lib/AST/ExprConstant.cpp =================================================================== --- lib/AST/ExprConstant.cpp +++ lib/AST/ExprConstant.cpp @@ -58,7 +58,7 @@ struct LValue; struct CallStackFrame; struct EvalInfo; - + struct SourceLocContextRAIIBase; static QualType getType(APValue::LValueBase B) { if (!B) return QualType(); if (const ValueDecl *D = B.dyn_cast<const ValueDecl*>()) @@ -588,6 +588,9 @@ /// \brief Whether or not we're currently speculatively evaluating. bool IsSpeculativelyEvaluating; + SourceLocContextRAIIBase *EvaluatingDefaultArg; + SourceLocContextRAIIBase *EvaluatingDefaultMemberInit; + enum EvaluationMode { /// Evaluate as a constant expression. Stop if we find that the expression /// is not a constant expression. @@ -661,6 +664,7 @@ EvaluatingDecl((const ValueDecl *)nullptr), EvaluatingDeclValue(nullptr), HasActiveDiagnostic(false), HasFoldFailureDiagnostic(false), IsSpeculativelyEvaluating(false), + EvaluatingDefaultArg(nullptr), EvaluatingDefaultMemberInit(nullptr), EvalMode(Mode) {} void setEvaluatingDecl(APValue::LValueBase Base, APValue &Value) { @@ -960,6 +964,54 @@ } }; + /// Temporarily override 'this'. + struct SourceLocContextRAIIBase { + protected: + SourceLocContextRAIIBase(EvalInfo &Info, SourceLocation NewLoc, + const DeclContext *CurContext, + SourceLocContextRAIIBase **Dest, bool Enable) + : Info(Info), Loc(NewLoc), CurContext(CurContext), + CurrentCall(Info.CurrentCall), Dest(Dest), OldVal(*Dest), + Enable(Enable) { + if (Enable) + *Dest = this; + } + ~SourceLocContextRAIIBase() { if (Enable) *Dest = OldVal; } + + public: + EvalInfo &Info; + SourceLocation Loc; + const DeclContext *CurContext; + CallStackFrame *CurrentCall; + + bool isInSameCurrentCall() const { return CurrentCall == Info.CurrentCall;} + private: + + SourceLocContextRAIIBase **Dest; + SourceLocContextRAIIBase *OldVal; + bool Enable; + }; + class SourceLocDefaultArgContextRAII : public SourceLocContextRAIIBase { + static bool ShouldEnable(EvalInfo &Info) { + return !Info.EvaluatingDefaultArg || + !Info.EvaluatingDefaultArg->isInSameCurrentCall(); + } + public: + SourceLocDefaultArgContextRAII(EvalInfo &Info, SourceLocation Loc, + const DeclContext *CurContext) + : SourceLocContextRAIIBase(Info, Loc, CurContext, + &Info.EvaluatingDefaultArg, + ShouldEnable(Info)) {} + }; + class SourceLocDefaultMemberInitContextRAII + : public SourceLocContextRAIIBase { + public: + SourceLocDefaultMemberInitContextRAII(EvalInfo &Info, SourceLocation Loc, + const DeclContext *CurContext, + bool Enable = true) + : SourceLocContextRAIIBase(Info, Loc, CurContext, + &Info.EvaluatingDefaultMemberInit, Enable) {} + }; /// RAII object used to treat the current evaluation as the correct pointer /// offset fold for the current EvalMode struct FoldOffsetRAII { @@ -4245,6 +4297,7 @@ const CXXConstructorDecl *Definition, EvalInfo &Info, APValue &Result) { SourceLocation CallLoc = E->getExprLoc(); + if (!Info.CheckCallLimit(CallLoc)) return false; @@ -4502,14 +4555,20 @@ { return StmtVisitorTy::Visit(E->getResultExpr()); } bool VisitSubstNonTypeTemplateParmExpr(const SubstNonTypeTemplateParmExpr *E) { return StmtVisitorTy::Visit(E->getReplacement()); } - bool VisitCXXDefaultArgExpr(const CXXDefaultArgExpr *E) - { return StmtVisitorTy::Visit(E->getExpr()); } + bool VisitCXXDefaultArgExpr(const CXXDefaultArgExpr *E) { + SourceLocDefaultArgContextRAII Guard(Info, E->getUsedLocation(), + E->getUsedContext()); + return StmtVisitorTy::Visit(E->getExpr()); + } bool VisitCXXDefaultInitExpr(const CXXDefaultInitExpr *E) { // The initializer may not have been parsed yet, or might be erroneous. if (!E->getExpr()) return Error(E); + SourceLocDefaultMemberInitContextRAII Guard(Info, E->getLocStart(), + E->getUsedContext()); return StmtVisitorTy::Visit(E->getExpr()); } + // We cannot create any objects for which cleanups are required, so there is // nothing to do here; all cleanups must come from unevaluated subexpressions. bool VisitExprWithCleanups(const ExprWithCleanups *E) @@ -5485,7 +5544,6 @@ bool visitNonBuiltinCallExpr(const CallExpr *E); public: - PointerExprEvaluator(EvalInfo &info, LValue &Result, bool InvalidBaseOK) : ExprEvaluatorBaseTy(info), Result(Result), InvalidBaseOK(InvalidBaseOK) {} @@ -5555,6 +5613,38 @@ return true; } + bool VisitSourceLocExpr(const SourceLocExpr *E) { + assert(E && !E->isLineOrColumn()); + Expr *Value = nullptr; + SourceLocContextRAIIBase *ArgCtx = Info.EvaluatingDefaultMemberInit; + if (!ArgCtx && Info.EvaluatingDefaultArg + && Info.EvaluatingDefaultArg->isInSameCurrentCall()) + ArgCtx = Info.EvaluatingDefaultArg; + if (ArgCtx) { + assert(ArgCtx->CurContext); + Value = E->getValue(Info.Ctx, ArgCtx->Loc, ArgCtx->CurContext); + } else + Value = E->getValue(Info.Ctx); + + if (!Value) + return Error(E); + + LValue Obj; + assert(Value->isGLValue()); + + if (!evaluateLValue(Value, Result)) + return Error(E); + // The result is a pointer to the first element of the array. + if (const ConstantArrayType *CAT + = Info.Ctx.getAsConstantArrayType(Value->getType())) + Result.addArray(Info, Value, CAT); + else + Result.Designator.setInvalid(); + + assert(Result.Designator.Invalid == false); + return true; + } + // FIXME: Missing: @protocol, @selector }; } // end anonymous namespace @@ -6186,9 +6276,10 @@ if (!HandleLValueMember(Info, InitExpr, Subobject, Field, &Layout)) return false; + const auto *DIE = dyn_cast<CXXDefaultInitExpr>(InitExpr); // Temporarily override This, in case there's a CXXDefaultInitExpr in here. ThisOverrideRAII ThisOverride(*Info.CurrentCall, &This, - isa<CXXDefaultInitExpr>(InitExpr)); + DIE != nullptr); return EvaluateInPlace(Result.getUnionValue(), Info, Subobject, InitExpr); } @@ -6930,7 +7021,6 @@ //===--------------------------------------------------------------------===// // Visitor Methods //===--------------------------------------------------------------------===// - bool VisitIntegerLiteral(const IntegerLiteral *E) { return Success(E->getValue(), E); } @@ -7003,7 +7093,7 @@ bool VisitCXXNoexceptExpr(const CXXNoexceptExpr *E); bool VisitSizeOfPackExpr(const SizeOfPackExpr *E); - + bool VisitSourceLocExpr(const SourceLocExpr *E); // FIXME: Missing: array subscript of vector, member of vector }; } // end anonymous namespace @@ -7035,7 +7125,17 @@ Result = Val.getInt(); return true; } - +bool IntExprEvaluator::VisitSourceLocExpr(const SourceLocExpr *E) { + assert(E && E->isLineOrColumn()); + llvm::APInt Result; + SourceLocation Loc = E->getLocStart(); + if (Info.EvaluatingDefaultMemberInit) + Loc = Info.EvaluatingDefaultMemberInit->Loc; + else if (Info.EvaluatingDefaultArg && Info.EvaluatingDefaultArg->isInSameCurrentCall()) + Loc = Info.EvaluatingDefaultArg->Loc; + Result = E->getIntValue(Info.Ctx, Loc); + return Success(Result, E); +} /// Check whether the given declaration can be directly converted to an integral /// rvalue. If not, no diagnostic is produced; there are other things we can /// try. @@ -10278,6 +10378,7 @@ case Expr::SizeOfPackExprClass: case Expr::GNUNullExprClass: + case Expr::SourceLocExprClass: // GCC considers the GNU __null value to be an integral constant expression. return NoDiag(); Index: lib/AST/ExprClassification.cpp =================================================================== --- lib/AST/ExprClassification.cpp +++ lib/AST/ExprClassification.cpp @@ -190,6 +190,7 @@ case Expr::ArrayInitIndexExprClass: case Expr::NoInitExprClass: case Expr::DesignatedInitUpdateExprClass: + case Expr::SourceLocExprClass: return Cl::CL_PRValue; // Next come the complicated cases. Index: lib/AST/ExprCXX.cpp =================================================================== --- lib/AST/ExprCXX.cpp +++ lib/AST/ExprCXX.cpp @@ -707,13 +707,14 @@ } CXXDefaultInitExpr::CXXDefaultInitExpr(const ASTContext &C, SourceLocation Loc, - FieldDecl *Field, QualType T) + FieldDecl *Field, QualType T, + DeclContext *UsedContext) : Expr(CXXDefaultInitExprClass, T.getNonLValueExprType(C), - T->isLValueReferenceType() ? VK_LValue : T->isRValueReferenceType() - ? VK_XValue - : VK_RValue, + T->isLValueReferenceType() + ? VK_LValue + : T->isRValueReferenceType() ? VK_XValue : VK_RValue, /*FIXME*/ OK_Ordinary, false, false, false, false), - Field(Field), Loc(Loc) { + Field(Field), UsedContext(UsedContext), Loc(Loc) { assert(Field->hasInClassInitializer()); } Index: lib/AST/Expr.cpp =================================================================== --- lib/AST/Expr.cpp +++ lib/AST/Expr.cpp @@ -1829,13 +1829,116 @@ return OverOps[Opc]; } +SourceLocExpr::SourceLocExpr(IdentType Type, SourceLocation BLoc, + SourceLocation RParenLoc, QualType Ty, + DeclContext *ParentContext) + : Expr(SourceLocExprClass, Ty, VK_RValue, OK_Ordinary, false, false, false, + false), + BuiltinLoc(BLoc), RParenLoc(RParenLoc), ParentContext(ParentContext) { + SourceLocExprBits.Type = Type; + assert(!Ty->isDependentType() && "Type should never be dependent"); + assert(!Ty->isArrayType() && "Type should never be an array"); +} + +StringRef SourceLocExpr::getBuiltinStr() const { + switch (getIdentType()) { + case File: + return "__builtin_FILE"; + case Function: + return "__builtin_FUNCTION"; + case Line: + return "__builtin_LINE"; + case Column: + return "__builtin_COLUMN"; + } +} + +static PresumedLoc getPresumedSourceLoc(const ASTContext &Ctx, + SourceLocation Loc) { + auto &SourceMgr = Ctx.getSourceManager(); + PresumedLoc PLoc = + SourceMgr.getPresumedLoc(SourceMgr.getExpansionRange(Loc).second); + assert(PLoc.isValid()); // FIXME: Learn how to handle this. + return PLoc; +} + +llvm::APInt SourceLocExpr::getIntValue(const ASTContext &Ctx, + SourceLocation Loc) const { + auto PLoc = getPresumedSourceLoc(Ctx, Loc); + unsigned Value = [&]() { + switch (getIdentType()) { + default: + llvm_unreachable("should not be here"); + case Line: + return PLoc.getLine(); + case Column: + return PLoc.getColumn(); + } + }(); + unsigned MaxWidth = Ctx.getTargetInfo().getIntWidth(); + return llvm::APInt(MaxWidth, Value); +} + +StringLiteral * +SourceLocExpr::getStringValue(const ASTContext &Ctx, SourceLocation Loc, + const DeclContext *CurContext) const { + auto PLoc = getPresumedSourceLoc(Ctx, Loc); + auto CreateString = [&](StringRef SVal) { + QualType StrTy = BuildSourceLocExprType(Ctx, getIdentType(), + /*IsArray*/ true, SVal.size() + 1); + StringLiteral *Lit = StringLiteral::Create(Ctx, SVal, StringLiteral::Ascii, + /*Pascal*/ false, StrTy, Loc); + assert(Lit && "should not be null"); + return Lit; + }; + switch (getIdentType()) { + default: + llvm_unreachable("should not be here"); + case SourceLocExpr::File: + return CreateString(PLoc.getFilename()); + case SourceLocExpr::Function: { + DeclarationName Name; + if (const auto *FD = dyn_cast_or_null<FunctionDecl>(CurContext)) + Name = FD->getDeclName(); + return CreateString(Name ? Name.getAsString() : ""); + } + } +} + +QualType SourceLocExpr::BuildSourceLocExprType(const ASTContext &Ctx, + IdentType Type, bool MakeArray, + int ArraySize) { + assert(MakeArray == (ArraySize != -1)); + bool IsIntType = Type == SourceLocExpr::Line || Type == SourceLocExpr::Column; + assert(!IsIntType || (IsIntType && !MakeArray)); + if (IsIntType) + return Ctx.UnsignedIntTy; + QualType Ty = Ctx.CharTy; + // A C++ string literal has a const-qualified element type (C++ 2.13.4p1). + if (Ctx.getLangOpts().CPlusPlus || Ctx.getLangOpts().ConstStrings) + Ty = Ty.withConst(); + if (!MakeArray) + return Ctx.getPointerType(Ty); + return Ctx.getConstantArrayType(Ty, llvm::APInt(32, ArraySize), + ArrayType::Normal, 0); +} + +Expr *SourceLocExpr::getValue(const ASTContext &Ctx, SourceLocation Loc, + const DeclContext *CurContext) const { + if (isLineOrColumn()) { + + return IntegerLiteral::Create(Ctx, getIntValue(Ctx, Loc), Ctx.UnsignedIntTy, + Loc); + } + return getStringValue(Ctx, Loc, CurContext); +} + InitListExpr::InitListExpr(const ASTContext &C, SourceLocation lbraceloc, ArrayRef<Expr *> initExprs, SourceLocation rbraceloc) : Expr(InitListExprClass, QualType(), VK_RValue, OK_Ordinary, false, false, false, false), - InitExprs(C, initExprs.size()), - LBraceLoc(lbraceloc), RBraceLoc(rbraceloc), AltForm(nullptr, true) -{ + InitExprs(C, initExprs.size()), LBraceLoc(lbraceloc), + RBraceLoc(rbraceloc), AltForm(nullptr, true) { sawArrayRangeDesignator(false); for (unsigned I = 0; I != initExprs.size(); ++I) { if (initExprs[I]->isTypeDependent()) @@ -1860,9 +1963,11 @@ InitExprs.resize(C, NumInits, nullptr); } -Expr *InitListExpr::updateInit(const ASTContext &C, unsigned Init, Expr *expr) { + Expr *InitListExpr::updateInit(const ASTContext &C, unsigned Init, + Expr *expr) { if (Init >= InitExprs.size()) { - InitExprs.insert(C, InitExprs.end(), Init - InitExprs.size() + 1, nullptr); + InitExprs.insert(C, InitExprs.end(), Init - InitExprs.size() + 1, + nullptr); setInit(Init, expr); return nullptr; } @@ -1905,8 +2010,8 @@ return true; } - // Otherwise, we're sugar if and only if we have exactly one initializer that - // is of the same type. + // Otherwise, we're sugar if and only if we have exactly one initializer + // that is of the same type. if (getNumInits() != 1 || !getInit(0)) return false; @@ -1960,19 +2065,15 @@ const FunctionProtoType *BlockExpr::getFunctionType() const { // The block pointer is never sugared, but the function type might be. return cast<BlockPointerType>(getType()) - ->getPointeeType()->castAs<FunctionProtoType>(); + ->getPointeeType() + ->castAs<FunctionProtoType>(); } SourceLocation BlockExpr::getCaretLocation() const { return TheBlock->getCaretLocation(); } -const Stmt *BlockExpr::getBody() const { - return TheBlock->getBody(); -} -Stmt *BlockExpr::getBody() { - return TheBlock->getBody(); -} - + const Stmt *BlockExpr::getBody() const { return TheBlock->getBody(); } + Stmt *BlockExpr::getBody() { return TheBlock->getBody(); } //===----------------------------------------------------------------------===// // Generic Expression Routines @@ -1999,14 +2100,15 @@ R1 = getSourceRange(); return true; case ParenExprClass: - return cast<ParenExpr>(this)->getSubExpr()-> - isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx); + return cast<ParenExpr>(this)->getSubExpr()->isUnusedResultAWarning( + WarnE, Loc, R1, R2, Ctx); case GenericSelectionExprClass: - return cast<GenericSelectionExpr>(this)->getResultExpr()-> - isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx); + return cast<GenericSelectionExpr>(this) + ->getResultExpr() + ->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx); case ChooseExprClass: - return cast<ChooseExpr>(this)->getChosenSubExpr()-> - isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx); + return cast<ChooseExpr>(this)->getChosenSubExpr()->isUnusedResultAWarning( + WarnE, Loc, R1, R2, Ctx); case UnaryOperatorClass: { const UnaryOperator *UO = cast<UnaryOperator>(this); @@ -2034,7 +2136,8 @@ return false; break; case UO_Extension: - return UO->getSubExpr()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx); + return UO->getSubExpr()->isUnusedResultAWarning(WarnE, Loc, R1, R2, + Ctx); } WarnE = this; Loc = UO->getOperatorLoc(); @@ -2954,6 +3057,7 @@ case ObjCAvailabilityCheckExprClass: case CXXUuidofExprClass: case OpaqueValueExprClass: + case SourceLocExprClass: // These never have a side-effect. return false; Index: lib/AST/ASTImporter.cpp =================================================================== --- lib/AST/ASTImporter.cpp +++ lib/AST/ASTImporter.cpp @@ -5077,8 +5077,13 @@ if (!Param) return nullptr; - return CXXDefaultArgExpr::Create( - Importer.getToContext(), Importer.Import(E->getUsedLocation()), Param); + DeclContext *DC = Importer.ImportContext(E->getUsedContext()); + if (!DC) + return nullptr; + + return CXXDefaultArgExpr::Create(Importer.getToContext(), + Importer.Import(E->getUsedLocation()), Param, + DC); } Expr *ASTNodeImporter::VisitCXXScalarValueInitExpr(CXXScalarValueInitExpr *E) { @@ -5451,8 +5456,13 @@ if (!ToField && DIE->getField()) return nullptr; - return CXXDefaultInitExpr::Create( - Importer.getToContext(), Importer.Import(DIE->getLocStart()), ToField); + DeclContext *DC = Importer.ImportContext(DIE->getUsedContext()); + if (!DC) + return nullptr; + + return CXXDefaultInitExpr::Create(Importer.getToContext(), + Importer.Import(DIE->getLocStart()), + ToField, DC); } Expr *ASTNodeImporter::VisitCXXNamedCastExpr(CXXNamedCastExpr *E) { Index: include/clang/Serialization/ASTBitCodes.h =================================================================== --- include/clang/Serialization/ASTBitCodes.h +++ include/clang/Serialization/ASTBitCodes.h @@ -1343,6 +1343,8 @@ EXPR_CHOOSE, /// \brief A GNUNullExpr record. EXPR_GNU_NULL, + /// \brief A SourceLocExpr record + EXPR_SOURCE_LOC, /// \brief A ShuffleVectorExpr record. EXPR_SHUFFLE_VECTOR, /// \brief A ConvertVectorExpr record. Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -4396,6 +4396,21 @@ ExprResult BuildVAArgExpr(SourceLocation BuiltinLoc, Expr *E, TypeSourceInfo *TInfo, SourceLocation RPLoc); + // __builtin_LINE(), __builtin_FUNCTION(), __builtin_FILE(), + // __builtin_COLUMN() + ExprResult ActOnSourceLocExpr(SourceLocExpr::IdentType Type, + SourceLocation BuiltinLoc, + SourceLocation RPLoc); + + /// \brief build a potentially resolved SourceLocExpr. + /// + /// \param SubExpr - null when the SourceLocExpr is unresolved, otherwise + /// SubExpr will be a literal expression representing the value of the + /// builtin call. + ExprResult BuildSourceLocExpr(SourceLocExpr::IdentType Type, + SourceLocation BuiltinLoc, SourceLocation RPLoc, + DeclContext *ParentContext); + // __null ExprResult ActOnGNUNullExpr(SourceLocation TokenLoc); Index: include/clang/Basic/TokenKinds.def =================================================================== --- include/clang/Basic/TokenKinds.def +++ include/clang/Basic/TokenKinds.def @@ -391,6 +391,11 @@ KEYWORD(__attribute , KEYALL) KEYWORD(__builtin_choose_expr , KEYALL) KEYWORD(__builtin_offsetof , KEYALL) +KEYWORD(__builtin_FILE , KEYALL) +KEYWORD(__builtin_FUNCTION , KEYALL) +KEYWORD(__builtin_LINE , KEYALL) +KEYWORD(__builtin_COLUMN , KEYALL) + // __builtin_types_compatible_p is a GNU C extension that we handle like a C++ // type trait. TYPE_TRAIT_2(__builtin_types_compatible_p, TypeCompatible, KEYNOCXX) Index: include/clang/Basic/StmtNodes.td =================================================================== --- include/clang/Basic/StmtNodes.td +++ include/clang/Basic/StmtNodes.td @@ -91,6 +91,7 @@ def VAArgExpr : DStmt<Expr>; def GenericSelectionExpr : DStmt<Expr>; def PseudoObjectExpr : DStmt<Expr>; +def SourceLocExpr : DStmt<Expr>; // Atomic expressions def AtomicExpr : DStmt<Expr>; Index: include/clang/AST/Stmt.h =================================================================== --- include/clang/AST/Stmt.h +++ include/clang/AST/Stmt.h @@ -261,6 +261,16 @@ unsigned IsImplicit : 1; }; + class SourceLocExprBitfields { + friend class SourceLocExpr; + + unsigned : NumExprBits; + + /// \brief The type of source location builtin represented by the + /// SourceLocExpr. Ex. __builtin_LINE, __builtin_FUNCTION, ect. + unsigned Type : 2; + }; + union { StmtBitfields StmtBits; CompoundStmtBitfields CompoundStmtBits; @@ -278,6 +288,7 @@ InitListExprBitfields InitListExprBits; TypeTraitExprBitfields TypeTraitExprBits; CoawaitExprBitfields CoawaitBits; + SourceLocExprBitfields SourceLocExprBits; }; friend class ASTStmtReader; Index: include/clang/AST/RecursiveASTVisitor.h =================================================================== --- include/clang/AST/RecursiveASTVisitor.h +++ include/clang/AST/RecursiveASTVisitor.h @@ -2495,6 +2495,8 @@ DEF_TRAVERSE_STMT(ShuffleVectorExpr, {}) DEF_TRAVERSE_STMT(ConvertVectorExpr, {}) DEF_TRAVERSE_STMT(StmtExpr, {}) +DEF_TRAVERSE_STMT(SourceLocExpr, {}) + DEF_TRAVERSE_STMT(UnresolvedLookupExpr, { TRY_TO(TraverseNestedNameSpecifierLoc(S->getQualifierLoc())); if (S->hasExplicitTemplateArgs()) { Index: include/clang/AST/ExprCXX.h =================================================================== --- include/clang/AST/ExprCXX.h +++ include/clang/AST/ExprCXX.h @@ -983,26 +983,33 @@ /// \brief The parameter whose default is being used. ParmVarDecl *Param; + /// \brief The context where the default argument expression was used. + DeclContext *UsedContext; + /// \brief The location where the default argument expression was used. SourceLocation Loc; - CXXDefaultArgExpr(StmtClass SC, SourceLocation Loc, ParmVarDecl *param) + CXXDefaultArgExpr(StmtClass SC, SourceLocation Loc, ParmVarDecl *param, + DeclContext *UsedContext) : Expr(SC, param->hasUnparsedDefaultArg() ? param->getType().getNonReferenceType() : param->getDefaultArg()->getType(), param->getDefaultArg()->getValueKind(), - param->getDefaultArg()->getObjectKind(), false, false, false, false), - Param(param), Loc(Loc) { } + param->getDefaultArg()->getObjectKind(), false, false, false, + false), + Param(param), UsedContext(UsedContext), Loc(Loc) {} public: CXXDefaultArgExpr(EmptyShell Empty) : Expr(CXXDefaultArgExprClass, Empty) {} // \p Param is the parameter whose default argument is used by this // expression. static CXXDefaultArgExpr *Create(const ASTContext &C, SourceLocation Loc, - ParmVarDecl *Param) { - return new (C) CXXDefaultArgExpr(CXXDefaultArgExprClass, Loc, Param); + ParmVarDecl *Param, + DeclContext *UsedContext) { + return new (C) + CXXDefaultArgExpr(CXXDefaultArgExprClass, Loc, Param, UsedContext); } // Retrieve the parameter that the argument was created from. @@ -1017,6 +1024,8 @@ return getParam()->getDefaultArg(); } + const DeclContext *getUsedContext() const { return UsedContext; } + DeclContext *getUsedContext() { return UsedContext; } /// \brief Retrieve the location where this default argument was actually /// used. SourceLocation getUsedLocation() const { return Loc; } @@ -1053,20 +1062,25 @@ /// \brief The field whose default is being used. FieldDecl *Field; + /// \brief The context where the default initializer expression was used. + DeclContext *UsedContext; + /// \brief The location where the default initializer expression was used. SourceLocation Loc; CXXDefaultInitExpr(const ASTContext &C, SourceLocation Loc, FieldDecl *Field, - QualType T); + QualType T, DeclContext *UsedContext); CXXDefaultInitExpr(EmptyShell Empty) : Expr(CXXDefaultInitExprClass, Empty) {} public: /// \p Field is the non-static data member whose default initializer is used /// by this expression. static CXXDefaultInitExpr *Create(const ASTContext &C, SourceLocation Loc, - FieldDecl *Field) { - return new (C) CXXDefaultInitExpr(C, Loc, Field, Field->getType()); + FieldDecl *Field, + DeclContext *UsedContext) { + return new (C) + CXXDefaultInitExpr(C, Loc, Field, Field->getType(), UsedContext); } /// \brief Get the field whose initializer will be used. @@ -1083,6 +1097,9 @@ return Field->getInClassInitializer(); } + const DeclContext *getUsedContext() const { return UsedContext; } + DeclContext *getUsedContext() { return UsedContext; } + SourceLocation getLocStart() const LLVM_READONLY { return Loc; } SourceLocation getLocEnd() const LLVM_READONLY { return Loc; } Index: include/clang/AST/Expr.h =================================================================== --- include/clang/AST/Expr.h +++ include/clang/AST/Expr.h @@ -3811,6 +3811,85 @@ } }; +/// SourceLocExpr - Represents a function call to one of +/// __builtin_LINE(), __builtin_COLUMN(), __builtin_FUNCTION(), or +/// __builtin_FILE() +class SourceLocExpr final : public Expr { +public: + enum IdentType { Function, File, Line, Column }; + +private: + SourceLocation BuiltinLoc, RParenLoc; + DeclContext *ParentContext; + +public: + static QualType BuildSourceLocExprType(const ASTContext &Ctx, IdentType Type, + bool MakeArray = false, + int ArraySize = -1); + SourceLocExpr(IdentType Type, SourceLocation BLoc, SourceLocation RParenLoc, + QualType Ty, DeclContext *Context); + + /// \brief Build an empty call expression. + explicit SourceLocExpr(EmptyShell Empty) : Expr(SourceLocExprClass, Empty) {} + + /// \brief Return a string representing the name of the specific builtin + /// function. + StringRef getBuiltinStr() const LLVM_READONLY; + + IdentType getIdentType() const LLVM_READONLY { + return static_cast<IdentType>(SourceLocExprBits.Type); + } + + bool isLineOrColumn() const LLVM_READONLY { + return getIdentType() == Line || getIdentType() == Column; + } + + llvm::APInt getIntValue(const ASTContext &Ctx, SourceLocation Loc) const; + llvm::APInt getIntValue(const ASTContext &Ctx) const { + return getIntValue(Ctx, BuiltinLoc); + } + + StringLiteral *getStringValue(const ASTContext &Ctx, SourceLocation Loc, + const DeclContext *UsedContext) const; + StringLiteral *getStringValue(const ASTContext &Ctx) const { + return getStringValue(Ctx, BuiltinLoc, ParentContext); + } + Expr *getValue(const ASTContext &Ctx, SourceLocation Loc, + const DeclContext *UsedContext) const; + Expr *getValue(const ASTContext &Ctx) const { + return getValue(Ctx, BuiltinLoc, ParentContext); + } + + /// \brief If the SourceLocExpr has been resolved return the subexpression + /// representing the resolved value. Otherwise return null. + const DeclContext *getParentContext() const { return ParentContext; } + DeclContext *getParentContext() { return ParentContext; } + + SourceLocation getLocation() const LLVM_READONLY { return BuiltinLoc; } + SourceLocation getLocStart() const LLVM_READONLY { return BuiltinLoc; } + SourceLocation getLocEnd() const LLVM_READONLY { return RParenLoc; } + + child_range children() { + return child_range(child_iterator(), child_iterator()); + } + + const_child_range children() const { + return const_child_range(child_iterator(), child_iterator()); + } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == SourceLocExprClass; + } + +private: + friend class ASTStmtReader; + + void setIdentType(IdentType T) { SourceLocExprBits.Type = T; } + void setParentContext(DeclContext *DC) { ParentContext = DC; } + void setLocStart(SourceLocation L) { BuiltinLoc = L; } + void setLocEnd(SourceLocation L) { RParenLoc = L; } +}; + /// @brief Describes an C or C++ initializer list. /// /// InitListExpr describes an initializer list, which can be used to Index: include/clang/AST/Decl.h =================================================================== --- include/clang/AST/Decl.h +++ include/clang/AST/Decl.h @@ -2415,8 +2415,8 @@ protected: FieldDecl(Kind DK, DeclContext *DC, SourceLocation StartLoc, - SourceLocation IdLoc, IdentifierInfo *Id, - QualType T, TypeSourceInfo *TInfo, Expr *BW, bool Mutable, + SourceLocation IdLoc, IdentifierInfo *Id, QualType T, + TypeSourceInfo *TInfo, Expr *BW, bool Mutable, InClassInitStyle InitStyle) : DeclaratorDecl(DK, DC, IdLoc, Id, T, TInfo, StartLoc), BitField(false), Mutable(Mutable), CachedFieldIndex(0), Index: docs/LanguageExtensions.rst =================================================================== --- docs/LanguageExtensions.rst +++ docs/LanguageExtensions.rst @@ -2067,6 +2067,61 @@ token `none`. If a user calls `__builin_suspend`, clang will insert `token none` as the first argument to the intrinsic. +Source location builtins +------------------------ + +Clang provides experimental builtins to support C++ standard library implementation +of `std::experimental::source_location` as specified in http://wg21.link/N4600. +With the exception of `__builtin_COLUMN`, these builtins are also implemented by +GCC. + +**Syntax**: + +.. code-block:: c + + const char *__builtin_FILE(); + const char *__builtin_FUNCTION(); + unsigned __builtin_LINE(); + unsigned __builtin_COLUMN(); // Clang only + +**Example of use**: + +.. code-block:: c++ + + void my_assert(bool pred, int line = __builtin_LINE(), // Captures line of caller + const char* file = __builtin_FILE(), + const char* function = __builtin_FUNCTION()) { + if (pred) return; + printf("%s:%d assertion failed in function %s\n", file, line, function); + std::abort(); + } + + struct MyAggregateType { + int x; + int line = __builtin_LINE(); // captures line where aggregate initialization occurs + }; + static_assert(MyAggregateType{42}.line == __LINE__); + + struct MyClassType { + int line = __builtin_LINE(); // captures line of the constructor used during initialization + constexpr MyClassType(int) { assert(line == __LINE__); } + }; + +**Description**: + +The builtins `__builtin_LINE`, `__builtin_FUNCTION`, and `__builtin_FILE` return +the values, at the "invocation point", for `__LINE__`, `__FUNCTION__`, and +`__FILE__` respectively. These builtins are constant expressions. + +When the builtins appears as part of a default function argument the invocation +point is the location of the caller. When the builtins appear as part of a +default member initializer, the invocation point is the location of the +constructor or aggregate initialization used to create the object. Otherwise +the invocation point is the same as the location of the builtin. + +When the invocation point of `__builtin_FUNCTION` is not a function scope the +empty string is returned. + Non-standard C++11 Attributes =============================
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits