diff options
Diffstat (limited to 'gcc')
-rw-r--r-- | gcc/c-family/c-cppbuiltin.c | 2 | ||||
-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 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/cpp2a/consteval-virtual1.C | 12 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/cpp2a/consteval-virtual2.C | 22 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/cpp2a/consteval-virtual3.C | 53 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/cpp2a/consteval-virtual4.C | 48 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/cpp2a/consteval-virtual5.C | 61 |
11 files changed, 307 insertions, 34 deletions
diff --git a/gcc/c-family/c-cppbuiltin.c b/gcc/c-family/c-cppbuiltin.c index a7d65d6..83f52fd 100644 --- a/gcc/c-family/c-cppbuiltin.c +++ b/gcc/c-family/c-cppbuiltin.c @@ -995,7 +995,7 @@ c_cpp_builtins (cpp_reader *pfile) 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"); 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. */ diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-virtual1.C b/gcc/testsuite/g++.dg/cpp2a/consteval-virtual1.C new file mode 100644 index 0000000..5cdb75a --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/consteval-virtual1.C @@ -0,0 +1,12 @@ +// { 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" } +}; diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-virtual2.C b/gcc/testsuite/g++.dg/cpp2a/consteval-virtual2.C new file mode 100644 index 0000000..d5d8f79 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/consteval-virtual2.C @@ -0,0 +1,22 @@ +// { 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; diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-virtual3.C b/gcc/testsuite/g++.dg/cpp2a/consteval-virtual3.C new file mode 100644 index 0000000..376e3ba --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/consteval-virtual3.C @@ -0,0 +1,53 @@ +// { 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); diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-virtual4.C b/gcc/testsuite/g++.dg/cpp2a/consteval-virtual4.C new file mode 100644 index 0000000..83405fe --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/consteval-virtual4.C @@ -0,0 +1,48 @@ +// { 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); diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-virtual5.C b/gcc/testsuite/g++.dg/cpp2a/consteval-virtual5.C new file mode 100644 index 0000000..85ad118 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/consteval-virtual5.C @@ -0,0 +1,61 @@ +// { 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); |