c++: Reject addresses of immediate functions in constexpr vars inside of immediate functions or consteval if [PR102753]

Another thing that wasn't in the previous patch, but I'm wondering whether we don't
handle it incorrectly.  constexpr.c has:
  /* Check that immediate invocation does not return an expression referencing
     any immediate function decls.  They need to be allowed while parsing
     immediate functions, but can't leak outside of them.  */
  if (is_consteval
      && t != r
      && (current_function_decl == NULL_TREE
          || !DECL_IMMEDIATE_FUNCTION_P (current_function_decl)))
as condition for the discovery of embedded immediate FUNCTION_DECLs
(or now PTRMEM_CSTs).  If I remove the && (current... ..._decl))
then g++.dg/cpp2a/consteval7.C's
struct S { int b; int (*c) (); };
consteval S baz () { return { 5, foo }; }
consteval int qux () { S s = baz (); return s.b + s.c (); }
consteval int quux () { constexpr S s = baz (); return s.b + s.c (); }
quux line fails, but based on
http://eel.is/c++draft/expr.const#11
I wonder if it shouldn't fail (clang++ -std=c++20 rejects it),
and be only accepted without the constexpr keyword before S s.
Also wonder about e.g.
consteval int foo () { return 42; }

consteval int
bar ()
{
  auto fn1 = foo;  // This must be ok
  constexpr auto fn2 = foo; // Isn't this an error?
  return fn1 () + fn2 ();
}

constexpr int
baz ()
{
  if consteval {
    auto fn1 = foo; // This must be ok
    constexpr auto fn2 = foo; // Isn't this an error?
    return fn1 () + fn2 ();
  }
  return 0;
}

auto a = bar ();

static_assert (bar () == 84);
static_assert (baz () == 84);
(again, clang++ -std=c++20 rejects the fn2 = foo; case,
but doesn't implement consteval if, so can't test the other one).
For taking address of an immediate function or method if it is taken
outside of immediate function context we already have diagnostics
about it, but shouldn't the immediate FUNCTION_DECL discovery in
cxx_eval_outermost_constant_expression be instead guarded with something
like
  if (is_consteval || in_immediate_context ())
and be done regardless of whether t != r?

2021-10-27  Jakub Jelinek  <jakub@redhat.com>

	PR c++/102753
	* constexpr.c (cxx_eval_outermost_constant_expr): Perform
	find_immediate_fndecl discovery if is_consteval or
	in_immediate_context () rather than if is_consteval, t != r
	and not in immediate function's body.

	* g++.dg/cpp2a/consteval7.C: Expect diagnostics on quux.
	* g++.dg/cpp2a/consteval24.C: New test.
	* g++.dg/cpp23/consteval-if12.C: New test.
This commit is contained in:
Jakub Jelinek 2021-10-27 09:08:19 +02:00
parent 4b2fda8bea
commit 7473b8a904
4 changed files with 67 additions and 7 deletions

View file

@ -7472,12 +7472,8 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant,
}
/* Check that immediate invocation does not return an expression referencing
any immediate function decls. They need to be allowed while parsing
immediate functions, but can't leak outside of them. */
if (is_consteval
&& t != r
&& (current_function_decl == NULL_TREE
|| !DECL_IMMEDIATE_FUNCTION_P (current_function_decl)))
any immediate function decls. */
if (is_consteval || in_immediate_context ())
if (tree immediate_fndecl
= cp_walk_tree_without_duplicates (&r, find_immediate_fndecl,
NULL))

View file

@ -0,0 +1,34 @@
// PR c++/102753
// { dg-do compile { target c++20 } }
// { dg-options "" }
struct S {
constexpr S () : s (0) {}
consteval int foo () { return 1; }
virtual consteval int bar () { return 2; }
int s;
};
consteval int foo () { return 42; }
consteval auto baz () { return foo; }
consteval auto qux () { return &S::foo; }
consteval auto corge () { return &S::bar; }
constexpr int
bar ()
{
S s;
if consteval { // { dg-warning "'if consteval' only available with" "" { target c++20_only } }
constexpr auto fn1 = foo; // { dg-error "immediate evaluation returns address of immediate function" }
constexpr auto fn2 = &foo; // { dg-error "immediate evaluation returns address of immediate function" }
constexpr auto fn3 = &S::foo; // { dg-error "immediate evaluation returns address of immediate function" }
constexpr auto fn4 = &S::bar; // { dg-error "immediate evaluation returns address of immediate function" }
constexpr auto fn5 = baz (); // { dg-error "immediate evaluation returns address of immediate function" }
constexpr auto fn6 = qux (); // { dg-error "immediate evaluation returns address of immediate function" }
constexpr auto fn7 = corge (); // { dg-error "immediate evaluation returns address of immediate function" }
return fn1 () + fn2 () + (s.*fn3) () + (s.*fn4) () + fn5 () + (s.*fn6) () + (s.*fn7) ();
}
return 0;
}
auto a = bar ();

View file

@ -0,0 +1,30 @@
// PR c++/102753
// { dg-do compile { target c++20 } }
struct S {
constexpr S () : s (0) {}
consteval int foo () { return 1; }
virtual consteval int bar () { return 2; }
int s;
};
consteval int foo () { return 42; }
consteval auto baz () { return foo; }
consteval auto qux () { return &S::foo; }
consteval auto corge () { return &S::bar; }
consteval int
bar ()
{
S s;
constexpr auto fn1 = foo; // { dg-error "immediate evaluation returns address of immediate function" }
constexpr auto fn2 = &foo; // { dg-error "immediate evaluation returns address of immediate function" }
constexpr auto fn3 = &S::foo; // { dg-error "immediate evaluation returns address of immediate function" }
constexpr auto fn4 = &S::bar; // { dg-error "immediate evaluation returns address of immediate function" }
constexpr auto fn5 = baz (); // { dg-error "immediate evaluation returns address of immediate function" }
constexpr auto fn6 = qux (); // { dg-error "immediate evaluation returns address of immediate function" }
constexpr auto fn7 = corge (); // { dg-error "immediate evaluation returns address of immediate function" }
return fn1 () + fn2 () + (s.*fn3) () + (s.*fn4) () + fn5 () + (s.*fn6) () + (s.*fn7) ();
}
auto a = bar ();

View file

@ -7,7 +7,7 @@ constexpr auto a = bar (); // { dg-error "immediate evaluation returns address o
struct S { int b; int (*c) (); };
consteval S baz () { return { 5, foo }; }
consteval int qux () { S s = baz (); return s.b + s.c (); }
consteval int quux () { constexpr S s = baz (); return s.b + s.c (); }
consteval int quux () { constexpr S s = baz (); return s.b + s.c (); } // { dg-error "immediate evaluation returns address of immediate function 'consteval int foo\\(\\)'" }
constexpr auto d = baz (); // { dg-error "immediate evaluation returns address of immediate function 'consteval int foo\\(\\)'" }
constexpr auto e = qux ();
constexpr auto f = quux ();