diff mbox series

[C++] __builtin_source_location ()

Message ID 20191115122817.GJ4650@tucnak
State New
Headers show
Series [C++] __builtin_source_location () | expand

Commit Message

Jakub Jelinek Nov. 15, 2019, 12:28 p.m. UTC
On Thu, Nov 14, 2019 at 08:34:26PM +0100, Jakub Jelinek wrote:
> The following WIP patch implements __builtin_source_location (),
> which returns const void pointer to a std::source_location::__impl
> struct that is required to contain __file, __function, __line and __column
> fields, the first two with const char * type, the latter some integral type.

Here is hopefully final version, with the hashing implemented,
__file renamed to __file_name and __function to __function_name to match
how the standard names the methods and with testsuite coverage.

Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

2019-11-15  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): 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.



	Jakub

Comments

Jonathan Wakely Nov. 15, 2019, 12:35 p.m. UTC | #1
On 15/11/19 13:28 +0100, Jakub Jelinek wrote:
>On Thu, Nov 14, 2019 at 08:34:26PM +0100, Jakub Jelinek wrote:
>> The following WIP patch implements __builtin_source_location (),
>> which returns const void pointer to a std::source_location::__impl
>> struct that is required to contain __file, __function, __line and __column
>> fields, the first two with const char * type, the latter some integral type.
>
>Here is hopefully final version, with the hashing implemented,
>__file renamed to __file_name and __function to __function_name to match
>how the standard names the methods and with testsuite coverage.

The libstdc++ naming convention says the data members should be
_M_file_name, _M_line etc.

Since this is just a normal library type now, not defined by the
compiler, I think we should follow the library convention (sorry for
not noticing that sooner).
Jakub Jelinek Nov. 15, 2019, 12:49 p.m. UTC | #2
On Fri, Nov 15, 2019 at 12:35:22PM +0000, Jonathan Wakely wrote:
> On 15/11/19 13:28 +0100, Jakub Jelinek wrote:
> > On Thu, Nov 14, 2019 at 08:34:26PM +0100, Jakub Jelinek wrote:
> > > The following WIP patch implements __builtin_source_location (),
> > > which returns const void pointer to a std::source_location::__impl
> > > struct that is required to contain __file, __function, __line and __column
> > > fields, the first two with const char * type, the latter some integral type.
> > 
> > Here is hopefully final version, with the hashing implemented,
> > __file renamed to __file_name and __function to __function_name to match
> > how the standard names the methods and with testsuite coverage.
> 
> The libstdc++ naming convention says the data members should be
> _M_file_name, _M_line etc.
> 
> Since this is just a normal library type now, not defined by the
> compiler, I think we should follow the library convention (sorry for
> not noticing that sooner).

I guess it depends on what are the chances other compilers will implement
the same builtin, because doesn't say libc++ use __name rather than
_M_name convention?
Yet another option would be for the builtin to accept either
_M_file_name or __file_name, could be handled by
  const char *n = IDENTIFIER_POINTER (DECL_NAME (field));
  if (strncmp (n, "_M_", 3) == 0)
    n += 3;
  else if (strncmp (n, "__", 2) == 0)
    n += 2;
  else
    n = "";
and then do the comparisons without __ in the patch.

	Jakub
David Malcolm Nov. 15, 2019, 2:04 p.m. UTC | #3
On Fri, 2019-11-15 at 13:28 +0100, Jakub Jelinek wrote:
> On Thu, Nov 14, 2019 at 08:34:26PM +0100, Jakub Jelinek wrote:
> > The following WIP patch implements __builtin_source_location (),
> > which returns const void pointer to a std::source_location::__impl
> > struct that is required to contain __file, __function, __line and
> > __column
> > fields, the first two with const char * type, the latter some
> > integral type.
> 
> Here is hopefully final version, with the hashing implemented,
> __file renamed to __file_name and __function to __function_name to
> match
> how the standard names the methods and with testsuite coverage.
> 
> Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

[...snip...]

> --- gcc/cp/cp-gimplify.c.jj	2019-11-15 00:37:14.511247252 +0100
> +++ gcc/cp/cp-gimplify.c	2019-11-15 11:23:50.574203292 +0100
[...]
> @@ -2868,4 +2883,246 @@ process_stmt_hotness_attribute (tree std
>    return std_attrs;
>  }
>  
> +/* Helper of fold_builtin_source_location, return the
> +   std::source_location::__impl type after performing verification
> +   on it.  */
> +
> +static tree
> +get_source_location_impl (location_t loc)
> +{

FWIW, I found this function confusing at first, and wondered what the
purpose of LOC was (given that the function is building a type, rather
than an instance of that type).

Maybe add this to the leading comment:
  "LOC is used for reporting any errors."
(and if so, perhaps replace the various "error" with "error_at", though
that's rather academic).

Maybe rename the function to "get_source_location_impl_type" to
emphasize the distinction?

Hope this is constructive
Dave


> +  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 ("%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 ("%qD is not a type", decl);
> +      return error_mark_node;
> +    }
> +  type = TREE_TYPE (decl);
> +  if (TREE_CODE (type) != RECORD_TYPE)
> +    {
> +      error ("%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, "__file_name") == 0
> +	      || strcmp (n, "__function_name") == 0)
> +	    {
> +	      if (TREE_TYPE (field) != const_string_type_node)
> +		{
> +		  error ("%qD does not have %<const char *%> type",
> field);
> +		  return error_mark_node;
> +		}
> +	      cnt++;
> +	      continue;
> +	    }
> +	  else if (strcmp (n, "__line") == 0 || strcmp (n, "__column")
> == 0)
> +	    {
> +	      if (TREE_CODE (TREE_TYPE (field)) != INTEGER_TYPE)
> +		{
> +		  error ("%qD does not have integral type", field);
> +		  return error_mark_node;
> +		}
> +	      cnt++;
> +	      continue;
> +	    }
> +	}
> +      cnt = 0;
> +      break;
> +    }
> +  if (cnt != 4)
> +    {
> +      error ("%<std::source_location::__impl%> does not contain only
> "
> +	     "non-static data members %<__file_name%>,
> %<__function_name%>, "
> +	     "%<__line%> and %<__column%>");
> +      return error_mark_node;
> +    }
> +  return build_qualified_type (type, TYPE_QUAL_CONST);
> +}

[...snip...]
Jonathan Wakely Nov. 15, 2019, 2:05 p.m. UTC | #4
On 15/11/19 13:49 +0100, Jakub Jelinek wrote:
>On Fri, Nov 15, 2019 at 12:35:22PM +0000, Jonathan Wakely wrote:
>> On 15/11/19 13:28 +0100, Jakub Jelinek wrote:
>> > On Thu, Nov 14, 2019 at 08:34:26PM +0100, Jakub Jelinek wrote:
>> > > The following WIP patch implements __builtin_source_location (),
>> > > which returns const void pointer to a std::source_location::__impl
>> > > struct that is required to contain __file, __function, __line and __column
>> > > fields, the first two with const char * type, the latter some integral type.
>> >
>> > Here is hopefully final version, with the hashing implemented,
>> > __file renamed to __file_name and __function to __function_name to match
>> > how the standard names the methods and with testsuite coverage.
>>
>> The libstdc++ naming convention says the data members should be
>> _M_file_name, _M_line etc.
>>
>> Since this is just a normal library type now, not defined by the
>> compiler, I think we should follow the library convention (sorry for
>> not noticing that sooner).
>
>I guess it depends on what are the chances other compilers will implement
>the same builtin, because doesn't say libc++ use __name rather than
>_M_name convention?

Yes, that's what libc++ uses, but I get the impression Clang isn't
going to add this built-in anyway.

>Yet another option would be for the builtin to accept either
>_M_file_name or __file_name, could be handled by
>  const char *n = IDENTIFIER_POINTER (DECL_NAME (field));
>  if (strncmp (n, "_M_", 3) == 0)
>    n += 3;
>  else if (strncmp (n, "__", 2) == 0)
>    n += 2;
>  else
>    n = "";
>and then do the comparisons without __ in the patch.

My inclination is YAGNI. At this point, a libc++ implementation that
would want to use __builtin_source_location when compiled with GCC is
hypothetical, and would already need various #if conditions around it.

BCCing libc++ maintainers in case they want to request that GCC's
built-in pre-emptively support a std::source_location::__impl type
that uses __ prefixes on its data members. For more context see
https://gcc.gnu.org/ml/gcc-patches/2019-11/msg01389.html and the rest
of the thread.
Jakub Jelinek Nov. 15, 2019, 2:16 p.m. UTC | #5
On Fri, Nov 15, 2019 at 09:04:17AM -0500, David Malcolm wrote:
> > +/* Helper of fold_builtin_source_location, return the
> > +   std::source_location::__impl type after performing verification
> > +   on it.  */
> > +
> > +static tree
> > +get_source_location_impl (location_t loc)
> > +{
> 
> FWIW, I found this function confusing at first, and wondered what the
> purpose of LOC was (given that the function is building a type, rather
> than an instance of that type).
> 
> Maybe add this to the leading comment:
>   "LOC is used for reporting any errors."
> (and if so, perhaps replace the various "error" with "error_at", though
> that's rather academic).
> 
> Maybe rename the function to "get_source_location_impl_type" to
> emphasize the distinction?

Consider it done.

	Jakub
Jakub Jelinek Dec. 3, 2019, 3:19 p.m. UTC | #6
Hi!

On Fri, Nov 15, 2019 at 01:28:17PM +0100, Jakub Jelinek wrote:
> On Thu, Nov 14, 2019 at 08:34:26PM +0100, Jakub Jelinek wrote:
> > The following WIP patch implements __builtin_source_location (),
> > which returns const void pointer to a std::source_location::__impl
> > struct that is required to contain __file, __function, __line and __column
> > fields, the first two with const char * type, the latter some integral type.
> 
> Here is hopefully final version, with the hashing implemented,
> __file renamed to __file_name and __function to __function_name to match
> how the standard names the methods and with testsuite coverage.
> 
> Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

I'd like to ping this patch.
I know David and Jonathan had comments on the patch and can try to
incorporate them, but more importantly I'd like to ask whether we want to
add this at all or wait till GCC 11 and add something else.
Yet another option would be make it clear that the builtin is a temporary
thing until we implement something different and then would be removed,
say by reporting error if the builtin is used anywhere but in default
argument of std::source_location::current or something similar; I mean if
the ABI is sound, but we might have __builtin_constexpr_force_static or
whatever else or some new expression form one day and replace the builtin
uses with that.

> 2019-11-15  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): 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.

	Jakub
Jason Merrill Dec. 3, 2019, 8:37 p.m. UTC | #7
On 11/15/19 7:28 AM, Jakub Jelinek wrote:
> +  loc = LOCATION_LOCUS (loc);
...
> +  entry.loc
> +    = linemap_resolve_location (line_table, loc, LRK_MACRO_EXPANSION_POINT,
> +				&map);

You don't need LOCATION_LOCUS if you're calling 
linemap_resolve_location.  LGTM with that change and David's.

> I know David and Jonathan had comments on the patch and can try to
> incorporate them, but more importantly I'd like to ask whether we want to
> add this at all or wait till GCC 11 and add something else.
> Yet another option would be make it clear that the builtin is a temporary
> thing until we implement something different and then would be removed,
> say by reporting error if the builtin is used anywhere but in default
> argument of std::source_location::current or something similar; I mean if
> the ABI is sound, but we might have __builtin_constexpr_force_static or
> whatever else or some new expression form one day and replace the builtin
> uses with that.

I don't think we need to worry about enforcement; we say in general that 
C++20 support is experimental, and we don't try to maintain 
compatibility; even more so for internal details like this.

Jason
Jakub Jelinek Dec. 3, 2019, 9:02 p.m. UTC | #8
On Tue, Dec 03, 2019 at 03:37:41PM -0500, Jason Merrill wrote:
> On 11/15/19 7:28 AM, Jakub Jelinek wrote:
> > +  loc = LOCATION_LOCUS (loc);
> ...
> > +  entry.loc
> > +    = linemap_resolve_location (line_table, loc, LRK_MACRO_EXPANSION_POINT,
> > +				&map);
> 
> You don't need LOCATION_LOCUS if you're calling linemap_resolve_location.
> LGTM with that change and David's.

I had to apply also Jon's suggestion to use _M_* names of the members
instead of __* names (didn't use the variant that would support both), while
__impl remains with two underscores.  Below is what I'll be retesting.

> > I know David and Jonathan had comments on the patch and can try to
> > incorporate them, but more importantly I'd like to ask whether we want to
> > add this at all or wait till GCC 11 and add something else.
> > Yet another option would be make it clear that the builtin is a temporary
> > thing until we implement something different and then would be removed,
> > say by reporting error if the builtin is used anywhere but in default
> > argument of std::source_location::current or something similar; I mean if
> > the ABI is sound, but we might have __builtin_constexpr_force_static or
> > whatever else or some new expression form one day and replace the builtin
> > uses with that.
> 
> I don't think we need to worry about enforcement; we say in general that
> C++20 support is experimental, and we don't try to maintain compatibility;
> even more so for internal details like this.

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.

	* 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.

--- gcc/cp/cp-tree.h.jj	2019-12-03 20:21:29.743476880 +0100
+++ gcc/cp/cp-tree.h	2019-12-03 21:45:31.807989738 +0100
@@ -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
 /* 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"))) tin
 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);
--- gcc/cp/cp-gimplify.c.jj	2019-11-27 17:26:57.233013947 +0100
+++ gcc/cp/cp-gimplify.c	2019-12-03 21:52:31.720546585 +0100
@@ -35,6 +35,9 @@ along with GCC; see the file COPYING3.
 #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_s
 	  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
   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"
--- gcc/cp/constexpr.c.jj	2019-12-03 20:25:37.155668167 +0100
+++ gcc/cp/constexpr.c	2019-12-03 21:45:31.810989692 +0100
@@ -1240,6 +1240,9 @@ cxx_eval_builtin_function_call (const co
       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;
--- gcc/cp/tree.c.jj	2019-12-03 20:21:29.728477112 +0100
+++ gcc/cp/tree.c	2019-12-03 21:45:31.811989676 +0100
@@ -445,7 +445,9 @@ builtin_valid_in_constant_expr_p (const_
   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;
--- gcc/cp/decl.c.jj	2019-12-03 20:21:29.706477450 +0100
+++ gcc/cp/decl.c	2019-12-03 21:45:31.815989615 +0100
@@ -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.  */
--- gcc/cp/name-lookup.c.jj	2019-11-23 11:01:50.824196001 +0100
+++ gcc/cp/name-lookup.c	2019-12-03 21:45:31.816989600 +0100
@@ -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},
--- gcc/testsuite/g++.dg/cpp2a/srcloc1.C.jj	2019-12-03 21:45:31.816989600 +0100
+++ gcc/testsuite/g++.dg/cpp2a/srcloc1.C	2019-12-03 21:54:12.200006385 +0100
@@ -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 ());
--- gcc/testsuite/g++.dg/cpp2a/srcloc2.C.jj	2019-12-03 21:45:31.816989600 +0100
+++ gcc/testsuite/g++.dg/cpp2a/srcloc2.C	2019-12-03 21:54:12.201006370 +0100
@@ -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 ());
--- gcc/testsuite/g++.dg/cpp2a/srcloc3.C.jj	2019-12-03 21:45:31.816989600 +0100
+++ gcc/testsuite/g++.dg/cpp2a/srcloc3.C	2019-12-03 21:54:12.201006370 +0100
@@ -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 }
--- gcc/testsuite/g++.dg/cpp2a/srcloc4.C.jj	2019-12-03 21:45:31.816989600 +0100
+++ gcc/testsuite/g++.dg/cpp2a/srcloc4.C	2019-12-03 21:54:12.201006370 +0100
@@ -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 }
--- gcc/testsuite/g++.dg/cpp2a/srcloc5.C.jj	2019-12-03 21:45:31.816989600 +0100
+++ gcc/testsuite/g++.dg/cpp2a/srcloc5.C	2019-12-03 21:54:12.201006370 +0100
@@ -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 }
--- gcc/testsuite/g++.dg/cpp2a/srcloc6.C.jj	2019-12-03 21:45:31.816989600 +0100
+++ gcc/testsuite/g++.dg/cpp2a/srcloc6.C	2019-12-03 21:54:12.201006370 +0100
@@ -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 }
--- gcc/testsuite/g++.dg/cpp2a/srcloc7.C.jj	2019-12-03 21:45:31.816989600 +0100
+++ gcc/testsuite/g++.dg/cpp2a/srcloc7.C	2019-12-03 21:54:12.202006355 +0100
@@ -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 }
--- gcc/testsuite/g++.dg/cpp2a/srcloc8.C.jj	2019-12-03 21:45:31.816989600 +0100
+++ gcc/testsuite/g++.dg/cpp2a/srcloc8.C	2019-12-03 21:54:12.202006355 +0100
@@ -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 }
--- gcc/testsuite/g++.dg/cpp2a/srcloc9.C.jj	2019-12-03 21:45:31.816989600 +0100
+++ gcc/testsuite/g++.dg/cpp2a/srcloc9.C	2019-12-03 21:54:12.202006355 +0100
@@ -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 }
--- gcc/testsuite/g++.dg/cpp2a/srcloc10.C.jj	2019-12-03 21:45:31.817989584 +0100
+++ gcc/testsuite/g++.dg/cpp2a/srcloc10.C	2019-12-03 21:54:12.199006401 +0100
@@ -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 }
--- gcc/testsuite/g++.dg/cpp2a/srcloc11.C.jj	2019-12-03 21:45:31.817989584 +0100
+++ gcc/testsuite/g++.dg/cpp2a/srcloc11.C	2019-12-03 21:54:12.199006401 +0100
@@ -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 }
--- gcc/testsuite/g++.dg/cpp2a/srcloc12.C.jj	2019-12-03 21:45:31.817989584 +0100
+++ gcc/testsuite/g++.dg/cpp2a/srcloc12.C	2019-12-03 21:54:12.199006401 +0100
@@ -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 }
--- gcc/testsuite/g++.dg/cpp2a/srcloc13.C.jj	2019-12-03 21:45:31.817989584 +0100
+++ gcc/testsuite/g++.dg/cpp2a/srcloc13.C	2019-12-03 21:54:12.199006401 +0100
@@ -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 }
--- gcc/testsuite/g++.dg/cpp2a/srcloc14.C.jj	2019-12-03 21:45:31.817989584 +0100
+++ gcc/testsuite/g++.dg/cpp2a/srcloc14.C	2019-12-03 21:54:12.200006385 +0100
@@ -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 ();


	Jakub
diff mbox series

Patch

--- gcc/cp/cp-tree.h.jj	2019-11-15 00:37:14.282250695 +0100
+++ gcc/cp/cp-tree.h	2019-11-15 09:39:58.514862699 +0100
@@ -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
 /* 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.  */
 
@@ -6175,6 +6180,7 @@  struct GTY((chain_next ("%h.next"))) tin
 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
 };
 
@@ -7723,6 +7729,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);
--- gcc/cp/cp-gimplify.c.jj	2019-11-15 00:37:14.511247252 +0100
+++ gcc/cp/cp-gimplify.c	2019-11-15 11:23:50.574203292 +0100
@@ -35,6 +35,9 @@  along with GCC; see the file COPYING3.
 #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_s
 	  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;
 
@@ -2641,9 +2648,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);
@@ -2868,4 +2883,246 @@  process_stmt_hotness_attribute (tree std
   return std_attrs;
 }
 
+/* Helper of fold_builtin_source_location, return the
+   std::source_location::__impl type after performing verification
+   on it.  */
+
+static tree
+get_source_location_impl (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 ("%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 ("%qD is not a type", decl);
+      return error_mark_node;
+    }
+  type = TREE_TYPE (decl);
+  if (TREE_CODE (type) != RECORD_TYPE)
+    {
+      error ("%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, "__file_name") == 0
+	      || strcmp (n, "__function_name") == 0)
+	    {
+	      if (TREE_TYPE (field) != const_string_type_node)
+		{
+		  error ("%qD does not have %<const char *%> type", field);
+		  return error_mark_node;
+		}
+	      cnt++;
+	      continue;
+	    }
+	  else if (strcmp (n, "__line") == 0 || strcmp (n, "__column") == 0)
+	    {
+	      if (TREE_CODE (TREE_TYPE (field)) != INTEGER_TYPE)
+		{
+		  error ("%qD does not have integral type", field);
+		  return error_mark_node;
+		}
+	      cnt++;
+	      continue;
+	    }
+	}
+      cnt = 0;
+      break;
+    }
+  if (cnt != 4)
+    {
+      error ("%<std::source_location::__impl%> does not contain only "
+	     "non-static data members %<__file_name%>, %<__function_name%>, "
+	     "%<__line%> and %<__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 (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);
+  loc = LOCATION_LOCUS (loc);
+  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, "__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, "__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, "__line") == 0)
+	    val = build_int_cst (TREE_TYPE (field), LOCATION_LINE (loc));
+	  else if (strcmp (n, "__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"
--- gcc/cp/constexpr.c.jj	2019-11-15 00:37:14.239251341 +0100
+++ gcc/cp/constexpr.c	2019-11-15 09:39:58.509862775 +0100
@@ -1238,6 +1238,9 @@  cxx_eval_builtin_function_call (const co
       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;
--- gcc/cp/tree.c.jj	2019-11-15 00:37:14.452248140 +0100
+++ gcc/cp/tree.c	2019-11-15 09:39:58.507862806 +0100
@@ -445,7 +445,9 @@  builtin_valid_in_constant_expr_p (const_
   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;
--- gcc/cp/decl.c.jj	2019-11-15 00:37:14.355249597 +0100
+++ gcc/cp/decl.c	2019-11-15 09:39:58.516862669 +0100
@@ -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.  */
--- gcc/cp/name-lookup.c.jj	2019-11-15 00:37:14.410248771 +0100
+++ gcc/cp/name-lookup.c	2019-11-15 09:39:58.511862745 +0100
@@ -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},
--- gcc/testsuite/g++.dg/cpp2a/srcloc1.C.jj	2019-11-15 10:49:47.705194823 +0100
+++ gcc/testsuite/g++.dg/cpp2a/srcloc1.C	2019-11-15 11:43:33.203286115 +0100
@@ -0,0 +1,114 @@ 
+// { dg-do compile { target c++2a } }
+
+namespace std {
+  struct source_location {
+    struct __impl {
+      const char *__file_name;
+      const char *__function_name;
+      unsigned int __line, __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->__file_name : "";
+    }
+    constexpr const char *function_name () const {
+      return __ptr ? __ptr->__function_name : "";
+    }
+    constexpr unsigned line () const {
+      return __ptr ? __ptr->__line : 0;
+    }
+    constexpr unsigned column () const {
+      return __ptr ? __ptr->__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 ());
--- gcc/testsuite/g++.dg/cpp2a/srcloc2.C.jj	2019-11-15 10:50:58.372122068 +0100
+++ gcc/testsuite/g++.dg/cpp2a/srcloc2.C	2019-11-15 11:44:12.267694376 +0100
@@ -0,0 +1,118 @@ 
+// { dg-do compile { target c++2a } }
+
+namespace std {
+  inline namespace _8 { }
+  namespace _8 {
+  struct source_location {
+    struct __impl {
+      const char *__file_name;
+      const char *__function_name;
+      unsigned int __line, __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->__file_name : "";
+    }
+    constexpr const char *function_name () const {
+      return __ptr ? __ptr->__function_name : "";
+    }
+    constexpr unsigned line () const {
+      return __ptr ? __ptr->__line : 0;
+    }
+    constexpr unsigned column () const {
+      return __ptr ? __ptr->__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 ());
--- gcc/testsuite/g++.dg/cpp2a/srcloc3.C.jj	2019-11-15 10:53:14.183060484 +0100
+++ gcc/testsuite/g++.dg/cpp2a/srcloc3.C	2019-11-15 11:25:03.541097184 +0100
@@ -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 }
--- gcc/testsuite/g++.dg/cpp2a/srcloc4.C.jj	2019-11-15 10:56:23.052193749 +0100
+++ gcc/testsuite/g++.dg/cpp2a/srcloc4.C	2019-11-15 11:25:03.541097184 +0100
@@ -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 }
--- gcc/testsuite/g++.dg/cpp2a/srcloc5.C.jj	2019-11-15 10:57:35.421095311 +0100
+++ gcc/testsuite/g++.dg/cpp2a/srcloc5.C	2019-11-15 11:25:03.541097184 +0100
@@ -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 }
--- gcc/testsuite/g++.dg/cpp2a/srcloc6.C.jj	2019-11-15 11:00:52.896097950 +0100
+++ gcc/testsuite/g++.dg/cpp2a/srcloc6.C	2019-11-15 11:25:03.541097184 +0100
@@ -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 }
--- gcc/testsuite/g++.dg/cpp2a/srcloc7.C.jj	2019-11-15 11:01:26.967580801 +0100
+++ gcc/testsuite/g++.dg/cpp2a/srcloc7.C	2019-11-15 11:25:03.542097169 +0100
@@ -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 }
--- gcc/testsuite/g++.dg/cpp2a/srcloc8.C.jj	2019-11-15 11:03:04.160105574 +0100
+++ gcc/testsuite/g++.dg/cpp2a/srcloc8.C	2019-11-15 11:25:03.542097169 +0100
@@ -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 }
--- gcc/testsuite/g++.dg/cpp2a/srcloc9.C.jj	2019-11-15 11:04:03.663202412 +0100
+++ gcc/testsuite/g++.dg/cpp2a/srcloc9.C	2019-11-15 11:25:03.542097169 +0100
@@ -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 '__file_name', '__function_name', '__line' and '__column'" }
+// { dg-message "evaluating '__builtin_source_location'" "" { target *-*-* } .-1 }
--- gcc/testsuite/g++.dg/cpp2a/srcloc10.C.jj	2019-11-15 11:05:09.253206861 +0100
+++ gcc/testsuite/g++.dg/cpp2a/srcloc10.C	2019-11-15 11:25:03.542097169 +0100
@@ -0,0 +1,13 @@ 
+// { dg-do compile { target c++2a } }
+
+namespace std {
+  struct source_location {
+    struct __impl {
+      const char *__file_name, *__function_name;
+      int __foo, __line, __column;
+    };
+  };
+}
+
+auto x = __builtin_source_location ();	// { dg-error "'std::source_location::__impl' does not contain only non-static data members '__file_name', '__function_name', '__line' and '__column'" }
+// { dg-message "evaluating '__builtin_source_location'" "" { target *-*-* } .-1 }
--- gcc/testsuite/g++.dg/cpp2a/srcloc11.C.jj	2019-11-15 11:06:01.856408428 +0100
+++ gcc/testsuite/g++.dg/cpp2a/srcloc11.C	2019-11-15 11:25:03.542097169 +0100
@@ -0,0 +1,13 @@ 
+// { dg-do compile { target c++2a } }
+
+namespace std {
+  struct source_location {
+    struct __impl {
+      const char *__file_name, *__function_name;
+      int __line;
+    };
+  };
+}
+
+auto x = __builtin_source_location ();	// { dg-error "'std::source_location::__impl' does not contain only non-static data members '__file_name', '__function_name', '__line' and '__column'" }
+// { dg-message "evaluating '__builtin_source_location'" "" { target *-*-* } .-1 }
--- gcc/testsuite/g++.dg/cpp2a/srcloc12.C.jj	2019-11-15 11:06:25.999041982 +0100
+++ gcc/testsuite/g++.dg/cpp2a/srcloc12.C	2019-11-15 11:25:03.543097153 +0100
@@ -0,0 +1,14 @@ 
+// { dg-do compile { target c++2a } }
+
+namespace std {
+  struct source_location {
+    struct __impl {
+      const void *__file_name;
+      const char *__function_name;
+      int __line, __column;
+    };
+  };
+}
+
+auto x = __builtin_source_location ();	// { dg-error "'std::source_location::__impl::__file_name' does not have 'const char \\*' type" }
+// { dg-message "evaluating '__builtin_source_location'" "" { target *-*-* } .-1 }
--- gcc/testsuite/g++.dg/cpp2a/srcloc13.C.jj	2019-11-15 11:18:34.184999454 +0100
+++ gcc/testsuite/g++.dg/cpp2a/srcloc13.C	2019-11-15 11:25:03.543097153 +0100
@@ -0,0 +1,15 @@ 
+// { dg-do compile { target c++2a } }
+
+namespace std {
+  struct source_location {
+    struct __impl {
+      const char *__file_name;
+      const char *__function_name;
+      float __line;
+      int __column;
+    };
+  };
+}
+
+auto x = __builtin_source_location ();	// { dg-error "'std::source_location::__impl::__line' does not have integral type" }
+// { dg-message "evaluating '__builtin_source_location'" "" { target *-*-* } .-1 }
--- gcc/testsuite/g++.dg/cpp2a/srcloc14.C.jj	2019-11-15 11:19:51.561826493 +0100
+++ gcc/testsuite/g++.dg/cpp2a/srcloc14.C	2019-11-15 11:25:03.543097153 +0100
@@ -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 __column;
+      const char *__file_name;
+      int __line;
+      const char *__function_name;
+    };
+  };
+}
+
+auto x = __builtin_source_location ();