cp-tree.h (enum cp_tree_index): Add CPTI_SOURCE_LOCATION_IMPL.
authorJakub Jelinek <jakub@redhat.com>
Tue, 3 Dec 2019 23:32:15 +0000 (00:32 +0100)
committerJakub Jelinek <jakub@gcc.gnu.org>
Tue, 3 Dec 2019 23:32:15 +0000 (00:32 +0100)
* cp-tree.h (enum cp_tree_index): Add CPTI_SOURCE_LOCATION_IMPL.
(source_location_impl): Define.
(enum cp_built_in_function): Add CP_BUILT_IN_SOURCE_LOCATION.
(fold_builtin_source_location): Declare.
* cp-gimplify.c: Include output.h, file-prefix-map.h and cgraph.h.
(cp_gimplify_expr, cp_fold): Handle CP_BUILT_IN_SOURCE_LOCATION.
Formatting fix.
(get_source_location_impl_type): New function.
(struct source_location_table_entry,
struct source_location_table_entry_hash): New types.
(source_location_table, source_location_id): New variables.
(fold_builtin_source_location): New function.
* constexpr.c (cxx_eval_builtin_function_call): Handle
CP_BUILT_IN_SOURCE_LOCATION.
* tree.c (builtin_valid_in_constant_expr_p): Likewise.  Formatting
fix.
* decl.c (cxx_init_decl_processing): Register
__builtin_source_location.
* name-lookup.c (get_std_name_hint): Add source_location entry.

* g++.dg/cpp2a/srcloc1.C: New test.
* g++.dg/cpp2a/srcloc2.C: New test.
* g++.dg/cpp2a/srcloc3.C: New test.
* g++.dg/cpp2a/srcloc4.C: New test.
* g++.dg/cpp2a/srcloc5.C: New test.
* g++.dg/cpp2a/srcloc6.C: New test.
* g++.dg/cpp2a/srcloc7.C: New test.
* g++.dg/cpp2a/srcloc8.C: New test.
* g++.dg/cpp2a/srcloc9.C: New test.
* g++.dg/cpp2a/srcloc10.C: New test.
* g++.dg/cpp2a/srcloc11.C: New test.
* g++.dg/cpp2a/srcloc12.C: New test.
* g++.dg/cpp2a/srcloc13.C: New test.
* g++.dg/cpp2a/srcloc14.C: New test.

From-SVN: r278949

22 files changed:
gcc/cp/ChangeLog
gcc/cp/constexpr.c
gcc/cp/cp-gimplify.c
gcc/cp/cp-tree.h
gcc/cp/decl.c
gcc/cp/name-lookup.c
gcc/cp/tree.c
gcc/testsuite/ChangeLog
gcc/testsuite/g++.dg/cpp2a/srcloc1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp2a/srcloc10.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp2a/srcloc11.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp2a/srcloc12.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp2a/srcloc13.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp2a/srcloc14.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp2a/srcloc2.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp2a/srcloc3.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp2a/srcloc4.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp2a/srcloc5.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp2a/srcloc6.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp2a/srcloc7.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp2a/srcloc8.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp2a/srcloc9.C [new file with mode: 0644]

index 3c86cecfa6e8b6dae5e90b9c5cb284d51a5a674a..474a8506e86f6727c6df78e9cca7c7c57e8c867a 100644 (file)
@@ -1,3 +1,25 @@
+2019-12-03  Jakub Jelinek  <jakub@redhat.com>
+
+       * cp-tree.h (enum cp_tree_index): Add CPTI_SOURCE_LOCATION_IMPL.
+       (source_location_impl): Define.
+       (enum cp_built_in_function): Add CP_BUILT_IN_SOURCE_LOCATION.
+       (fold_builtin_source_location): Declare.
+       * cp-gimplify.c: Include output.h, file-prefix-map.h and cgraph.h.
+       (cp_gimplify_expr, cp_fold): Handle CP_BUILT_IN_SOURCE_LOCATION.
+       Formatting fix.
+       (get_source_location_impl_type): New function.
+       (struct source_location_table_entry,
+       struct source_location_table_entry_hash): New types.
+       (source_location_table, source_location_id): New variables.
+       (fold_builtin_source_location): New function.
+       * constexpr.c (cxx_eval_builtin_function_call): Handle
+       CP_BUILT_IN_SOURCE_LOCATION.
+       * tree.c (builtin_valid_in_constant_expr_p): Likewise.  Formatting
+       fix.
+       * decl.c (cxx_init_decl_processing): Register
+       __builtin_source_location.
+       * name-lookup.c (get_std_name_hint): Add source_location entry.
+
 2019-12-03  Paolo Carlini  <paolo.carlini@oracle.com>
 
        * typeck.c (cp_build_addr_expr_1): Use the cp_expr_loc_or_input_loc
index 5afcc876e7643360dec9e03da655f529a1d471e4..370633467abfec78b2b57865f2465136328ee69b 100644 (file)
@@ -1240,6 +1240,9 @@ cxx_eval_builtin_function_call (const constexpr_ctx *ctx, tree t, tree fun,
       return boolean_true_node;
     }
 
+  if (fndecl_built_in_p (fun, CP_BUILT_IN_SOURCE_LOCATION, BUILT_IN_FRONTEND))
+    return fold_builtin_source_location (EXPR_LOCATION (t));
+
   /* Be permissive for arguments to built-ins; __builtin_constant_p should
      return constant false for a non-constant argument.  */
   constexpr_ctx new_ctx = *ctx;
index 7942afa7ecead609f6114574d93bf5f80f4bc317..fb12c8a142027eb77ba59c8b9022df99dc02fcc6 100644 (file)
@@ -35,6 +35,9 @@ along with GCC; see the file COPYING3.  If not see
 #include "attribs.h"
 #include "asan.h"
 #include "gcc-rich-location.h"
+#include "output.h"
+#include "file-prefix-map.h"
+#include "cgraph.h"
 
 /* Forward declarations.  */
 
@@ -896,8 +899,12 @@ cp_gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p)
          tree decl = cp_get_callee_fndecl_nofold (*expr_p);
          if (decl
              && fndecl_built_in_p (decl, CP_BUILT_IN_IS_CONSTANT_EVALUATED,
-                                 BUILT_IN_FRONTEND))
+                                   BUILT_IN_FRONTEND))
            *expr_p = boolean_false_node;
+         else if (decl
+                  && fndecl_built_in_p (decl, CP_BUILT_IN_SOURCE_LOCATION,
+                                        BUILT_IN_FRONTEND))
+           *expr_p = fold_builtin_source_location (EXPR_LOCATION (*expr_p));
        }
       break;
 
@@ -2657,9 +2664,17 @@ cp_fold (tree x)
        /* Defer folding __builtin_is_constant_evaluated.  */
        if (callee
            && fndecl_built_in_p (callee, CP_BUILT_IN_IS_CONSTANT_EVALUATED,
-                               BUILT_IN_FRONTEND))
+                                 BUILT_IN_FRONTEND))
          break;
 
+       if (callee
+           && fndecl_built_in_p (callee, CP_BUILT_IN_SOURCE_LOCATION,
+                                 BUILT_IN_FRONTEND))
+         {
+           x = fold_builtin_source_location (EXPR_LOCATION (x));
+           break;
+         }
+
        x = copy_node (x);
 
        m = call_expr_nargs (x);
@@ -2884,4 +2899,246 @@ process_stmt_hotness_attribute (tree std_attrs, location_t attrs_loc)
   return std_attrs;
 }
 
+/* Helper of fold_builtin_source_location, return the
+   std::source_location::__impl type after performing verification
+   on it.  LOC is used for reporting any errors.  */
+
+static tree
+get_source_location_impl_type (location_t loc)
+{
+  tree name = get_identifier ("source_location");
+  tree decl = lookup_qualified_name (std_node, name);
+  if (TREE_CODE (decl) != TYPE_DECL)
+    {
+      auto_diagnostic_group d;
+      if (decl == error_mark_node || TREE_CODE (decl) == TREE_LIST)
+       qualified_name_lookup_error (std_node, name, decl, loc);
+      else
+       error_at (loc, "%qD is not a type", decl);
+      return error_mark_node;
+    }
+  name = get_identifier ("__impl");
+  tree type = TREE_TYPE (decl);
+  decl = lookup_qualified_name (type, name);
+  if (TREE_CODE (decl) != TYPE_DECL)
+    {
+      auto_diagnostic_group d;
+      if (decl == error_mark_node || TREE_CODE (decl) == TREE_LIST)
+       qualified_name_lookup_error (type, name, decl, loc);
+      else
+       error_at (loc, "%qD is not a type", decl);
+      return error_mark_node;
+    }
+  type = TREE_TYPE (decl);
+  if (TREE_CODE (type) != RECORD_TYPE)
+    {
+      error_at (loc, "%qD is not a class type", decl);
+      return error_mark_node;
+    }
+
+  int cnt = 0;
+  for (tree field = TYPE_FIELDS (type);
+       (field = next_initializable_field (field)) != NULL_TREE;
+       field = DECL_CHAIN (field))
+    {
+      if (DECL_NAME (field) != NULL_TREE)
+       {
+         const char *n = IDENTIFIER_POINTER (DECL_NAME (field));
+         if (strcmp (n, "_M_file_name") == 0
+             || strcmp (n, "_M_function_name") == 0)
+           {
+             if (TREE_TYPE (field) != const_string_type_node)
+               {
+                 error_at (loc, "%qD does not have %<const char *%> type",
+                           field);
+                 return error_mark_node;
+               }
+             cnt++;
+             continue;
+           }
+         else if (strcmp (n, "_M_line") == 0 || strcmp (n, "_M_column") == 0)
+           {
+             if (TREE_CODE (TREE_TYPE (field)) != INTEGER_TYPE)
+               {
+                 error_at (loc, "%qD does not have integral type", field);
+                 return error_mark_node;
+               }
+             cnt++;
+             continue;
+           }
+       }
+      cnt = 0;
+      break;
+    }
+  if (cnt != 4)
+    {
+      error_at (loc, "%<std::source_location::__impl%> does not contain only "
+                    "non-static data members %<_M_file_name%>, "
+                    "%<_M_function_name%>, %<_M_line%> and %<_M_column%>");
+      return error_mark_node;
+    }
+  return build_qualified_type (type, TYPE_QUAL_CONST);
+}
+
+/* Type for source_location_table hash_set.  */
+struct GTY((for_user)) source_location_table_entry {
+  location_t loc;
+  unsigned uid;
+  tree var;
+};
+
+/* Traits class for function start hash maps below.  */
+
+struct source_location_table_entry_hash
+  : ggc_remove <source_location_table_entry>
+{
+  typedef source_location_table_entry value_type;
+  typedef source_location_table_entry compare_type;
+
+  static hashval_t
+  hash (const source_location_table_entry &ref)
+  {
+    inchash::hash hstate (0);
+    hstate.add_int (ref.loc);
+    hstate.add_int (ref.uid);
+    return hstate.end ();
+  }
+
+  static bool
+  equal (const source_location_table_entry &ref1,
+        const source_location_table_entry &ref2)
+  {
+    return ref1.loc == ref2.loc && ref1.uid == ref2.uid;
+  }
+
+  static void
+  mark_deleted (source_location_table_entry &ref)
+  {
+    ref.loc = UNKNOWN_LOCATION;
+    ref.uid = -1U;
+    ref.var = NULL_TREE;
+  }
+
+  static void
+  mark_empty (source_location_table_entry &ref)
+  {
+    ref.loc = UNKNOWN_LOCATION;
+    ref.uid = 0;
+    ref.var = NULL_TREE;
+  }
+
+  static bool
+  is_deleted (const source_location_table_entry &ref)
+  {
+    return (ref.loc == UNKNOWN_LOCATION
+           && ref.uid == -1U
+           && ref.var == NULL_TREE);
+  }
+
+  static bool
+  is_empty (const source_location_table_entry &ref)
+  {
+    return (ref.loc == UNKNOWN_LOCATION
+           && ref.uid == 0
+           && ref.var == NULL_TREE);
+  }
+};
+
+static GTY(()) hash_table <source_location_table_entry_hash>
+  *source_location_table;
+static GTY(()) unsigned int source_location_id;
+
+/* Fold __builtin_source_location () call.  LOC is the location
+   of the call.  */
+
+tree
+fold_builtin_source_location (location_t loc)
+{
+  if (source_location_impl == NULL_TREE)
+    {
+      auto_diagnostic_group d;
+      source_location_impl = get_source_location_impl_type (loc);
+      if (source_location_impl == error_mark_node)
+       inform (loc, "evaluating %qs", "__builtin_source_location");
+    }
+  if (source_location_impl == error_mark_node)
+    return build_zero_cst (const_ptr_type_node);
+  if (source_location_table == NULL)
+    source_location_table
+      = hash_table <source_location_table_entry_hash>::create_ggc (64);
+  const line_map_ordinary *map;
+  source_location_table_entry entry;
+  entry.loc
+    = linemap_resolve_location (line_table, loc, LRK_MACRO_EXPANSION_POINT,
+                               &map);
+  entry.uid = current_function_decl ? DECL_UID (current_function_decl) : -1;
+  entry.var = error_mark_node;
+  source_location_table_entry *entryp
+    = source_location_table->find_slot (entry, INSERT);
+  tree var;
+  if (entryp->var)
+    var = entryp->var;
+  else
+    {
+      char tmp_name[32];
+      ASM_GENERATE_INTERNAL_LABEL (tmp_name, "Lsrc_loc", source_location_id++);
+      var = build_decl (loc, VAR_DECL, get_identifier (tmp_name),
+                       source_location_impl);
+      TREE_STATIC (var) = 1;
+      TREE_PUBLIC (var) = 0;
+      DECL_ARTIFICIAL (var) = 1;
+      DECL_IGNORED_P (var) = 1;
+      DECL_EXTERNAL (var) = 0;
+      DECL_DECLARED_CONSTEXPR_P (var) = 1;
+      DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (var) = 1;
+      layout_decl (var, 0);
+
+      vec<constructor_elt, va_gc> *v = NULL;
+      vec_alloc (v, 4);
+      for (tree field = TYPE_FIELDS (source_location_impl);
+          (field = next_initializable_field (field)) != NULL_TREE;
+          field = DECL_CHAIN (field))
+       {
+         const char *n = IDENTIFIER_POINTER (DECL_NAME (field));
+         tree val = NULL_TREE;
+         if (strcmp (n, "_M_file_name") == 0)
+           {
+             if (const char *fname = LOCATION_FILE (loc))
+               {
+                 fname = remap_macro_filename (fname);
+                 val = build_string_literal (strlen (fname) + 1, fname);
+               }
+             else
+               val = build_string_literal (1, "");
+           }
+         else if (strcmp (n, "_M_function_name") == 0)
+           {
+             const char *name = "";
+
+             if (current_function_decl)
+               name = cxx_printable_name (current_function_decl, 0);
+
+             val = build_string_literal (strlen (name) + 1, name);
+           }
+         else if (strcmp (n, "_M_line") == 0)
+           val = build_int_cst (TREE_TYPE (field), LOCATION_LINE (loc));
+         else if (strcmp (n, "_M_column") == 0)
+           val = build_int_cst (TREE_TYPE (field), LOCATION_COLUMN (loc));
+         else
+           gcc_unreachable ();
+         CONSTRUCTOR_APPEND_ELT (v, field, val);
+       }
+
+      tree ctor = build_constructor (source_location_impl, v);
+      TREE_CONSTANT (ctor) = 1;
+      TREE_STATIC (ctor) = 1;
+      DECL_INITIAL (var) = ctor;
+      varpool_node::finalize_decl (var);
+      *entryp = entry;
+      entryp->var = var;
+    }
+
+  return build_fold_addr_expr_with_type_loc (loc, var, const_ptr_type_node);
+}
+
 #include "gt-cp-cp-gimplify.h"
index 4af18b0ae6253a0c84ac7206807c9790be29d23c..666894ff882636e249c3f64f3cb5180cc3bb7014 100644 (file)
@@ -204,6 +204,8 @@ enum cp_tree_index
 
     CPTI_ANY_TARG,
 
+    CPTI_SOURCE_LOCATION_IMPL,
+
     CPTI_MAX
 };
 
@@ -356,6 +358,9 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX];
 /* A node which matches any template argument.  */
 #define any_targ_node                  cp_global_trees[CPTI_ANY_TARG]
 
+/* std::source_location::__impl class.  */
+#define source_location_impl           cp_global_trees[CPTI_SOURCE_LOCATION_IMPL]
+
 /* Node to indicate default access. This must be distinct from the
    access nodes in tree.h.  */
 
@@ -6182,6 +6187,7 @@ struct GTY((chain_next ("%h.next"))) tinst_level {
 enum cp_built_in_function {
   CP_BUILT_IN_IS_CONSTANT_EVALUATED,
   CP_BUILT_IN_INTEGER_PACK,
+  CP_BUILT_IN_SOURCE_LOCATION,
   CP_BUILT_IN_LAST
 };
 
@@ -7735,6 +7741,7 @@ extern void clear_fold_cache                      (void);
 extern tree lookup_hotness_attribute           (tree);
 extern tree process_stmt_hotness_attribute     (tree, location_t);
 extern bool simple_empty_class_p               (tree, tree, tree_code);
+extern tree fold_builtin_source_location       (location_t);
 
 /* in name-lookup.c */
 extern tree strip_using_decl                    (tree);
index 54f0950751685b430e1baec3876aaa0b41f87e8e..481c798a2cfa02068b3e3e5ada692a0255a3f261 100644 (file)
@@ -4290,6 +4290,12 @@ cxx_init_decl_processing (void)
                            BUILT_IN_FRONTEND, NULL, NULL_TREE);
   set_call_expr_flags (decl, ECF_CONST | ECF_NOTHROW | ECF_LEAF);
 
+  tree cptr_ftype = build_function_type_list (const_ptr_type_node, NULL_TREE);
+  decl = add_builtin_function ("__builtin_source_location",
+                              cptr_ftype, CP_BUILT_IN_SOURCE_LOCATION,
+                              BUILT_IN_FRONTEND, NULL, NULL_TREE);
+  set_call_expr_flags (decl, ECF_CONST | ECF_NOTHROW | ECF_LEAF);
+
   integer_two_node = build_int_cst (NULL_TREE, 2);
 
   /* Guess at the initial static decls size.  */
index a298b61bda9d8de34ff896a96d3f2a83a308fe75..e82eaf222c0247f47250ff14e3435292c9fe3fd7 100644 (file)
@@ -5747,6 +5747,8 @@ get_std_name_hint (const char *name)
     {"shared_lock", "<shared_mutex>", cxx14},
     {"shared_mutex", "<shared_mutex>", cxx17},
     {"shared_timed_mutex", "<shared_mutex>", cxx14},
+    /* <source_location>.  */
+    {"source_location", "<source_location>", cxx2a},
     /* <sstream>.  */
     {"basic_stringbuf", "<sstream>", cxx98},
     {"basic_istringstream", "<sstream>", cxx98},
index 620e2c2699cb3a6de9de2b9dbcedfcc5783c06e4..57745543163971051df9aab28b58136937a5486e 100644 (file)
@@ -445,7 +445,9 @@ builtin_valid_in_constant_expr_p (const_tree decl)
   if (DECL_BUILT_IN_CLASS (decl) != BUILT_IN_NORMAL)
     {
       if (fndecl_built_in_p (decl, CP_BUILT_IN_IS_CONSTANT_EVALUATED,
-                          BUILT_IN_FRONTEND))
+                            BUILT_IN_FRONTEND)
+         || fndecl_built_in_p (decl, CP_BUILT_IN_SOURCE_LOCATION,
+                               BUILT_IN_FRONTEND))
        return true;
       /* Not a built-in.  */
       return false;
index 330b75b4ff0d80911110cf79ca8540de5c8a9411..8ae30c286a74d2539770f9b4ee64c96c894697ce 100644 (file)
@@ -1,3 +1,20 @@
+2019-12-03  Jakub Jelinek  <jakub@redhat.com>
+
+       * g++.dg/cpp2a/srcloc1.C: New test.
+       * g++.dg/cpp2a/srcloc2.C: New test.
+       * g++.dg/cpp2a/srcloc3.C: New test.
+       * g++.dg/cpp2a/srcloc4.C: New test.
+       * g++.dg/cpp2a/srcloc5.C: New test.
+       * g++.dg/cpp2a/srcloc6.C: New test.
+       * g++.dg/cpp2a/srcloc7.C: New test.
+       * g++.dg/cpp2a/srcloc8.C: New test.
+       * g++.dg/cpp2a/srcloc9.C: New test.
+       * g++.dg/cpp2a/srcloc10.C: New test.
+       * g++.dg/cpp2a/srcloc11.C: New test.
+       * g++.dg/cpp2a/srcloc12.C: New test.
+       * g++.dg/cpp2a/srcloc13.C: New test.
+       * g++.dg/cpp2a/srcloc14.C: New test.
+
 2019-12-03  Paolo Carlini  <paolo.carlini@oracle.com>
 
        * g++.dg/diagnostic/inconsistent-deduction-1.C: New.
diff --git a/gcc/testsuite/g++.dg/cpp2a/srcloc1.C b/gcc/testsuite/g++.dg/cpp2a/srcloc1.C
new file mode 100644 (file)
index 0000000..c88bc14
--- /dev/null
@@ -0,0 +1,114 @@
+// { dg-do compile { target c++2a } }
+
+namespace std {
+  struct source_location {
+    struct __impl {
+      const char *_M_file_name;
+      const char *_M_function_name;
+      unsigned int _M_line, _M_column;
+    };
+    const __impl *__ptr;
+    constexpr source_location () : __ptr (nullptr) {}
+    static consteval source_location
+    current (const void *__p = __builtin_source_location ()) {
+      source_location __ret;
+      __ret.__ptr = static_cast <const __impl *> (__p);
+      return __ret;
+    }
+    constexpr const char *file_name () const {
+      return __ptr ? __ptr->_M_file_name : "";
+    }
+    constexpr const char *function_name () const {
+      return __ptr ? __ptr->_M_function_name : "";
+    }
+    constexpr unsigned line () const {
+      return __ptr ? __ptr->_M_line : 0;
+    }
+    constexpr unsigned column () const {
+      return __ptr ? __ptr->_M_column : 0;
+    }
+  };
+}
+
+using namespace std;
+
+consteval source_location
+bar (const source_location x = source_location::current ())
+{
+  return x;
+}
+
+void
+foo (const char **p, unsigned *q)
+{
+  constexpr source_location s = source_location::current ();
+  constexpr source_location t = bar ();
+  p[0] = s.file_name ();
+  p[1] = s.function_name ();
+  q[0] = s.line ();
+  q[1] = s.column ();
+  p[2] = t.file_name ();
+  p[3] = t.function_name ();
+  q[2] = t.line ();
+  q[3] = t.column ();
+  constexpr const char *r = s.file_name ();
+}
+
+source_location s3 = source_location::current ();
+
+template <int N>
+constexpr source_location
+baz ()
+{
+  return source_location::current ();
+}
+
+#define A \
+  source_location s[3] = { source_location::current (), \
+                          source_location::current (), \
+                          source_location::current () }
+
+source_location *
+boo ()
+{
+  static A;
+  return &s[0];
+}
+
+constexpr source_location s1 = baz <0> ();
+constexpr source_location s2 = baz <1> ();
+const source_location *p1 = &s1;
+const source_location *p2 = &s2;
+static_assert (source_location::current ().line () == __LINE__);
+static_assert (source_location::current ().column () == 42);
+
+constexpr bool
+quux ()
+{
+  const char *file1 = source_location::current ().file_name ();
+  const char *file2 = __FILE__;
+  const char *function1 = source_location::current ().function_name ();
+  const char *function2 = __FUNCTION__;
+  int line1 = source_location::current ().line ();
+  int line2 = __LINE__ - 1;
+  int column
+    = source_location::current ().column ();
+  int i = 0;
+  for (; file1[i]; i++)
+    if (file1[i] != file2[i])
+      return false;
+  if (file2[i])
+    return false;
+  for (i = 0; function1[i]; i++)
+    if (function1[i] != function2[i])
+      return false;
+  if (function2[i])
+    return false;
+  if (line1 != line2)
+    return false;
+  if (column != 33)
+    return false;
+  return true;
+}
+
+static_assert (quux ());
diff --git a/gcc/testsuite/g++.dg/cpp2a/srcloc10.C b/gcc/testsuite/g++.dg/cpp2a/srcloc10.C
new file mode 100644 (file)
index 0000000..a27016d
--- /dev/null
@@ -0,0 +1,13 @@
+// { dg-do compile { target c++2a } }
+
+namespace std {
+  struct source_location {
+    struct __impl {
+      const char *_M_file_name, *_M_function_name;
+      int __foo, _M_line, _M_column;
+    };
+  };
+}
+
+auto x = __builtin_source_location (); // { dg-error "'std::source_location::__impl' does not contain only non-static data members '_M_file_name', '_M_function_name', '_M_line' and '_M_column'" }
+// { dg-message "evaluating '__builtin_source_location'" "" { target *-*-* } .-1 }
diff --git a/gcc/testsuite/g++.dg/cpp2a/srcloc11.C b/gcc/testsuite/g++.dg/cpp2a/srcloc11.C
new file mode 100644 (file)
index 0000000..742a892
--- /dev/null
@@ -0,0 +1,13 @@
+// { dg-do compile { target c++2a } }
+
+namespace std {
+  struct source_location {
+    struct __impl {
+      const char *_M_file_name, *_M_function_name;
+      int _M_line;
+    };
+  };
+}
+
+auto x = __builtin_source_location (); // { dg-error "'std::source_location::__impl' does not contain only non-static data members '_M_file_name', '_M_function_name', '_M_line' and '_M_column'" }
+// { dg-message "evaluating '__builtin_source_location'" "" { target *-*-* } .-1 }
diff --git a/gcc/testsuite/g++.dg/cpp2a/srcloc12.C b/gcc/testsuite/g++.dg/cpp2a/srcloc12.C
new file mode 100644 (file)
index 0000000..900e5a8
--- /dev/null
@@ -0,0 +1,14 @@
+// { dg-do compile { target c++2a } }
+
+namespace std {
+  struct source_location {
+    struct __impl {
+      const void *_M_file_name;
+      const char *_M_function_name;
+      int _M_line, _M_column;
+    };
+  };
+}
+
+auto x = __builtin_source_location (); // { dg-error "'std::source_location::__impl::_M_file_name' does not have 'const char \\*' type" }
+// { dg-message "evaluating '__builtin_source_location'" "" { target *-*-* } .-1 }
diff --git a/gcc/testsuite/g++.dg/cpp2a/srcloc13.C b/gcc/testsuite/g++.dg/cpp2a/srcloc13.C
new file mode 100644 (file)
index 0000000..8909312
--- /dev/null
@@ -0,0 +1,15 @@
+// { dg-do compile { target c++2a } }
+
+namespace std {
+  struct source_location {
+    struct __impl {
+      const char *_M_file_name;
+      const char *_M_function_name;
+      float _M_line;
+      int _M_column;
+    };
+  };
+}
+
+auto x = __builtin_source_location (); // { dg-error "'std::source_location::__impl::_M_line' does not have integral type" }
+// { dg-message "evaluating '__builtin_source_location'" "" { target *-*-* } .-1 }
diff --git a/gcc/testsuite/g++.dg/cpp2a/srcloc14.C b/gcc/testsuite/g++.dg/cpp2a/srcloc14.C
new file mode 100644 (file)
index 0000000..21ab831
--- /dev/null
@@ -0,0 +1,15 @@
+// { dg-do compile { target c++2a } }
+
+namespace std {
+  struct source_location {
+    struct __impl {
+      // Test that ordering doesn't matter
+      long long _M_column;
+      const char *_M_file_name;
+      int _M_line;
+      const char *_M_function_name;
+    };
+  };
+}
+
+auto x = __builtin_source_location ();
diff --git a/gcc/testsuite/g++.dg/cpp2a/srcloc2.C b/gcc/testsuite/g++.dg/cpp2a/srcloc2.C
new file mode 100644 (file)
index 0000000..380d834
--- /dev/null
@@ -0,0 +1,118 @@
+// { dg-do compile { target c++2a } }
+
+namespace std {
+  inline namespace _8 { }
+  namespace _8 {
+  struct source_location {
+    struct __impl {
+      const char *_M_file_name;
+      const char *_M_function_name;
+      unsigned int _M_line, _M_column;
+    };
+    const __impl *__ptr;
+    constexpr source_location () : __ptr (nullptr) {}
+    static consteval source_location
+    current (const void *__p = __builtin_source_location ()) {
+      source_location __ret;
+      __ret.__ptr = static_cast <const __impl *> (__p);
+      return __ret;
+    }
+    constexpr const char *file_name () const {
+      return __ptr ? __ptr->_M_file_name : "";
+    }
+    constexpr const char *function_name () const {
+      return __ptr ? __ptr->_M_function_name : "";
+    }
+    constexpr unsigned line () const {
+      return __ptr ? __ptr->_M_line : 0;
+    }
+    constexpr unsigned column () const {
+      return __ptr ? __ptr->_M_column : 0;
+    }
+  };
+  }
+}
+
+using namespace std;
+
+consteval source_location
+bar (const source_location x = source_location::current ())
+{
+  return x;
+}
+
+void
+foo (const char **p, unsigned *q)
+{
+  constexpr source_location s = source_location::current ();
+  constexpr source_location t = bar ();
+  p[0] = s.file_name ();
+  p[1] = s.function_name ();
+  q[0] = s.line ();
+  q[1] = s.column ();
+  p[2] = t.file_name ();
+  p[3] = t.function_name ();
+  q[2] = t.line ();
+  q[3] = t.column ();
+  constexpr const char *r = s.file_name ();
+}
+
+source_location s3 = source_location::current ();
+
+template <int N>
+constexpr source_location
+baz ()
+{
+  return source_location::current ();
+}
+
+#define A \
+  source_location s[3] = { source_location::current (), \
+                          source_location::current (), \
+                          source_location::current () }
+
+source_location *
+boo ()
+{
+  static A;
+  return &s[0];
+}
+
+constexpr source_location s1 = baz <0> ();
+constexpr source_location s2 = baz <1> ();
+const source_location *p1 = &s1;
+const source_location *p2 = &s2;
+
+static_assert (source_location::current ().line () == __LINE__);
+static_assert (source_location::current ().column () == 42);
+
+constexpr bool
+quux ()
+{
+  const char *file1 = source_location::current ().file_name ();
+  const char *file2 = __FILE__;
+  const char *function1 = source_location::current ().function_name ();
+  const char *function2 = __FUNCTION__;
+  int line1 = source_location::current ().line ();
+  int line2 = __LINE__ - 1;
+  int column
+    = source_location::current ().column ();
+  int i = 0;
+  for (; file1[i]; i++)
+    if (file1[i] != file2[i])
+      return false;
+  if (file2[i])
+    return false;
+  for (i = 0; function1[i]; i++)
+    if (function1[i] != function2[i])
+      return false;
+  if (function2[i])
+    return false;
+  if (line1 != line2)
+    return false;
+  if (column != 33)
+    return false;
+  return true;
+}
+
+static_assert (quux ());
diff --git a/gcc/testsuite/g++.dg/cpp2a/srcloc3.C b/gcc/testsuite/g++.dg/cpp2a/srcloc3.C
new file mode 100644 (file)
index 0000000..b841ff6
--- /dev/null
@@ -0,0 +1,5 @@
+// { dg-do compile { target c++2a } }
+
+auto x = __builtin_source_location (); // { dg-error "'source_location' is not a member of 'std'" }
+// { dg-message "std::source_location' is defined in header '<source_location>'; did you forget to '#include <source_location>'" "" { target *-*-* } .-1 }
+// { dg-message "evaluating '__builtin_source_location'" "" { target *-*-* } .-2 }
diff --git a/gcc/testsuite/g++.dg/cpp2a/srcloc4.C b/gcc/testsuite/g++.dg/cpp2a/srcloc4.C
new file mode 100644 (file)
index 0000000..c8c5f51
--- /dev/null
@@ -0,0 +1,8 @@
+// { dg-do compile { target c++2a } }
+
+namespace std {
+  void source_location ();
+}
+
+auto x = __builtin_source_location (); // { dg-error "'void std::source_location\\(\\)' is not a type" }
+// { dg-message "evaluating '__builtin_source_location'" "" { target *-*-* } .-1 }
diff --git a/gcc/testsuite/g++.dg/cpp2a/srcloc5.C b/gcc/testsuite/g++.dg/cpp2a/srcloc5.C
new file mode 100644 (file)
index 0000000..0e89a00
--- /dev/null
@@ -0,0 +1,9 @@
+// { dg-do compile { target c++2a } }
+
+namespace std {
+  typedef int source_location;
+}
+
+auto x = __builtin_source_location (); // { dg-error "'std::source_location'\[^\n\r]*is not a class type" }
+// { dg-error "'__impl' is not a member of 'std::source_location'" "" { target *-*-* } .-1 }
+// { dg-message "evaluating '__builtin_source_location'" "" { target *-*-* } .-2 }
diff --git a/gcc/testsuite/g++.dg/cpp2a/srcloc6.C b/gcc/testsuite/g++.dg/cpp2a/srcloc6.C
new file mode 100644 (file)
index 0000000..a389add
--- /dev/null
@@ -0,0 +1,9 @@
+// { dg-do compile { target c++2a } }
+
+namespace std {
+  struct source_location {
+  };
+}
+
+auto x = __builtin_source_location (); // { dg-error "'__impl' is not a member of 'std::source_location'" }
+// { dg-message "evaluating '__builtin_source_location'" "" { target *-*-* } .-1 }
diff --git a/gcc/testsuite/g++.dg/cpp2a/srcloc7.C b/gcc/testsuite/g++.dg/cpp2a/srcloc7.C
new file mode 100644 (file)
index 0000000..2a8b569
--- /dev/null
@@ -0,0 +1,10 @@
+// { dg-do compile { target c++2a } }
+
+namespace std {
+  struct source_location {
+    static void __impl ();
+  };
+}
+
+auto x = __builtin_source_location (); // { dg-error "'std::source_location::__impl\\(\\)' is not a type" }
+// { dg-message "evaluating '__builtin_source_location'" "" { target *-*-* } .-1 }
diff --git a/gcc/testsuite/g++.dg/cpp2a/srcloc8.C b/gcc/testsuite/g++.dg/cpp2a/srcloc8.C
new file mode 100644 (file)
index 0000000..db56bb5
--- /dev/null
@@ -0,0 +1,10 @@
+// { dg-do compile { target c++2a } }
+
+namespace std {
+  struct source_location {
+    typedef int __impl;
+  };
+}
+
+auto x = __builtin_source_location (); // { dg-error "'std::source_location::__impl()' is not a class type" }
+// { dg-message "evaluating '__builtin_source_location'" "" { target *-*-* } .-1 }
diff --git a/gcc/testsuite/g++.dg/cpp2a/srcloc9.C b/gcc/testsuite/g++.dg/cpp2a/srcloc9.C
new file mode 100644 (file)
index 0000000..01ba00a
--- /dev/null
@@ -0,0 +1,11 @@
+// { dg-do compile { target c++2a } }
+
+namespace std {
+  struct source_location {
+    struct __impl {
+    };
+  };
+}
+
+auto x = __builtin_source_location (); // { dg-error "'std::source_location::__impl' does not contain only non-static data members '_M_file_name', '_M_function_name', '_M_line' and '_M_column'" }
+// { dg-message "evaluating '__builtin_source_location'" "" { target *-*-* } .-1 }