diff mbox series

c++: Implement LWG3396 Clarify point of reference for source_location::current() [PR80780, PR93093]

Message ID 20201201085949.GR3788@tucnak
State New
Headers show
Series c++: Implement LWG3396 Clarify point of reference for source_location::current() [PR80780, PR93093] | expand

Commit Message

Jakub Jelinek Dec. 1, 2020, 8:59 a.m. UTC
Hi!

While std::source_location::current () is static consteval source_location
current() noexcept; in the standard, it also says with LWG3396:
"Any call to current that appears as a default member initializer
([class.mem]), or as a subexpression thereof, should correspond to the
location of the constructor definition or aggregate initialization that uses
the default member initializer.  Any call to current that appears as a
default argument ([dcl.fct.default]), or as a subexpression thereof, should
correspond to the location of the invocation of the function that uses the
default argument ([expr.call])."
so it must work as compiler magic rather than normal immediate functions,
in particular we need to defer its evaluation when parsing default arguments
or nsdmis.

The following patch implements that.

Bootstrapped/regtested on x86_64-linux and i686-linux.

Jon, does it fix all the library testcases you and JeanHeyd had?

2020-12-01  Jakub Jelinek  <jakub@redhat.com>

	LWG3396 Clarify point of reference for source_location::current()
	PR c++/80780
	PR c++/93093
	* cp-tree.h (source_location_current_p): Declare.
	* tree.c (source_location_current_p): New function.
	(bot_manip): Resolve deferred calls to immediate function
	std::source_location::current ().
	* call.c (build_over_call): Don't evaluate calls to immediate
	function std::source_location::current () in default arguments
	or when parsing nsdmi.

	* g++.dg/cpp2a/srcloc15.C: New test.
	* g++.dg/cpp2a/srcloc16.C: New test.


	Jakub

Comments

Jonathan Wakely Dec. 1, 2020, 1:03 p.m. UTC | #1
On 01/12/20 09:59 +0100, Jakub Jelinek wrote:
>Hi!
>
>While std::source_location::current () is static consteval source_location
>current() noexcept; in the standard, it also says with LWG3396:
>"Any call to current that appears as a default member initializer
>([class.mem]), or as a subexpression thereof, should correspond to the
>location of the constructor definition or aggregate initialization that uses
>the default member initializer.  Any call to current that appears as a
>default argument ([dcl.fct.default]), or as a subexpression thereof, should
>correspond to the location of the invocation of the function that uses the
>default argument ([expr.call])."
>so it must work as compiler magic rather than normal immediate functions,
>in particular we need to defer its evaluation when parsing default arguments
>or nsdmis.
>
>The following patch implements that.

Nice.

>Bootstrapped/regtested on x86_64-linux and i686-linux.
>
>Jon, does it fix all the library testcases you and JeanHeyd had?

Most of them pass now, but this case fails:

using namespace std;

struct S {
   const char* func;
   unsigned line = 0;
   source_location loc = source_location::current();

   S(int l, source_location loc = source_location::current())
   : func(__FUNCTION__), line(l), loc(loc) // values of loc will be from call-site
   {}

   S(void*)
   : func(__FUNCTION__), line(__LINE__) // values of loc should be hereabouts
   {}
};

int main()
{
   S s0(__LINE__);
   assert( s0.loc.line() == s0.line );
   assert( !__builtin_strcmp(s0.loc.file_name(), __FILE__) );
   assert( !__builtin_strcmp(s0.loc.function_name(), __FUNCTION__) );
}

The location is the constructor parameter list, but it should be in
main() where the default argument is evaluated.

JeanHeyd's test also verify that the function_name() for the NSDMI
cases is "s::s" but that fails because __FUNCTION__ is only "s".

I mentioned in PR 80780 that a __builtin__PRETTY_FUNCTION would have
been nice, because __FUNCTION__ isn't very useful for C++, because of
overloading and namespace/class scopes. There are an unlimited number
of functions that have __FUNCTION__ == "s", e.g. "ns::s(int)" and
"ns::s()" and "another_scope::s::s<T...>(T...)" etc.

Since __builtin_source_location() can do whatever it wants (without
needing to add __builtin__PRETTY_FUNCTION) it might be nice to use the
__PRETTY_FUNCTION__ string. JeanHeyd's tests would still need changes,
because the name would be "s::s(void*)" not "s::s" but that still
seems better for users.
Jakub Jelinek Dec. 1, 2020, 3 p.m. UTC | #2
On Tue, Dec 01, 2020 at 01:03:52PM +0000, Jonathan Wakely wrote:
> Most of them pass now, but this case fails:
> 
> using namespace std;
> 
> struct S {
>   const char* func;
>   unsigned line = 0;
>   source_location loc = source_location::current();
> 
>   S(int l, source_location loc = source_location::current())
>   : func(__FUNCTION__), line(l), loc(loc) // values of loc will be from call-site
>   {}
> 
>   S(void*)
>   : func(__FUNCTION__), line(__LINE__) // values of loc should be hereabouts
>   {}
> };
> 
> int main()
> {
>   S s0(__LINE__);
>// ...
> }

Ah, I see what is going on here.

using namespace std;

struct S {
  const char *func;
  unsigned line = 0;
  source_location loc = source_location::current ();

  S (int l, source_location);

  S (void *)
  : func(__FUNCTION__), line(__LINE__) // values of loc should be hereabouts
  {}
};

S::S (int l, source_location loc = source_location::current ())
: func(__FUNCTION__), line(l), loc(loc) // values of loc will be from call-site
{}

int
main ()
{
  S s0(__LINE__);
///...
}

would work fine.

We have (since this patch, but before that it was already mostly like that
too):
          && DECL_IMMEDIATE_FUNCTION_P (fndecl)
          && cp_unevaluated_operand == 0
          && (current_function_decl == NULL_TREE
              || !DECL_IMMEDIATE_FUNCTION_P (current_function_decl))
          && (current_binding_level->kind != sk_function_parms
              || (!current_binding_level->immediate_fn_ctx_p
                  && !source_location_current_p (fndecl)))
          && (!parsing_nsdmi () || !source_location_current_p (fndecl)))
as the condition whether to evaluate an immediate function right away or
not.
The intent is do not evaluate it in default arguments of immediate functions
(will be done at constant expression evaluation time instead), and (newly)
for std::source_location::current () not evaluated in nsdmis and in default
arguments.

The problem is that with your/JeanHeyd's testcase above, the default
argument parsing is deferred until the whole class is parsed and then
cp_parser_late_parsing_default_args performs that deferred parsing.
Except at this point nothing creates the sk_function_params scope.

So, I think we need some way for the build_over_call code to know if
it is called from within cp_parser_late_parsing_default_args and whether
for an immediate function or not, and take that into account too.

I see cp_parser_late_parsing_default_args calls push_defarg_context,
so perhaps it could check default_arg_context vector, except that
convert_default_arg calls that too around calling break_out_target_exprs
which in the patch is used to perform the late evaluation of the
std::source_location::current() calls.  So it might need to temporarily
pop_defarg_context before break_out_target_exprs and push it again?

Jason, any thoughts on that?

And guess we need some tests if it works as expected also in templates;
I bet we create scope for the default params in that case, but I could be
wrong.

Thanks

	Jakub
Jason Merrill Dec. 1, 2020, 9:05 p.m. UTC | #3
On 12/1/20 3:59 AM, Jakub Jelinek wrote:
> Hi!
> 
> While std::source_location::current () is static consteval source_location
> current() noexcept; in the standard, it also says with LWG3396:
> "Any call to current that appears as a default member initializer
> ([class.mem]), or as a subexpression thereof, should correspond to the
> location of the constructor definition or aggregate initialization that uses
> the default member initializer.  Any call to current that appears as a
> default argument ([dcl.fct.default]), or as a subexpression thereof, should
> correspond to the location of the invocation of the function that uses the
> default argument ([expr.call])."
> so it must work as compiler magic rather than normal immediate functions,
> in particular we need to defer its evaluation when parsing default arguments
> or nsdmis.

....

> The problem is that with your/JeanHeyd's testcase above, the default
> argument parsing is deferred until the whole class is parsed and then
> cp_parser_late_parsing_default_args performs that deferred parsing.
> Except at this point nothing creates the sk_function_params scope.
> 
> So, I think we need some way for the build_over_call code to know if
> it is called from within cp_parser_late_parsing_default_args and whether
> for an immediate function or not, and take that into account too.
> 
> I see cp_parser_late_parsing_default_args calls push_defarg_context,
> so perhaps it could check default_arg_context vector, except that
> convert_default_arg calls that too around calling break_out_target_exprs
> which in the patch is used to perform the late evaluation of the
> std::source_location::current() calls.  So it might need to temporarily
> pop_defarg_context before break_out_target_exprs and push it again?

How about forcing the constant evaluation in bot_manip?

Or simpler might be to always defer immediate evaluation of 
source_location::current() until genericize time.

> And guess we need some tests if it works as expected also in templates;

Please.

> 2020-12-01  Jakub Jelinek  <jakub@redhat.com>
> 
> 	LWG3396 Clarify point of reference for source_location::current()
> 	PR c++/80780
> 	PR c++/93093
> 	* cp-tree.h (source_location_current_p): Declare.
> 	* tree.c (source_location_current_p): New function.
> 	(bot_manip): Resolve deferred calls to immediate function
> 	std::source_location::current ().
> 	* call.c (build_over_call): Don't evaluate calls to immediate
> 	function std::source_location::current () in default arguments
> 	or when parsing nsdmi.
> 
> 	* g++.dg/cpp2a/srcloc15.C: New test.
> 	* g++.dg/cpp2a/srcloc16.C: New test.
> 
> --- gcc/cp/cp-tree.h.jj	2020-11-26 16:22:24.252407018 +0100
> +++ gcc/cp/cp-tree.h	2020-11-30 19:45:36.784286305 +0100
> @@ -7413,6 +7413,7 @@ extern tree bind_template_template_parm
>   extern tree array_type_nelts_total		(tree);
>   extern tree array_type_nelts_top		(tree);
>   extern bool array_of_unknown_bound_p		(const_tree);
> +extern bool source_location_current_p		(tree);
>   extern tree break_out_target_exprs		(tree, bool = false);
>   extern tree build_ctor_subob_ref		(tree, tree, tree);
>   extern tree replace_placeholders		(tree, tree, bool * = NULL);
> --- gcc/cp/tree.c.jj	2020-11-26 16:22:24.264406884 +0100
> +++ gcc/cp/tree.c	2020-11-30 19:53:58.034686074 +0100
> @@ -2968,6 +2968,37 @@ array_type_nelts_total (tree type)
>     return sz;
>   }
>   
> +/* Return true if FNDECL is std::source_location::current () method.  */
> +
> +bool
> +source_location_current_p (tree fndecl)
> +{
> +  gcc_checking_assert (TREE_CODE (fndecl) == FUNCTION_DECL
> +		       && DECL_IMMEDIATE_FUNCTION_P (fndecl));
> +  if (DECL_NAME (fndecl) == NULL_TREE
> +      || TREE_CODE (TREE_TYPE (fndecl)) != FUNCTION_TYPE
> +      || TREE_CODE (TREE_TYPE (TREE_TYPE (fndecl))) != RECORD_TYPE
> +      || DECL_CONTEXT (fndecl) != TREE_TYPE (TREE_TYPE (fndecl)))
> +    return false;
> +
> +  if (strcmp (IDENTIFIER_POINTER (DECL_NAME (fndecl)), "current") != 0)

You can use id_equal for comparing identifiers to strings.

> +    return false;
> +
> +  tree source_location = DECL_CONTEXT (fndecl);
> +  if (TYPE_NAME (source_location) == NULL_TREE
> +      || TREE_CODE (TYPE_NAME (source_location)) != TYPE_DECL
> +      || DECL_NAME (TYPE_NAME (source_location)) == NULL_TREE
> +      || strcmp (IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (source_location))),
> +		 "source_location") != 0)

TYPE_IDENTIFIER would make this shorter.

> +    return false;
> +
> +  tree decl
> +    = lookup_qualified_name (std_node,
> +			     DECL_NAME (TYPE_NAME (source_location)),
> +			     LOOK_want::TYPE, tf_none);
> +  return TYPE_NAME (source_location) == decl;

Why not TYPE_CONTEXT (source_location) == std_node?  I don't think we 
need to do name lookup here.

> +}
> +
>   struct bot_data
>   {
>     splay_tree target_remap;
> @@ -3064,6 +3095,38 @@ bot_manip (tree* tp, int* walk_subtrees,
>         set_flags_from_callee (*tp);
>     if (data.clear_location && EXPR_HAS_LOCATION (*tp))
>       SET_EXPR_LOCATION (*tp, input_location);
> +  if (TREE_CODE (*tp) == CALL_EXPR
> +      && !processing_template_decl
> +      && (current_function_decl == NULL_TREE
> +	  || !DECL_IMMEDIATE_FUNCTION_P (current_function_decl)))
> +    if (tree fndecl = cp_get_callee_fndecl_nofold (*tp))
> +      if (DECL_IMMEDIATE_FUNCTION_P (fndecl)
> +	  && source_location_current_p (fndecl))
> +	{
> +	  bool ok = call_expr_nargs (*tp) == 0;
> +	  if (call_expr_nargs (*tp) == 1
> +	      && TREE_CODE (CALL_EXPR_ARG (*tp, 0)) == CALL_EXPR)
> +	    if (tree fndecl2
> +		= cp_get_callee_fndecl_nofold (CALL_EXPR_ARG (*tp, 0)))
> +	      if (fndecl_built_in_p (fndecl2, CP_BUILT_IN_SOURCE_LOCATION,
> +				     BUILT_IN_FRONTEND))
> +		ok = true;
> +	  if (!ok)
> +	    {
> +	      error_at (EXPR_LOCATION (*tp),
> +			"%qD called with arguments", fndecl);
> +	      *tp = build_constructor (TREE_TYPE (TREE_TYPE (fndecl)),
> +				       NULL);
> +	    }
> +	  else
> +	    {
> +	      vec<tree, va_gc> *args = NULL;
> +	      push_deferring_access_checks (dk_no_check);
> +	      *tp = build_new_function_call (fndecl, &args,
> +					     tf_warning_or_error);
> +	      pop_deferring_access_checks ();
> +	    }
> +	}
>     return t;
>   }
>   
> --- gcc/cp/call.c.jj	2020-11-18 09:40:09.000000000 +0100
> +++ gcc/cp/call.c	2020-11-30 19:47:03.951312431 +0100
> @@ -8613,7 +8613,9 @@ build_over_call (struct z_candidate *can
>   	  && (current_function_decl == NULL_TREE
>   	      || !DECL_IMMEDIATE_FUNCTION_P (current_function_decl))
>   	  && (current_binding_level->kind != sk_function_parms
> -	      || !current_binding_level->immediate_fn_ctx_p))
> +	      || (!current_binding_level->immediate_fn_ctx_p
> +		  && !source_location_current_p (fn)))
> +	  && (!parsing_nsdmi () || !source_location_current_p (fn)))
>   	{
>   	  tree obj_arg = NULL_TREE, exprimm = expr;
>   	  if (DECL_CONSTRUCTOR_P (fn))

Please factor this condition out into a separate function.

> @@ -9257,7 +9259,9 @@ build_over_call (struct z_candidate *can
>   	  && (current_function_decl == NULL_TREE
>   	      || !DECL_IMMEDIATE_FUNCTION_P (current_function_decl))
>   	  && (current_binding_level->kind != sk_function_parms
> -	      || !current_binding_level->immediate_fn_ctx_p))
> +	      || (!current_binding_level->immediate_fn_ctx_p
> +		  && !source_location_current_p (fndecl)))
> +	  && (!parsing_nsdmi () || !source_location_current_p (fndecl)))
>   	{
>   	  tree obj_arg = NULL_TREE;
>   	  if (DECL_CONSTRUCTOR_P (fndecl))
> --- gcc/testsuite/g++.dg/cpp2a/srcloc15.C.jj	2020-11-30 17:57:41.506279816 +0100
> +++ gcc/testsuite/g++.dg/cpp2a/srcloc15.C	2020-11-30 17:57:21.169507324 +0100
> @@ -0,0 +1,82 @@
> +// { dg-do compile { target c++20 } }
> +
> +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;
> +
> +constexpr source_location
> +foo (const source_location x = source_location::current ())
> +{
> +  return x;
> +}
> +
> +constexpr bool
> +bar ()
> +{
> +  int line = __LINE__;
> +  source_location a = foo ();
> +  source_location b = source_location::current ();
> +  source_location c = foo ();
> +  //                       ^ column 28
> +  //                                            ^ column 49
> +  const source_location *d[3] = { &a, &b, &c };
> +  const char *file1 = __FILE__;
> +  const char *function1 = __FUNCTION__;
> +  for (int j = 0; j < 3; j++)
> +    {
> +      int i= 0;
> +      const char *file2 = d[j]->file_name ();
> +      const char *function2 = d[j]->function_name ();
> +      for (i = 0; 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 (d[j]->line () != line + j + 1)
> +	return false;
> +      if (d[j]->column () != (j == 1 ? 49 : 28))
> +	return false;
> +    }
> +  return true;
> +}
> +
> +static_assert (bar ());
> +
> +int
> +main ()
> +{
> +  bar ();
> +}
> --- gcc/testsuite/g++.dg/cpp2a/srcloc16.C.jj	2020-11-30 20:15:25.416283572 +0100
> +++ gcc/testsuite/g++.dg/cpp2a/srcloc16.C	2020-11-30 20:15:06.285497601 +0100
> @@ -0,0 +1,96 @@
> +// { dg-do compile { target c++20 } }
> +
> +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;
> +
> +struct S
> +{
> +  source_location a = source_location::current ();
> +  source_location b = source_location::current ();
> +  source_location c = source_location ();
> +  constexpr S () { c = source_location::current (); }
> +};
> +
> +struct T
> +{
> +  int t;
> +  source_location u = source_location::current ();
> +  int v = __builtin_LINE ();
> +};
> +
> +constexpr S s;
> +constexpr T t = { 1 };
> +
> +constexpr bool
> +cmp (const char *p, const char *q)
> +{
> +  for (; *p && *q; p++, q++)
> +    if (*p != *q)
> +      return true;
> +  return *p || *q;
> +}
> +
> +constexpr bool
> +foo ()
> +{
> +  T u = { 2 };
> +  source_location v = source_location::current ();
> +  if (cmp (s.a.file_name (), s.c.file_name ())
> +      || cmp (s.b.file_name (), s.c.file_name ())
> +      || cmp (t.u.file_name (), s.c.file_name ())
> +      || cmp (u.u.file_name (), s.c.file_name ())
> +      || cmp (v.file_name (), s.c.file_name ())
> +      || cmp (s.a.function_name (), s.c.function_name ())
> +      || cmp (s.b.function_name (), s.c.function_name ())
> +      || cmp (t.u.function_name (), "")
> +      || cmp (u.u.function_name (), v.function_name ())
> +      || s.a.line () != s.c.line ()
> +      || s.b.line () != s.c.line ()
> +      || t.u.line () != t.v
> +      || u.u.line () + 1 != v.line ()
> +      || s.a.column () != 18
> +      || s.b.column () != 18
> +      || s.c.column () != 50
> +      || t.u.column () != 21
> +      || u.u.column () != 13
> +      || v.column () != 49)
> +    return false;
> +  return true;
> +}
> +
> +static_assert (foo ());
> +
> +int
> +main ()
> +{
> +  foo ();
> +}
> 
> 	Jakub
>
Jakub Jelinek Dec. 1, 2020, 9:21 p.m. UTC | #4
On Tue, Dec 01, 2020 at 04:05:22PM -0500, Jason Merrill wrote:
> > I see cp_parser_late_parsing_default_args calls push_defarg_context,
> > so perhaps it could check default_arg_context vector, except that
> > convert_default_arg calls that too around calling break_out_target_exprs
> > which in the patch is used to perform the late evaluation of the
> > std::source_location::current() calls.  So it might need to temporarily
> > pop_defarg_context before break_out_target_exprs and push it again?
> 
> How about forcing the constant evaluation in bot_manip?

That is probably a good idea anyway.

> Or simpler might be to always defer immediate evaluation of
> source_location::current() until genericize time.

Will try this too.

Though, I still wonder if it will be ok that we'll evaluate immediate
functions other than std::source_location::current() during the late
parsing of default arguments.

Testcase could be something like:
consteval bool foo (bool x) { if (x) throw 1; return false; }
consteval bool bar (bool x = foo (true)) { return true; }
struct S
{
  consteval static bool baz (bool x = foo (true)) { return true; }
};
constexpr bool a = bar (true);
constexpr bool b = S::baz (true);

We accept now bar, but reject baz.
I think the foo (true) expressions are in both cases in
immediate function context and therefore foo (true) calls are there
not immediate invocations (sure, if one calls bar () or S::baz () 
then it will be invalid anyway).

	Jakub
diff mbox series

Patch

--- gcc/cp/cp-tree.h.jj	2020-11-26 16:22:24.252407018 +0100
+++ gcc/cp/cp-tree.h	2020-11-30 19:45:36.784286305 +0100
@@ -7413,6 +7413,7 @@  extern tree bind_template_template_parm
 extern tree array_type_nelts_total		(tree);
 extern tree array_type_nelts_top		(tree);
 extern bool array_of_unknown_bound_p		(const_tree);
+extern bool source_location_current_p		(tree);
 extern tree break_out_target_exprs		(tree, bool = false);
 extern tree build_ctor_subob_ref		(tree, tree, tree);
 extern tree replace_placeholders		(tree, tree, bool * = NULL);
--- gcc/cp/tree.c.jj	2020-11-26 16:22:24.264406884 +0100
+++ gcc/cp/tree.c	2020-11-30 19:53:58.034686074 +0100
@@ -2968,6 +2968,37 @@  array_type_nelts_total (tree type)
   return sz;
 }
 
+/* Return true if FNDECL is std::source_location::current () method.  */
+
+bool
+source_location_current_p (tree fndecl)
+{
+  gcc_checking_assert (TREE_CODE (fndecl) == FUNCTION_DECL
+		       && DECL_IMMEDIATE_FUNCTION_P (fndecl));
+  if (DECL_NAME (fndecl) == NULL_TREE
+      || TREE_CODE (TREE_TYPE (fndecl)) != FUNCTION_TYPE
+      || TREE_CODE (TREE_TYPE (TREE_TYPE (fndecl))) != RECORD_TYPE
+      || DECL_CONTEXT (fndecl) != TREE_TYPE (TREE_TYPE (fndecl)))
+    return false;
+
+  if (strcmp (IDENTIFIER_POINTER (DECL_NAME (fndecl)), "current") != 0)
+    return false;
+
+  tree source_location = DECL_CONTEXT (fndecl);
+  if (TYPE_NAME (source_location) == NULL_TREE
+      || TREE_CODE (TYPE_NAME (source_location)) != TYPE_DECL
+      || DECL_NAME (TYPE_NAME (source_location)) == NULL_TREE
+      || strcmp (IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (source_location))),
+		 "source_location") != 0)
+    return false;
+
+  tree decl
+    = lookup_qualified_name (std_node,
+			     DECL_NAME (TYPE_NAME (source_location)),
+			     LOOK_want::TYPE, tf_none);
+  return TYPE_NAME (source_location) == decl;
+}
+
 struct bot_data
 {
   splay_tree target_remap;
@@ -3064,6 +3095,38 @@  bot_manip (tree* tp, int* walk_subtrees,
       set_flags_from_callee (*tp);
   if (data.clear_location && EXPR_HAS_LOCATION (*tp))
     SET_EXPR_LOCATION (*tp, input_location);
+  if (TREE_CODE (*tp) == CALL_EXPR
+      && !processing_template_decl
+      && (current_function_decl == NULL_TREE
+	  || !DECL_IMMEDIATE_FUNCTION_P (current_function_decl)))
+    if (tree fndecl = cp_get_callee_fndecl_nofold (*tp))
+      if (DECL_IMMEDIATE_FUNCTION_P (fndecl)
+	  && source_location_current_p (fndecl))
+	{
+	  bool ok = call_expr_nargs (*tp) == 0;
+	  if (call_expr_nargs (*tp) == 1
+	      && TREE_CODE (CALL_EXPR_ARG (*tp, 0)) == CALL_EXPR)
+	    if (tree fndecl2
+		= cp_get_callee_fndecl_nofold (CALL_EXPR_ARG (*tp, 0)))
+	      if (fndecl_built_in_p (fndecl2, CP_BUILT_IN_SOURCE_LOCATION,
+				     BUILT_IN_FRONTEND))
+		ok = true;
+	  if (!ok)
+	    {
+	      error_at (EXPR_LOCATION (*tp),
+			"%qD called with arguments", fndecl);
+	      *tp = build_constructor (TREE_TYPE (TREE_TYPE (fndecl)),
+				       NULL);
+	    }
+	  else
+	    {
+	      vec<tree, va_gc> *args = NULL;
+	      push_deferring_access_checks (dk_no_check);
+	      *tp = build_new_function_call (fndecl, &args,
+					     tf_warning_or_error);
+	      pop_deferring_access_checks ();
+	    }
+	}
   return t;
 }
 
--- gcc/cp/call.c.jj	2020-11-18 09:40:09.000000000 +0100
+++ gcc/cp/call.c	2020-11-30 19:47:03.951312431 +0100
@@ -8613,7 +8613,9 @@  build_over_call (struct z_candidate *can
 	  && (current_function_decl == NULL_TREE
 	      || !DECL_IMMEDIATE_FUNCTION_P (current_function_decl))
 	  && (current_binding_level->kind != sk_function_parms
-	      || !current_binding_level->immediate_fn_ctx_p))
+	      || (!current_binding_level->immediate_fn_ctx_p
+		  && !source_location_current_p (fn)))
+	  && (!parsing_nsdmi () || !source_location_current_p (fn)))
 	{
 	  tree obj_arg = NULL_TREE, exprimm = expr;
 	  if (DECL_CONSTRUCTOR_P (fn))
@@ -9257,7 +9259,9 @@  build_over_call (struct z_candidate *can
 	  && (current_function_decl == NULL_TREE
 	      || !DECL_IMMEDIATE_FUNCTION_P (current_function_decl))
 	  && (current_binding_level->kind != sk_function_parms
-	      || !current_binding_level->immediate_fn_ctx_p))
+	      || (!current_binding_level->immediate_fn_ctx_p
+		  && !source_location_current_p (fndecl)))
+	  && (!parsing_nsdmi () || !source_location_current_p (fndecl)))
 	{
 	  tree obj_arg = NULL_TREE;
 	  if (DECL_CONSTRUCTOR_P (fndecl))
--- gcc/testsuite/g++.dg/cpp2a/srcloc15.C.jj	2020-11-30 17:57:41.506279816 +0100
+++ gcc/testsuite/g++.dg/cpp2a/srcloc15.C	2020-11-30 17:57:21.169507324 +0100
@@ -0,0 +1,82 @@ 
+// { dg-do compile { target c++20 } }
+
+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;
+
+constexpr source_location
+foo (const source_location x = source_location::current ())
+{
+  return x;
+}
+
+constexpr bool
+bar ()
+{
+  int line = __LINE__;
+  source_location a = foo ();
+  source_location b = source_location::current ();
+  source_location c = foo ();
+  //                       ^ column 28
+  //                                            ^ column 49
+  const source_location *d[3] = { &a, &b, &c };
+  const char *file1 = __FILE__;
+  const char *function1 = __FUNCTION__;
+  for (int j = 0; j < 3; j++)
+    {
+      int i= 0;
+      const char *file2 = d[j]->file_name ();
+      const char *function2 = d[j]->function_name ();
+      for (i = 0; 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 (d[j]->line () != line + j + 1)
+	return false;
+      if (d[j]->column () != (j == 1 ? 49 : 28))
+	return false;
+    }
+  return true;
+}
+
+static_assert (bar ());
+
+int
+main ()
+{
+  bar ();
+}
--- gcc/testsuite/g++.dg/cpp2a/srcloc16.C.jj	2020-11-30 20:15:25.416283572 +0100
+++ gcc/testsuite/g++.dg/cpp2a/srcloc16.C	2020-11-30 20:15:06.285497601 +0100
@@ -0,0 +1,96 @@ 
+// { dg-do compile { target c++20 } }
+
+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;
+
+struct S
+{
+  source_location a = source_location::current ();
+  source_location b = source_location::current ();
+  source_location c = source_location ();
+  constexpr S () { c = source_location::current (); }
+};
+
+struct T
+{
+  int t;
+  source_location u = source_location::current ();
+  int v = __builtin_LINE ();
+};
+
+constexpr S s;
+constexpr T t = { 1 };
+
+constexpr bool
+cmp (const char *p, const char *q)
+{
+  for (; *p && *q; p++, q++)
+    if (*p != *q)
+      return true;
+  return *p || *q;
+}
+
+constexpr bool
+foo ()
+{
+  T u = { 2 };
+  source_location v = source_location::current ();
+  if (cmp (s.a.file_name (), s.c.file_name ())
+      || cmp (s.b.file_name (), s.c.file_name ())
+      || cmp (t.u.file_name (), s.c.file_name ())
+      || cmp (u.u.file_name (), s.c.file_name ())
+      || cmp (v.file_name (), s.c.file_name ())
+      || cmp (s.a.function_name (), s.c.function_name ())
+      || cmp (s.b.function_name (), s.c.function_name ())
+      || cmp (t.u.function_name (), "")
+      || cmp (u.u.function_name (), v.function_name ())
+      || s.a.line () != s.c.line ()
+      || s.b.line () != s.c.line ()
+      || t.u.line () != t.v
+      || u.u.line () + 1 != v.line ()
+      || s.a.column () != 18
+      || s.b.column () != 18
+      || s.c.column () != 50
+      || t.u.column () != 21
+      || u.u.column () != 13
+      || v.column () != 49)
+    return false;
+  return true;
+}
+
+static_assert (foo ());
+
+int
+main ()
+{
+  foo ();
+}