diff options
author | Jason Merrill <jason@redhat.com> | 2020-06-24 20:46:09 -0400 |
---|---|---|
committer | Jason Merrill <jason@redhat.com> | 2020-07-02 13:20:23 -0400 |
commit | e6321c4508b2a85c21246c1c06a8208e2a151e48 (patch) | |
tree | 7185013f41363425d08894d5e1704a3c3d6e7025 /gcc/cp | |
parent | 105ecbea5f402713130fef4d41bb000e2d23493b (diff) | |
download | gcc-e6321c4508b2a85c21246c1c06a8208e2a151e48.zip gcc-e6321c4508b2a85c21246c1c06a8208e2a151e48.tar.gz gcc-e6321c4508b2a85c21246c1c06a8208e2a151e48.tar.bz2 |
c++: Support C++20 virtual consteval functions. [PR88335]
Jakub's partial implementation of consteval virtual had trouble with the
current ABI requirement that we omit the vtable slot for a consteval virtual
function; it's difficult to use the normal code for constant evaluation and
also magically make the slots disappear if the vtables get written out. I
notice that Clang trunk also doesn't implement that requirement, and it
seems unnecessary to me; I expect consteval virtual functions to be
extremely rare, so it should be fine to just give them a vtable slot as
normal but put zero in it if the vtable gets emitted. I've commented as
much to the ABI committee.
One of Jakub's testcases points out that we weren't handling thunks in
our constexpr virtual handling; that is fixed here as well.
Incidentally, being able to use C++11 range-for definitely simplified
clear_consteval_vfns.
gcc/c-family/ChangeLog:
* c-cppbuiltin.c (c_cpp_builtins): Define __cpp_consteval.
gcc/cp/ChangeLog:
* decl.c (grokfndecl): Allow consteval virtual.
* search.c (check_final_overrider): Check consteval mismatch.
* constexpr.c (cxx_eval_thunk_call): New.
(cxx_eval_call_expression): Call it.
* cvt.c (cp_get_fndecl_from_callee): Handle FDESC_EXPR.
* decl2.c (mark_vtable_entries): Track vtables with consteval.
(maybe_emit_vtables): Pass consteval_vtables through.
(clear_consteval_vfns): Replace consteval with nullptr.
(c_parse_final_cleanups): Call it.
gcc/testsuite/ChangeLog:
* g++.dg/cpp2a/consteval-virtual1.C: New test.
* g++.dg/cpp2a/consteval-virtual2.C: New test.
* g++.dg/cpp2a/consteval-virtual3.C: New test.
* g++.dg/cpp2a/consteval-virtual4.C: New test.
* g++.dg/cpp2a/consteval-virtual5.C: New test.
Co-authored-by: Jakub Jelinek <jakub@redhat.com>
Diffstat (limited to 'gcc/cp')
-rw-r--r-- | gcc/cp/constexpr.c | 48 | ||||
-rw-r--r-- | gcc/cp/cvt.c | 11 | ||||
-rw-r--r-- | gcc/cp/decl.c | 9 | ||||
-rw-r--r-- | gcc/cp/decl2.c | 39 | ||||
-rw-r--r-- | gcc/cp/search.c | 36 |
5 files changed, 110 insertions, 33 deletions
diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c index f766abd..1939166 100644 --- a/gcc/cp/constexpr.c +++ b/gcc/cp/constexpr.c @@ -2129,6 +2129,52 @@ replace_result_decl (tree *tp, tree decl, tree replacement) 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. */ @@ -2209,6 +2255,8 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, 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 diff --git a/gcc/cp/cvt.c b/gcc/cp/cvt.c index 3710028..c9e7b1f 100644 --- a/gcc/cp/cvt.c +++ b/gcc/cp/cvt.c @@ -1000,12 +1000,11 @@ cp_get_fndecl_from_callee (tree fn, bool fold /* = true */) 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; } diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index 45c871a..1eb5c2a 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -9560,15 +9560,6 @@ grokfndecl (tree ctype, } } - /* 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); diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c index 93e3034..ddc2023 100644 --- a/gcc/cp/decl2.c +++ b/gcc/cp/decl2.c @@ -65,8 +65,6 @@ typedef struct priority_info_s { 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); @@ -1879,7 +1877,7 @@ coerce_delete_type (tree decl, location_t loc) 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; @@ -1887,6 +1885,8 @@ mark_vtable_entries (tree decl) /* 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) { @@ -1901,6 +1901,15 @@ mark_vtable_entries (tree decl) 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, @@ -1917,6 +1926,20 @@ mark_vtable_entries (tree decl) } } +/* 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. */ @@ -2228,7 +2251,7 @@ decl_needed_p (tree decl) 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; @@ -2273,7 +2296,7 @@ maybe_emit_vtables (tree ctype) 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) { @@ -4887,6 +4910,9 @@ c_parse_final_cleanups (void) emit_support_tinfos (); + /* Track vtables we want to emit that refer to consteval functions. */ + auto_vec<tree> consteval_vtables; + do { tree t; @@ -4906,7 +4932,7 @@ c_parse_final_cleanups (void) 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); @@ -5177,6 +5203,7 @@ c_parse_final_cleanups (void) perform_deferred_noexcept_checks (); fini_constexpr (); + clear_consteval_vfns (consteval_vtables); /* The entire file is now complete. If requested, dump everything to a file. */ diff --git a/gcc/cp/search.c b/gcc/cp/search.c index a1a45a5..e36a8ae 100644 --- a/gcc/cp/search.c +++ b/gcc/cp/search.c @@ -1958,20 +1958,13 @@ check_final_overrider (tree overrider, tree basefn) /* 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; } @@ -1993,6 +1986,25 @@ check_final_overrider (tree overrider, tree basefn) 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. */ |