EricWF updated this revision to Diff 115162.
EricWF added a comment.
- Remove accidentally committed test files.
- Attempt to remove incidental whitespace changes.
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/source_location.cpp
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/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
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits