cpp_define (pfile, "__cpp_constexpr=201907L");
cpp_define (pfile, "__cpp_constexpr_in_decltype=201711L");
cpp_define (pfile, "__cpp_conditional_explicit=201806L");
- /* cpp_define (pfile, "__cpp_consteval=201811L"); */
+ cpp_define (pfile, "__cpp_consteval=201811L");
cpp_define (pfile, "__cpp_constinit=201907L");
cpp_define (pfile, "__cpp_deduction_guides=201907L");
cpp_define (pfile, "__cpp_nontype_template_parameter_class=201806L");
return data.changed;
}
+/* Evaluate the call T to virtual function thunk THUNK_FNDECL. */
+
+static tree
+cxx_eval_thunk_call (const constexpr_ctx *ctx, tree t, tree thunk_fndecl,
+ bool lval,
+ bool *non_constant_p, bool *overflow_p)
+{
+ tree function = THUNK_TARGET (thunk_fndecl);
+
+ /* virtual_offset is only set in the presence of virtual bases, which make
+ the class non-literal, so we don't need to handle it here. */
+ if (THUNK_VIRTUAL_OFFSET (thunk_fndecl))
+ {
+ gcc_assert (!DECL_DECLARED_CONSTEXPR_P (function));
+ if (!ctx->quiet)
+ {
+ error ("call to non-%<constexpr%> function %qD", function);
+ explain_invalid_constexpr_fn (function);
+ }
+ *non_constant_p = true;
+ return t;
+ }
+
+ tree new_call = copy_node (t);
+ CALL_EXPR_FN (new_call) = function;
+ TREE_TYPE (new_call) = TREE_TYPE (TREE_TYPE (function));
+
+ tree offset = size_int (THUNK_FIXED_OFFSET (thunk_fndecl));
+
+ if (DECL_THIS_THUNK_P (thunk_fndecl))
+ {
+ /* 'this'-adjusting thunk. */
+ tree this_arg = CALL_EXPR_ARG (t, 0);
+ this_arg = build2 (POINTER_PLUS_EXPR, TREE_TYPE (this_arg),
+ this_arg, offset);
+ CALL_EXPR_ARG (new_call, 0) = this_arg;
+ }
+ else
+ /* Return-adjusting thunk. */
+ new_call = build2 (POINTER_PLUS_EXPR, TREE_TYPE (new_call),
+ new_call, offset);
+
+ return cxx_eval_constant_expression (ctx, new_call, lval,
+ non_constant_p, overflow_p);
+}
+
/* Subroutine of cxx_eval_constant_expression.
Evaluate the call expression tree T in the context of OLD_CALL expression
evaluation. */
if (fndecl_built_in_p (fun))
return cxx_eval_builtin_function_call (ctx, t, fun,
lval, non_constant_p, overflow_p);
+ if (DECL_THUNK_P (fun))
+ return cxx_eval_thunk_call (ctx, t, fun, lval, non_constant_p, overflow_p);
if (!DECL_DECLARED_CONSTEXPR_P (fun))
{
if (TREE_CODE (t) == CALL_EXPR
if (fold)
fn = maybe_constant_init (fn);
STRIP_NOPS (fn);
- if (TREE_CODE (fn) == ADDR_EXPR)
- {
- fn = TREE_OPERAND (fn, 0);
- if (TREE_CODE (fn) == FUNCTION_DECL)
- return fn;
- }
+ if (TREE_CODE (fn) == ADDR_EXPR
+ || TREE_CODE (fn) == FDESC_EXPR)
+ fn = TREE_OPERAND (fn, 0);
+ if (TREE_CODE (fn) == FUNCTION_DECL)
+ return fn;
return NULL_TREE;
}
}
}
- /* FIXME: For now. */
- if (virtualp && (inlinep & 8) != 0)
- {
- sorry_at (DECL_SOURCE_LOCATION (decl),
- "%<virtual%> %<consteval%> method %qD not supported yet",
- decl);
- inlinep &= ~8;
- }
-
/* If this decl has namespace scope, set that up. */
if (in_namespace)
set_decl_namespace (decl, in_namespace, friendp);
int destructions_p;
} *priority_info;
-static void mark_vtable_entries (tree);
-static bool maybe_emit_vtables (tree);
static tree start_objects (int, int);
static void finish_objects (int, int, tree);
static tree start_static_storage_duration_function (unsigned);
and mark them as needed. */
static void
-mark_vtable_entries (tree decl)
+mark_vtable_entries (tree decl, vec<tree> &consteval_vtables)
{
tree fnaddr;
unsigned HOST_WIDE_INT idx;
/* It's OK for the vtable to refer to deprecated virtual functions. */
warning_sentinel w(warn_deprecated_decl);
+ bool consteval_seen = false;
+
FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (DECL_INITIAL (decl)),
idx, fnaddr)
{
continue;
fn = TREE_OPERAND (fnaddr, 0);
+ if (TREE_CODE (fn) == FUNCTION_DECL && DECL_IMMEDIATE_FUNCTION_P (fn))
+ {
+ if (!consteval_seen)
+ {
+ consteval_seen = true;
+ consteval_vtables.safe_push (decl);
+ }
+ continue;
+ }
TREE_ADDRESSABLE (fn) = 1;
/* When we don't have vcall offsets, we output thunks whenever
we output the vtables that contain them. With vcall offsets,
}
}
+/* Replace any consteval functions in vtables with null pointers. */
+
+static void
+clear_consteval_vfns (vec<tree> &consteval_vtables)
+{
+ for (tree vtable : consteval_vtables)
+ for (constructor_elt &elt : *CONSTRUCTOR_ELTS (DECL_INITIAL (vtable)))
+ {
+ tree fn = cp_get_fndecl_from_callee (elt.value, /*fold*/false);
+ if (fn && DECL_IMMEDIATE_FUNCTION_P (fn))
+ elt.value = build_zero_cst (vtable_entry_type);
+ }
+}
+
/* Adjust the TLS model on variable DECL if need be, typically after
the linkage of DECL has been modified. */
Returns true if any vtables were emitted. */
static bool
-maybe_emit_vtables (tree ctype)
+maybe_emit_vtables (tree ctype, vec<tree> &consteval_vtables)
{
tree vtbl;
tree primary_vtbl;
for (vtbl = CLASSTYPE_VTABLES (ctype); vtbl; vtbl = DECL_CHAIN (vtbl))
{
/* Mark entities references from the virtual table as used. */
- mark_vtable_entries (vtbl);
+ mark_vtable_entries (vtbl, consteval_vtables);
if (TREE_TYPE (DECL_INITIAL (vtbl)) == 0)
{
emit_support_tinfos ();
+ /* Track vtables we want to emit that refer to consteval functions. */
+ auto_vec<tree> consteval_vtables;
+
do
{
tree t;
have to look at it again. */
for (i = keyed_classes->length ();
keyed_classes->iterate (--i, &t);)
- if (maybe_emit_vtables (t))
+ if (maybe_emit_vtables (t, consteval_vtables))
{
reconsider = true;
keyed_classes->unordered_remove (i);
perform_deferred_noexcept_checks ();
fini_constexpr ();
+ clear_consteval_vfns (consteval_vtables);
/* The entire file is now complete. If requested, dump everything
to a file. */
/* OK */;
else
{
+ auto_diagnostic_group d;
if (fail == 1)
- {
- auto_diagnostic_group d;
- error ("invalid covariant return type for %q+#D", overrider);
- inform (DECL_SOURCE_LOCATION (basefn),
- "overridden function is %q#D", basefn);
- }
+ error ("invalid covariant return type for %q+#D", overrider);
else
- {
- auto_diagnostic_group d;
- error ("conflicting return type specified for %q+#D", overrider);
- inform (DECL_SOURCE_LOCATION (basefn),
- "overridden function is %q#D", basefn);
- }
+ error ("conflicting return type specified for %q+#D", overrider);
+ inform (DECL_SOURCE_LOCATION (basefn),
+ "overridden function is %q#D", basefn);
DECL_INVALID_OVERRIDER_P (overrider) = 1;
return 0;
}
return 0;
}
+ /* A consteval virtual function shall not override a virtual function that is
+ not consteval. A consteval virtual function shall not be overridden by a
+ virtual function that is not consteval. */
+ if (DECL_IMMEDIATE_FUNCTION_P (overrider)
+ != DECL_IMMEDIATE_FUNCTION_P (basefn))
+ {
+ auto_diagnostic_group d;
+ if (DECL_IMMEDIATE_FUNCTION_P (overrider))
+ error ("%<consteval%> function %q+D overriding non-%<consteval%> "
+ "function", overrider);
+ else
+ error ("non-%<consteval%> function %q+D overriding %<consteval%> "
+ "function", overrider);
+ inform (DECL_SOURCE_LOCATION (basefn),
+ "overridden function is %qD", basefn);
+ DECL_INVALID_OVERRIDER_P (overrider) = 1;
+ return 0;
+ }
+
/* A function declared transaction_safe_dynamic that overrides a function
declared transaction_safe (but not transaction_safe_dynamic) is
ill-formed. */
--- /dev/null
+// { dg-do compile { target c++20 } }
+
+struct S {
+ virtual int foo () { return 42; } // { dg-message "overridden function is 'virtual int S::foo\\\(\\\)'" }
+ consteval virtual int bar () { return 43; } // { dg-message "overridden function is 'virtual consteval int S::bar\\\(\\\)'" }
+};
+struct T : public S {
+ int bar () { return 44; } // { dg-error "non-'consteval' function 'virtual int T::bar\\\(\\\)' overriding 'consteval' function" }
+};
+struct U : public S {
+ consteval virtual int foo () { return 45; } // { dg-error "'consteval' function 'virtual consteval int U::foo\\\(\\\)' overriding non-'consteval' function" }
+};
--- /dev/null
+// { dg-do compile { target c++20 } }
+
+struct A
+{
+ virtual consteval int f() const { return 1; };
+};
+
+struct B: A
+{
+ virtual consteval int f() const { return 2; };
+ virtual void g() { }
+};
+
+consteval int f()
+{
+ const A& ar = B();
+ return ar.f();
+}
+
+static_assert (f() == 2);
+
+B b;
--- /dev/null
+// { dg-do compile { target c++20 } }
+
+struct S {
+ constexpr S () : s (0) {}
+ virtual int foo () const { return 42; }
+ consteval virtual int bar () const { return 43; }
+ consteval virtual int baz () const { return 44; }
+ consteval virtual int qux () const { return 47; }
+ int s;
+};
+struct T : public S {
+ constexpr T () : t (0) {}
+ consteval int bar () const { return 45; }
+ consteval virtual int baz () const { return 46; }
+ consteval virtual int grault () const { return 48; }
+ int t;
+};
+
+consteval int
+foo ()
+{
+ S s;
+ T t;
+ S *u = (S *) &t;
+ T *v = &t;
+ if (s.bar () != 43) throw 1;
+ if (s.baz () != 44) throw 2;
+ if (t.bar () != 45) throw 3;
+ if (t.baz () != 46) throw 4;
+ if (u->bar () != 45) throw 5;
+ if (u->baz () != 46) throw 6;
+ if (s.qux () != 47) throw 7;
+ if (t.qux () != 47) throw 8;
+ if (u->qux () != 47) throw 9;
+ if (v->qux () != 47) throw 10;
+ if (v->grault () != 48) throw 11;
+ return 0;
+}
+
+constexpr S s;
+constexpr T t;
+
+constexpr const S *
+bar (bool x)
+{
+ return x ? &s : (const S *) &t;
+}
+
+int a = foo ();
+int b = bar (false)->bar ();
+int c = bar (true)->baz ();
+static_assert (bar (false)->bar () == 45);
+static_assert (bar (true)->baz () == 44);
--- /dev/null
+// { dg-do compile { target c++20 } }
+
+struct S {
+ constexpr S () : s (0) {}
+ virtual int foo () const { return 42; }
+ consteval virtual int bar () const { return 43; }
+ consteval virtual int baz () const { return 44; }
+ int s;
+};
+struct T : public S {
+ constexpr T () : t (0) {}
+ consteval int bar () const { return 45; }
+ consteval virtual int baz () const { return 46; }
+ int t;
+};
+
+consteval int
+foo ()
+{
+ S s;
+ T t;
+ S *u = (S *) &t;
+ T *v = &t;
+ auto pmf1 = &S::bar;
+ auto pmf2 = &S::baz;
+ if ((s.*pmf1) () != 43) throw 1;
+ if ((s.*pmf2) () != 44) throw 2;
+ if ((t.*pmf1) () != 45) throw 3;
+ if ((t.*pmf2) () != 46) throw 4;
+ if ((u->*pmf1) () != 45) throw 5;
+ if ((u->*pmf2) () != 46) throw 6;
+ return 0;
+}
+
+constexpr S s;
+constexpr T t;
+
+constexpr const S *
+bar (bool x)
+{
+ return x ? &s : (const S *) &t;
+}
+
+int a = foo ();
+int b = bar (false)->bar ();
+int c = bar (true)->baz ();
+static_assert (bar (false)->bar () == 45);
+static_assert (bar (true)->baz () == 44);
--- /dev/null
+// { dg-do compile { target c++20 } }
+
+struct B1;
+struct B2;
+struct D;
+
+struct B1
+{
+ virtual consteval const B1 *foo1 () const {return this;}
+ virtual consteval const B2 *foo2 (const D *) const;
+};
+struct B2
+{
+ virtual consteval const B2 *baz1 () const {return this;}
+ virtual consteval const B1 *baz2 (const D *) const;
+};
+
+struct D : public B1, B2
+{
+ virtual consteval const D *foo1 () const {return this;}
+ virtual consteval const D *foo2 (const D *d) const {return d;}
+ virtual consteval const D *baz1 () const {return this;}
+ virtual consteval const D *baz2 (const D *d) const {return d;}
+};
+
+consteval const B2 *B1::foo2 (const D *d) const {return d;}
+consteval const B1 *B2::baz2 (const D *d) const {return d;}
+
+consteval int
+test (const B1 *b1, const B2 *b2, const D *d)
+{
+ if (b1->foo1 () != b1)
+ return 1;
+ if (b2->baz1 () != b2)
+ return 2;
+ if (b1->foo2 (d) != b2)
+ return 3;
+ if (b2->baz2 (d) != b1)
+ return 4;
+ return 0;
+}
+
+consteval int
+test (const D *d)
+{
+ if (d->foo2 (d) != d)
+ return 11;
+ if (d->baz2 (d) != d)
+ return 12;
+ if (d->foo1 () != d)
+ return 13;
+ if (d->baz1 () != d)
+ return 14;
+ return 0;
+}
+
+constexpr D d;
+constexpr auto e = test (&d, &d, &d);
+constexpr auto f = test (&d);
+static_assert (e == 0);
+static_assert (f == 0);