• R/O
  • HTTP
  • SSH
  • HTTPS

Commit

Tags
No Tags

Frequently used words (click to add to your profile)

javac++androidlinuxc#windowsobjective-ccocoa誰得qtpythonphprubygameguibathyscaphec計画中(planning stage)翻訳omegatframeworktwitterdomtestvb.netdirectxゲームエンジンbtronarduinopreviewer

GCC with patches for OS216


Commit MetaInfo

Revisione6321c4508b2a85c21246c1c06a8208e2a151e48 (tree)
Time2020-07-03 02:20:23
AuthorJason Merrill <jason@redh...>
CommiterJason Merrill

Log Message

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>

Change Summary

Incremental Difference

--- a/gcc/c-family/c-cppbuiltin.c
+++ b/gcc/c-family/c-cppbuiltin.c
@@ -995,7 +995,7 @@ c_cpp_builtins (cpp_reader *pfile)
995995 cpp_define (pfile, "__cpp_constexpr=201907L");
996996 cpp_define (pfile, "__cpp_constexpr_in_decltype=201711L");
997997 cpp_define (pfile, "__cpp_conditional_explicit=201806L");
998- /* cpp_define (pfile, "__cpp_consteval=201811L"); */
998+ cpp_define (pfile, "__cpp_consteval=201811L");
999999 cpp_define (pfile, "__cpp_constinit=201907L");
10001000 cpp_define (pfile, "__cpp_deduction_guides=201907L");
10011001 cpp_define (pfile, "__cpp_nontype_template_parameter_class=201806L");
--- a/gcc/cp/constexpr.c
+++ b/gcc/cp/constexpr.c
@@ -2129,6 +2129,52 @@ replace_result_decl (tree *tp, tree decl, tree replacement)
21292129 return data.changed;
21302130 }
21312131
2132+/* Evaluate the call T to virtual function thunk THUNK_FNDECL. */
2133+
2134+static tree
2135+cxx_eval_thunk_call (const constexpr_ctx *ctx, tree t, tree thunk_fndecl,
2136+ bool lval,
2137+ bool *non_constant_p, bool *overflow_p)
2138+{
2139+ tree function = THUNK_TARGET (thunk_fndecl);
2140+
2141+ /* virtual_offset is only set in the presence of virtual bases, which make
2142+ the class non-literal, so we don't need to handle it here. */
2143+ if (THUNK_VIRTUAL_OFFSET (thunk_fndecl))
2144+ {
2145+ gcc_assert (!DECL_DECLARED_CONSTEXPR_P (function));
2146+ if (!ctx->quiet)
2147+ {
2148+ error ("call to non-%<constexpr%> function %qD", function);
2149+ explain_invalid_constexpr_fn (function);
2150+ }
2151+ *non_constant_p = true;
2152+ return t;
2153+ }
2154+
2155+ tree new_call = copy_node (t);
2156+ CALL_EXPR_FN (new_call) = function;
2157+ TREE_TYPE (new_call) = TREE_TYPE (TREE_TYPE (function));
2158+
2159+ tree offset = size_int (THUNK_FIXED_OFFSET (thunk_fndecl));
2160+
2161+ if (DECL_THIS_THUNK_P (thunk_fndecl))
2162+ {
2163+ /* 'this'-adjusting thunk. */
2164+ tree this_arg = CALL_EXPR_ARG (t, 0);
2165+ this_arg = build2 (POINTER_PLUS_EXPR, TREE_TYPE (this_arg),
2166+ this_arg, offset);
2167+ CALL_EXPR_ARG (new_call, 0) = this_arg;
2168+ }
2169+ else
2170+ /* Return-adjusting thunk. */
2171+ new_call = build2 (POINTER_PLUS_EXPR, TREE_TYPE (new_call),
2172+ new_call, offset);
2173+
2174+ return cxx_eval_constant_expression (ctx, new_call, lval,
2175+ non_constant_p, overflow_p);
2176+}
2177+
21322178 /* Subroutine of cxx_eval_constant_expression.
21332179 Evaluate the call expression tree T in the context of OLD_CALL expression
21342180 evaluation. */
@@ -2209,6 +2255,8 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
22092255 if (fndecl_built_in_p (fun))
22102256 return cxx_eval_builtin_function_call (ctx, t, fun,
22112257 lval, non_constant_p, overflow_p);
2258+ if (DECL_THUNK_P (fun))
2259+ return cxx_eval_thunk_call (ctx, t, fun, lval, non_constant_p, overflow_p);
22122260 if (!DECL_DECLARED_CONSTEXPR_P (fun))
22132261 {
22142262 if (TREE_CODE (t) == CALL_EXPR
--- a/gcc/cp/cvt.c
+++ b/gcc/cp/cvt.c
@@ -1000,12 +1000,11 @@ cp_get_fndecl_from_callee (tree fn, bool fold /* = true */)
10001000 if (fold)
10011001 fn = maybe_constant_init (fn);
10021002 STRIP_NOPS (fn);
1003- if (TREE_CODE (fn) == ADDR_EXPR)
1004- {
1005- fn = TREE_OPERAND (fn, 0);
1006- if (TREE_CODE (fn) == FUNCTION_DECL)
1007- return fn;
1008- }
1003+ if (TREE_CODE (fn) == ADDR_EXPR
1004+ || TREE_CODE (fn) == FDESC_EXPR)
1005+ fn = TREE_OPERAND (fn, 0);
1006+ if (TREE_CODE (fn) == FUNCTION_DECL)
1007+ return fn;
10091008 return NULL_TREE;
10101009 }
10111010
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -9560,15 +9560,6 @@ grokfndecl (tree ctype,
95609560 }
95619561 }
95629562
9563- /* FIXME: For now. */
9564- if (virtualp && (inlinep & 8) != 0)
9565- {
9566- sorry_at (DECL_SOURCE_LOCATION (decl),
9567- "%<virtual%> %<consteval%> method %qD not supported yet",
9568- decl);
9569- inlinep &= ~8;
9570- }
9571-
95729563 /* If this decl has namespace scope, set that up. */
95739564 if (in_namespace)
95749565 set_decl_namespace (decl, in_namespace, friendp);
--- a/gcc/cp/decl2.c
+++ b/gcc/cp/decl2.c
@@ -65,8 +65,6 @@ typedef struct priority_info_s {
6565 int destructions_p;
6666 } *priority_info;
6767
68-static void mark_vtable_entries (tree);
69-static bool maybe_emit_vtables (tree);
7068 static tree start_objects (int, int);
7169 static void finish_objects (int, int, tree);
7270 static tree start_static_storage_duration_function (unsigned);
@@ -1879,7 +1877,7 @@ coerce_delete_type (tree decl, location_t loc)
18791877 and mark them as needed. */
18801878
18811879 static void
1882-mark_vtable_entries (tree decl)
1880+mark_vtable_entries (tree decl, vec<tree> &consteval_vtables)
18831881 {
18841882 tree fnaddr;
18851883 unsigned HOST_WIDE_INT idx;
@@ -1887,6 +1885,8 @@ mark_vtable_entries (tree decl)
18871885 /* It's OK for the vtable to refer to deprecated virtual functions. */
18881886 warning_sentinel w(warn_deprecated_decl);
18891887
1888+ bool consteval_seen = false;
1889+
18901890 FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (DECL_INITIAL (decl)),
18911891 idx, fnaddr)
18921892 {
@@ -1901,6 +1901,15 @@ mark_vtable_entries (tree decl)
19011901 continue;
19021902
19031903 fn = TREE_OPERAND (fnaddr, 0);
1904+ if (TREE_CODE (fn) == FUNCTION_DECL && DECL_IMMEDIATE_FUNCTION_P (fn))
1905+ {
1906+ if (!consteval_seen)
1907+ {
1908+ consteval_seen = true;
1909+ consteval_vtables.safe_push (decl);
1910+ }
1911+ continue;
1912+ }
19041913 TREE_ADDRESSABLE (fn) = 1;
19051914 /* When we don't have vcall offsets, we output thunks whenever
19061915 we output the vtables that contain them. With vcall offsets,
@@ -1917,6 +1926,20 @@ mark_vtable_entries (tree decl)
19171926 }
19181927 }
19191928
1929+/* Replace any consteval functions in vtables with null pointers. */
1930+
1931+static void
1932+clear_consteval_vfns (vec<tree> &consteval_vtables)
1933+{
1934+ for (tree vtable : consteval_vtables)
1935+ for (constructor_elt &elt : *CONSTRUCTOR_ELTS (DECL_INITIAL (vtable)))
1936+ {
1937+ tree fn = cp_get_fndecl_from_callee (elt.value, /*fold*/false);
1938+ if (fn && DECL_IMMEDIATE_FUNCTION_P (fn))
1939+ elt.value = build_zero_cst (vtable_entry_type);
1940+ }
1941+}
1942+
19201943 /* Adjust the TLS model on variable DECL if need be, typically after
19211944 the linkage of DECL has been modified. */
19221945
@@ -2228,7 +2251,7 @@ decl_needed_p (tree decl)
22282251 Returns true if any vtables were emitted. */
22292252
22302253 static bool
2231-maybe_emit_vtables (tree ctype)
2254+maybe_emit_vtables (tree ctype, vec<tree> &consteval_vtables)
22322255 {
22332256 tree vtbl;
22342257 tree primary_vtbl;
@@ -2273,7 +2296,7 @@ maybe_emit_vtables (tree ctype)
22732296 for (vtbl = CLASSTYPE_VTABLES (ctype); vtbl; vtbl = DECL_CHAIN (vtbl))
22742297 {
22752298 /* Mark entities references from the virtual table as used. */
2276- mark_vtable_entries (vtbl);
2299+ mark_vtable_entries (vtbl, consteval_vtables);
22772300
22782301 if (TREE_TYPE (DECL_INITIAL (vtbl)) == 0)
22792302 {
@@ -4887,6 +4910,9 @@ c_parse_final_cleanups (void)
48874910
48884911 emit_support_tinfos ();
48894912
4913+ /* Track vtables we want to emit that refer to consteval functions. */
4914+ auto_vec<tree> consteval_vtables;
4915+
48904916 do
48914917 {
48924918 tree t;
@@ -4906,7 +4932,7 @@ c_parse_final_cleanups (void)
49064932 have to look at it again. */
49074933 for (i = keyed_classes->length ();
49084934 keyed_classes->iterate (--i, &t);)
4909- if (maybe_emit_vtables (t))
4935+ if (maybe_emit_vtables (t, consteval_vtables))
49104936 {
49114937 reconsider = true;
49124938 keyed_classes->unordered_remove (i);
@@ -5177,6 +5203,7 @@ c_parse_final_cleanups (void)
51775203 perform_deferred_noexcept_checks ();
51785204
51795205 fini_constexpr ();
5206+ clear_consteval_vfns (consteval_vtables);
51805207
51815208 /* The entire file is now complete. If requested, dump everything
51825209 to a file. */
--- a/gcc/cp/search.c
+++ b/gcc/cp/search.c
@@ -1958,20 +1958,13 @@ check_final_overrider (tree overrider, tree basefn)
19581958 /* OK */;
19591959 else
19601960 {
1961+ auto_diagnostic_group d;
19611962 if (fail == 1)
1962- {
1963- auto_diagnostic_group d;
1964- error ("invalid covariant return type for %q+#D", overrider);
1965- inform (DECL_SOURCE_LOCATION (basefn),
1966- "overridden function is %q#D", basefn);
1967- }
1963+ error ("invalid covariant return type for %q+#D", overrider);
19681964 else
1969- {
1970- auto_diagnostic_group d;
1971- error ("conflicting return type specified for %q+#D", overrider);
1972- inform (DECL_SOURCE_LOCATION (basefn),
1973- "overridden function is %q#D", basefn);
1974- }
1965+ error ("conflicting return type specified for %q+#D", overrider);
1966+ inform (DECL_SOURCE_LOCATION (basefn),
1967+ "overridden function is %q#D", basefn);
19751968 DECL_INVALID_OVERRIDER_P (overrider) = 1;
19761969 return 0;
19771970 }
@@ -1993,6 +1986,25 @@ check_final_overrider (tree overrider, tree basefn)
19931986 return 0;
19941987 }
19951988
1989+ /* A consteval virtual function shall not override a virtual function that is
1990+ not consteval. A consteval virtual function shall not be overridden by a
1991+ virtual function that is not consteval. */
1992+ if (DECL_IMMEDIATE_FUNCTION_P (overrider)
1993+ != DECL_IMMEDIATE_FUNCTION_P (basefn))
1994+ {
1995+ auto_diagnostic_group d;
1996+ if (DECL_IMMEDIATE_FUNCTION_P (overrider))
1997+ error ("%<consteval%> function %q+D overriding non-%<consteval%> "
1998+ "function", overrider);
1999+ else
2000+ error ("non-%<consteval%> function %q+D overriding %<consteval%> "
2001+ "function", overrider);
2002+ inform (DECL_SOURCE_LOCATION (basefn),
2003+ "overridden function is %qD", basefn);
2004+ DECL_INVALID_OVERRIDER_P (overrider) = 1;
2005+ return 0;
2006+ }
2007+
19962008 /* A function declared transaction_safe_dynamic that overrides a function
19972009 declared transaction_safe (but not transaction_safe_dynamic) is
19982010 ill-formed. */
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/consteval-virtual1.C
@@ -0,0 +1,12 @@
1+// { dg-do compile { target c++20 } }
2+
3+struct S {
4+ virtual int foo () { return 42; } // { dg-message "overridden function is 'virtual int S::foo\\\(\\\)'" }
5+ consteval virtual int bar () { return 43; } // { dg-message "overridden function is 'virtual consteval int S::bar\\\(\\\)'" }
6+};
7+struct T : public S {
8+ int bar () { return 44; } // { dg-error "non-'consteval' function 'virtual int T::bar\\\(\\\)' overriding 'consteval' function" }
9+};
10+struct U : public S {
11+ consteval virtual int foo () { return 45; } // { dg-error "'consteval' function 'virtual consteval int U::foo\\\(\\\)' overriding non-'consteval' function" }
12+};
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/consteval-virtual2.C
@@ -0,0 +1,22 @@
1+// { dg-do compile { target c++20 } }
2+
3+struct A
4+{
5+ virtual consteval int f() const { return 1; };
6+};
7+
8+struct B: A
9+{
10+ virtual consteval int f() const { return 2; };
11+ virtual void g() { }
12+};
13+
14+consteval int f()
15+{
16+ const A& ar = B();
17+ return ar.f();
18+}
19+
20+static_assert (f() == 2);
21+
22+B b;
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/consteval-virtual3.C
@@ -0,0 +1,53 @@
1+// { dg-do compile { target c++20 } }
2+
3+struct S {
4+ constexpr S () : s (0) {}
5+ virtual int foo () const { return 42; }
6+ consteval virtual int bar () const { return 43; }
7+ consteval virtual int baz () const { return 44; }
8+ consteval virtual int qux () const { return 47; }
9+ int s;
10+};
11+struct T : public S {
12+ constexpr T () : t (0) {}
13+ consteval int bar () const { return 45; }
14+ consteval virtual int baz () const { return 46; }
15+ consteval virtual int grault () const { return 48; }
16+ int t;
17+};
18+
19+consteval int
20+foo ()
21+{
22+ S s;
23+ T t;
24+ S *u = (S *) &t;
25+ T *v = &t;
26+ if (s.bar () != 43) throw 1;
27+ if (s.baz () != 44) throw 2;
28+ if (t.bar () != 45) throw 3;
29+ if (t.baz () != 46) throw 4;
30+ if (u->bar () != 45) throw 5;
31+ if (u->baz () != 46) throw 6;
32+ if (s.qux () != 47) throw 7;
33+ if (t.qux () != 47) throw 8;
34+ if (u->qux () != 47) throw 9;
35+ if (v->qux () != 47) throw 10;
36+ if (v->grault () != 48) throw 11;
37+ return 0;
38+}
39+
40+constexpr S s;
41+constexpr T t;
42+
43+constexpr const S *
44+bar (bool x)
45+{
46+ return x ? &s : (const S *) &t;
47+}
48+
49+int a = foo ();
50+int b = bar (false)->bar ();
51+int c = bar (true)->baz ();
52+static_assert (bar (false)->bar () == 45);
53+static_assert (bar (true)->baz () == 44);
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/consteval-virtual4.C
@@ -0,0 +1,48 @@
1+// { dg-do compile { target c++20 } }
2+
3+struct S {
4+ constexpr S () : s (0) {}
5+ virtual int foo () const { return 42; }
6+ consteval virtual int bar () const { return 43; }
7+ consteval virtual int baz () const { return 44; }
8+ int s;
9+};
10+struct T : public S {
11+ constexpr T () : t (0) {}
12+ consteval int bar () const { return 45; }
13+ consteval virtual int baz () const { return 46; }
14+ int t;
15+};
16+
17+consteval int
18+foo ()
19+{
20+ S s;
21+ T t;
22+ S *u = (S *) &t;
23+ T *v = &t;
24+ auto pmf1 = &S::bar;
25+ auto pmf2 = &S::baz;
26+ if ((s.*pmf1) () != 43) throw 1;
27+ if ((s.*pmf2) () != 44) throw 2;
28+ if ((t.*pmf1) () != 45) throw 3;
29+ if ((t.*pmf2) () != 46) throw 4;
30+ if ((u->*pmf1) () != 45) throw 5;
31+ if ((u->*pmf2) () != 46) throw 6;
32+ return 0;
33+}
34+
35+constexpr S s;
36+constexpr T t;
37+
38+constexpr const S *
39+bar (bool x)
40+{
41+ return x ? &s : (const S *) &t;
42+}
43+
44+int a = foo ();
45+int b = bar (false)->bar ();
46+int c = bar (true)->baz ();
47+static_assert (bar (false)->bar () == 45);
48+static_assert (bar (true)->baz () == 44);
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/consteval-virtual5.C
@@ -0,0 +1,61 @@
1+// { dg-do compile { target c++20 } }
2+
3+struct B1;
4+struct B2;
5+struct D;
6+
7+struct B1
8+{
9+ virtual consteval const B1 *foo1 () const {return this;}
10+ virtual consteval const B2 *foo2 (const D *) const;
11+};
12+struct B2
13+{
14+ virtual consteval const B2 *baz1 () const {return this;}
15+ virtual consteval const B1 *baz2 (const D *) const;
16+};
17+
18+struct D : public B1, B2
19+{
20+ virtual consteval const D *foo1 () const {return this;}
21+ virtual consteval const D *foo2 (const D *d) const {return d;}
22+ virtual consteval const D *baz1 () const {return this;}
23+ virtual consteval const D *baz2 (const D *d) const {return d;}
24+};
25+
26+consteval const B2 *B1::foo2 (const D *d) const {return d;}
27+consteval const B1 *B2::baz2 (const D *d) const {return d;}
28+
29+consteval int
30+test (const B1 *b1, const B2 *b2, const D *d)
31+{
32+ if (b1->foo1 () != b1)
33+ return 1;
34+ if (b2->baz1 () != b2)
35+ return 2;
36+ if (b1->foo2 (d) != b2)
37+ return 3;
38+ if (b2->baz2 (d) != b1)
39+ return 4;
40+ return 0;
41+}
42+
43+consteval int
44+test (const D *d)
45+{
46+ if (d->foo2 (d) != d)
47+ return 11;
48+ if (d->baz2 (d) != d)
49+ return 12;
50+ if (d->foo1 () != d)
51+ return 13;
52+ if (d->baz1 () != d)
53+ return 14;
54+ return 0;
55+}
56+
57+constexpr D d;
58+constexpr auto e = test (&d, &d, &d);
59+constexpr auto f = test (&d);
60+static_assert (e == 0);
61+static_assert (f == 0);