diff --git a/gcc/cp/cp-gimplify.c b/gcc/cp/cp-gimplify.c
index c1691c3e073c1b42297b8730d26a314c82bcbe94..2fbb4236b929160369bed359fccb43796dce7b1d 100644
--- a/gcc/cp/cp-gimplify.c
+++ b/gcc/cp/cp-gimplify.c
@@ -900,8 +900,39 @@ struct cp_genericize_data
 static tree
 cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data)
 {
-  tree stmt;
-  enum tree_code code;
+  tree stmt = *stmt_p;
+  enum tree_code code = TREE_CODE (stmt);
+
+  switch (code)
+    {
+    case PTRMEM_CST:
+      if (TREE_CODE (PTRMEM_CST_MEMBER (stmt)) == FUNCTION_DECL
+	  && DECL_IMMEDIATE_FUNCTION_P (PTRMEM_CST_MEMBER (stmt)))
+	{
+	  if (!((hash_set<tree> *) data)->add (stmt))
+	    error_at (PTRMEM_CST_LOCATION (stmt),
+		      "taking address of an immediate function %qD",
+		      PTRMEM_CST_MEMBER (stmt));
+	  stmt = *stmt_p = build_zero_cst (TREE_TYPE (stmt));
+	  break;
+	}
+      break;
+
+    case ADDR_EXPR:
+      if (TREE_CODE (TREE_OPERAND (stmt, 0)) == FUNCTION_DECL
+	  && DECL_IMMEDIATE_FUNCTION_P (TREE_OPERAND (stmt, 0)))
+	{
+	  error_at (EXPR_LOCATION (stmt),
+		    "taking address of an immediate function %qD",
+		    TREE_OPERAND (stmt, 0));
+	  stmt = *stmt_p = build_zero_cst (TREE_TYPE (stmt));
+	  break;
+	}
+      break;
+
+    default:
+      break;
+    }
 
   *stmt_p = stmt = cp_fold (*stmt_p);
 
@@ -917,12 +948,16 @@ cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data)
     }
 
   code = TREE_CODE (stmt);
-  if (code == OMP_FOR || code == OMP_SIMD || code == OMP_DISTRIBUTE
-      || code == OMP_LOOP || code == OMP_TASKLOOP || code == OACC_LOOP)
+  switch (code)
     {
       tree x;
       int i, n;
-
+    case OMP_FOR:
+    case OMP_SIMD:
+    case OMP_DISTRIBUTE:
+    case OMP_LOOP:
+    case OMP_TASKLOOP:
+    case OACC_LOOP:
       cp_walk_tree (&OMP_FOR_BODY (stmt), cp_fold_r, data, NULL);
       cp_walk_tree (&OMP_FOR_CLAUSES (stmt), cp_fold_r, data, NULL);
       cp_walk_tree (&OMP_FOR_INIT (stmt), cp_fold_r, data, NULL);
@@ -961,6 +996,22 @@ cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data)
 	}
       cp_walk_tree (&OMP_FOR_PRE_BODY (stmt), cp_fold_r, data, NULL);
       *walk_subtrees = 0;
+      return NULL;
+
+    case IF_STMT:
+      if (IF_STMT_CONSTEVAL_P (stmt))
+	{
+	  /* Don't walk THEN_CLAUSE (stmt) for consteval if.  IF_COND is always
+	     boolean_false_node.  */
+	  cp_walk_tree (&ELSE_CLAUSE (stmt), cp_fold_r, data, NULL);
+	  cp_walk_tree (&IF_SCOPE (stmt), cp_fold_r, data, NULL);
+	  *walk_subtrees = 0;
+	  return NULL;
+	}
+      break;
+
+    default:
+      break;
     }
 
   return NULL;
@@ -1476,14 +1527,6 @@ cp_genericize_r (tree *stmt_p, int *walk_subtrees, void *data)
 	  break;
 	}
 
-      if (tree fndecl = cp_get_callee_fndecl_nofold (stmt))
-	if (DECL_IMMEDIATE_FUNCTION_P (fndecl))
-	  {
-	    gcc_assert (source_location_current_p (fndecl));
-	    *stmt_p = cxx_constant_value (stmt);
-	    break;
-	  }
-
       if (!wtd->no_sanitize_p
 	  && sanitize_flags_p ((SANITIZE_NULL
 				| SANITIZE_ALIGNMENT | SANITIZE_VPTR)))
@@ -2629,6 +2672,14 @@ cp_fold (tree x)
 	int sv = optimize, nw = sv;
 	tree callee = get_callee_fndecl (x);
 
+	if (tree fndecl = cp_get_callee_fndecl_nofold (x))
+	  if (DECL_IMMEDIATE_FUNCTION_P (fndecl)
+	      && source_location_current_p (fndecl))
+	    {
+	      x = cxx_constant_value (x);
+	      break;
+	    }
+
 	/* Some built-in function calls will be evaluated at compile-time in
 	   fold ().  Set optimize to 1 when folding __builtin_constant_p inside
 	   a constexpr function so that fold_builtin_1 doesn't fold it to 0.  */
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 477996911ff4ce0fba7cd468827ba4178cfac2db..1ee2c57e83c79a0efb722f44707077c8bb628cdb 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -703,6 +703,7 @@ struct GTY(()) template_parm_index {
 struct GTY(()) ptrmem_cst {
   struct tree_common common;
   tree member;
+  location_t locus;
 };
 typedef struct ptrmem_cst * ptrmem_cst_t;
 
@@ -4726,6 +4727,11 @@ more_aggr_init_expr_args_p (const aggr_init_expr_arg_iterator *iter)
 #define PTRMEM_CST_MEMBER(NODE) \
   (((ptrmem_cst_t)PTRMEM_CST_CHECK (NODE))->member)
 
+/* For a pointer-to-member constant `X::Y' this is a location where
+   the address of the member has been taken.  */
+#define PTRMEM_CST_LOCATION(NODE) \
+  (((ptrmem_cst_t)PTRMEM_CST_CHECK (NODE))->locus)
+
 /* The expression in question for a TYPEOF_TYPE.  */
 #define TYPEOF_TYPE_EXPR(NODE) (TYPE_VALUES_RAW (TYPEOF_TYPE_CHECK (NODE)))
 
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 221628ad3a3fdbc5adc50cb1049117c36fab4394..74323701a7dd29ce4f041e2eb34ec81b12c9a93e 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -17012,9 +17012,16 @@ tsubst_copy (tree t, tree args, tsubst_flags_t complain, tree in_decl)
       {
 	tree type = tsubst (TREE_TYPE (t), args, complain, in_decl);
 	tree op0 = tsubst_copy (TREE_OPERAND (t, 0), args, complain, in_decl);
-	r = build1 (code, type, op0);
+	r = build1_loc (EXPR_LOCATION (t), code, type, op0);
 	if (code == ALIGNOF_EXPR)
 	  ALIGNOF_EXPR_STD_P (r) = ALIGNOF_EXPR_STD_P (t);
+	/* For addresses of immediate functions ensure we have EXPR_LOCATION
+	   set for possible later diagnostics.  */
+	if (code == ADDR_EXPR
+	    && EXPR_LOCATION (r) == UNKNOWN_LOCATION
+	    && TREE_CODE (op0) == FUNCTION_DECL
+	    && DECL_IMMEDIATE_FUNCTION_P (op0))
+	  SET_EXPR_LOCATION (r, input_location);
 	return r;
       }
 
diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
index 7c58e23db3c28ac261caab261b50b3e37be56960..1471ed8a2efe0823524a7fb5cb057a4454f18b0d 100644
--- a/gcc/cp/tree.c
+++ b/gcc/cp/tree.c
@@ -5196,6 +5196,7 @@ make_ptrmem_cst (tree type, tree member)
   tree ptrmem_cst = make_node (PTRMEM_CST);
   TREE_TYPE (ptrmem_cst) = type;
   PTRMEM_CST_MEMBER (ptrmem_cst) = member;
+  PTRMEM_CST_LOCATION (ptrmem_cst) = input_location;
   return ptrmem_cst;
 }
 
@@ -6040,6 +6041,8 @@ cp_expr_location (const_tree t_)
       return STATIC_ASSERT_SOURCE_LOCATION (t);
     case TRAIT_EXPR:
       return TRAIT_EXPR_LOCATION (t);
+    case PTRMEM_CST:
+      return PTRMEM_CST_LOCATION (t);
     default:
       return EXPR_LOCATION (t);
     }
diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
index 872e944dc2e3ce601b004fa963a8e27426fba857..5ed9a5ab9ee92f5a4c142f3be49bde233a3c2048 100644
--- a/gcc/cp/typeck.c
+++ b/gcc/cp/typeck.c
@@ -6497,7 +6497,7 @@ build_x_unary_op (location_t loc, enum tree_code code, cp_expr xarg,
       exp = cp_build_addr_expr_strict (xarg, complain);
 
       if (TREE_CODE (exp) == PTRMEM_CST)
-	exp = maybe_wrap_with_location (exp, loc);
+	PTRMEM_CST_LOCATION (exp) = loc;
       else
 	protected_set_expr_location (exp, loc);
     }
@@ -6780,16 +6780,6 @@ cp_build_addr_expr_1 (tree arg, bool strict_lvalue, tsubst_flags_t complain)
 	    return error_mark_node;
 	  }
 
-	if (TREE_CODE (t) == FUNCTION_DECL
-	    && DECL_IMMEDIATE_FUNCTION_P (t)
-	    && !in_immediate_context ())
-	  {
-	    if (complain & tf_error)
-	      error_at (loc, "taking address of an immediate function %qD",
-			t);
-	    return error_mark_node;
-	  }
-
 	type = build_ptrmem_type (context_for_name_lookup (t),
 				  TREE_TYPE (t));
 	t = make_ptrmem_cst (type, t);
@@ -6815,15 +6805,6 @@ cp_build_addr_expr_1 (tree arg, bool strict_lvalue, tsubst_flags_t complain)
   if (processing_template_decl || TREE_CODE (arg) != COMPONENT_REF)
     {
       tree stripped_arg = tree_strip_any_location_wrapper (arg);
-      if (TREE_CODE (stripped_arg) == FUNCTION_DECL
-	  && DECL_IMMEDIATE_FUNCTION_P (stripped_arg)
-	  && !in_immediate_context ())
-	{
-	  if (complain & tf_error)
-	    error_at (loc, "taking address of an immediate function %qD",
-		      stripped_arg);
-	  return error_mark_node;
-	}
       if (TREE_CODE (stripped_arg) == FUNCTION_DECL
 	  && !mark_used (stripped_arg, complain) && !(complain & tf_error))
 	return error_mark_node;
@@ -6865,6 +6846,13 @@ cp_build_addr_expr_1 (tree arg, bool strict_lvalue, tsubst_flags_t complain)
 			      complain);
     }
 
+  /* For addresses of immediate functions ensure we have EXPR_LOCATION
+     set for possible later diagnostics.  */
+  if (TREE_CODE (val) == ADDR_EXPR
+      && TREE_CODE (TREE_OPERAND (val, 0)) == FUNCTION_DECL
+      && DECL_IMMEDIATE_FUNCTION_P (TREE_OPERAND (val, 0)))
+    SET_EXPR_LOCATION (val, input_location);
+
   return val;
 }
 
@@ -9571,8 +9559,12 @@ expand_ptrmemfunc_cst (tree cst, tree *delta, tree *pfn)
 				 /*c_cast_p=*/0, tf_warning_or_error);
 
   if (!DECL_VIRTUAL_P (fn))
-    *pfn = convert (TYPE_PTRMEMFUNC_FN_TYPE (type),
-		    build_addr_func (fn, tf_warning_or_error));
+    {
+      tree t = build_addr_func (fn, tf_warning_or_error);
+      if (TREE_CODE (t) == ADDR_EXPR)
+	SET_EXPR_LOCATION (t, PTRMEM_CST_LOCATION (cst));
+      *pfn = convert (TYPE_PTRMEMFUNC_FN_TYPE (type), t);
+    }
   else
     {
       /* If we're dealing with a virtual function, we have to adjust 'this'
@@ -9665,7 +9657,7 @@ convert_for_assignment (tree type, tree rhs,
   tree rhstype;
   enum tree_code coder;
 
-  location_t rhs_loc = EXPR_LOC_OR_LOC (rhs, input_location);
+  location_t rhs_loc = cp_expr_loc_or_input_loc (rhs);
   bool has_loc = EXPR_LOCATION (rhs) != UNKNOWN_LOCATION;
   /* Strip NON_LVALUE_EXPRs since we aren't using as an lvalue,
      but preserve location wrappers.  */
diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval20.C b/gcc/testsuite/g++.dg/cpp2a/consteval20.C
index 2c359637c6198299ab00df375dd324761683cd3d..bd44712c53516ac48f0f850b2b3715b764d86a2b 100644
--- a/gcc/testsuite/g++.dg/cpp2a/consteval20.C
+++ b/gcc/testsuite/g++.dg/cpp2a/consteval20.C
@@ -10,10 +10,14 @@ constexpr S s;
 int
 bar ()
 {
+  auto c = &S::foo;			// { dg-error "taking address of an immediate function" }
+  constexpr auto d = &S::foo;		// { dg-error "taking address of an immediate function" }
+  static auto e = &S::foo;		// { dg-error "taking address of an immediate function" }
   return (s.*&S::foo) ();		// { dg-error "taking address of an immediate function" }
 }
 
 constexpr auto a = &S::foo;		// { dg-error "taking address of an immediate function" }
+auto b = &S::foo;			// { dg-error "taking address of an immediate function" }
 
 consteval int
 baz ()
diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval23.C b/gcc/testsuite/g++.dg/cpp2a/consteval23.C
index 4c7e844c4509f95524768b00e10be41a5b95b7a2..a6fdaa836de6b69bac5163cc9149b01cf02ad30c 100644
--- a/gcc/testsuite/g++.dg/cpp2a/consteval23.C
+++ b/gcc/testsuite/g++.dg/cpp2a/consteval23.C
@@ -2,6 +2,7 @@
 // { dg-do compile { target c++20 } }
 
 consteval int foo () { return 42; }
+constexpr auto baz (int (*fn) ()) { return fn; }
 
 consteval int
 bar (int (*fn) () = foo)
@@ -11,3 +12,6 @@ bar (int (*fn) () = foo)
 
 static_assert (bar () == 42);
 static_assert (bar (foo) == 42);
+static_assert (bar (&foo) == 42);
+static_assert (bar (baz (foo)) == 42);
+static_assert (bar (baz (&foo)) == 42);
diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval25.C b/gcc/testsuite/g++.dg/cpp2a/consteval25.C
new file mode 100644
index 0000000000000000000000000000000000000000..80b76a474cbd9eab1777f8936794b042d074127f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/consteval25.C
@@ -0,0 +1,17 @@
+// PR c++/102753
+// { dg-do compile { target c++20 } }
+// { dg-options "" }
+
+consteval int foo () { return 42; }
+
+consteval int
+bar (int (*fn) ())
+{
+  return fn ();
+}
+
+void
+baz ()
+{
+  static_assert (bar (({ constexpr auto a = 1; foo; })) == 42);
+}
diff --git a/gcc/testsuite/g++.dg/cpp2a/srcloc20.C b/gcc/testsuite/g++.dg/cpp2a/srcloc20.C
new file mode 100644
index 0000000000000000000000000000000000000000..acdf5a6505fa56a7fb71b4380e0e5ffb594efeca
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/srcloc20.C
@@ -0,0 +1,44 @@
+// { 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;
+
+auto a = source_location::current;		// { dg-error "taking address of an immediate function" }
+constexpr auto b = &source_location::current;	// { dg-error "taking address of an immediate function" }
+
+void
+foo ()
+{
+  auto c = &source_location::current;		// { dg-error "taking address of an immediate function" }
+  constexpr auto d = source_location::current;	// { dg-error "taking address of an immediate function" }
+  static auto e = source_location::current;	// { dg-error "taking address of an immediate function" }
+}