Message ID | 20180914171950.GE5587@redhat.com |
---|---|
State | New |
Headers | show |
Series | C++ PATCH to implement P1064R0, Virtual Function Calls in Constant Expressions | expand |
On Fri, Sep 14, 2018 at 01:19:50PM -0400, Marek Polacek wrote: > + /* We expect something in the form of &x.D.2103.D.2094; get x. */ > + if (TREE_CODE (obj) != ADDR_EXPR) > + return t; Shouldn't it then be a gcc_assert instead, or code like: if (TREE_CODE (obj) != ADDR_EXPR) { if (!ctx->quiet) error (...); *non_constant_p = true; } to make it clear that we haven't handled it and don't consider it a constant expression? Jakub
On Fri, Sep 14, 2018 at 07:36:47PM +0200, Jakub Jelinek wrote: > On Fri, Sep 14, 2018 at 01:19:50PM -0400, Marek Polacek wrote: > > + /* We expect something in the form of &x.D.2103.D.2094; get x. */ > > + if (TREE_CODE (obj) != ADDR_EXPR) > > + return t; > > Shouldn't it then be a gcc_assert instead, or code like: > if (TREE_CODE (obj) != ADDR_EXPR) > { > if (!ctx->quiet) > error (...); > *non_constant_p = true; > } > to make it clear that we haven't handled it and don't consider it a constant > expression? Not an assert, but setting *non_constant_p is probably sensible. Thus: v2: Set *non_constant_p if OBJ_TYPE_REF is not in expected format. Bootstrapped/regtested on x86_64-linux. 2018-09-14 Marek Polacek <polacek@redhat.com> P1064R0 - Allowing Virtual Function Calls in Constant Expressions * call.c (build_over_call): Add FIXME. * constexpr.c (cxx_eval_array_reference): Handle referencing the vtable. (cxx_eval_constant_expression): Don't ignore _vptr's initializer. (potential_constant_expression_1): Handle OBJ_TYPE_REF. * decl.c (grokdeclarator): Change error to pedwarn. Only warn when pedantic and not C++2a. * g++.dg/cpp0x/constexpr-virtual5.C: Adjust dg-error. * g++.dg/cpp2a/constexpr-virtual1.C: New test. * g++.dg/cpp2a/constexpr-virtual2.C: New test. * g++.dg/cpp2a/constexpr-virtual3.C: New test. * g++.dg/cpp2a/constexpr-virtual4.C: New test. * g++.dg/cpp2a/constexpr-virtual5.C: New test. * g++.dg/cpp2a/constexpr-virtual6.C: New test. * g++.dg/cpp2a/constexpr-virtual7.C: New test. * g++.dg/cpp2a/constexpr-virtual8.C: New test. * g++.dg/cpp2a/constexpr-virtual9.C: New test. * g++.dg/diagnostic/virtual-constexpr.C: Skip for C++2a. Use -pedantic-errors. Adjust dg-error. diff --git gcc/cp/call.c gcc/cp/call.c index 69503ca7920..6c70874af40 100644 --- gcc/cp/call.c +++ gcc/cp/call.c @@ -8401,7 +8401,8 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain) if (DECL_VINDEX (fn) && (flags & LOOKUP_NONVIRTUAL) == 0 /* Don't mess with virtual lookup in instantiate_non_dependent_expr; - virtual functions can't be constexpr. */ + virtual functions can't be constexpr. FIXME Actually, no longer + true in C++2a. */ && !in_template_function ()) { tree t; diff --git gcc/cp/constexpr.c gcc/cp/constexpr.c index 88c73787961..eb6b8fa1842 100644 --- gcc/cp/constexpr.c +++ gcc/cp/constexpr.c @@ -2414,16 +2414,27 @@ cxx_eval_array_reference (const constexpr_ctx *ctx, tree t, bool *non_constant_p, bool *overflow_p) { tree oldary = TREE_OPERAND (t, 0); + tree oldidx = TREE_OPERAND (t, 1); + + /* The virtual table isn't constexpr, but has static storage duration and its + initializer is a compile-time constant, so we handle referencing an element + in the table specially. */ + if (TREE_TYPE (t) == vtable_entry_type) + { + VERIFY_CONSTANT (oldidx); + tree ctor = DECL_INITIAL (oldary); + return CONSTRUCTOR_ELT (ctor, tree_to_uhwi (oldidx))->value; + } + tree ary = cxx_eval_constant_expression (ctx, oldary, lval, non_constant_p, overflow_p); - tree index, oldidx; + tree index; HOST_WIDE_INT i = 0; tree elem_type = NULL_TREE; unsigned len = 0, elem_nchars = 1; if (*non_constant_p) return t; - oldidx = TREE_OPERAND (t, 1); index = cxx_eval_constant_expression (ctx, oldidx, false, non_constant_p, overflow_p); @@ -4209,7 +4220,14 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, CONST_DECL for aggregate constants. */ if (lval) return t; + /* is_really_empty_class doesn't take into account _vptr, so initializing + otherwise empty class with { } would overwrite the initializer that + initialize_vtable created for us. */ if (COMPLETE_TYPE_P (TREE_TYPE (t)) + && !(DECL_INITIAL (t) + && TREE_CODE (DECL_INITIAL (t)) == CONSTRUCTOR + /* But if DECL_INITIAL was { }, do mark it as constant. */ + && CONSTRUCTOR_NELTS (DECL_INITIAL (t)) > 0) && is_really_empty_class (TREE_TYPE (t))) { /* If the class is empty, we aren't actually loading anything. */ @@ -4778,7 +4796,6 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, case MODOP_EXPR: /* GCC internal stuff. */ case VA_ARG_EXPR: - case OBJ_TYPE_REF: case NON_DEPENDENT_EXPR: case BASELINK: case OFFSET_REF: @@ -4788,6 +4805,34 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, *non_constant_p = true; break; + case OBJ_TYPE_REF: + { + /* Virtual function call. Let the constexpr machinery figure out + the dynamic type. */ + int token = tree_to_shwi (OBJ_TYPE_REF_TOKEN (t)); + tree obj = OBJ_TYPE_REF_OBJECT (t); + obj = cxx_eval_constant_expression (ctx, obj, lval, non_constant_p, + overflow_p); + /* We expect something in the form of &x.D.2103.D.2094; get x. */ + if (TREE_CODE (obj) != ADDR_EXPR) + { + if (!ctx->quiet) + error_at (cp_expr_loc_or_loc (t, input_location), + "expression %qE is not a constant expression", t); + *non_constant_p = true; + return t; + } + obj = TREE_OPERAND (obj, 0); + while (handled_component_p (obj)) + obj = TREE_OPERAND (obj, 0); + tree objtype = TREE_TYPE (obj); + /* Find the function decl in the virtual functions list. TOKEN is + the DECL_VINDEX that says which function we're looking for. */ + tree virtuals = BINFO_VIRTUALS (TYPE_BINFO (objtype)); + r = TREE_VALUE (chain_index (token, virtuals)); + break; + } + case PLACEHOLDER_EXPR: /* Use of the value or address of the current object. */ if (tree ctor = lookup_placeholder (ctx, lval, TREE_TYPE (t))) @@ -5871,7 +5916,6 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now, case OACC_UPDATE: /* GCC internal stuff. */ case VA_ARG_EXPR: - case OBJ_TYPE_REF: case TRANSACTION_EXPR: case ASM_EXPR: case AT_ENCODE_EXPR: @@ -5880,6 +5924,14 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now, error_at (loc, "expression %qE is not a constant expression", t); return false; + case OBJ_TYPE_REF: + if (cxx_dialect >= cxx2a) + /* In C++2a virtual calls can be constexpr, don't give up yet. */ + return true; + else if (flags & tf_error) + error_at (loc, "virtual functions cannot be constexpr before C++2a"); + return false; + case TYPEID_EXPR: /* -- a typeid expression whose operand is of polymorphic class type; */ diff --git gcc/cp/decl.c gcc/cp/decl.c index 50b60e89df5..da3749254e9 100644 --- gcc/cp/decl.c +++ gcc/cp/decl.c @@ -10854,12 +10854,13 @@ grokdeclarator (const cp_declarator *declarator, storage_class = sc_none; staticp = 0; } - if (constexpr_p) + if (constexpr_p && cxx_dialect < cxx2a) { gcc_rich_location richloc (declspecs->locations[ds_virtual]); richloc.add_range (declspecs->locations[ds_constexpr]); - error_at (&richloc, "member %qD cannot be declared both %<virtual%> " - "and %<constexpr%>", dname); + pedwarn (&richloc, OPT_Wpedantic, "member %qD can be declared both " + "%<virtual%> and %<constexpr%> only in -std=c++2a or " + "-std=gnu++2a", dname); } } friendp = decl_spec_seq_has_spec_p (declspecs, ds_friend); diff --git gcc/testsuite/g++.dg/cpp0x/constexpr-virtual5.C gcc/testsuite/g++.dg/cpp0x/constexpr-virtual5.C index 2465f9d9b4f..5f9ab4d9c28 100644 --- gcc/testsuite/g++.dg/cpp0x/constexpr-virtual5.C +++ gcc/testsuite/g++.dg/cpp0x/constexpr-virtual5.C @@ -2,5 +2,5 @@ // { dg-do compile { target c++11 } } struct S { - constexpr virtual int f() { return 1; } // { dg-error "13:member .f. cannot be declared both .virtual. and .constexpr." } + constexpr virtual int f() { return 1; } // { dg-error "13:member .f. can be declared both .virtual. and .constexpr." "" { target c++17_down } } }; diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-virtual1.C gcc/testsuite/g++.dg/cpp2a/constexpr-virtual1.C index e69de29bb2d..fcf8cac6417 100644 --- gcc/testsuite/g++.dg/cpp2a/constexpr-virtual1.C +++ gcc/testsuite/g++.dg/cpp2a/constexpr-virtual1.C @@ -0,0 +1,8 @@ +// P1064R0 +// { dg-do compile { target c++11 } } +// { dg-options "-pedantic-errors" } + +struct X +{ + constexpr virtual int f() { return 0; } // { dg-error "member .f. can be declared both .virtual. and .constexpr. only" "" { target c++17_down } } +}; diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C index e69de29bb2d..9d82c5c59ac 100644 --- gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C +++ gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C @@ -0,0 +1,49 @@ +// P1064R0 +// { dg-do compile } +// { dg-options "-std=c++2a" } + +struct X1 +{ + virtual int f() const = 0; +}; + +struct X2: public X1 +{ + constexpr virtual int f() const { return 2; } +}; + +struct X3: public X2 +{ + virtual int f() const { return 3; } +}; + +struct X4: public X3 +{ + constexpr virtual int f() const { return 4; } +}; + +constexpr int (X1::*pf)() const = &X1::f; + +constexpr X2 x2; +static_assert(x2.f() == 2); +static_assert((x2.*pf)() == 2); + +constexpr X1 const& r2 = x2; +static_assert(r2.f() == 2); +static_assert((r2.*pf)() == 2); + +constexpr X1 const* p2 = &x2; +static_assert(p2->f() == 2); +static_assert((p2->*pf)() == 2); + +constexpr X4 x4; +static_assert(x4.f() == 4); +static_assert((x4.*pf)() == 4); + +constexpr X1 const& r4 = x4; +static_assert(r4.f() == 4); +static_assert((r4.*pf)() == 4); + +constexpr X1 const* p4 = &x4; +static_assert(p4->f() == 4); +static_assert((p4->*pf)() == 4); diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-virtual3.C gcc/testsuite/g++.dg/cpp2a/constexpr-virtual3.C index e69de29bb2d..d71422fc4d0 100644 --- gcc/testsuite/g++.dg/cpp2a/constexpr-virtual3.C +++ gcc/testsuite/g++.dg/cpp2a/constexpr-virtual3.C @@ -0,0 +1,52 @@ +// P1064R0 +// { dg-do compile } +// { dg-options "-std=c++2a" } + +struct X1 +{ + virtual int f() const = 0; +}; + +struct X2: public X1 +{ + int i2 = 42; + constexpr virtual int f() const { return 2; } +}; + +struct X3: public X2 +{ + int i3 = 42; + virtual int f() const { return 3; } +}; + +struct X4: public X3 +{ + int i4 = 42; + constexpr virtual int f() const { return 4; } +}; + +constexpr int (X1::*pf)() const = &X1::f; + +constexpr X2 x2; +static_assert(x2.f() == 2); +static_assert((x2.*pf)() == 2); + +constexpr X1 const& r2 = x2; +static_assert(r2.f() == 2); +static_assert((r2.*pf)() == 2); + +constexpr X1 const* p2 = &x2; +static_assert(p2->f() == 2); +static_assert((p2->*pf)() == 2); + +constexpr X4 x4; +static_assert(x4.f() == 4); +static_assert((x4.*pf)() == 4); + +constexpr X1 const& r4 = x4; +static_assert(r4.f() == 4); +static_assert((r4.*pf)() == 4); + +constexpr X1 const* p4 = &x4; +static_assert(p4->f() == 4); +static_assert((p4->*pf)() == 4); diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-virtual4.C gcc/testsuite/g++.dg/cpp2a/constexpr-virtual4.C index e69de29bb2d..2038bebc6d1 100644 --- gcc/testsuite/g++.dg/cpp2a/constexpr-virtual4.C +++ gcc/testsuite/g++.dg/cpp2a/constexpr-virtual4.C @@ -0,0 +1,57 @@ +// P1064R0 +// { dg-do compile } +// { dg-options "-std=c++2a" } + +struct X1 +{ + virtual int f1() const = 0; + virtual int f2() const = 0; + virtual int f3() const = 0; +}; + +struct X2: public X1 +{ + constexpr virtual int f1() const { return 21; } + constexpr virtual int f2() const { return 22; } + constexpr virtual int f3() const { return 23; } +}; + +struct X3: public X2 +{ + virtual int f1() const { return 31; } + virtual int f2() const { return 32; } + virtual int f3() const { return 33; } +}; + +struct X4: public X3 +{ + constexpr virtual int f1() const { return 41; } + constexpr virtual int f2() const { return 42; } + constexpr virtual int f3() const { return 43; } +}; + +constexpr int (X1::*pf)() const = &X1::f2; + +constexpr X2 x2; +static_assert(x2.f2() == 22); +static_assert((x2.*pf)() == 22); + +constexpr X1 const& r2 = x2; +static_assert(r2.f2() == 22); +static_assert((r2.*pf)() == 22); + +constexpr X1 const* p2 = &x2; +static_assert(p2->f2() == 22); +static_assert((p2->*pf)() == 22); + +constexpr X4 x4; +static_assert(x4.f2() == 42); +static_assert((x4.*pf)() == 42); + +constexpr X1 const& r4 = x4; +static_assert(r4.f2() == 42); +static_assert((r4.*pf)() == 42); + +constexpr X1 const* p4 = &x4; +static_assert(p4->f2() == 42); +static_assert((p4->*pf)() == 42); diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-virtual5.C gcc/testsuite/g++.dg/cpp2a/constexpr-virtual5.C index e69de29bb2d..6d27990a8b6 100644 --- gcc/testsuite/g++.dg/cpp2a/constexpr-virtual5.C +++ gcc/testsuite/g++.dg/cpp2a/constexpr-virtual5.C @@ -0,0 +1,60 @@ +// P1064R0 +// { dg-do compile } +// { dg-options "-std=c++2a" } + +struct X1 +{ + virtual int f1() const = 0; + virtual int f2() const = 0; + virtual int f3() const = 0; +}; + +struct X2: public X1 +{ + int i2 = 42; + constexpr virtual int f1() const { return 21; } + constexpr virtual int f2() const { return 22; } + constexpr virtual int f3() const { return 23; } +}; + +struct X3: public X2 +{ + int i3 = 42; + virtual int f1() const { return 31; } + virtual int f2() const { return 32; } + virtual int f3() const { return 33; } +}; + +struct X4: public X3 +{ + int i4 = 42; + constexpr virtual int f1() const { return 41; } + constexpr virtual int f2() const { return 42; } + constexpr virtual int f3() const { return 43; } +}; + +constexpr int (X1::*pf)() const = &X1::f2; + +constexpr X2 x2; +static_assert(x2.f2() == 22); +static_assert((x2.*pf)() == 22); + +constexpr X1 const& r2 = x2; +static_assert(r2.f2() == 22); +static_assert((r2.*pf)() == 22); + +constexpr X1 const* p2 = &x2; +static_assert(p2->f2() == 22); +static_assert((p2->*pf)() == 22); + +constexpr X4 x4; +static_assert(x4.f2() == 42); +static_assert((x4.*pf)() == 42); + +constexpr X1 const& r4 = x4; +static_assert(r4.f2() == 42); +static_assert((r4.*pf)() == 42); + +constexpr X1 const* p4 = &x4; +static_assert(p4->f2() == 42); +static_assert((p4->*pf)() == 42); diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-virtual6.C gcc/testsuite/g++.dg/cpp2a/constexpr-virtual6.C index e69de29bb2d..ece5e703c32 100644 --- gcc/testsuite/g++.dg/cpp2a/constexpr-virtual6.C +++ gcc/testsuite/g++.dg/cpp2a/constexpr-virtual6.C @@ -0,0 +1,25 @@ +// P1064R0 +// { dg-do compile } +// { dg-options "-std=c++2a" } + +struct X1 +{ + constexpr virtual X1 const *f() const { return this; } +}; + +struct Y +{ + int m = 0; +}; + +struct X2: public Y, public X1 +{ + constexpr virtual X2 const *f() const { return this; } +}; + +constexpr X1 x1; +static_assert(x1.f() == &x1); + +constexpr X2 x2; +constexpr X1 const& r2 = x2; +static_assert(r2.f() == &r2); diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-virtual7.C gcc/testsuite/g++.dg/cpp2a/constexpr-virtual7.C index e69de29bb2d..b0f499608ef 100644 --- gcc/testsuite/g++.dg/cpp2a/constexpr-virtual7.C +++ gcc/testsuite/g++.dg/cpp2a/constexpr-virtual7.C @@ -0,0 +1,87 @@ +// P1064R0 +// { dg-do compile } +// { dg-options "-std=c++2a" } + +struct X1 +{ + virtual int f() const = 0; +}; + +struct X2: public X1 +{ + constexpr virtual int f() const { return 2; } +}; + +struct X3: public X2 +{ + virtual int f() const { return 3; } +}; + +struct X4: public X3 +{ + constexpr virtual int f() const { return 4; } +}; + +constexpr int (X1::*pf)() const = &X1::f; + +constexpr X2 x2; + +struct S +{ + int i, j; + constexpr S() : i(x2.f()), j((x2.*pf)()) { } +}; + +static_assert(S().i == 2); +static_assert(S().j == 2); + +constexpr X1 const& r2 = x2; + +struct S2 +{ + int i, j; + constexpr S2() : i(r2.f()), j((r2.*pf)()) { } +}; + +static_assert(S2().i == 2); +static_assert(S2().j == 2); + +constexpr X1 const* p2 = &x2; +struct S3 +{ + int i, j; + constexpr S3() : i(p2->f()), j((p2->*pf)()) { } +}; + +static_assert(S3().i == 2); +static_assert(S3().j == 2); + +constexpr X4 x4; +struct S4 +{ + int i, j; + constexpr S4() : i(x4.f()), j((x4.*pf)()) { } +}; + +static_assert(S4().i == 4); +static_assert(S4().j == 4); + +constexpr X1 const& r4 = x4; +struct S5 +{ + int i, j; + constexpr S5() : i(r4.f()), j((r4.*pf)()) { } +}; + +static_assert(S5().i == 4); +static_assert(S5().j == 4); + +constexpr X1 const* p4 = &x4; +struct S6 +{ + int i, j; + constexpr S6() : i(p4->f()), j((p4->*pf)()) { } +}; + +static_assert(S6().i == 4); +static_assert(S6().j == 4); diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-virtual8.C gcc/testsuite/g++.dg/cpp2a/constexpr-virtual8.C index e69de29bb2d..4a7cc972a91 100644 --- gcc/testsuite/g++.dg/cpp2a/constexpr-virtual8.C +++ gcc/testsuite/g++.dg/cpp2a/constexpr-virtual8.C @@ -0,0 +1,50 @@ +// P1064R0 +// { dg-do compile } +// { dg-options "-std=c++2a" } + +template<typename T> +struct X1 +{ + virtual T f() const = 0; +}; + +struct X2: public X1<int> +{ + constexpr virtual int f() const { return 2; } +}; + +struct X3: public X2 +{ + virtual int f() const { return 3; } +}; + +struct X4: public X3 +{ + constexpr virtual int f() const { return 4; } +}; + +constexpr int (X1<int>::*pf)() const = &X1<int>::f; + +constexpr X2 x2; +static_assert(x2.f() == 2); +static_assert((x2.*pf)() == 2); + +constexpr X1<int> const& r2 = x2; +static_assert(r2.f() == 2); +static_assert((r2.*pf)() == 2); + +constexpr X1<int> const* p2 = &x2; +static_assert(p2->f() == 2); +static_assert((p2->*pf)() == 2); + +constexpr X4 x4; +static_assert(x4.f() == 4); +static_assert((x4.*pf)() == 4); + +constexpr X1<int> const& r4 = x4; +static_assert(r4.f() == 4); +static_assert((r4.*pf)() == 4); + +constexpr X1<int> const* p4 = &x4; +static_assert(p4->f() == 4); +static_assert((p4->*pf)() == 4); diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-virtual9.C gcc/testsuite/g++.dg/cpp2a/constexpr-virtual9.C index e69de29bb2d..3a12adc2659 100644 --- gcc/testsuite/g++.dg/cpp2a/constexpr-virtual9.C +++ gcc/testsuite/g++.dg/cpp2a/constexpr-virtual9.C @@ -0,0 +1,83 @@ +// P1064R0 +// { dg-do compile } +// { dg-options "-std=c++2a" } + +struct X1 +{ + virtual int f() const = 0; + virtual int f(int) const = 0; + virtual int f(int, int) const = 0; +}; + +struct X2: public X1 +{ + constexpr virtual int f() const { return 2; } + constexpr virtual int f(int) const { return 12; } + constexpr virtual int f(int, int) const { return 22; } +}; + +struct X3: public X2 +{ + virtual int f() const { return 3; } + virtual int f(int) const { return 13; } + virtual int f(int, int) const { return 23; } +}; + +struct X4: public X3 +{ + constexpr virtual int f() const { return 4; } + constexpr virtual int f(int) const { return 14; } + constexpr virtual int f(int, int) const { return 24; } +}; + +constexpr int (X1::*pf)() const = &X1::f; +constexpr int (X1::*pf1)(int) const = &X1::f; +constexpr int (X1::*pf2)(int, int) const = &X1::f; + +constexpr X2 x2; +static_assert(x2.f() == 2); +static_assert((x2.*pf)() == 2); +static_assert(x2.f(1) == 12); +static_assert((x2.*pf1)(1) == 12); +static_assert(x2.f(1, 2) == 22); +static_assert((x2.*pf2)(1, 2) == 22); + +constexpr X1 const& r2 = x2; +static_assert(r2.f() == 2); +static_assert((r2.*pf)() == 2); +static_assert(r2.f(1) == 12); +static_assert((r2.*pf1)(1) == 12); +static_assert(r2.f(1, 2) == 22); +static_assert((r2.*pf2)(1, 2) == 22); + +constexpr X1 const* p2 = &x2; +static_assert(p2->f() == 2); +static_assert((p2->*pf)() == 2); +static_assert(p2->f(1) == 12); +static_assert((p2->*pf1)(1) == 12); +static_assert(p2->f(1, 2) == 22); +static_assert((p2->*pf2)(1, 2) == 22); + +constexpr X4 x4; +static_assert(x4.f() == 4); +static_assert((x4.*pf)() == 4); +static_assert(x4.f(1) == 14); +static_assert((x4.*pf1)(1) == 14); +static_assert(x4.f(1, 2) == 24); +static_assert((x4.*pf2)(1, 2) == 24); + +constexpr X1 const& r4 = x4; +static_assert(r4.f() == 4); +static_assert((r4.*pf)() == 4); +static_assert(r4.f(1) == 14); +static_assert((r4.*pf1)(1) == 14); +static_assert(r4.f(1, 2) == 24); +static_assert((r4.*pf2)(1, 2) == 24); + +constexpr X1 const* p4 = &x4; +static_assert(p4->f() == 4); +static_assert((p4->*pf)() == 4); +static_assert(p4->f(1) == 14); +static_assert((p4->*pf1)(1) == 14); +static_assert(p4->f(1, 2) == 24); +static_assert((p4->*pf2)(1, 2) == 24); diff --git gcc/testsuite/g++.dg/diagnostic/virtual-constexpr.C gcc/testsuite/g++.dg/diagnostic/virtual-constexpr.C index 2c83236cae9..9223c692737 100644 --- gcc/testsuite/g++.dg/diagnostic/virtual-constexpr.C +++ gcc/testsuite/g++.dg/diagnostic/virtual-constexpr.C @@ -1,14 +1,15 @@ -// { dg-options "-fdiagnostics-show-caret" } +// { dg-options "-fdiagnostics-show-caret -pedantic-errors" } // { dg-do compile { target c++11 } } +// { dg-skip-if "virtual constexpr" { *-*-* } { "-std=gnu++2a" } { "" } } struct S { - virtual constexpr void foo(); // { dg-error "3:member .foo. cannot be declared both .virtual. and .constexpr." } + virtual constexpr void foo(); // { dg-error "3:member .foo. can be declared both .virtual. and .constexpr." } /* { dg-begin-multiline-output "" } virtual constexpr void foo(); ^~~~~~~ ~~~~~~~~~ { dg-end-multiline-output "" } */ - constexpr virtual void bar(); // { dg-error "13:member .bar. cannot be declared both .virtual. and .constexpr." } + constexpr virtual void bar(); // { dg-error "13:member .bar. can be declared both .virtual. and .constexpr." } /* { dg-begin-multiline-output "" } constexpr virtual void bar(); ~~~~~~~~~ ^~~~~~~
On Fri, Sep 14, 2018 at 1:19 PM, Marek Polacek <polacek@redhat.com> wrote: > This patch implements another bit of C++20, virtual calls in constant > expression: > <http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1064r0.html> > The basic idea is that since in a constant expression we know the dynamic > type (to detect invalid code etc.), the restriction that prohibits virtual > calls is unnecessary. > > Handling virtual function calls turned out to be fairly easy (as anticipated); > I simply let the constexpr machinery figure out the dynamic type and then > OBJ_TYPE_REF_TOKEN gives us the index into the virtual function table. That > way we get the function decl we're interested in, and cxx_eval_call_expression > takes it from there. > > But handling pointer-to-virtual-member-functions doesn't work like that. > get_member_function_from_ptrfunc creates a COND_EXPR which looks like > if (pf.__pfn & 1) // is it a virtual function? > // yes, find the pointer in the vtable > else > // no, just return the pointer > so ideally we want to evaluate the then-branch. Eventually it'll evaluate it > to something like _ZTV2X2[2], but the vtable isn't constexpr so we'd end up > with "not a constant expression" error. Then let's mark the vtable as constexpr, there's no reason for it not to be. > Since the vtable initializer is > a compile-time constant, I thought we could make it work by a hack as the one > in cxx_eval_array_reference. We'll then let cxx_eval_call_expression do its > job and everything is hunky-dory. > > Except when it isn't: I noticed that the presence of _vptr doesn't make the > class non-empty, and when cxx_eval_constant_expression saw a decl with an empty > class type, it just evaluated it to { }. But such a class still had gotten an > initializer that looks like {.D.2082 = {._vptr.X2 = &_ZTV2X2 + 16}}. So > replacing it with { } will lose the proper initializer whereupon we fail. > The check I've added to cxx_eval_constant_expression won't win any beauty > contests but unfortunately EMPTY_CONSTRUCTOR_P doesn't work there. Perhaps we should check !TYPE_POLYMORPHIC_P as well as is_really_empty_class. Perhaps there should be a predicate for that, say, is_really_nearly_empty_class... Jason
On Fri, Sep 14, 2018 at 04:30:46PM -0400, Jason Merrill wrote: > On Fri, Sep 14, 2018 at 1:19 PM, Marek Polacek <polacek@redhat.com> wrote: > > This patch implements another bit of C++20, virtual calls in constant > > expression: > > <http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1064r0.html> > > The basic idea is that since in a constant expression we know the dynamic > > type (to detect invalid code etc.), the restriction that prohibits virtual > > calls is unnecessary. > > > > Handling virtual function calls turned out to be fairly easy (as anticipated); > > I simply let the constexpr machinery figure out the dynamic type and then > > OBJ_TYPE_REF_TOKEN gives us the index into the virtual function table. That > > way we get the function decl we're interested in, and cxx_eval_call_expression > > takes it from there. > > > > But handling pointer-to-virtual-member-functions doesn't work like that. > > get_member_function_from_ptrfunc creates a COND_EXPR which looks like > > if (pf.__pfn & 1) // is it a virtual function? > > // yes, find the pointer in the vtable > > else > > // no, just return the pointer > > so ideally we want to evaluate the then-branch. Eventually it'll evaluate it > > to something like _ZTV2X2[2], but the vtable isn't constexpr so we'd end up > > with "not a constant expression" error. > > Then let's mark the vtable as constexpr, there's no reason for it not to be. Ok, unfortunately it wasn't as easy as merely marking it DECL_DECLARED_CONSTEXPR_P in initialize_artificial_var because then I saw "used in its own initializer" error. Which I don't know why, but now that I know you agree with this direction I can dig deeper. > > Since the vtable initializer is > > a compile-time constant, I thought we could make it work by a hack as the one > > in cxx_eval_array_reference. We'll then let cxx_eval_call_expression do its > > job and everything is hunky-dory. > > > > Except when it isn't: I noticed that the presence of _vptr doesn't make the > > class non-empty, and when cxx_eval_constant_expression saw a decl with an empty > > class type, it just evaluated it to { }. But such a class still had gotten an > > initializer that looks like {.D.2082 = {._vptr.X2 = &_ZTV2X2 + 16}}. So > > replacing it with { } will lose the proper initializer whereupon we fail. > > The check I've added to cxx_eval_constant_expression won't win any beauty > > contests but unfortunately EMPTY_CONSTRUCTOR_P doesn't work there. > > Perhaps we should check !TYPE_POLYMORPHIC_P as well as > is_really_empty_class. Perhaps there should be a predicate for that, > say, is_really_nearly_empty_class... Ack. Thanks, Marek
On Fri, Sep 14, 2018 at 04:45:22PM -0400, Marek Polacek wrote: > On Fri, Sep 14, 2018 at 04:30:46PM -0400, Jason Merrill wrote: > > On Fri, Sep 14, 2018 at 1:19 PM, Marek Polacek <polacek@redhat.com> wrote: > > > This patch implements another bit of C++20, virtual calls in constant > > > expression: > > > <http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1064r0.html> > > > The basic idea is that since in a constant expression we know the dynamic > > > type (to detect invalid code etc.), the restriction that prohibits virtual > > > calls is unnecessary. > > > > > > Handling virtual function calls turned out to be fairly easy (as anticipated); > > > I simply let the constexpr machinery figure out the dynamic type and then > > > OBJ_TYPE_REF_TOKEN gives us the index into the virtual function table. That > > > way we get the function decl we're interested in, and cxx_eval_call_expression > > > takes it from there. > > > > > > But handling pointer-to-virtual-member-functions doesn't work like that. > > > get_member_function_from_ptrfunc creates a COND_EXPR which looks like > > > if (pf.__pfn & 1) // is it a virtual function? > > > // yes, find the pointer in the vtable > > > else > > > // no, just return the pointer > > > so ideally we want to evaluate the then-branch. Eventually it'll evaluate it > > > to something like _ZTV2X2[2], but the vtable isn't constexpr so we'd end up > > > with "not a constant expression" error. > > > > Then let's mark the vtable as constexpr, there's no reason for it not to be. Done. But then I had to add indexes to the vtable's ctor (because find_array_ctor_elt expects it), which broke an assert in gimple_get_virt_method_for_vtable. But I don't need the array_ref hack anymore! Also, I had to set DECL_DECLARED_CONSTEXPR_P after maybe_commonize_var, otherwise we run into the sorry in that function with -fno-weak... > Ok, unfortunately it wasn't as easy as merely marking it DECL_DECLARED_CONSTEXPR_P > in initialize_artificial_var because then I saw "used in its own initializer" > error. Which I don't know why, but now that I know you agree with this direction > I can dig deeper. > > > > Since the vtable initializer is > > > a compile-time constant, I thought we could make it work by a hack as the one > > > in cxx_eval_array_reference. We'll then let cxx_eval_call_expression do its > > > job and everything is hunky-dory. > > > > > > Except when it isn't: I noticed that the presence of _vptr doesn't make the > > > class non-empty, and when cxx_eval_constant_expression saw a decl with an empty > > > class type, it just evaluated it to { }. But such a class still had gotten an > > > initializer that looks like {.D.2082 = {._vptr.X2 = &_ZTV2X2 + 16}}. So > > > replacing it with { } will lose the proper initializer whereupon we fail. > > > The check I've added to cxx_eval_constant_expression won't win any beauty > > > contests but unfortunately EMPTY_CONSTRUCTOR_P doesn't work there. > > > > Perhaps we should check !TYPE_POLYMORPHIC_P as well as > > is_really_empty_class. Perhaps there should be a predicate for that, > > say, is_really_nearly_empty_class... For now I've only added the !TYPE_POLYMORPHIC_P check, which works just fine. Bootstrapped/regtested on x86_64-linux, ok for trunk? 2018-09-17 Marek Polacek <polacek@redhat.com> P1064R0 - Allowing Virtual Function Calls in Constant Expressions * call.c (build_over_call): Add FIXME. * class.c (initialize_vtable): Mark the vtable as constexpr. (build_vtbl_initializer): Build vtable's constructor with indexes. * constexpr.c (cxx_eval_constant_expression): Don't ignore _vptr's initializer. Handle OBJ_TYPE_REF. (potential_constant_expression_1): Handle OBJ_TYPE_REF. * decl.c (grokdeclarator): Change error to pedwarn. Only warn when pedantic and not C++2a. * gimple-fold.c (gimple_get_virt_method_for_vtable): Remove assert. * g++.dg/cpp0x/constexpr-virtual5.C: Adjust dg-error. * g++.dg/cpp2a/constexpr-virtual1.C: New test. * g++.dg/cpp2a/constexpr-virtual2.C: New test. * g++.dg/cpp2a/constexpr-virtual3.C: New test. * g++.dg/cpp2a/constexpr-virtual4.C: New test. * g++.dg/cpp2a/constexpr-virtual5.C: New test. * g++.dg/cpp2a/constexpr-virtual6.C: New test. * g++.dg/cpp2a/constexpr-virtual7.C: New test. * g++.dg/cpp2a/constexpr-virtual8.C: New test. * g++.dg/cpp2a/constexpr-virtual9.C: New test. * g++.dg/diagnostic/virtual-constexpr.C: Skip for C++2a. Use -pedantic-errors. Adjust dg-error. diff --git gcc/cp/call.c gcc/cp/call.c index 69503ca7920..6c70874af40 100644 --- gcc/cp/call.c +++ gcc/cp/call.c @@ -8401,7 +8401,8 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain) if (DECL_VINDEX (fn) && (flags & LOOKUP_NONVIRTUAL) == 0 /* Don't mess with virtual lookup in instantiate_non_dependent_expr; - virtual functions can't be constexpr. */ + virtual functions can't be constexpr. FIXME Actually, no longer + true in C++2a. */ && !in_template_function ()) { tree t; diff --git gcc/cp/class.c gcc/cp/class.c index e950a7423f7..288033fe912 100644 --- gcc/cp/class.c +++ gcc/cp/class.c @@ -8681,6 +8681,10 @@ initialize_vtable (tree binfo, vec<constructor_elt, va_gc> *inits) layout_vtable_decl (binfo, vec_safe_length (inits)); decl = get_vtbl_decl_for_binfo (binfo); initialize_artificial_var (decl, inits); + /* Mark the vtable as constexpr so that we can access its content + at compile time. */ + DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (decl) = true; + DECL_DECLARED_CONSTEXPR_P (decl) = true; dump_vtable (BINFO_TYPE (binfo), binfo, decl); } @@ -9266,6 +9270,7 @@ build_vtbl_initializer (tree binfo, tree vcall_index; tree fn, fn_original; tree init = NULL_TREE; + tree idx = build_int_cst (size_type_node, jx++); fn = BV_FN (v); fn_original = fn; @@ -9369,7 +9374,7 @@ build_vtbl_initializer (tree binfo, int i; if (init == size_zero_node) for (i = 0; i < TARGET_VTABLE_USES_DESCRIPTORS; ++i) - CONSTRUCTOR_APPEND_ELT (*inits, NULL_TREE, init); + CONSTRUCTOR_APPEND_ELT (*inits, idx, init); else for (i = 0; i < TARGET_VTABLE_USES_DESCRIPTORS; ++i) { @@ -9377,11 +9382,11 @@ build_vtbl_initializer (tree binfo, fn, build_int_cst (NULL_TREE, i)); TREE_CONSTANT (fdesc) = 1; - CONSTRUCTOR_APPEND_ELT (*inits, NULL_TREE, fdesc); + CONSTRUCTOR_APPEND_ELT (*inits, idx, fdesc); } } else - CONSTRUCTOR_APPEND_ELT (*inits, NULL_TREE, init); + CONSTRUCTOR_APPEND_ELT (*inits, idx, init); } } diff --git gcc/cp/constexpr.c gcc/cp/constexpr.c index 88c73787961..aa33319875f 100644 --- gcc/cp/constexpr.c +++ gcc/cp/constexpr.c @@ -4209,7 +4209,11 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, CONST_DECL for aggregate constants. */ if (lval) return t; + /* is_really_empty_class doesn't take into account _vptr, so initializing + otherwise empty class with { } would overwrite the initializer that + initialize_vtable created for us. */ if (COMPLETE_TYPE_P (TREE_TYPE (t)) + && !TYPE_POLYMORPHIC_P (TREE_TYPE (t)) && is_really_empty_class (TREE_TYPE (t))) { /* If the class is empty, we aren't actually loading anything. */ @@ -4778,7 +4782,6 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, case MODOP_EXPR: /* GCC internal stuff. */ case VA_ARG_EXPR: - case OBJ_TYPE_REF: case NON_DEPENDENT_EXPR: case BASELINK: case OFFSET_REF: @@ -4788,6 +4791,34 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, *non_constant_p = true; break; + case OBJ_TYPE_REF: + { + /* Virtual function call. Let the constexpr machinery figure out + the dynamic type. */ + int token = tree_to_shwi (OBJ_TYPE_REF_TOKEN (t)); + tree obj = OBJ_TYPE_REF_OBJECT (t); + obj = cxx_eval_constant_expression (ctx, obj, lval, non_constant_p, + overflow_p); + /* We expect something in the form of &x.D.2103.D.2094; get x. */ + if (TREE_CODE (obj) != ADDR_EXPR) + { + if (!ctx->quiet) + error_at (cp_expr_loc_or_loc (t, input_location), + "expression %qE is not a constant expression", t); + *non_constant_p = true; + return t; + } + obj = TREE_OPERAND (obj, 0); + while (handled_component_p (obj)) + obj = TREE_OPERAND (obj, 0); + tree objtype = TREE_TYPE (obj); + /* Find the function decl in the virtual functions list. TOKEN is + the DECL_VINDEX that says which function we're looking for. */ + tree virtuals = BINFO_VIRTUALS (TYPE_BINFO (objtype)); + r = TREE_VALUE (chain_index (token, virtuals)); + break; + } + case PLACEHOLDER_EXPR: /* Use of the value or address of the current object. */ if (tree ctor = lookup_placeholder (ctx, lval, TREE_TYPE (t))) @@ -5871,7 +5902,6 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now, case OACC_UPDATE: /* GCC internal stuff. */ case VA_ARG_EXPR: - case OBJ_TYPE_REF: case TRANSACTION_EXPR: case ASM_EXPR: case AT_ENCODE_EXPR: @@ -5880,6 +5910,14 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now, error_at (loc, "expression %qE is not a constant expression", t); return false; + case OBJ_TYPE_REF: + if (cxx_dialect >= cxx2a) + /* In C++2a virtual calls can be constexpr, don't give up yet. */ + return true; + else if (flags & tf_error) + error_at (loc, "virtual functions cannot be constexpr before C++2a"); + return false; + case TYPEID_EXPR: /* -- a typeid expression whose operand is of polymorphic class type; */ diff --git gcc/cp/decl.c gcc/cp/decl.c index 50b60e89df5..da3749254e9 100644 --- gcc/cp/decl.c +++ gcc/cp/decl.c @@ -10854,12 +10854,13 @@ grokdeclarator (const cp_declarator *declarator, storage_class = sc_none; staticp = 0; } - if (constexpr_p) + if (constexpr_p && cxx_dialect < cxx2a) { gcc_rich_location richloc (declspecs->locations[ds_virtual]); richloc.add_range (declspecs->locations[ds_constexpr]); - error_at (&richloc, "member %qD cannot be declared both %<virtual%> " - "and %<constexpr%>", dname); + pedwarn (&richloc, OPT_Wpedantic, "member %qD can be declared both " + "%<virtual%> and %<constexpr%> only in -std=c++2a or " + "-std=gnu++2a", dname); } } friendp = decl_spec_seq_has_spec_p (declspecs, ds_friend); diff --git gcc/gimple-fold.c gcc/gimple-fold.c index 362ab59e9c0..dd30a30b8c5 100644 --- gcc/gimple-fold.c +++ gcc/gimple-fold.c @@ -6988,12 +6988,11 @@ gimple_get_virt_method_for_vtable (HOST_WIDE_INT token, access_index = offset / BITS_PER_UNIT / elt_size; gcc_checking_assert (offset % (elt_size * BITS_PER_UNIT) == 0); - /* This code makes an assumption that there are no - indexed fileds produced by C++ FE, so we can directly index the array. */ + /* The C++ FE now produces indexed fields but we can index the array + directly. */ if (access_index < CONSTRUCTOR_NELTS (init)) { fn = CONSTRUCTOR_ELT (init, access_index)->value; - gcc_checking_assert (!CONSTRUCTOR_ELT (init, access_index)->index); STRIP_NOPS (fn); } else diff --git gcc/testsuite/g++.dg/cpp0x/constexpr-virtual5.C gcc/testsuite/g++.dg/cpp0x/constexpr-virtual5.C index 2465f9d9b4f..5f9ab4d9c28 100644 --- gcc/testsuite/g++.dg/cpp0x/constexpr-virtual5.C +++ gcc/testsuite/g++.dg/cpp0x/constexpr-virtual5.C @@ -2,5 +2,5 @@ // { dg-do compile { target c++11 } } struct S { - constexpr virtual int f() { return 1; } // { dg-error "13:member .f. cannot be declared both .virtual. and .constexpr." } + constexpr virtual int f() { return 1; } // { dg-error "13:member .f. can be declared both .virtual. and .constexpr." "" { target c++17_down } } }; diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-virtual1.C gcc/testsuite/g++.dg/cpp2a/constexpr-virtual1.C index e69de29bb2d..fcf8cac6417 100644 --- gcc/testsuite/g++.dg/cpp2a/constexpr-virtual1.C +++ gcc/testsuite/g++.dg/cpp2a/constexpr-virtual1.C @@ -0,0 +1,8 @@ +// P1064R0 +// { dg-do compile { target c++11 } } +// { dg-options "-pedantic-errors" } + +struct X +{ + constexpr virtual int f() { return 0; } // { dg-error "member .f. can be declared both .virtual. and .constexpr. only" "" { target c++17_down } } +}; diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C index e69de29bb2d..9d82c5c59ac 100644 --- gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C +++ gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C @@ -0,0 +1,49 @@ +// P1064R0 +// { dg-do compile } +// { dg-options "-std=c++2a" } + +struct X1 +{ + virtual int f() const = 0; +}; + +struct X2: public X1 +{ + constexpr virtual int f() const { return 2; } +}; + +struct X3: public X2 +{ + virtual int f() const { return 3; } +}; + +struct X4: public X3 +{ + constexpr virtual int f() const { return 4; } +}; + +constexpr int (X1::*pf)() const = &X1::f; + +constexpr X2 x2; +static_assert(x2.f() == 2); +static_assert((x2.*pf)() == 2); + +constexpr X1 const& r2 = x2; +static_assert(r2.f() == 2); +static_assert((r2.*pf)() == 2); + +constexpr X1 const* p2 = &x2; +static_assert(p2->f() == 2); +static_assert((p2->*pf)() == 2); + +constexpr X4 x4; +static_assert(x4.f() == 4); +static_assert((x4.*pf)() == 4); + +constexpr X1 const& r4 = x4; +static_assert(r4.f() == 4); +static_assert((r4.*pf)() == 4); + +constexpr X1 const* p4 = &x4; +static_assert(p4->f() == 4); +static_assert((p4->*pf)() == 4); diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-virtual3.C gcc/testsuite/g++.dg/cpp2a/constexpr-virtual3.C index e69de29bb2d..d71422fc4d0 100644 --- gcc/testsuite/g++.dg/cpp2a/constexpr-virtual3.C +++ gcc/testsuite/g++.dg/cpp2a/constexpr-virtual3.C @@ -0,0 +1,52 @@ +// P1064R0 +// { dg-do compile } +// { dg-options "-std=c++2a" } + +struct X1 +{ + virtual int f() const = 0; +}; + +struct X2: public X1 +{ + int i2 = 42; + constexpr virtual int f() const { return 2; } +}; + +struct X3: public X2 +{ + int i3 = 42; + virtual int f() const { return 3; } +}; + +struct X4: public X3 +{ + int i4 = 42; + constexpr virtual int f() const { return 4; } +}; + +constexpr int (X1::*pf)() const = &X1::f; + +constexpr X2 x2; +static_assert(x2.f() == 2); +static_assert((x2.*pf)() == 2); + +constexpr X1 const& r2 = x2; +static_assert(r2.f() == 2); +static_assert((r2.*pf)() == 2); + +constexpr X1 const* p2 = &x2; +static_assert(p2->f() == 2); +static_assert((p2->*pf)() == 2); + +constexpr X4 x4; +static_assert(x4.f() == 4); +static_assert((x4.*pf)() == 4); + +constexpr X1 const& r4 = x4; +static_assert(r4.f() == 4); +static_assert((r4.*pf)() == 4); + +constexpr X1 const* p4 = &x4; +static_assert(p4->f() == 4); +static_assert((p4->*pf)() == 4); diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-virtual4.C gcc/testsuite/g++.dg/cpp2a/constexpr-virtual4.C index e69de29bb2d..2038bebc6d1 100644 --- gcc/testsuite/g++.dg/cpp2a/constexpr-virtual4.C +++ gcc/testsuite/g++.dg/cpp2a/constexpr-virtual4.C @@ -0,0 +1,57 @@ +// P1064R0 +// { dg-do compile } +// { dg-options "-std=c++2a" } + +struct X1 +{ + virtual int f1() const = 0; + virtual int f2() const = 0; + virtual int f3() const = 0; +}; + +struct X2: public X1 +{ + constexpr virtual int f1() const { return 21; } + constexpr virtual int f2() const { return 22; } + constexpr virtual int f3() const { return 23; } +}; + +struct X3: public X2 +{ + virtual int f1() const { return 31; } + virtual int f2() const { return 32; } + virtual int f3() const { return 33; } +}; + +struct X4: public X3 +{ + constexpr virtual int f1() const { return 41; } + constexpr virtual int f2() const { return 42; } + constexpr virtual int f3() const { return 43; } +}; + +constexpr int (X1::*pf)() const = &X1::f2; + +constexpr X2 x2; +static_assert(x2.f2() == 22); +static_assert((x2.*pf)() == 22); + +constexpr X1 const& r2 = x2; +static_assert(r2.f2() == 22); +static_assert((r2.*pf)() == 22); + +constexpr X1 const* p2 = &x2; +static_assert(p2->f2() == 22); +static_assert((p2->*pf)() == 22); + +constexpr X4 x4; +static_assert(x4.f2() == 42); +static_assert((x4.*pf)() == 42); + +constexpr X1 const& r4 = x4; +static_assert(r4.f2() == 42); +static_assert((r4.*pf)() == 42); + +constexpr X1 const* p4 = &x4; +static_assert(p4->f2() == 42); +static_assert((p4->*pf)() == 42); diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-virtual5.C gcc/testsuite/g++.dg/cpp2a/constexpr-virtual5.C index e69de29bb2d..6d27990a8b6 100644 --- gcc/testsuite/g++.dg/cpp2a/constexpr-virtual5.C +++ gcc/testsuite/g++.dg/cpp2a/constexpr-virtual5.C @@ -0,0 +1,60 @@ +// P1064R0 +// { dg-do compile } +// { dg-options "-std=c++2a" } + +struct X1 +{ + virtual int f1() const = 0; + virtual int f2() const = 0; + virtual int f3() const = 0; +}; + +struct X2: public X1 +{ + int i2 = 42; + constexpr virtual int f1() const { return 21; } + constexpr virtual int f2() const { return 22; } + constexpr virtual int f3() const { return 23; } +}; + +struct X3: public X2 +{ + int i3 = 42; + virtual int f1() const { return 31; } + virtual int f2() const { return 32; } + virtual int f3() const { return 33; } +}; + +struct X4: public X3 +{ + int i4 = 42; + constexpr virtual int f1() const { return 41; } + constexpr virtual int f2() const { return 42; } + constexpr virtual int f3() const { return 43; } +}; + +constexpr int (X1::*pf)() const = &X1::f2; + +constexpr X2 x2; +static_assert(x2.f2() == 22); +static_assert((x2.*pf)() == 22); + +constexpr X1 const& r2 = x2; +static_assert(r2.f2() == 22); +static_assert((r2.*pf)() == 22); + +constexpr X1 const* p2 = &x2; +static_assert(p2->f2() == 22); +static_assert((p2->*pf)() == 22); + +constexpr X4 x4; +static_assert(x4.f2() == 42); +static_assert((x4.*pf)() == 42); + +constexpr X1 const& r4 = x4; +static_assert(r4.f2() == 42); +static_assert((r4.*pf)() == 42); + +constexpr X1 const* p4 = &x4; +static_assert(p4->f2() == 42); +static_assert((p4->*pf)() == 42); diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-virtual6.C gcc/testsuite/g++.dg/cpp2a/constexpr-virtual6.C index e69de29bb2d..ece5e703c32 100644 --- gcc/testsuite/g++.dg/cpp2a/constexpr-virtual6.C +++ gcc/testsuite/g++.dg/cpp2a/constexpr-virtual6.C @@ -0,0 +1,25 @@ +// P1064R0 +// { dg-do compile } +// { dg-options "-std=c++2a" } + +struct X1 +{ + constexpr virtual X1 const *f() const { return this; } +}; + +struct Y +{ + int m = 0; +}; + +struct X2: public Y, public X1 +{ + constexpr virtual X2 const *f() const { return this; } +}; + +constexpr X1 x1; +static_assert(x1.f() == &x1); + +constexpr X2 x2; +constexpr X1 const& r2 = x2; +static_assert(r2.f() == &r2); diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-virtual7.C gcc/testsuite/g++.dg/cpp2a/constexpr-virtual7.C index e69de29bb2d..b0f499608ef 100644 --- gcc/testsuite/g++.dg/cpp2a/constexpr-virtual7.C +++ gcc/testsuite/g++.dg/cpp2a/constexpr-virtual7.C @@ -0,0 +1,87 @@ +// P1064R0 +// { dg-do compile } +// { dg-options "-std=c++2a" } + +struct X1 +{ + virtual int f() const = 0; +}; + +struct X2: public X1 +{ + constexpr virtual int f() const { return 2; } +}; + +struct X3: public X2 +{ + virtual int f() const { return 3; } +}; + +struct X4: public X3 +{ + constexpr virtual int f() const { return 4; } +}; + +constexpr int (X1::*pf)() const = &X1::f; + +constexpr X2 x2; + +struct S +{ + int i, j; + constexpr S() : i(x2.f()), j((x2.*pf)()) { } +}; + +static_assert(S().i == 2); +static_assert(S().j == 2); + +constexpr X1 const& r2 = x2; + +struct S2 +{ + int i, j; + constexpr S2() : i(r2.f()), j((r2.*pf)()) { } +}; + +static_assert(S2().i == 2); +static_assert(S2().j == 2); + +constexpr X1 const* p2 = &x2; +struct S3 +{ + int i, j; + constexpr S3() : i(p2->f()), j((p2->*pf)()) { } +}; + +static_assert(S3().i == 2); +static_assert(S3().j == 2); + +constexpr X4 x4; +struct S4 +{ + int i, j; + constexpr S4() : i(x4.f()), j((x4.*pf)()) { } +}; + +static_assert(S4().i == 4); +static_assert(S4().j == 4); + +constexpr X1 const& r4 = x4; +struct S5 +{ + int i, j; + constexpr S5() : i(r4.f()), j((r4.*pf)()) { } +}; + +static_assert(S5().i == 4); +static_assert(S5().j == 4); + +constexpr X1 const* p4 = &x4; +struct S6 +{ + int i, j; + constexpr S6() : i(p4->f()), j((p4->*pf)()) { } +}; + +static_assert(S6().i == 4); +static_assert(S6().j == 4); diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-virtual8.C gcc/testsuite/g++.dg/cpp2a/constexpr-virtual8.C index e69de29bb2d..4a7cc972a91 100644 --- gcc/testsuite/g++.dg/cpp2a/constexpr-virtual8.C +++ gcc/testsuite/g++.dg/cpp2a/constexpr-virtual8.C @@ -0,0 +1,50 @@ +// P1064R0 +// { dg-do compile } +// { dg-options "-std=c++2a" } + +template<typename T> +struct X1 +{ + virtual T f() const = 0; +}; + +struct X2: public X1<int> +{ + constexpr virtual int f() const { return 2; } +}; + +struct X3: public X2 +{ + virtual int f() const { return 3; } +}; + +struct X4: public X3 +{ + constexpr virtual int f() const { return 4; } +}; + +constexpr int (X1<int>::*pf)() const = &X1<int>::f; + +constexpr X2 x2; +static_assert(x2.f() == 2); +static_assert((x2.*pf)() == 2); + +constexpr X1<int> const& r2 = x2; +static_assert(r2.f() == 2); +static_assert((r2.*pf)() == 2); + +constexpr X1<int> const* p2 = &x2; +static_assert(p2->f() == 2); +static_assert((p2->*pf)() == 2); + +constexpr X4 x4; +static_assert(x4.f() == 4); +static_assert((x4.*pf)() == 4); + +constexpr X1<int> const& r4 = x4; +static_assert(r4.f() == 4); +static_assert((r4.*pf)() == 4); + +constexpr X1<int> const* p4 = &x4; +static_assert(p4->f() == 4); +static_assert((p4->*pf)() == 4); diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-virtual9.C gcc/testsuite/g++.dg/cpp2a/constexpr-virtual9.C index e69de29bb2d..3a12adc2659 100644 --- gcc/testsuite/g++.dg/cpp2a/constexpr-virtual9.C +++ gcc/testsuite/g++.dg/cpp2a/constexpr-virtual9.C @@ -0,0 +1,83 @@ +// P1064R0 +// { dg-do compile } +// { dg-options "-std=c++2a" } + +struct X1 +{ + virtual int f() const = 0; + virtual int f(int) const = 0; + virtual int f(int, int) const = 0; +}; + +struct X2: public X1 +{ + constexpr virtual int f() const { return 2; } + constexpr virtual int f(int) const { return 12; } + constexpr virtual int f(int, int) const { return 22; } +}; + +struct X3: public X2 +{ + virtual int f() const { return 3; } + virtual int f(int) const { return 13; } + virtual int f(int, int) const { return 23; } +}; + +struct X4: public X3 +{ + constexpr virtual int f() const { return 4; } + constexpr virtual int f(int) const { return 14; } + constexpr virtual int f(int, int) const { return 24; } +}; + +constexpr int (X1::*pf)() const = &X1::f; +constexpr int (X1::*pf1)(int) const = &X1::f; +constexpr int (X1::*pf2)(int, int) const = &X1::f; + +constexpr X2 x2; +static_assert(x2.f() == 2); +static_assert((x2.*pf)() == 2); +static_assert(x2.f(1) == 12); +static_assert((x2.*pf1)(1) == 12); +static_assert(x2.f(1, 2) == 22); +static_assert((x2.*pf2)(1, 2) == 22); + +constexpr X1 const& r2 = x2; +static_assert(r2.f() == 2); +static_assert((r2.*pf)() == 2); +static_assert(r2.f(1) == 12); +static_assert((r2.*pf1)(1) == 12); +static_assert(r2.f(1, 2) == 22); +static_assert((r2.*pf2)(1, 2) == 22); + +constexpr X1 const* p2 = &x2; +static_assert(p2->f() == 2); +static_assert((p2->*pf)() == 2); +static_assert(p2->f(1) == 12); +static_assert((p2->*pf1)(1) == 12); +static_assert(p2->f(1, 2) == 22); +static_assert((p2->*pf2)(1, 2) == 22); + +constexpr X4 x4; +static_assert(x4.f() == 4); +static_assert((x4.*pf)() == 4); +static_assert(x4.f(1) == 14); +static_assert((x4.*pf1)(1) == 14); +static_assert(x4.f(1, 2) == 24); +static_assert((x4.*pf2)(1, 2) == 24); + +constexpr X1 const& r4 = x4; +static_assert(r4.f() == 4); +static_assert((r4.*pf)() == 4); +static_assert(r4.f(1) == 14); +static_assert((r4.*pf1)(1) == 14); +static_assert(r4.f(1, 2) == 24); +static_assert((r4.*pf2)(1, 2) == 24); + +constexpr X1 const* p4 = &x4; +static_assert(p4->f() == 4); +static_assert((p4->*pf)() == 4); +static_assert(p4->f(1) == 14); +static_assert((p4->*pf1)(1) == 14); +static_assert(p4->f(1, 2) == 24); +static_assert((p4->*pf2)(1, 2) == 24); diff --git gcc/testsuite/g++.dg/diagnostic/virtual-constexpr.C gcc/testsuite/g++.dg/diagnostic/virtual-constexpr.C index 2c83236cae9..9223c692737 100644 --- gcc/testsuite/g++.dg/diagnostic/virtual-constexpr.C +++ gcc/testsuite/g++.dg/diagnostic/virtual-constexpr.C @@ -1,14 +1,15 @@ -// { dg-options "-fdiagnostics-show-caret" } +// { dg-options "-fdiagnostics-show-caret -pedantic-errors" } // { dg-do compile { target c++11 } } +// { dg-skip-if "virtual constexpr" { *-*-* } { "-std=gnu++2a" } { "" } } struct S { - virtual constexpr void foo(); // { dg-error "3:member .foo. cannot be declared both .virtual. and .constexpr." } + virtual constexpr void foo(); // { dg-error "3:member .foo. can be declared both .virtual. and .constexpr." } /* { dg-begin-multiline-output "" } virtual constexpr void foo(); ^~~~~~~ ~~~~~~~~~ { dg-end-multiline-output "" } */ - constexpr virtual void bar(); // { dg-error "13:member .bar. cannot be declared both .virtual. and .constexpr." } + constexpr virtual void bar(); // { dg-error "13:member .bar. can be declared both .virtual. and .constexpr." } /* { dg-begin-multiline-output "" } constexpr virtual void bar(); ~~~~~~~~~ ^~~~~~~
On Mon, Sep 17, 2018 at 5:39 PM, Marek Polacek <polacek@redhat.com> wrote: > On Fri, Sep 14, 2018 at 04:45:22PM -0400, Marek Polacek wrote: >> On Fri, Sep 14, 2018 at 04:30:46PM -0400, Jason Merrill wrote: >> > On Fri, Sep 14, 2018 at 1:19 PM, Marek Polacek <polacek@redhat.com> wrote: >> > > This patch implements another bit of C++20, virtual calls in constant >> > > expression: >> > > <http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1064r0.html> >> > > The basic idea is that since in a constant expression we know the dynamic >> > > type (to detect invalid code etc.), the restriction that prohibits virtual >> > > calls is unnecessary. >> > > >> > > Handling virtual function calls turned out to be fairly easy (as anticipated); >> > > I simply let the constexpr machinery figure out the dynamic type and then >> > > OBJ_TYPE_REF_TOKEN gives us the index into the virtual function table. That >> > > way we get the function decl we're interested in, and cxx_eval_call_expression >> > > takes it from there. >> > > >> > > But handling pointer-to-virtual-member-functions doesn't work like that. >> > > get_member_function_from_ptrfunc creates a COND_EXPR which looks like >> > > if (pf.__pfn & 1) // is it a virtual function? >> > > // yes, find the pointer in the vtable >> > > else >> > > // no, just return the pointer >> > > so ideally we want to evaluate the then-branch. Eventually it'll evaluate it >> > > to something like _ZTV2X2[2], but the vtable isn't constexpr so we'd end up >> > > with "not a constant expression" error. >> > >> > Then let's mark the vtable as constexpr, there's no reason for it not to be. > > Done. But then I had to add indexes to the vtable's ctor (because find_array_ctor_elt > expects it), which broke an assert in gimple_get_virt_method_for_vtable. But I > don't need the array_ref hack anymore! > Also, I had to set DECL_DECLARED_CONSTEXPR_P after maybe_commonize_var, > otherwise we run into the sorry in that function with -fno-weak... Hmm, we shouldn't give that sorry for DECL_ARTIFICIAL variables. Looking more closely, it seems that the call to maybe_commonize_var from initialize_artificial_var did nothing before this change, since the vtable is DECL_ARTIFICIAL, so it didn't pass the condition at the top. I suppose we should extend the !DECL_ARTIFICIAL check in maybe_commonize_var to the inline variable case as well. >> Ok, unfortunately it wasn't as easy as merely marking it DECL_DECLARED_CONSTEXPR_P >> in initialize_artificial_var because then I saw "used in its own initializer" >> error. Which I don't know why, but now that I know you agree with this direction >> I can dig deeper. >> >> > > Since the vtable initializer is >> > > a compile-time constant, I thought we could make it work by a hack as the one >> > > in cxx_eval_array_reference. We'll then let cxx_eval_call_expression do its >> > > job and everything is hunky-dory. >> > > >> > > Except when it isn't: I noticed that the presence of _vptr doesn't make the >> > > class non-empty, and when cxx_eval_constant_expression saw a decl with an empty >> > > class type, it just evaluated it to { }. But such a class still had gotten an >> > > initializer that looks like {.D.2082 = {._vptr.X2 = &_ZTV2X2 + 16}}. So >> > > replacing it with { } will lose the proper initializer whereupon we fail. >> > > The check I've added to cxx_eval_constant_expression won't win any beauty >> > > contests but unfortunately EMPTY_CONSTRUCTOR_P doesn't work there. >> > >> > Perhaps we should check !TYPE_POLYMORPHIC_P as well as >> > is_really_empty_class. Perhaps there should be a predicate for that, >> > say, is_really_nearly_empty_class... > > For now I've only added the !TYPE_POLYMORPHIC_P check, which works just fine. > > Bootstrapped/regtested on x86_64-linux, ok for trunk? > > 2018-09-17 Marek Polacek <polacek@redhat.com> > > P1064R0 - Allowing Virtual Function Calls in Constant Expressions > * call.c (build_over_call): Add FIXME. > * class.c (initialize_vtable): Mark the vtable as constexpr. > (build_vtbl_initializer): Build vtable's constructor with indexes. > * constexpr.c (cxx_eval_constant_expression): Don't ignore _vptr's > initializer. Handle OBJ_TYPE_REF. > (potential_constant_expression_1): Handle OBJ_TYPE_REF. > * decl.c (grokdeclarator): Change error to pedwarn. Only warn when > pedantic and not C++2a. > > * gimple-fold.c (gimple_get_virt_method_for_vtable): Remove assert. > > * g++.dg/cpp0x/constexpr-virtual5.C: Adjust dg-error. > * g++.dg/cpp2a/constexpr-virtual1.C: New test. > * g++.dg/cpp2a/constexpr-virtual2.C: New test. > * g++.dg/cpp2a/constexpr-virtual3.C: New test. > * g++.dg/cpp2a/constexpr-virtual4.C: New test. > * g++.dg/cpp2a/constexpr-virtual5.C: New test. > * g++.dg/cpp2a/constexpr-virtual6.C: New test. > * g++.dg/cpp2a/constexpr-virtual7.C: New test. > * g++.dg/cpp2a/constexpr-virtual8.C: New test. > * g++.dg/cpp2a/constexpr-virtual9.C: New test. > * g++.dg/diagnostic/virtual-constexpr.C: Skip for C++2a. Use > -pedantic-errors. Adjust dg-error. > > diff --git gcc/cp/call.c gcc/cp/call.c > index 69503ca7920..6c70874af40 100644 > --- gcc/cp/call.c > +++ gcc/cp/call.c > @@ -8401,7 +8401,8 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain) > > if (DECL_VINDEX (fn) && (flags & LOOKUP_NONVIRTUAL) == 0 > /* Don't mess with virtual lookup in instantiate_non_dependent_expr; > - virtual functions can't be constexpr. */ > + virtual functions can't be constexpr. FIXME Actually, no longer > + true in C++2a. */ > && !in_template_function ()) I notice that removing the in_template_function check doesn't break template/virtual4.C nowadays. Does it break anything else? > + /* The C++ FE now produces indexed fields but we can index the array > + directly. */ > if (access_index < CONSTRUCTOR_NELTS (init)) > { > fn = CONSTRUCTOR_ELT (init, access_index)->value; > - gcc_checking_assert (!CONSTRUCTOR_ELT (init, access_index)->index); Rather than remove this assert, let's fix it to check that the ->index matches access_index. Jason
On Mon, Sep 17, 2018 at 11:28:06PM -0400, Jason Merrill wrote: > On Mon, Sep 17, 2018 at 5:39 PM, Marek Polacek <polacek@redhat.com> wrote: > > On Fri, Sep 14, 2018 at 04:45:22PM -0400, Marek Polacek wrote: > >> On Fri, Sep 14, 2018 at 04:30:46PM -0400, Jason Merrill wrote: > >> > On Fri, Sep 14, 2018 at 1:19 PM, Marek Polacek <polacek@redhat.com> wrote: > >> > > This patch implements another bit of C++20, virtual calls in constant > >> > > expression: > >> > > <http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1064r0.html> > >> > > The basic idea is that since in a constant expression we know the dynamic > >> > > type (to detect invalid code etc.), the restriction that prohibits virtual > >> > > calls is unnecessary. > >> > > > >> > > Handling virtual function calls turned out to be fairly easy (as anticipated); > >> > > I simply let the constexpr machinery figure out the dynamic type and then > >> > > OBJ_TYPE_REF_TOKEN gives us the index into the virtual function table. That > >> > > way we get the function decl we're interested in, and cxx_eval_call_expression > >> > > takes it from there. > >> > > > >> > > But handling pointer-to-virtual-member-functions doesn't work like that. > >> > > get_member_function_from_ptrfunc creates a COND_EXPR which looks like > >> > > if (pf.__pfn & 1) // is it a virtual function? > >> > > // yes, find the pointer in the vtable > >> > > else > >> > > // no, just return the pointer > >> > > so ideally we want to evaluate the then-branch. Eventually it'll evaluate it > >> > > to something like _ZTV2X2[2], but the vtable isn't constexpr so we'd end up > >> > > with "not a constant expression" error. > >> > > >> > Then let's mark the vtable as constexpr, there's no reason for it not to be. > > > > Done. But then I had to add indexes to the vtable's ctor (because find_array_ctor_elt > > expects it), which broke an assert in gimple_get_virt_method_for_vtable. But I > > don't need the array_ref hack anymore! > > > Also, I had to set DECL_DECLARED_CONSTEXPR_P after maybe_commonize_var, > > otherwise we run into the sorry in that function with -fno-weak... > > Hmm, we shouldn't give that sorry for DECL_ARTIFICIAL variables. > > Looking more closely, it seems that the call to maybe_commonize_var > from initialize_artificial_var did nothing before this change, since > the vtable is DECL_ARTIFICIAL, so it didn't pass the condition at the > top. I suppose we should extend the !DECL_ARTIFICIAL check in > maybe_commonize_var to the inline variable case as well. Done. And then I could move setting DECL_DECLARED_CONSTEXPR_P to initialize_artificial_var, which is where I think it belongs. > >> Ok, unfortunately it wasn't as easy as merely marking it DECL_DECLARED_CONSTEXPR_P > >> in initialize_artificial_var because then I saw "used in its own initializer" > >> error. Which I don't know why, but now that I know you agree with this direction > >> I can dig deeper. > >> > >> > > Since the vtable initializer is > >> > > a compile-time constant, I thought we could make it work by a hack as the one > >> > > in cxx_eval_array_reference. We'll then let cxx_eval_call_expression do its > >> > > job and everything is hunky-dory. > >> > > > >> > > Except when it isn't: I noticed that the presence of _vptr doesn't make the > >> > > class non-empty, and when cxx_eval_constant_expression saw a decl with an empty > >> > > class type, it just evaluated it to { }. But such a class still had gotten an > >> > > initializer that looks like {.D.2082 = {._vptr.X2 = &_ZTV2X2 + 16}}. So > >> > > replacing it with { } will lose the proper initializer whereupon we fail. > >> > > The check I've added to cxx_eval_constant_expression won't win any beauty > >> > > contests but unfortunately EMPTY_CONSTRUCTOR_P doesn't work there. > >> > > >> > Perhaps we should check !TYPE_POLYMORPHIC_P as well as > >> > is_really_empty_class. Perhaps there should be a predicate for that, > >> > say, is_really_nearly_empty_class... > > > > For now I've only added the !TYPE_POLYMORPHIC_P check, which works just fine. > > > > Bootstrapped/regtested on x86_64-linux, ok for trunk? > > > > 2018-09-17 Marek Polacek <polacek@redhat.com> > > > > P1064R0 - Allowing Virtual Function Calls in Constant Expressions > > * call.c (build_over_call): Add FIXME. > > * class.c (initialize_vtable): Mark the vtable as constexpr. > > (build_vtbl_initializer): Build vtable's constructor with indexes. > > * constexpr.c (cxx_eval_constant_expression): Don't ignore _vptr's > > initializer. Handle OBJ_TYPE_REF. > > (potential_constant_expression_1): Handle OBJ_TYPE_REF. > > * decl.c (grokdeclarator): Change error to pedwarn. Only warn when > > pedantic and not C++2a. > > > > * gimple-fold.c (gimple_get_virt_method_for_vtable): Remove assert. > > > > * g++.dg/cpp0x/constexpr-virtual5.C: Adjust dg-error. > > * g++.dg/cpp2a/constexpr-virtual1.C: New test. > > * g++.dg/cpp2a/constexpr-virtual2.C: New test. > > * g++.dg/cpp2a/constexpr-virtual3.C: New test. > > * g++.dg/cpp2a/constexpr-virtual4.C: New test. > > * g++.dg/cpp2a/constexpr-virtual5.C: New test. > > * g++.dg/cpp2a/constexpr-virtual6.C: New test. > > * g++.dg/cpp2a/constexpr-virtual7.C: New test. > > * g++.dg/cpp2a/constexpr-virtual8.C: New test. > > * g++.dg/cpp2a/constexpr-virtual9.C: New test. > > * g++.dg/diagnostic/virtual-constexpr.C: Skip for C++2a. Use > > -pedantic-errors. Adjust dg-error. > > > > diff --git gcc/cp/call.c gcc/cp/call.c > > index 69503ca7920..6c70874af40 100644 > > --- gcc/cp/call.c > > +++ gcc/cp/call.c > > @@ -8401,7 +8401,8 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain) > > > > if (DECL_VINDEX (fn) && (flags & LOOKUP_NONVIRTUAL) == 0 > > /* Don't mess with virtual lookup in instantiate_non_dependent_expr; > > - virtual functions can't be constexpr. */ > > + virtual functions can't be constexpr. FIXME Actually, no longer > > + true in C++2a. */ > > && !in_template_function ()) > > I notice that removing the in_template_function check doesn't break > template/virtual4.C nowadays. Does it break anything else? It doesn't seem to! So, removed. > > + /* The C++ FE now produces indexed fields but we can index the array > > + directly. */ > > if (access_index < CONSTRUCTOR_NELTS (init)) > > { > > fn = CONSTRUCTOR_ELT (init, access_index)->value; > > - gcc_checking_assert (!CONSTRUCTOR_ELT (init, access_index)->index); > > Rather than remove this assert, let's fix it to check that the ->index > matches access_index. Done. Bootstrapped/regtested on ppc64le-linux, additionally tested dg.exp with GXX_TESTSUITE_STDS=98,11,14,17,2a on x86_64-linux, ok for trunk? 2018-09-18 Marek Polacek <polacek@redhat.com> P1064R0 - Allowing Virtual Function Calls in Constant Expressions * call.c (build_over_call): No longer check if we're outside a template function. * class.c (build_vtbl_initializer): Build vtable's constructor with indexes. * constexpr.c (cxx_eval_constant_expression): Don't ignore _vptr's initializer. Handle OBJ_TYPE_REF. (potential_constant_expression_1): Handle OBJ_TYPE_REF. * decl.c (maybe_commonize_var): Bail out for any DECL_ARTIFICIAL. (initialize_artificial_var): Mark the variable as constexpr. (grokdeclarator): Change error to pedwarn. Only warn when pedantic and not C++2a. * gimple-fold.c (gimple_get_virt_method_for_vtable): Adjust assert. * g++.dg/cpp0x/constexpr-virtual5.C: Adjust dg-error. * g++.dg/cpp2a/constexpr-virtual1.C: New test. * g++.dg/cpp2a/constexpr-virtual2.C: New test. * g++.dg/cpp2a/constexpr-virtual3.C: New test. * g++.dg/cpp2a/constexpr-virtual4.C: New test. * g++.dg/cpp2a/constexpr-virtual5.C: New test. * g++.dg/cpp2a/constexpr-virtual6.C: New test. * g++.dg/cpp2a/constexpr-virtual7.C: New test. * g++.dg/cpp2a/constexpr-virtual8.C: New test. * g++.dg/cpp2a/constexpr-virtual9.C: New test. * g++.dg/diagnostic/virtual-constexpr.C: Skip for C++2a. Use -pedantic-errors. Adjust dg-error. diff --git gcc/cp/call.c gcc/cp/call.c index 69503ca7920..ddf0ed044a0 100644 --- gcc/cp/call.c +++ gcc/cp/call.c @@ -8399,10 +8399,7 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain) && DECL_BUILT_IN_CLASS (fn) == BUILT_IN_NORMAL) maybe_warn_class_memaccess (input_location, fn, args); - if (DECL_VINDEX (fn) && (flags & LOOKUP_NONVIRTUAL) == 0 - /* Don't mess with virtual lookup in instantiate_non_dependent_expr; - virtual functions can't be constexpr. */ - && !in_template_function ()) + if (DECL_VINDEX (fn) && (flags & LOOKUP_NONVIRTUAL) == 0) { tree t; tree binfo = lookup_base (TREE_TYPE (TREE_TYPE (argarray[0])), diff --git gcc/cp/class.c gcc/cp/class.c index e950a7423f7..b9cde037337 100644 --- gcc/cp/class.c +++ gcc/cp/class.c @@ -9266,6 +9266,7 @@ build_vtbl_initializer (tree binfo, tree vcall_index; tree fn, fn_original; tree init = NULL_TREE; + tree idx = build_int_cst (size_type_node, jx++); fn = BV_FN (v); fn_original = fn; @@ -9369,7 +9370,7 @@ build_vtbl_initializer (tree binfo, int i; if (init == size_zero_node) for (i = 0; i < TARGET_VTABLE_USES_DESCRIPTORS; ++i) - CONSTRUCTOR_APPEND_ELT (*inits, NULL_TREE, init); + CONSTRUCTOR_APPEND_ELT (*inits, idx, init); else for (i = 0; i < TARGET_VTABLE_USES_DESCRIPTORS; ++i) { @@ -9377,11 +9378,11 @@ build_vtbl_initializer (tree binfo, fn, build_int_cst (NULL_TREE, i)); TREE_CONSTANT (fdesc) = 1; - CONSTRUCTOR_APPEND_ELT (*inits, NULL_TREE, fdesc); + CONSTRUCTOR_APPEND_ELT (*inits, idx, fdesc); } } else - CONSTRUCTOR_APPEND_ELT (*inits, NULL_TREE, init); + CONSTRUCTOR_APPEND_ELT (*inits, idx, init); } } diff --git gcc/cp/constexpr.c gcc/cp/constexpr.c index 88c73787961..aa33319875f 100644 --- gcc/cp/constexpr.c +++ gcc/cp/constexpr.c @@ -4209,7 +4209,11 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, CONST_DECL for aggregate constants. */ if (lval) return t; + /* is_really_empty_class doesn't take into account _vptr, so initializing + otherwise empty class with { } would overwrite the initializer that + initialize_vtable created for us. */ if (COMPLETE_TYPE_P (TREE_TYPE (t)) + && !TYPE_POLYMORPHIC_P (TREE_TYPE (t)) && is_really_empty_class (TREE_TYPE (t))) { /* If the class is empty, we aren't actually loading anything. */ @@ -4778,7 +4782,6 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, case MODOP_EXPR: /* GCC internal stuff. */ case VA_ARG_EXPR: - case OBJ_TYPE_REF: case NON_DEPENDENT_EXPR: case BASELINK: case OFFSET_REF: @@ -4788,6 +4791,34 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, *non_constant_p = true; break; + case OBJ_TYPE_REF: + { + /* Virtual function call. Let the constexpr machinery figure out + the dynamic type. */ + int token = tree_to_shwi (OBJ_TYPE_REF_TOKEN (t)); + tree obj = OBJ_TYPE_REF_OBJECT (t); + obj = cxx_eval_constant_expression (ctx, obj, lval, non_constant_p, + overflow_p); + /* We expect something in the form of &x.D.2103.D.2094; get x. */ + if (TREE_CODE (obj) != ADDR_EXPR) + { + if (!ctx->quiet) + error_at (cp_expr_loc_or_loc (t, input_location), + "expression %qE is not a constant expression", t); + *non_constant_p = true; + return t; + } + obj = TREE_OPERAND (obj, 0); + while (handled_component_p (obj)) + obj = TREE_OPERAND (obj, 0); + tree objtype = TREE_TYPE (obj); + /* Find the function decl in the virtual functions list. TOKEN is + the DECL_VINDEX that says which function we're looking for. */ + tree virtuals = BINFO_VIRTUALS (TYPE_BINFO (objtype)); + r = TREE_VALUE (chain_index (token, virtuals)); + break; + } + case PLACEHOLDER_EXPR: /* Use of the value or address of the current object. */ if (tree ctor = lookup_placeholder (ctx, lval, TREE_TYPE (t))) @@ -5871,7 +5902,6 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now, case OACC_UPDATE: /* GCC internal stuff. */ case VA_ARG_EXPR: - case OBJ_TYPE_REF: case TRANSACTION_EXPR: case ASM_EXPR: case AT_ENCODE_EXPR: @@ -5880,6 +5910,14 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now, error_at (loc, "expression %qE is not a constant expression", t); return false; + case OBJ_TYPE_REF: + if (cxx_dialect >= cxx2a) + /* In C++2a virtual calls can be constexpr, don't give up yet. */ + return true; + else if (flags & tf_error) + error_at (loc, "virtual functions cannot be constexpr before C++2a"); + return false; + case TYPEID_EXPR: /* -- a typeid expression whose operand is of polymorphic class type; */ diff --git gcc/cp/decl.c gcc/cp/decl.c index 50b60e89df5..827c1720335 100644 --- gcc/cp/decl.c +++ gcc/cp/decl.c @@ -5583,11 +5583,13 @@ layout_var_decl (tree decl) void maybe_commonize_var (tree decl) { + /* Don't mess with __FUNCTION__ and similar. */ + if (DECL_ARTIFICIAL (decl)) + return; + /* Static data in a function with comdat linkage also has comdat linkage. */ if ((TREE_STATIC (decl) - /* Don't mess with __FUNCTION__. */ - && ! DECL_ARTIFICIAL (decl) && DECL_FUNCTION_SCOPE_P (decl) && vague_linkage_p (DECL_CONTEXT (decl))) || (TREE_PUBLIC (decl) && DECL_INLINE_VAR_P (decl))) @@ -6774,6 +6776,10 @@ initialize_artificial_var (tree decl, vec<constructor_elt, va_gc> *v) gcc_assert (TREE_CODE (init) == CONSTRUCTOR); DECL_INITIAL (decl) = init; DECL_INITIALIZED_P (decl) = 1; + /* Mark the decl as constexpr so that we can access its content + at compile time. */ + DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (decl) = true; + DECL_DECLARED_CONSTEXPR_P (decl) = true; determine_visibility (decl); layout_var_decl (decl); maybe_commonize_var (decl); @@ -10854,12 +10860,13 @@ grokdeclarator (const cp_declarator *declarator, storage_class = sc_none; staticp = 0; } - if (constexpr_p) + if (constexpr_p && cxx_dialect < cxx2a) { gcc_rich_location richloc (declspecs->locations[ds_virtual]); richloc.add_range (declspecs->locations[ds_constexpr]); - error_at (&richloc, "member %qD cannot be declared both %<virtual%> " - "and %<constexpr%>", dname); + pedwarn (&richloc, OPT_Wpedantic, "member %qD can be declared both " + "%<virtual%> and %<constexpr%> only in -std=c++2a or " + "-std=gnu++2a", dname); } } friendp = decl_spec_seq_has_spec_p (declspecs, ds_friend); diff --git gcc/gimple-fold.c gcc/gimple-fold.c index 362ab59e9c0..1e84722d22d 100644 --- gcc/gimple-fold.c +++ gcc/gimple-fold.c @@ -6988,12 +6988,13 @@ gimple_get_virt_method_for_vtable (HOST_WIDE_INT token, access_index = offset / BITS_PER_UNIT / elt_size; gcc_checking_assert (offset % (elt_size * BITS_PER_UNIT) == 0); - /* This code makes an assumption that there are no - indexed fileds produced by C++ FE, so we can directly index the array. */ + /* The C++ FE can now produce indexed fields, and we check if the indexes + match. */ if (access_index < CONSTRUCTOR_NELTS (init)) { fn = CONSTRUCTOR_ELT (init, access_index)->value; - gcc_checking_assert (!CONSTRUCTOR_ELT (init, access_index)->index); + tree idx = CONSTRUCTOR_ELT (init, access_index)->index; + gcc_checking_assert (!idx || tree_to_uhwi (idx) == access_index); STRIP_NOPS (fn); } else diff --git gcc/testsuite/g++.dg/cpp0x/constexpr-virtual5.C gcc/testsuite/g++.dg/cpp0x/constexpr-virtual5.C index 2465f9d9b4f..5f9ab4d9c28 100644 --- gcc/testsuite/g++.dg/cpp0x/constexpr-virtual5.C +++ gcc/testsuite/g++.dg/cpp0x/constexpr-virtual5.C @@ -2,5 +2,5 @@ // { dg-do compile { target c++11 } } struct S { - constexpr virtual int f() { return 1; } // { dg-error "13:member .f. cannot be declared both .virtual. and .constexpr." } + constexpr virtual int f() { return 1; } // { dg-error "13:member .f. can be declared both .virtual. and .constexpr." "" { target c++17_down } } }; diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-virtual1.C gcc/testsuite/g++.dg/cpp2a/constexpr-virtual1.C index e69de29bb2d..fcf8cac6417 100644 --- gcc/testsuite/g++.dg/cpp2a/constexpr-virtual1.C +++ gcc/testsuite/g++.dg/cpp2a/constexpr-virtual1.C @@ -0,0 +1,8 @@ +// P1064R0 +// { dg-do compile { target c++11 } } +// { dg-options "-pedantic-errors" } + +struct X +{ + constexpr virtual int f() { return 0; } // { dg-error "member .f. can be declared both .virtual. and .constexpr. only" "" { target c++17_down } } +}; diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C index e69de29bb2d..9d82c5c59ac 100644 --- gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C +++ gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C @@ -0,0 +1,49 @@ +// P1064R0 +// { dg-do compile } +// { dg-options "-std=c++2a" } + +struct X1 +{ + virtual int f() const = 0; +}; + +struct X2: public X1 +{ + constexpr virtual int f() const { return 2; } +}; + +struct X3: public X2 +{ + virtual int f() const { return 3; } +}; + +struct X4: public X3 +{ + constexpr virtual int f() const { return 4; } +}; + +constexpr int (X1::*pf)() const = &X1::f; + +constexpr X2 x2; +static_assert(x2.f() == 2); +static_assert((x2.*pf)() == 2); + +constexpr X1 const& r2 = x2; +static_assert(r2.f() == 2); +static_assert((r2.*pf)() == 2); + +constexpr X1 const* p2 = &x2; +static_assert(p2->f() == 2); +static_assert((p2->*pf)() == 2); + +constexpr X4 x4; +static_assert(x4.f() == 4); +static_assert((x4.*pf)() == 4); + +constexpr X1 const& r4 = x4; +static_assert(r4.f() == 4); +static_assert((r4.*pf)() == 4); + +constexpr X1 const* p4 = &x4; +static_assert(p4->f() == 4); +static_assert((p4->*pf)() == 4); diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-virtual3.C gcc/testsuite/g++.dg/cpp2a/constexpr-virtual3.C index e69de29bb2d..d71422fc4d0 100644 --- gcc/testsuite/g++.dg/cpp2a/constexpr-virtual3.C +++ gcc/testsuite/g++.dg/cpp2a/constexpr-virtual3.C @@ -0,0 +1,52 @@ +// P1064R0 +// { dg-do compile } +// { dg-options "-std=c++2a" } + +struct X1 +{ + virtual int f() const = 0; +}; + +struct X2: public X1 +{ + int i2 = 42; + constexpr virtual int f() const { return 2; } +}; + +struct X3: public X2 +{ + int i3 = 42; + virtual int f() const { return 3; } +}; + +struct X4: public X3 +{ + int i4 = 42; + constexpr virtual int f() const { return 4; } +}; + +constexpr int (X1::*pf)() const = &X1::f; + +constexpr X2 x2; +static_assert(x2.f() == 2); +static_assert((x2.*pf)() == 2); + +constexpr X1 const& r2 = x2; +static_assert(r2.f() == 2); +static_assert((r2.*pf)() == 2); + +constexpr X1 const* p2 = &x2; +static_assert(p2->f() == 2); +static_assert((p2->*pf)() == 2); + +constexpr X4 x4; +static_assert(x4.f() == 4); +static_assert((x4.*pf)() == 4); + +constexpr X1 const& r4 = x4; +static_assert(r4.f() == 4); +static_assert((r4.*pf)() == 4); + +constexpr X1 const* p4 = &x4; +static_assert(p4->f() == 4); +static_assert((p4->*pf)() == 4); diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-virtual4.C gcc/testsuite/g++.dg/cpp2a/constexpr-virtual4.C index e69de29bb2d..2038bebc6d1 100644 --- gcc/testsuite/g++.dg/cpp2a/constexpr-virtual4.C +++ gcc/testsuite/g++.dg/cpp2a/constexpr-virtual4.C @@ -0,0 +1,57 @@ +// P1064R0 +// { dg-do compile } +// { dg-options "-std=c++2a" } + +struct X1 +{ + virtual int f1() const = 0; + virtual int f2() const = 0; + virtual int f3() const = 0; +}; + +struct X2: public X1 +{ + constexpr virtual int f1() const { return 21; } + constexpr virtual int f2() const { return 22; } + constexpr virtual int f3() const { return 23; } +}; + +struct X3: public X2 +{ + virtual int f1() const { return 31; } + virtual int f2() const { return 32; } + virtual int f3() const { return 33; } +}; + +struct X4: public X3 +{ + constexpr virtual int f1() const { return 41; } + constexpr virtual int f2() const { return 42; } + constexpr virtual int f3() const { return 43; } +}; + +constexpr int (X1::*pf)() const = &X1::f2; + +constexpr X2 x2; +static_assert(x2.f2() == 22); +static_assert((x2.*pf)() == 22); + +constexpr X1 const& r2 = x2; +static_assert(r2.f2() == 22); +static_assert((r2.*pf)() == 22); + +constexpr X1 const* p2 = &x2; +static_assert(p2->f2() == 22); +static_assert((p2->*pf)() == 22); + +constexpr X4 x4; +static_assert(x4.f2() == 42); +static_assert((x4.*pf)() == 42); + +constexpr X1 const& r4 = x4; +static_assert(r4.f2() == 42); +static_assert((r4.*pf)() == 42); + +constexpr X1 const* p4 = &x4; +static_assert(p4->f2() == 42); +static_assert((p4->*pf)() == 42); diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-virtual5.C gcc/testsuite/g++.dg/cpp2a/constexpr-virtual5.C index e69de29bb2d..6d27990a8b6 100644 --- gcc/testsuite/g++.dg/cpp2a/constexpr-virtual5.C +++ gcc/testsuite/g++.dg/cpp2a/constexpr-virtual5.C @@ -0,0 +1,60 @@ +// P1064R0 +// { dg-do compile } +// { dg-options "-std=c++2a" } + +struct X1 +{ + virtual int f1() const = 0; + virtual int f2() const = 0; + virtual int f3() const = 0; +}; + +struct X2: public X1 +{ + int i2 = 42; + constexpr virtual int f1() const { return 21; } + constexpr virtual int f2() const { return 22; } + constexpr virtual int f3() const { return 23; } +}; + +struct X3: public X2 +{ + int i3 = 42; + virtual int f1() const { return 31; } + virtual int f2() const { return 32; } + virtual int f3() const { return 33; } +}; + +struct X4: public X3 +{ + int i4 = 42; + constexpr virtual int f1() const { return 41; } + constexpr virtual int f2() const { return 42; } + constexpr virtual int f3() const { return 43; } +}; + +constexpr int (X1::*pf)() const = &X1::f2; + +constexpr X2 x2; +static_assert(x2.f2() == 22); +static_assert((x2.*pf)() == 22); + +constexpr X1 const& r2 = x2; +static_assert(r2.f2() == 22); +static_assert((r2.*pf)() == 22); + +constexpr X1 const* p2 = &x2; +static_assert(p2->f2() == 22); +static_assert((p2->*pf)() == 22); + +constexpr X4 x4; +static_assert(x4.f2() == 42); +static_assert((x4.*pf)() == 42); + +constexpr X1 const& r4 = x4; +static_assert(r4.f2() == 42); +static_assert((r4.*pf)() == 42); + +constexpr X1 const* p4 = &x4; +static_assert(p4->f2() == 42); +static_assert((p4->*pf)() == 42); diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-virtual6.C gcc/testsuite/g++.dg/cpp2a/constexpr-virtual6.C index e69de29bb2d..ece5e703c32 100644 --- gcc/testsuite/g++.dg/cpp2a/constexpr-virtual6.C +++ gcc/testsuite/g++.dg/cpp2a/constexpr-virtual6.C @@ -0,0 +1,25 @@ +// P1064R0 +// { dg-do compile } +// { dg-options "-std=c++2a" } + +struct X1 +{ + constexpr virtual X1 const *f() const { return this; } +}; + +struct Y +{ + int m = 0; +}; + +struct X2: public Y, public X1 +{ + constexpr virtual X2 const *f() const { return this; } +}; + +constexpr X1 x1; +static_assert(x1.f() == &x1); + +constexpr X2 x2; +constexpr X1 const& r2 = x2; +static_assert(r2.f() == &r2); diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-virtual7.C gcc/testsuite/g++.dg/cpp2a/constexpr-virtual7.C index e69de29bb2d..b0f499608ef 100644 --- gcc/testsuite/g++.dg/cpp2a/constexpr-virtual7.C +++ gcc/testsuite/g++.dg/cpp2a/constexpr-virtual7.C @@ -0,0 +1,87 @@ +// P1064R0 +// { dg-do compile } +// { dg-options "-std=c++2a" } + +struct X1 +{ + virtual int f() const = 0; +}; + +struct X2: public X1 +{ + constexpr virtual int f() const { return 2; } +}; + +struct X3: public X2 +{ + virtual int f() const { return 3; } +}; + +struct X4: public X3 +{ + constexpr virtual int f() const { return 4; } +}; + +constexpr int (X1::*pf)() const = &X1::f; + +constexpr X2 x2; + +struct S +{ + int i, j; + constexpr S() : i(x2.f()), j((x2.*pf)()) { } +}; + +static_assert(S().i == 2); +static_assert(S().j == 2); + +constexpr X1 const& r2 = x2; + +struct S2 +{ + int i, j; + constexpr S2() : i(r2.f()), j((r2.*pf)()) { } +}; + +static_assert(S2().i == 2); +static_assert(S2().j == 2); + +constexpr X1 const* p2 = &x2; +struct S3 +{ + int i, j; + constexpr S3() : i(p2->f()), j((p2->*pf)()) { } +}; + +static_assert(S3().i == 2); +static_assert(S3().j == 2); + +constexpr X4 x4; +struct S4 +{ + int i, j; + constexpr S4() : i(x4.f()), j((x4.*pf)()) { } +}; + +static_assert(S4().i == 4); +static_assert(S4().j == 4); + +constexpr X1 const& r4 = x4; +struct S5 +{ + int i, j; + constexpr S5() : i(r4.f()), j((r4.*pf)()) { } +}; + +static_assert(S5().i == 4); +static_assert(S5().j == 4); + +constexpr X1 const* p4 = &x4; +struct S6 +{ + int i, j; + constexpr S6() : i(p4->f()), j((p4->*pf)()) { } +}; + +static_assert(S6().i == 4); +static_assert(S6().j == 4); diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-virtual8.C gcc/testsuite/g++.dg/cpp2a/constexpr-virtual8.C index e69de29bb2d..4a7cc972a91 100644 --- gcc/testsuite/g++.dg/cpp2a/constexpr-virtual8.C +++ gcc/testsuite/g++.dg/cpp2a/constexpr-virtual8.C @@ -0,0 +1,50 @@ +// P1064R0 +// { dg-do compile } +// { dg-options "-std=c++2a" } + +template<typename T> +struct X1 +{ + virtual T f() const = 0; +}; + +struct X2: public X1<int> +{ + constexpr virtual int f() const { return 2; } +}; + +struct X3: public X2 +{ + virtual int f() const { return 3; } +}; + +struct X4: public X3 +{ + constexpr virtual int f() const { return 4; } +}; + +constexpr int (X1<int>::*pf)() const = &X1<int>::f; + +constexpr X2 x2; +static_assert(x2.f() == 2); +static_assert((x2.*pf)() == 2); + +constexpr X1<int> const& r2 = x2; +static_assert(r2.f() == 2); +static_assert((r2.*pf)() == 2); + +constexpr X1<int> const* p2 = &x2; +static_assert(p2->f() == 2); +static_assert((p2->*pf)() == 2); + +constexpr X4 x4; +static_assert(x4.f() == 4); +static_assert((x4.*pf)() == 4); + +constexpr X1<int> const& r4 = x4; +static_assert(r4.f() == 4); +static_assert((r4.*pf)() == 4); + +constexpr X1<int> const* p4 = &x4; +static_assert(p4->f() == 4); +static_assert((p4->*pf)() == 4); diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-virtual9.C gcc/testsuite/g++.dg/cpp2a/constexpr-virtual9.C index e69de29bb2d..3a12adc2659 100644 --- gcc/testsuite/g++.dg/cpp2a/constexpr-virtual9.C +++ gcc/testsuite/g++.dg/cpp2a/constexpr-virtual9.C @@ -0,0 +1,83 @@ +// P1064R0 +// { dg-do compile } +// { dg-options "-std=c++2a" } + +struct X1 +{ + virtual int f() const = 0; + virtual int f(int) const = 0; + virtual int f(int, int) const = 0; +}; + +struct X2: public X1 +{ + constexpr virtual int f() const { return 2; } + constexpr virtual int f(int) const { return 12; } + constexpr virtual int f(int, int) const { return 22; } +}; + +struct X3: public X2 +{ + virtual int f() const { return 3; } + virtual int f(int) const { return 13; } + virtual int f(int, int) const { return 23; } +}; + +struct X4: public X3 +{ + constexpr virtual int f() const { return 4; } + constexpr virtual int f(int) const { return 14; } + constexpr virtual int f(int, int) const { return 24; } +}; + +constexpr int (X1::*pf)() const = &X1::f; +constexpr int (X1::*pf1)(int) const = &X1::f; +constexpr int (X1::*pf2)(int, int) const = &X1::f; + +constexpr X2 x2; +static_assert(x2.f() == 2); +static_assert((x2.*pf)() == 2); +static_assert(x2.f(1) == 12); +static_assert((x2.*pf1)(1) == 12); +static_assert(x2.f(1, 2) == 22); +static_assert((x2.*pf2)(1, 2) == 22); + +constexpr X1 const& r2 = x2; +static_assert(r2.f() == 2); +static_assert((r2.*pf)() == 2); +static_assert(r2.f(1) == 12); +static_assert((r2.*pf1)(1) == 12); +static_assert(r2.f(1, 2) == 22); +static_assert((r2.*pf2)(1, 2) == 22); + +constexpr X1 const* p2 = &x2; +static_assert(p2->f() == 2); +static_assert((p2->*pf)() == 2); +static_assert(p2->f(1) == 12); +static_assert((p2->*pf1)(1) == 12); +static_assert(p2->f(1, 2) == 22); +static_assert((p2->*pf2)(1, 2) == 22); + +constexpr X4 x4; +static_assert(x4.f() == 4); +static_assert((x4.*pf)() == 4); +static_assert(x4.f(1) == 14); +static_assert((x4.*pf1)(1) == 14); +static_assert(x4.f(1, 2) == 24); +static_assert((x4.*pf2)(1, 2) == 24); + +constexpr X1 const& r4 = x4; +static_assert(r4.f() == 4); +static_assert((r4.*pf)() == 4); +static_assert(r4.f(1) == 14); +static_assert((r4.*pf1)(1) == 14); +static_assert(r4.f(1, 2) == 24); +static_assert((r4.*pf2)(1, 2) == 24); + +constexpr X1 const* p4 = &x4; +static_assert(p4->f() == 4); +static_assert((p4->*pf)() == 4); +static_assert(p4->f(1) == 14); +static_assert((p4->*pf1)(1) == 14); +static_assert(p4->f(1, 2) == 24); +static_assert((p4->*pf2)(1, 2) == 24); diff --git gcc/testsuite/g++.dg/diagnostic/virtual-constexpr.C gcc/testsuite/g++.dg/diagnostic/virtual-constexpr.C index 2c83236cae9..9223c692737 100644 --- gcc/testsuite/g++.dg/diagnostic/virtual-constexpr.C +++ gcc/testsuite/g++.dg/diagnostic/virtual-constexpr.C @@ -1,14 +1,15 @@ -// { dg-options "-fdiagnostics-show-caret" } +// { dg-options "-fdiagnostics-show-caret -pedantic-errors" } // { dg-do compile { target c++11 } } +// { dg-skip-if "virtual constexpr" { *-*-* } { "-std=gnu++2a" } { "" } } struct S { - virtual constexpr void foo(); // { dg-error "3:member .foo. cannot be declared both .virtual. and .constexpr." } + virtual constexpr void foo(); // { dg-error "3:member .foo. can be declared both .virtual. and .constexpr." } /* { dg-begin-multiline-output "" } virtual constexpr void foo(); ^~~~~~~ ~~~~~~~~~ { dg-end-multiline-output "" } */ - constexpr virtual void bar(); // { dg-error "13:member .bar. cannot be declared both .virtual. and .constexpr." } + constexpr virtual void bar(); // { dg-error "13:member .bar. can be declared both .virtual. and .constexpr." } /* { dg-begin-multiline-output "" } constexpr virtual void bar(); ~~~~~~~~~ ^~~~~~~
On Tue, Sep 18, 2018 at 11:25 AM, Marek Polacek <polacek@redhat.com> wrote: > On Mon, Sep 17, 2018 at 11:28:06PM -0400, Jason Merrill wrote: >> On Mon, Sep 17, 2018 at 5:39 PM, Marek Polacek <polacek@redhat.com> wrote: >> > On Fri, Sep 14, 2018 at 04:45:22PM -0400, Marek Polacek wrote: >> >> On Fri, Sep 14, 2018 at 04:30:46PM -0400, Jason Merrill wrote: >> >> > On Fri, Sep 14, 2018 at 1:19 PM, Marek Polacek <polacek@redhat.com> wrote: >> >> > > This patch implements another bit of C++20, virtual calls in constant >> >> > > expression: >> >> > > <http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1064r0.html> >> >> > > The basic idea is that since in a constant expression we know the dynamic >> >> > > type (to detect invalid code etc.), the restriction that prohibits virtual >> >> > > calls is unnecessary. >> >> > > >> >> > > Handling virtual function calls turned out to be fairly easy (as anticipated); >> >> > > I simply let the constexpr machinery figure out the dynamic type and then >> >> > > OBJ_TYPE_REF_TOKEN gives us the index into the virtual function table. That >> >> > > way we get the function decl we're interested in, and cxx_eval_call_expression >> >> > > takes it from there. >> >> > > >> >> > > But handling pointer-to-virtual-member-functions doesn't work like that. >> >> > > get_member_function_from_ptrfunc creates a COND_EXPR which looks like >> >> > > if (pf.__pfn & 1) // is it a virtual function? >> >> > > // yes, find the pointer in the vtable >> >> > > else >> >> > > // no, just return the pointer >> >> > > so ideally we want to evaluate the then-branch. Eventually it'll evaluate it >> >> > > to something like _ZTV2X2[2], but the vtable isn't constexpr so we'd end up >> >> > > with "not a constant expression" error. >> >> > >> >> > Then let's mark the vtable as constexpr, there's no reason for it not to be. >> > >> > Done. But then I had to add indexes to the vtable's ctor (because find_array_ctor_elt >> > expects it), which broke an assert in gimple_get_virt_method_for_vtable. But I >> > don't need the array_ref hack anymore! >> >> > Also, I had to set DECL_DECLARED_CONSTEXPR_P after maybe_commonize_var, >> > otherwise we run into the sorry in that function with -fno-weak... >> >> Hmm, we shouldn't give that sorry for DECL_ARTIFICIAL variables. >> >> Looking more closely, it seems that the call to maybe_commonize_var >> from initialize_artificial_var did nothing before this change, since >> the vtable is DECL_ARTIFICIAL, so it didn't pass the condition at the >> top. I suppose we should extend the !DECL_ARTIFICIAL check in >> maybe_commonize_var to the inline variable case as well. > > Done. And then I could move setting DECL_DECLARED_CONSTEXPR_P to > initialize_artificial_var, which is where I think it belongs. > >> >> Ok, unfortunately it wasn't as easy as merely marking it DECL_DECLARED_CONSTEXPR_P >> >> in initialize_artificial_var because then I saw "used in its own initializer" >> >> error. Which I don't know why, but now that I know you agree with this direction >> >> I can dig deeper. >> >> >> >> > > Since the vtable initializer is >> >> > > a compile-time constant, I thought we could make it work by a hack as the one >> >> > > in cxx_eval_array_reference. We'll then let cxx_eval_call_expression do its >> >> > > job and everything is hunky-dory. >> >> > > >> >> > > Except when it isn't: I noticed that the presence of _vptr doesn't make the >> >> > > class non-empty, and when cxx_eval_constant_expression saw a decl with an empty >> >> > > class type, it just evaluated it to { }. But such a class still had gotten an >> >> > > initializer that looks like {.D.2082 = {._vptr.X2 = &_ZTV2X2 + 16}}. So >> >> > > replacing it with { } will lose the proper initializer whereupon we fail. >> >> > > The check I've added to cxx_eval_constant_expression won't win any beauty >> >> > > contests but unfortunately EMPTY_CONSTRUCTOR_P doesn't work there. >> >> > >> >> > Perhaps we should check !TYPE_POLYMORPHIC_P as well as >> >> > is_really_empty_class. Perhaps there should be a predicate for that, >> >> > say, is_really_nearly_empty_class... >> > >> > For now I've only added the !TYPE_POLYMORPHIC_P check, which works just fine. >> > >> > Bootstrapped/regtested on x86_64-linux, ok for trunk? >> > >> > 2018-09-17 Marek Polacek <polacek@redhat.com> >> > >> > P1064R0 - Allowing Virtual Function Calls in Constant Expressions >> > * call.c (build_over_call): Add FIXME. >> > * class.c (initialize_vtable): Mark the vtable as constexpr. >> > (build_vtbl_initializer): Build vtable's constructor with indexes. >> > * constexpr.c (cxx_eval_constant_expression): Don't ignore _vptr's >> > initializer. Handle OBJ_TYPE_REF. >> > (potential_constant_expression_1): Handle OBJ_TYPE_REF. >> > * decl.c (grokdeclarator): Change error to pedwarn. Only warn when >> > pedantic and not C++2a. >> > >> > * gimple-fold.c (gimple_get_virt_method_for_vtable): Remove assert. >> > >> > * g++.dg/cpp0x/constexpr-virtual5.C: Adjust dg-error. >> > * g++.dg/cpp2a/constexpr-virtual1.C: New test. >> > * g++.dg/cpp2a/constexpr-virtual2.C: New test. >> > * g++.dg/cpp2a/constexpr-virtual3.C: New test. >> > * g++.dg/cpp2a/constexpr-virtual4.C: New test. >> > * g++.dg/cpp2a/constexpr-virtual5.C: New test. >> > * g++.dg/cpp2a/constexpr-virtual6.C: New test. >> > * g++.dg/cpp2a/constexpr-virtual7.C: New test. >> > * g++.dg/cpp2a/constexpr-virtual8.C: New test. >> > * g++.dg/cpp2a/constexpr-virtual9.C: New test. >> > * g++.dg/diagnostic/virtual-constexpr.C: Skip for C++2a. Use >> > -pedantic-errors. Adjust dg-error. >> > >> > diff --git gcc/cp/call.c gcc/cp/call.c >> > index 69503ca7920..6c70874af40 100644 >> > --- gcc/cp/call.c >> > +++ gcc/cp/call.c >> > @@ -8401,7 +8401,8 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain) >> > >> > if (DECL_VINDEX (fn) && (flags & LOOKUP_NONVIRTUAL) == 0 >> > /* Don't mess with virtual lookup in instantiate_non_dependent_expr; >> > - virtual functions can't be constexpr. */ >> > + virtual functions can't be constexpr. FIXME Actually, no longer >> > + true in C++2a. */ >> > && !in_template_function ()) >> >> I notice that removing the in_template_function check doesn't break >> template/virtual4.C nowadays. Does it break anything else? > > It doesn't seem to! So, removed. > >> > + /* The C++ FE now produces indexed fields but we can index the array >> > + directly. */ >> > if (access_index < CONSTRUCTOR_NELTS (init)) >> > { >> > fn = CONSTRUCTOR_ELT (init, access_index)->value; >> > - gcc_checking_assert (!CONSTRUCTOR_ELT (init, access_index)->index); >> >> Rather than remove this assert, let's fix it to check that the ->index >> matches access_index. > > Done. > > Bootstrapped/regtested on ppc64le-linux, additionally tested dg.exp with > GXX_TESTSUITE_STDS=98,11,14,17,2a on x86_64-linux, ok for trunk? > > 2018-09-18 Marek Polacek <polacek@redhat.com> > > P1064R0 - Allowing Virtual Function Calls in Constant Expressions > * call.c (build_over_call): No longer check if we're outside a template > function. > * class.c (build_vtbl_initializer): Build vtable's constructor with > indexes. > * constexpr.c (cxx_eval_constant_expression): Don't ignore _vptr's > initializer. Handle OBJ_TYPE_REF. > (potential_constant_expression_1): Handle OBJ_TYPE_REF. > * decl.c (maybe_commonize_var): Bail out for any DECL_ARTIFICIAL. > (initialize_artificial_var): Mark the variable as constexpr. > (grokdeclarator): Change error to pedwarn. Only warn when > pedantic and not C++2a. > > * gimple-fold.c (gimple_get_virt_method_for_vtable): Adjust assert. > > * g++.dg/cpp0x/constexpr-virtual5.C: Adjust dg-error. > * g++.dg/cpp2a/constexpr-virtual1.C: New test. > * g++.dg/cpp2a/constexpr-virtual2.C: New test. > * g++.dg/cpp2a/constexpr-virtual3.C: New test. > * g++.dg/cpp2a/constexpr-virtual4.C: New test. > * g++.dg/cpp2a/constexpr-virtual5.C: New test. > * g++.dg/cpp2a/constexpr-virtual6.C: New test. > * g++.dg/cpp2a/constexpr-virtual7.C: New test. > * g++.dg/cpp2a/constexpr-virtual8.C: New test. > * g++.dg/cpp2a/constexpr-virtual9.C: New test. > * g++.dg/diagnostic/virtual-constexpr.C: Skip for C++2a. Use > -pedantic-errors. Adjust dg-error. > > diff --git gcc/cp/call.c gcc/cp/call.c > index 69503ca7920..ddf0ed044a0 100644 > --- gcc/cp/call.c > +++ gcc/cp/call.c > @@ -8399,10 +8399,7 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain) > && DECL_BUILT_IN_CLASS (fn) == BUILT_IN_NORMAL) > maybe_warn_class_memaccess (input_location, fn, args); > > - if (DECL_VINDEX (fn) && (flags & LOOKUP_NONVIRTUAL) == 0 > - /* Don't mess with virtual lookup in instantiate_non_dependent_expr; > - virtual functions can't be constexpr. */ > - && !in_template_function ()) > + if (DECL_VINDEX (fn) && (flags & LOOKUP_NONVIRTUAL) == 0) > { > tree t; > tree binfo = lookup_base (TREE_TYPE (TREE_TYPE (argarray[0])), > diff --git gcc/cp/class.c gcc/cp/class.c > index e950a7423f7..b9cde037337 100644 > --- gcc/cp/class.c > +++ gcc/cp/class.c > @@ -9266,6 +9266,7 @@ build_vtbl_initializer (tree binfo, > tree vcall_index; > tree fn, fn_original; > tree init = NULL_TREE; > + tree idx = build_int_cst (size_type_node, jx++); Let's use size_int here, like in process_init_constructor_array. OK with that change. Jason
On Tue, Sep 18, 2018 at 02:29:16PM -0400, Jason Merrill wrote: > > --- gcc/cp/class.c > > +++ gcc/cp/class.c > > @@ -9266,6 +9266,7 @@ build_vtbl_initializer (tree binfo, > > tree vcall_index; > > tree fn, fn_original; > > tree init = NULL_TREE; > > + tree idx = build_int_cst (size_type_node, jx++); > > Let's use size_int here, like in process_init_constructor_array. Done. > OK with that change. Thanks for quick reviews! Next one: explicit(bool). Applying to trunk. 2018-09-18 Marek Polacek <polacek@redhat.com> P1064R0 - Allowing Virtual Function Calls in Constant Expressions * call.c (build_over_call): No longer check if we're outside a template function. * class.c (build_vtbl_initializer): Build vtable's constructor with indexes. * constexpr.c (cxx_eval_constant_expression): Don't ignore _vptr's initializer. Handle OBJ_TYPE_REF. (potential_constant_expression_1): Handle OBJ_TYPE_REF. * decl.c (maybe_commonize_var): Bail out for any DECL_ARTIFICIAL. (initialize_artificial_var): Mark the variable as constexpr. (grokdeclarator): Change error to pedwarn. Only warn when pedantic and not C++2a. * gimple-fold.c (gimple_get_virt_method_for_vtable): Adjust assert. * g++.dg/cpp0x/constexpr-virtual5.C: Adjust dg-error. * g++.dg/cpp2a/constexpr-virtual1.C: New test. * g++.dg/cpp2a/constexpr-virtual2.C: New test. * g++.dg/cpp2a/constexpr-virtual3.C: New test. * g++.dg/cpp2a/constexpr-virtual4.C: New test. * g++.dg/cpp2a/constexpr-virtual5.C: New test. * g++.dg/cpp2a/constexpr-virtual6.C: New test. * g++.dg/cpp2a/constexpr-virtual7.C: New test. * g++.dg/cpp2a/constexpr-virtual8.C: New test. * g++.dg/cpp2a/constexpr-virtual9.C: New test. * g++.dg/diagnostic/virtual-constexpr.C: Skip for C++2a. Use -pedantic-errors. Adjust dg-error. diff --git gcc/cp/call.c gcc/cp/call.c index 69503ca7920..ddf0ed044a0 100644 --- gcc/cp/call.c +++ gcc/cp/call.c @@ -8399,10 +8399,7 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain) && DECL_BUILT_IN_CLASS (fn) == BUILT_IN_NORMAL) maybe_warn_class_memaccess (input_location, fn, args); - if (DECL_VINDEX (fn) && (flags & LOOKUP_NONVIRTUAL) == 0 - /* Don't mess with virtual lookup in instantiate_non_dependent_expr; - virtual functions can't be constexpr. */ - && !in_template_function ()) + if (DECL_VINDEX (fn) && (flags & LOOKUP_NONVIRTUAL) == 0) { tree t; tree binfo = lookup_base (TREE_TYPE (TREE_TYPE (argarray[0])), diff --git gcc/cp/class.c gcc/cp/class.c index e950a7423f7..9ca46441871 100644 --- gcc/cp/class.c +++ gcc/cp/class.c @@ -9266,6 +9266,7 @@ build_vtbl_initializer (tree binfo, tree vcall_index; tree fn, fn_original; tree init = NULL_TREE; + tree idx = size_int (jx++); fn = BV_FN (v); fn_original = fn; @@ -9369,7 +9370,7 @@ build_vtbl_initializer (tree binfo, int i; if (init == size_zero_node) for (i = 0; i < TARGET_VTABLE_USES_DESCRIPTORS; ++i) - CONSTRUCTOR_APPEND_ELT (*inits, NULL_TREE, init); + CONSTRUCTOR_APPEND_ELT (*inits, idx, init); else for (i = 0; i < TARGET_VTABLE_USES_DESCRIPTORS; ++i) { @@ -9377,11 +9378,11 @@ build_vtbl_initializer (tree binfo, fn, build_int_cst (NULL_TREE, i)); TREE_CONSTANT (fdesc) = 1; - CONSTRUCTOR_APPEND_ELT (*inits, NULL_TREE, fdesc); + CONSTRUCTOR_APPEND_ELT (*inits, idx, fdesc); } } else - CONSTRUCTOR_APPEND_ELT (*inits, NULL_TREE, init); + CONSTRUCTOR_APPEND_ELT (*inits, idx, init); } } diff --git gcc/cp/constexpr.c gcc/cp/constexpr.c index 88c73787961..aa33319875f 100644 --- gcc/cp/constexpr.c +++ gcc/cp/constexpr.c @@ -4209,7 +4209,11 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, CONST_DECL for aggregate constants. */ if (lval) return t; + /* is_really_empty_class doesn't take into account _vptr, so initializing + otherwise empty class with { } would overwrite the initializer that + initialize_vtable created for us. */ if (COMPLETE_TYPE_P (TREE_TYPE (t)) + && !TYPE_POLYMORPHIC_P (TREE_TYPE (t)) && is_really_empty_class (TREE_TYPE (t))) { /* If the class is empty, we aren't actually loading anything. */ @@ -4778,7 +4782,6 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, case MODOP_EXPR: /* GCC internal stuff. */ case VA_ARG_EXPR: - case OBJ_TYPE_REF: case NON_DEPENDENT_EXPR: case BASELINK: case OFFSET_REF: @@ -4788,6 +4791,34 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, *non_constant_p = true; break; + case OBJ_TYPE_REF: + { + /* Virtual function call. Let the constexpr machinery figure out + the dynamic type. */ + int token = tree_to_shwi (OBJ_TYPE_REF_TOKEN (t)); + tree obj = OBJ_TYPE_REF_OBJECT (t); + obj = cxx_eval_constant_expression (ctx, obj, lval, non_constant_p, + overflow_p); + /* We expect something in the form of &x.D.2103.D.2094; get x. */ + if (TREE_CODE (obj) != ADDR_EXPR) + { + if (!ctx->quiet) + error_at (cp_expr_loc_or_loc (t, input_location), + "expression %qE is not a constant expression", t); + *non_constant_p = true; + return t; + } + obj = TREE_OPERAND (obj, 0); + while (handled_component_p (obj)) + obj = TREE_OPERAND (obj, 0); + tree objtype = TREE_TYPE (obj); + /* Find the function decl in the virtual functions list. TOKEN is + the DECL_VINDEX that says which function we're looking for. */ + tree virtuals = BINFO_VIRTUALS (TYPE_BINFO (objtype)); + r = TREE_VALUE (chain_index (token, virtuals)); + break; + } + case PLACEHOLDER_EXPR: /* Use of the value or address of the current object. */ if (tree ctor = lookup_placeholder (ctx, lval, TREE_TYPE (t))) @@ -5871,7 +5902,6 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now, case OACC_UPDATE: /* GCC internal stuff. */ case VA_ARG_EXPR: - case OBJ_TYPE_REF: case TRANSACTION_EXPR: case ASM_EXPR: case AT_ENCODE_EXPR: @@ -5880,6 +5910,14 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now, error_at (loc, "expression %qE is not a constant expression", t); return false; + case OBJ_TYPE_REF: + if (cxx_dialect >= cxx2a) + /* In C++2a virtual calls can be constexpr, don't give up yet. */ + return true; + else if (flags & tf_error) + error_at (loc, "virtual functions cannot be constexpr before C++2a"); + return false; + case TYPEID_EXPR: /* -- a typeid expression whose operand is of polymorphic class type; */ diff --git gcc/cp/decl.c gcc/cp/decl.c index 50b60e89df5..827c1720335 100644 --- gcc/cp/decl.c +++ gcc/cp/decl.c @@ -5583,11 +5583,13 @@ layout_var_decl (tree decl) void maybe_commonize_var (tree decl) { + /* Don't mess with __FUNCTION__ and similar. */ + if (DECL_ARTIFICIAL (decl)) + return; + /* Static data in a function with comdat linkage also has comdat linkage. */ if ((TREE_STATIC (decl) - /* Don't mess with __FUNCTION__. */ - && ! DECL_ARTIFICIAL (decl) && DECL_FUNCTION_SCOPE_P (decl) && vague_linkage_p (DECL_CONTEXT (decl))) || (TREE_PUBLIC (decl) && DECL_INLINE_VAR_P (decl))) @@ -6774,6 +6776,10 @@ initialize_artificial_var (tree decl, vec<constructor_elt, va_gc> *v) gcc_assert (TREE_CODE (init) == CONSTRUCTOR); DECL_INITIAL (decl) = init; DECL_INITIALIZED_P (decl) = 1; + /* Mark the decl as constexpr so that we can access its content + at compile time. */ + DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (decl) = true; + DECL_DECLARED_CONSTEXPR_P (decl) = true; determine_visibility (decl); layout_var_decl (decl); maybe_commonize_var (decl); @@ -10854,12 +10860,13 @@ grokdeclarator (const cp_declarator *declarator, storage_class = sc_none; staticp = 0; } - if (constexpr_p) + if (constexpr_p && cxx_dialect < cxx2a) { gcc_rich_location richloc (declspecs->locations[ds_virtual]); richloc.add_range (declspecs->locations[ds_constexpr]); - error_at (&richloc, "member %qD cannot be declared both %<virtual%> " - "and %<constexpr%>", dname); + pedwarn (&richloc, OPT_Wpedantic, "member %qD can be declared both " + "%<virtual%> and %<constexpr%> only in -std=c++2a or " + "-std=gnu++2a", dname); } } friendp = decl_spec_seq_has_spec_p (declspecs, ds_friend); diff --git gcc/gimple-fold.c gcc/gimple-fold.c index 362ab59e9c0..1e84722d22d 100644 --- gcc/gimple-fold.c +++ gcc/gimple-fold.c @@ -6988,12 +6988,13 @@ gimple_get_virt_method_for_vtable (HOST_WIDE_INT token, access_index = offset / BITS_PER_UNIT / elt_size; gcc_checking_assert (offset % (elt_size * BITS_PER_UNIT) == 0); - /* This code makes an assumption that there are no - indexed fileds produced by C++ FE, so we can directly index the array. */ + /* The C++ FE can now produce indexed fields, and we check if the indexes + match. */ if (access_index < CONSTRUCTOR_NELTS (init)) { fn = CONSTRUCTOR_ELT (init, access_index)->value; - gcc_checking_assert (!CONSTRUCTOR_ELT (init, access_index)->index); + tree idx = CONSTRUCTOR_ELT (init, access_index)->index; + gcc_checking_assert (!idx || tree_to_uhwi (idx) == access_index); STRIP_NOPS (fn); } else diff --git gcc/testsuite/g++.dg/cpp0x/constexpr-virtual5.C gcc/testsuite/g++.dg/cpp0x/constexpr-virtual5.C index 2465f9d9b4f..5f9ab4d9c28 100644 --- gcc/testsuite/g++.dg/cpp0x/constexpr-virtual5.C +++ gcc/testsuite/g++.dg/cpp0x/constexpr-virtual5.C @@ -2,5 +2,5 @@ // { dg-do compile { target c++11 } } struct S { - constexpr virtual int f() { return 1; } // { dg-error "13:member .f. cannot be declared both .virtual. and .constexpr." } + constexpr virtual int f() { return 1; } // { dg-error "13:member .f. can be declared both .virtual. and .constexpr." "" { target c++17_down } } }; diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-virtual1.C gcc/testsuite/g++.dg/cpp2a/constexpr-virtual1.C index e69de29bb2d..fcf8cac6417 100644 --- gcc/testsuite/g++.dg/cpp2a/constexpr-virtual1.C +++ gcc/testsuite/g++.dg/cpp2a/constexpr-virtual1.C @@ -0,0 +1,8 @@ +// P1064R0 +// { dg-do compile { target c++11 } } +// { dg-options "-pedantic-errors" } + +struct X +{ + constexpr virtual int f() { return 0; } // { dg-error "member .f. can be declared both .virtual. and .constexpr. only" "" { target c++17_down } } +}; diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C index e69de29bb2d..9d82c5c59ac 100644 --- gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C +++ gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C @@ -0,0 +1,49 @@ +// P1064R0 +// { dg-do compile } +// { dg-options "-std=c++2a" } + +struct X1 +{ + virtual int f() const = 0; +}; + +struct X2: public X1 +{ + constexpr virtual int f() const { return 2; } +}; + +struct X3: public X2 +{ + virtual int f() const { return 3; } +}; + +struct X4: public X3 +{ + constexpr virtual int f() const { return 4; } +}; + +constexpr int (X1::*pf)() const = &X1::f; + +constexpr X2 x2; +static_assert(x2.f() == 2); +static_assert((x2.*pf)() == 2); + +constexpr X1 const& r2 = x2; +static_assert(r2.f() == 2); +static_assert((r2.*pf)() == 2); + +constexpr X1 const* p2 = &x2; +static_assert(p2->f() == 2); +static_assert((p2->*pf)() == 2); + +constexpr X4 x4; +static_assert(x4.f() == 4); +static_assert((x4.*pf)() == 4); + +constexpr X1 const& r4 = x4; +static_assert(r4.f() == 4); +static_assert((r4.*pf)() == 4); + +constexpr X1 const* p4 = &x4; +static_assert(p4->f() == 4); +static_assert((p4->*pf)() == 4); diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-virtual3.C gcc/testsuite/g++.dg/cpp2a/constexpr-virtual3.C index e69de29bb2d..d71422fc4d0 100644 --- gcc/testsuite/g++.dg/cpp2a/constexpr-virtual3.C +++ gcc/testsuite/g++.dg/cpp2a/constexpr-virtual3.C @@ -0,0 +1,52 @@ +// P1064R0 +// { dg-do compile } +// { dg-options "-std=c++2a" } + +struct X1 +{ + virtual int f() const = 0; +}; + +struct X2: public X1 +{ + int i2 = 42; + constexpr virtual int f() const { return 2; } +}; + +struct X3: public X2 +{ + int i3 = 42; + virtual int f() const { return 3; } +}; + +struct X4: public X3 +{ + int i4 = 42; + constexpr virtual int f() const { return 4; } +}; + +constexpr int (X1::*pf)() const = &X1::f; + +constexpr X2 x2; +static_assert(x2.f() == 2); +static_assert((x2.*pf)() == 2); + +constexpr X1 const& r2 = x2; +static_assert(r2.f() == 2); +static_assert((r2.*pf)() == 2); + +constexpr X1 const* p2 = &x2; +static_assert(p2->f() == 2); +static_assert((p2->*pf)() == 2); + +constexpr X4 x4; +static_assert(x4.f() == 4); +static_assert((x4.*pf)() == 4); + +constexpr X1 const& r4 = x4; +static_assert(r4.f() == 4); +static_assert((r4.*pf)() == 4); + +constexpr X1 const* p4 = &x4; +static_assert(p4->f() == 4); +static_assert((p4->*pf)() == 4); diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-virtual4.C gcc/testsuite/g++.dg/cpp2a/constexpr-virtual4.C index e69de29bb2d..2038bebc6d1 100644 --- gcc/testsuite/g++.dg/cpp2a/constexpr-virtual4.C +++ gcc/testsuite/g++.dg/cpp2a/constexpr-virtual4.C @@ -0,0 +1,57 @@ +// P1064R0 +// { dg-do compile } +// { dg-options "-std=c++2a" } + +struct X1 +{ + virtual int f1() const = 0; + virtual int f2() const = 0; + virtual int f3() const = 0; +}; + +struct X2: public X1 +{ + constexpr virtual int f1() const { return 21; } + constexpr virtual int f2() const { return 22; } + constexpr virtual int f3() const { return 23; } +}; + +struct X3: public X2 +{ + virtual int f1() const { return 31; } + virtual int f2() const { return 32; } + virtual int f3() const { return 33; } +}; + +struct X4: public X3 +{ + constexpr virtual int f1() const { return 41; } + constexpr virtual int f2() const { return 42; } + constexpr virtual int f3() const { return 43; } +}; + +constexpr int (X1::*pf)() const = &X1::f2; + +constexpr X2 x2; +static_assert(x2.f2() == 22); +static_assert((x2.*pf)() == 22); + +constexpr X1 const& r2 = x2; +static_assert(r2.f2() == 22); +static_assert((r2.*pf)() == 22); + +constexpr X1 const* p2 = &x2; +static_assert(p2->f2() == 22); +static_assert((p2->*pf)() == 22); + +constexpr X4 x4; +static_assert(x4.f2() == 42); +static_assert((x4.*pf)() == 42); + +constexpr X1 const& r4 = x4; +static_assert(r4.f2() == 42); +static_assert((r4.*pf)() == 42); + +constexpr X1 const* p4 = &x4; +static_assert(p4->f2() == 42); +static_assert((p4->*pf)() == 42); diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-virtual5.C gcc/testsuite/g++.dg/cpp2a/constexpr-virtual5.C index e69de29bb2d..6d27990a8b6 100644 --- gcc/testsuite/g++.dg/cpp2a/constexpr-virtual5.C +++ gcc/testsuite/g++.dg/cpp2a/constexpr-virtual5.C @@ -0,0 +1,60 @@ +// P1064R0 +// { dg-do compile } +// { dg-options "-std=c++2a" } + +struct X1 +{ + virtual int f1() const = 0; + virtual int f2() const = 0; + virtual int f3() const = 0; +}; + +struct X2: public X1 +{ + int i2 = 42; + constexpr virtual int f1() const { return 21; } + constexpr virtual int f2() const { return 22; } + constexpr virtual int f3() const { return 23; } +}; + +struct X3: public X2 +{ + int i3 = 42; + virtual int f1() const { return 31; } + virtual int f2() const { return 32; } + virtual int f3() const { return 33; } +}; + +struct X4: public X3 +{ + int i4 = 42; + constexpr virtual int f1() const { return 41; } + constexpr virtual int f2() const { return 42; } + constexpr virtual int f3() const { return 43; } +}; + +constexpr int (X1::*pf)() const = &X1::f2; + +constexpr X2 x2; +static_assert(x2.f2() == 22); +static_assert((x2.*pf)() == 22); + +constexpr X1 const& r2 = x2; +static_assert(r2.f2() == 22); +static_assert((r2.*pf)() == 22); + +constexpr X1 const* p2 = &x2; +static_assert(p2->f2() == 22); +static_assert((p2->*pf)() == 22); + +constexpr X4 x4; +static_assert(x4.f2() == 42); +static_assert((x4.*pf)() == 42); + +constexpr X1 const& r4 = x4; +static_assert(r4.f2() == 42); +static_assert((r4.*pf)() == 42); + +constexpr X1 const* p4 = &x4; +static_assert(p4->f2() == 42); +static_assert((p4->*pf)() == 42); diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-virtual6.C gcc/testsuite/g++.dg/cpp2a/constexpr-virtual6.C index e69de29bb2d..ece5e703c32 100644 --- gcc/testsuite/g++.dg/cpp2a/constexpr-virtual6.C +++ gcc/testsuite/g++.dg/cpp2a/constexpr-virtual6.C @@ -0,0 +1,25 @@ +// P1064R0 +// { dg-do compile } +// { dg-options "-std=c++2a" } + +struct X1 +{ + constexpr virtual X1 const *f() const { return this; } +}; + +struct Y +{ + int m = 0; +}; + +struct X2: public Y, public X1 +{ + constexpr virtual X2 const *f() const { return this; } +}; + +constexpr X1 x1; +static_assert(x1.f() == &x1); + +constexpr X2 x2; +constexpr X1 const& r2 = x2; +static_assert(r2.f() == &r2); diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-virtual7.C gcc/testsuite/g++.dg/cpp2a/constexpr-virtual7.C index e69de29bb2d..b0f499608ef 100644 --- gcc/testsuite/g++.dg/cpp2a/constexpr-virtual7.C +++ gcc/testsuite/g++.dg/cpp2a/constexpr-virtual7.C @@ -0,0 +1,87 @@ +// P1064R0 +// { dg-do compile } +// { dg-options "-std=c++2a" } + +struct X1 +{ + virtual int f() const = 0; +}; + +struct X2: public X1 +{ + constexpr virtual int f() const { return 2; } +}; + +struct X3: public X2 +{ + virtual int f() const { return 3; } +}; + +struct X4: public X3 +{ + constexpr virtual int f() const { return 4; } +}; + +constexpr int (X1::*pf)() const = &X1::f; + +constexpr X2 x2; + +struct S +{ + int i, j; + constexpr S() : i(x2.f()), j((x2.*pf)()) { } +}; + +static_assert(S().i == 2); +static_assert(S().j == 2); + +constexpr X1 const& r2 = x2; + +struct S2 +{ + int i, j; + constexpr S2() : i(r2.f()), j((r2.*pf)()) { } +}; + +static_assert(S2().i == 2); +static_assert(S2().j == 2); + +constexpr X1 const* p2 = &x2; +struct S3 +{ + int i, j; + constexpr S3() : i(p2->f()), j((p2->*pf)()) { } +}; + +static_assert(S3().i == 2); +static_assert(S3().j == 2); + +constexpr X4 x4; +struct S4 +{ + int i, j; + constexpr S4() : i(x4.f()), j((x4.*pf)()) { } +}; + +static_assert(S4().i == 4); +static_assert(S4().j == 4); + +constexpr X1 const& r4 = x4; +struct S5 +{ + int i, j; + constexpr S5() : i(r4.f()), j((r4.*pf)()) { } +}; + +static_assert(S5().i == 4); +static_assert(S5().j == 4); + +constexpr X1 const* p4 = &x4; +struct S6 +{ + int i, j; + constexpr S6() : i(p4->f()), j((p4->*pf)()) { } +}; + +static_assert(S6().i == 4); +static_assert(S6().j == 4); diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-virtual8.C gcc/testsuite/g++.dg/cpp2a/constexpr-virtual8.C index e69de29bb2d..4a7cc972a91 100644 --- gcc/testsuite/g++.dg/cpp2a/constexpr-virtual8.C +++ gcc/testsuite/g++.dg/cpp2a/constexpr-virtual8.C @@ -0,0 +1,50 @@ +// P1064R0 +// { dg-do compile } +// { dg-options "-std=c++2a" } + +template<typename T> +struct X1 +{ + virtual T f() const = 0; +}; + +struct X2: public X1<int> +{ + constexpr virtual int f() const { return 2; } +}; + +struct X3: public X2 +{ + virtual int f() const { return 3; } +}; + +struct X4: public X3 +{ + constexpr virtual int f() const { return 4; } +}; + +constexpr int (X1<int>::*pf)() const = &X1<int>::f; + +constexpr X2 x2; +static_assert(x2.f() == 2); +static_assert((x2.*pf)() == 2); + +constexpr X1<int> const& r2 = x2; +static_assert(r2.f() == 2); +static_assert((r2.*pf)() == 2); + +constexpr X1<int> const* p2 = &x2; +static_assert(p2->f() == 2); +static_assert((p2->*pf)() == 2); + +constexpr X4 x4; +static_assert(x4.f() == 4); +static_assert((x4.*pf)() == 4); + +constexpr X1<int> const& r4 = x4; +static_assert(r4.f() == 4); +static_assert((r4.*pf)() == 4); + +constexpr X1<int> const* p4 = &x4; +static_assert(p4->f() == 4); +static_assert((p4->*pf)() == 4); diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-virtual9.C gcc/testsuite/g++.dg/cpp2a/constexpr-virtual9.C index e69de29bb2d..3a12adc2659 100644 --- gcc/testsuite/g++.dg/cpp2a/constexpr-virtual9.C +++ gcc/testsuite/g++.dg/cpp2a/constexpr-virtual9.C @@ -0,0 +1,83 @@ +// P1064R0 +// { dg-do compile } +// { dg-options "-std=c++2a" } + +struct X1 +{ + virtual int f() const = 0; + virtual int f(int) const = 0; + virtual int f(int, int) const = 0; +}; + +struct X2: public X1 +{ + constexpr virtual int f() const { return 2; } + constexpr virtual int f(int) const { return 12; } + constexpr virtual int f(int, int) const { return 22; } +}; + +struct X3: public X2 +{ + virtual int f() const { return 3; } + virtual int f(int) const { return 13; } + virtual int f(int, int) const { return 23; } +}; + +struct X4: public X3 +{ + constexpr virtual int f() const { return 4; } + constexpr virtual int f(int) const { return 14; } + constexpr virtual int f(int, int) const { return 24; } +}; + +constexpr int (X1::*pf)() const = &X1::f; +constexpr int (X1::*pf1)(int) const = &X1::f; +constexpr int (X1::*pf2)(int, int) const = &X1::f; + +constexpr X2 x2; +static_assert(x2.f() == 2); +static_assert((x2.*pf)() == 2); +static_assert(x2.f(1) == 12); +static_assert((x2.*pf1)(1) == 12); +static_assert(x2.f(1, 2) == 22); +static_assert((x2.*pf2)(1, 2) == 22); + +constexpr X1 const& r2 = x2; +static_assert(r2.f() == 2); +static_assert((r2.*pf)() == 2); +static_assert(r2.f(1) == 12); +static_assert((r2.*pf1)(1) == 12); +static_assert(r2.f(1, 2) == 22); +static_assert((r2.*pf2)(1, 2) == 22); + +constexpr X1 const* p2 = &x2; +static_assert(p2->f() == 2); +static_assert((p2->*pf)() == 2); +static_assert(p2->f(1) == 12); +static_assert((p2->*pf1)(1) == 12); +static_assert(p2->f(1, 2) == 22); +static_assert((p2->*pf2)(1, 2) == 22); + +constexpr X4 x4; +static_assert(x4.f() == 4); +static_assert((x4.*pf)() == 4); +static_assert(x4.f(1) == 14); +static_assert((x4.*pf1)(1) == 14); +static_assert(x4.f(1, 2) == 24); +static_assert((x4.*pf2)(1, 2) == 24); + +constexpr X1 const& r4 = x4; +static_assert(r4.f() == 4); +static_assert((r4.*pf)() == 4); +static_assert(r4.f(1) == 14); +static_assert((r4.*pf1)(1) == 14); +static_assert(r4.f(1, 2) == 24); +static_assert((r4.*pf2)(1, 2) == 24); + +constexpr X1 const* p4 = &x4; +static_assert(p4->f() == 4); +static_assert((p4->*pf)() == 4); +static_assert(p4->f(1) == 14); +static_assert((p4->*pf1)(1) == 14); +static_assert(p4->f(1, 2) == 24); +static_assert((p4->*pf2)(1, 2) == 24); diff --git gcc/testsuite/g++.dg/diagnostic/virtual-constexpr.C gcc/testsuite/g++.dg/diagnostic/virtual-constexpr.C index 2c83236cae9..9223c692737 100644 --- gcc/testsuite/g++.dg/diagnostic/virtual-constexpr.C +++ gcc/testsuite/g++.dg/diagnostic/virtual-constexpr.C @@ -1,14 +1,15 @@ -// { dg-options "-fdiagnostics-show-caret" } +// { dg-options "-fdiagnostics-show-caret -pedantic-errors" } // { dg-do compile { target c++11 } } +// { dg-skip-if "virtual constexpr" { *-*-* } { "-std=gnu++2a" } { "" } } struct S { - virtual constexpr void foo(); // { dg-error "3:member .foo. cannot be declared both .virtual. and .constexpr." } + virtual constexpr void foo(); // { dg-error "3:member .foo. can be declared both .virtual. and .constexpr." } /* { dg-begin-multiline-output "" } virtual constexpr void foo(); ^~~~~~~ ~~~~~~~~~ { dg-end-multiline-output "" } */ - constexpr virtual void bar(); // { dg-error "13:member .bar. cannot be declared both .virtual. and .constexpr." } + constexpr virtual void bar(); // { dg-error "13:member .bar. can be declared both .virtual. and .constexpr." } /* { dg-begin-multiline-output "" } constexpr virtual void bar(); ~~~~~~~~~ ^~~~~~~
I'm getting this ICE on ia64: libtool: compile: /usr/local/gcc/gcc-20180919/Build/./gcc/xgcc -shared-libgcc -B/usr/local/gcc/gcc-20180919/Build/./gcc -nostdinc++ -L/usr/local/gcc/gcc-20180919/Build/ia64-suse-linux/libstdc++-v3/src -L/usr/local/gcc/gcc-20180919/Build/ia64-suse-linux/libstdc++-v3/src/.libs -L/usr/local/gcc/gcc-20180919/Build/ia64-suse-linux/libstdc++-v3/libsupc++/.libs -B/usr/ia64-suse-linux/bin/ -B/usr/ia64-suse-linux/lib/ -isystem /usr/ia64-suse-linux/include -isystem /usr/ia64-suse-linux/sys-include -fno-checking -I/usr/local/gcc/gcc-20180919/libstdc++-v3/../libgcc -I/usr/local/gcc/gcc-20180919/Build/ia64-suse-linux/libstdc++-v3/include/ia64-suse-linux -I/usr/local/gcc/gcc-20180919/Build/ia64-suse-linux/libstdc++-v3/include -I/usr/local/gcc/gcc-20180919/libstdc++-v3/libsupc++ -D_GLIBCXX_SHARED -fno-implicit-templates -Wall -Wextra -Wwrite-strings -Wcast-qual -Wabi=2 -fdiagnostics-show-location=once -ffunction-sections -fdata-sections -frandom-seed=bad_typeid.lo -O2 -g -D_GNU_SOURCE -c ../../../../libstdc++-v3/libsupc++/bad_typeid.cc -fPIC -DPIC -D_GLIBCXX_SHARED -o bad_typeid.o ../../../../libstdc++-v3/libsupc++/bad_typeid.cc:36:1: internal compiler error: in output_constructor_regular_field, at varasm.c:5165 36 | } // namespace std | ^ 0x400000000176abaf output_constructor_regular_field ../../gcc/varasm.c:5165 0x400000000176d09f output_constructor ../../gcc/varasm.c:5475 0x400000000176940f output_constant ../../gcc/varasm.c:4967 0x400000000175414f assemble_variable_contents ../../gcc/varasm.c:2143 0x400000000175586f assemble_variable(tree_node*, int, int, int) ../../gcc/varasm.c:2319 0x40000000017a564f varpool_node::assemble_decl() ../../gcc/varpool.c:586 0x40000000017a74cf symbol_table::output_variables() ../../gcc/varpool.c:752 0x40000000007b806f symbol_table::compile() ../../gcc/cgraphunit.c:2611 0x40000000007bd8ef symbol_table::compile() ../../gcc/cgraphunit.c:2791 0x40000000007bd8ef symbol_table::finalize_compilation_unit() ../../gcc/cgraphunit.c:2788 (gdb) up #1 0x400000000176abb0 in output_constructor_regular_field ( local=0x600ffffffffee920) at ../../gcc/varasm.c:5165 5165 gcc_assert (fieldpos == local->total_bytes); (gdb) p fieldpos $1 = 16 (gdb) p local->total_bytes $2 = 24 Andreas.
On Wed, Sep 19, 2018 at 03:25:02PM +0200, Andreas Schwab wrote: > I'm getting this ICE on ia64: > > libtool: compile: /usr/local/gcc/gcc-20180919/Build/./gcc/xgcc -shared-libgcc -B/usr/local/gcc/gcc-20180919/Build/./gcc -nostdinc++ -L/usr/local/gcc/gcc-20180919/Build/ia64-suse-linux/libstdc++-v3/src -L/usr/local/gcc/gcc-20180919/Build/ia64-suse-linux/libstdc++-v3/src/.libs -L/usr/local/gcc/gcc-20180919/Build/ia64-suse-linux/libstdc++-v3/libsupc++/.libs -B/usr/ia64-suse-linux/bin/ -B/usr/ia64-suse-linux/lib/ -isystem /usr/ia64-suse-linux/include -isystem /usr/ia64-suse-linux/sys-include -fno-checking -I/usr/local/gcc/gcc-20180919/libstdc++-v3/../libgcc -I/usr/local/gcc/gcc-20180919/Build/ia64-suse-linux/libstdc++-v3/include/ia64-suse-linux -I/usr/local/gcc/gcc-20180919/Build/ia64-suse-linux/libstdc++-v3/include -I/usr/local/gcc/gcc-20180919/libstdc++-v3/libsupc++ -D_GLIBCXX_SHARED -fno-implicit-templates -Wall -Wextra -Wwrite-strings -Wcast-qual -Wabi=2 -fdiagnostics-show-location=once -ffunction-sections -fdata-sections -frandom-seed=bad_typeid.lo -O2 -g -D_GNU_SOURCE -c ../../../../libstdc++-v3/libsupc++/bad_typeid.cc -fPIC -DPIC -D_GLIBCXX_SHARED -o bad_typeid.o > ../../../../libstdc++-v3/libsupc++/bad_typeid.cc:36:1: internal compiler error: in output_constructor_regular_field, at varasm.c:5165 > 36 | } // namespace std > | ^ > 0x400000000176abaf output_constructor_regular_field > ../../gcc/varasm.c:5165 > 0x400000000176d09f output_constructor > ../../gcc/varasm.c:5475 > 0x400000000176940f output_constant > ../../gcc/varasm.c:4967 > 0x400000000175414f assemble_variable_contents > ../../gcc/varasm.c:2143 > 0x400000000175586f assemble_variable(tree_node*, int, int, int) > ../../gcc/varasm.c:2319 > 0x40000000017a564f varpool_node::assemble_decl() > ../../gcc/varpool.c:586 > 0x40000000017a74cf symbol_table::output_variables() > ../../gcc/varpool.c:752 > 0x40000000007b806f symbol_table::compile() > ../../gcc/cgraphunit.c:2611 > 0x40000000007bd8ef symbol_table::compile() > ../../gcc/cgraphunit.c:2791 > 0x40000000007bd8ef symbol_table::finalize_compilation_unit() > ../../gcc/cgraphunit.c:2788 > > (gdb) up > #1 0x400000000176abb0 in output_constructor_regular_field ( > local=0x600ffffffffee920) at ../../gcc/varasm.c:5165 > 5165 gcc_assert (fieldpos == local->total_bytes); > (gdb) p fieldpos > $1 = 16 > (gdb) p local->total_bytes > $2 = 24 That muset be because ia64 uses TARGET_VTABLE_USES_DESCRIPTORS. Does this help? 2018-09-19 Marek Polacek <polacek@redhat.com> * class.c (build_vtbl_initializer): Don't mess with *inits for TARGET_VTABLE_USES_DESCRIPTORS. diff --git gcc/cp/class.c gcc/cp/class.c index 9ca46441871..0239f6ae045 100644 --- gcc/cp/class.c +++ gcc/cp/class.c @@ -9370,7 +9370,7 @@ build_vtbl_initializer (tree binfo, int i; if (init == size_zero_node) for (i = 0; i < TARGET_VTABLE_USES_DESCRIPTORS; ++i) - CONSTRUCTOR_APPEND_ELT (*inits, idx, init); + CONSTRUCTOR_APPEND_ELT (*inits, NULL_TREE, init); else for (i = 0; i < TARGET_VTABLE_USES_DESCRIPTORS; ++i) { @@ -9378,7 +9378,7 @@ build_vtbl_initializer (tree binfo, fn, build_int_cst (NULL_TREE, i)); TREE_CONSTANT (fdesc) = 1; - CONSTRUCTOR_APPEND_ELT (*inits, idx, fdesc); + CONSTRUCTOR_APPEND_ELT (*inits, NULL_TREE, fdesc); } } else
On Sep 19 2018, Marek Polacek <polacek@redhat.com> wrote: > 2018-09-19 Marek Polacek <polacek@redhat.com> > > * class.c (build_vtbl_initializer): Don't mess with *inits for > TARGET_VTABLE_USES_DESCRIPTORS. > > diff --git gcc/cp/class.c gcc/cp/class.c > index 9ca46441871..0239f6ae045 100644 > --- gcc/cp/class.c > +++ gcc/cp/class.c > @@ -9370,7 +9370,7 @@ build_vtbl_initializer (tree binfo, > int i; > if (init == size_zero_node) > for (i = 0; i < TARGET_VTABLE_USES_DESCRIPTORS; ++i) > - CONSTRUCTOR_APPEND_ELT (*inits, idx, init); > + CONSTRUCTOR_APPEND_ELT (*inits, NULL_TREE, init); > else > for (i = 0; i < TARGET_VTABLE_USES_DESCRIPTORS; ++i) > { > @@ -9378,7 +9378,7 @@ build_vtbl_initializer (tree binfo, > fn, build_int_cst (NULL_TREE, i)); > TREE_CONSTANT (fdesc) = 1; > > - CONSTRUCTOR_APPEND_ELT (*inits, idx, fdesc); > + CONSTRUCTOR_APPEND_ELT (*inits, NULL_TREE, fdesc); > } > } > else Thanks, that works. Andreas.
On Wed, Sep 19, 2018 at 05:07:34PM +0200, Andreas Schwab wrote: > On Sep 19 2018, Marek Polacek <polacek@redhat.com> wrote: > > > 2018-09-19 Marek Polacek <polacek@redhat.com> > > > > * class.c (build_vtbl_initializer): Don't mess with *inits for > > TARGET_VTABLE_USES_DESCRIPTORS. > > > > diff --git gcc/cp/class.c gcc/cp/class.c > > index 9ca46441871..0239f6ae045 100644 > > --- gcc/cp/class.c > > +++ gcc/cp/class.c > > @@ -9370,7 +9370,7 @@ build_vtbl_initializer (tree binfo, > > int i; > > if (init == size_zero_node) > > for (i = 0; i < TARGET_VTABLE_USES_DESCRIPTORS; ++i) > > - CONSTRUCTOR_APPEND_ELT (*inits, idx, init); > > + CONSTRUCTOR_APPEND_ELT (*inits, NULL_TREE, init); > > else > > for (i = 0; i < TARGET_VTABLE_USES_DESCRIPTORS; ++i) > > { > > @@ -9378,7 +9378,7 @@ build_vtbl_initializer (tree binfo, > > fn, build_int_cst (NULL_TREE, i)); > > TREE_CONSTANT (fdesc) = 1; > > > > - CONSTRUCTOR_APPEND_ELT (*inits, idx, fdesc); > > + CONSTRUCTOR_APPEND_ELT (*inits, NULL_TREE, fdesc); > > } > > } > > else > > Thanks, that works. Great. Jason, can I commit it then? Marek
On Wed, Sep 19, 2018 at 11:10 AM, Marek Polacek <polacek@redhat.com> wrote: > On Wed, Sep 19, 2018 at 05:07:34PM +0200, Andreas Schwab wrote: >> On Sep 19 2018, Marek Polacek <polacek@redhat.com> wrote: >> >> > 2018-09-19 Marek Polacek <polacek@redhat.com> >> > >> > * class.c (build_vtbl_initializer): Don't mess with *inits for >> > TARGET_VTABLE_USES_DESCRIPTORS. >> > >> > diff --git gcc/cp/class.c gcc/cp/class.c >> > index 9ca46441871..0239f6ae045 100644 >> > --- gcc/cp/class.c >> > +++ gcc/cp/class.c >> > @@ -9370,7 +9370,7 @@ build_vtbl_initializer (tree binfo, >> > int i; >> > if (init == size_zero_node) >> > for (i = 0; i < TARGET_VTABLE_USES_DESCRIPTORS; ++i) >> > - CONSTRUCTOR_APPEND_ELT (*inits, idx, init); >> > + CONSTRUCTOR_APPEND_ELT (*inits, NULL_TREE, init); >> > else >> > for (i = 0; i < TARGET_VTABLE_USES_DESCRIPTORS; ++i) >> > { >> > @@ -9378,7 +9378,7 @@ build_vtbl_initializer (tree binfo, >> > fn, build_int_cst (NULL_TREE, i)); >> > TREE_CONSTANT (fdesc) = 1; >> > >> > - CONSTRUCTOR_APPEND_ELT (*inits, idx, fdesc); >> > + CONSTRUCTOR_APPEND_ELT (*inits, NULL_TREE, fdesc); >> > } >> > } >> > else >> >> Thanks, that works. > > Great. Jason, can I commit it then? Andreas, do the new testcases pass? That would surprise me, but OK if so. Jason
On Sep 19 2018, Jason Merrill <jason@redhat.com> wrote:
> Andreas, do the new testcases pass? That would surprise me, but OK if so.
No, they don't.
/usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:29:26: error: non-constant condition for static assertion
/usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:29:23: error: expression '((& X2::_ZTV2X2) + 16)' does not designate a 'constexpr' function
/usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:33:26: error: non-constant condition for static assertion
/usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:33:23: error: expression '((& X2::_ZTV2X2) + 16)' does not designate a 'constexpr' function
/usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:37:27: error: non-constant condition for static assertion
/usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:37:24: error: expression '((& X2::_ZTV2X2) + 16)' does not designate a 'constexpr' function
/usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:41:26: error: non-constant condition for static assertion
/usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:41:23: error: expression '((& X4::_ZTV2X4) + 16)' does not designate a 'constexpr' function
/usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:45:26: error: non-constant condition for static assertion
/usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:45:23: error: expression '((& X4::_ZTV2X4) + 16)' does not designate a 'constexpr' function
/usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:49:27: error: non-constant condition for static assertion
/usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:49:24: error: expression '((& X4::_ZTV2X4) + 16)' does not designate a 'constexpr' function
compiler exited with status 1
FAIL: g++.dg/cpp2a/constexpr-virtual2.C (test for excess errors)
Andreas.
On Thu, Sep 20, 2018 at 09:12:53AM +0200, Andreas Schwab wrote: > On Sep 19 2018, Jason Merrill <jason@redhat.com> wrote: > > > Andreas, do the new testcases pass? That would surprise me, but OK if so. > > No, they don't. > > /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:29:26: error: non-constant condition for static assertion > /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:29:23: error: expression '((& X2::_ZTV2X2) + 16)' does not designate a 'constexpr' function > /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:33:26: error: non-constant condition for static assertion > /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:33:23: error: expression '((& X2::_ZTV2X2) + 16)' does not designate a 'constexpr' function > /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:37:27: error: non-constant condition for static assertion > /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:37:24: error: expression '((& X2::_ZTV2X2) + 16)' does not designate a 'constexpr' function > /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:41:26: error: non-constant condition for static assertion > /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:41:23: error: expression '((& X4::_ZTV2X4) + 16)' does not designate a 'constexpr' function > /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:45:26: error: non-constant condition for static assertion > /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:45:23: error: expression '((& X4::_ZTV2X4) + 16)' does not designate a 'constexpr' function > /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:49:27: error: non-constant condition for static assertion > /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:49:24: error: expression '((& X4::_ZTV2X4) + 16)' does not designate a 'constexpr' function > compiler exited with status 1 > FAIL: g++.dg/cpp2a/constexpr-virtual2.C (test for excess errors) I think the primary problem here is: /* When using function descriptors, the address of the vtable entry is treated as a function pointer. */ if (TARGET_VTABLE_USES_DESCRIPTORS) e2 = build1 (NOP_EXPR, TREE_TYPE (e2), cp_build_addr_expr (e2, complain)); in typeck.c, on non-descriptor targets we have an INDIRECT_REF where we read the vtable function pointer. On ia64, the above optimizes the INDIRECT_REF away, so what the cxx_eval_call_expression actually gets after constexpr evaluating the CALL_FN is not ADDR_EXPR of a function, but the address of the function descriptor (e.g. &_ZTV2X2 + 16 ). So, perhaps in cxx_eval_call_expression we need: if (TREE_CODE (fun) == ADDR_EXPR) fun = TREE_OPERAND (fun, 0); + else if (TARGET_VTABLE_USES_DESCRIPTORS + && TREE_CODE (fun) == POINTER_PLUS_EXPR + && ...) where we verify that p+ first argument is ADDR_EXPR of a virtual table, second arg is INTEGER_CST and just walk the DECL_INITIAL of that, finding the FDESC_EXPR at the right offset (therefore, I believe you need following rather than the patch you've posted, so that you can actually find it) and finally pick the function from the FDESC_EXPR entry. Makes me wonder what happens with indirect calls in constexpr evaluation, e.g. if I do: constexpr int bar () { return 42; } constexpr int foo () { int (*fn) () = bar; return fn (); } static_assert (foo () == 42); but apparently this works. --- gcc/cp/class.c.jj 2018-09-20 09:56:59.229751895 +0200 +++ gcc/cp/class.c 2018-09-20 10:12:17.447370890 +0200 @@ -9266,7 +9266,6 @@ build_vtbl_initializer (tree binfo, tree vcall_index; tree fn, fn_original; tree init = NULL_TREE; - tree idx = size_int (jx++); fn = BV_FN (v); fn_original = fn; @@ -9370,7 +9369,7 @@ build_vtbl_initializer (tree binfo, int i; if (init == size_zero_node) for (i = 0; i < TARGET_VTABLE_USES_DESCRIPTORS; ++i) - CONSTRUCTOR_APPEND_ELT (*inits, idx, init); + CONSTRUCTOR_APPEND_ELT (*inits, size_int (jx++), init); else for (i = 0; i < TARGET_VTABLE_USES_DESCRIPTORS; ++i) { @@ -9378,11 +9377,11 @@ build_vtbl_initializer (tree binfo, fn, build_int_cst (NULL_TREE, i)); TREE_CONSTANT (fdesc) = 1; - CONSTRUCTOR_APPEND_ELT (*inits, idx, fdesc); + CONSTRUCTOR_APPEND_ELT (*inits, size_int (jx++), fdesc); } } else - CONSTRUCTOR_APPEND_ELT (*inits, idx, init); + CONSTRUCTOR_APPEND_ELT (*inits, size_int (jx++), init); } } Jakub
On Thu, Sep 20, 2018 at 5:20 AM, Jakub Jelinek <jakub@redhat.com> wrote: > On Thu, Sep 20, 2018 at 09:12:53AM +0200, Andreas Schwab wrote: >> On Sep 19 2018, Jason Merrill <jason@redhat.com> wrote: >> >> > Andreas, do the new testcases pass? That would surprise me, but OK if so. >> >> No, they don't. >> >> /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:29:26: error: non-constant condition for static assertion >> /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:29:23: error: expression '((& X2::_ZTV2X2) + 16)' does not designate a 'constexpr' function >> /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:33:26: error: non-constant condition for static assertion >> /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:33:23: error: expression '((& X2::_ZTV2X2) + 16)' does not designate a 'constexpr' function >> /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:37:27: error: non-constant condition for static assertion >> /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:37:24: error: expression '((& X2::_ZTV2X2) + 16)' does not designate a 'constexpr' function >> /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:41:26: error: non-constant condition for static assertion >> /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:41:23: error: expression '((& X4::_ZTV2X4) + 16)' does not designate a 'constexpr' function >> /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:45:26: error: non-constant condition for static assertion >> /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:45:23: error: expression '((& X4::_ZTV2X4) + 16)' does not designate a 'constexpr' function >> /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:49:27: error: non-constant condition for static assertion >> /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:49:24: error: expression '((& X4::_ZTV2X4) + 16)' does not designate a 'constexpr' function >> compiler exited with status 1 >> FAIL: g++.dg/cpp2a/constexpr-virtual2.C (test for excess errors) > > I think the primary problem here is: > /* When using function descriptors, the address of the > vtable entry is treated as a function pointer. */ > if (TARGET_VTABLE_USES_DESCRIPTORS) > e2 = build1 (NOP_EXPR, TREE_TYPE (e2), > cp_build_addr_expr (e2, complain)); > in typeck.c, on non-descriptor targets we have an INDIRECT_REF where we > read the vtable function pointer. On ia64, the above optimizes the > INDIRECT_REF away, so what the cxx_eval_call_expression actually gets > after constexpr evaluating the CALL_FN is not ADDR_EXPR of a function, > but the address of the function descriptor (e.g. &_ZTV2X2 + 16 ). > > So, perhaps in cxx_eval_call_expression we need: > if (TREE_CODE (fun) == ADDR_EXPR) > fun = TREE_OPERAND (fun, 0); > + else if (TARGET_VTABLE_USES_DESCRIPTORS > + && TREE_CODE (fun) == POINTER_PLUS_EXPR > + && ...) > where we verify that p+ first argument is ADDR_EXPR of a virtual table, > second arg is INTEGER_CST and just walk the DECL_INITIAL of that, finding > the FDESC_EXPR at the right offset (therefore, I believe you need following > rather than the patch you've posted, so that you can actually find it) and > finally pick the function from the FDESC_EXPR entry. > Makes me wonder what happens with indirect calls in constexpr evaluation, > e.g. if I do: > constexpr int bar () { return 42; } > constexpr int foo () { int (*fn) () = bar; return fn (); } > static_assert (foo () == 42); > but apparently this works. > > --- gcc/cp/class.c.jj 2018-09-20 09:56:59.229751895 +0200 > +++ gcc/cp/class.c 2018-09-20 10:12:17.447370890 +0200 > @@ -9266,7 +9266,6 @@ build_vtbl_initializer (tree binfo, > tree vcall_index; > tree fn, fn_original; > tree init = NULL_TREE; > - tree idx = size_int (jx++); > > fn = BV_FN (v); > fn_original = fn; > @@ -9370,7 +9369,7 @@ build_vtbl_initializer (tree binfo, > int i; > if (init == size_zero_node) > for (i = 0; i < TARGET_VTABLE_USES_DESCRIPTORS; ++i) > - CONSTRUCTOR_APPEND_ELT (*inits, idx, init); > + CONSTRUCTOR_APPEND_ELT (*inits, size_int (jx++), init); > else > for (i = 0; i < TARGET_VTABLE_USES_DESCRIPTORS; ++i) > { > @@ -9378,11 +9377,11 @@ build_vtbl_initializer (tree binfo, > fn, build_int_cst (NULL_TREE, i)); > TREE_CONSTANT (fdesc) = 1; > > - CONSTRUCTOR_APPEND_ELT (*inits, idx, fdesc); > + CONSTRUCTOR_APPEND_ELT (*inits, size_int (jx++), fdesc); > } > } > else > - CONSTRUCTOR_APPEND_ELT (*inits, idx, init); > + CONSTRUCTOR_APPEND_ELT (*inits, size_int (jx++), init); > } > } This patch is OK. And your suggestion for cxx_eval_call_expression sounds right, too. Marek, will you follow up on that? Jason
On Thu, Sep 27, 2018 at 01:15:46AM -0400, Jason Merrill wrote: > On Thu, Sep 20, 2018 at 5:20 AM, Jakub Jelinek <jakub@redhat.com> wrote: > > On Thu, Sep 20, 2018 at 09:12:53AM +0200, Andreas Schwab wrote: > >> On Sep 19 2018, Jason Merrill <jason@redhat.com> wrote: > >> > >> > Andreas, do the new testcases pass? That would surprise me, but OK if so. > >> > >> No, they don't. > >> > >> /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:29:26: error: non-constant condition for static assertion > >> /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:29:23: error: expression '((& X2::_ZTV2X2) + 16)' does not designate a 'constexpr' function > >> /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:33:26: error: non-constant condition for static assertion > >> /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:33:23: error: expression '((& X2::_ZTV2X2) + 16)' does not designate a 'constexpr' function > >> /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:37:27: error: non-constant condition for static assertion > >> /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:37:24: error: expression '((& X2::_ZTV2X2) + 16)' does not designate a 'constexpr' function > >> /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:41:26: error: non-constant condition for static assertion > >> /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:41:23: error: expression '((& X4::_ZTV2X4) + 16)' does not designate a 'constexpr' function > >> /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:45:26: error: non-constant condition for static assertion > >> /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:45:23: error: expression '((& X4::_ZTV2X4) + 16)' does not designate a 'constexpr' function > >> /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:49:27: error: non-constant condition for static assertion > >> /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:49:24: error: expression '((& X4::_ZTV2X4) + 16)' does not designate a 'constexpr' function > >> compiler exited with status 1 > >> FAIL: g++.dg/cpp2a/constexpr-virtual2.C (test for excess errors) > > > > I think the primary problem here is: > > /* When using function descriptors, the address of the > > vtable entry is treated as a function pointer. */ > > if (TARGET_VTABLE_USES_DESCRIPTORS) > > e2 = build1 (NOP_EXPR, TREE_TYPE (e2), > > cp_build_addr_expr (e2, complain)); > > in typeck.c, on non-descriptor targets we have an INDIRECT_REF where we > > read the vtable function pointer. On ia64, the above optimizes the > > INDIRECT_REF away, so what the cxx_eval_call_expression actually gets > > after constexpr evaluating the CALL_FN is not ADDR_EXPR of a function, > > but the address of the function descriptor (e.g. &_ZTV2X2 + 16 ). > > > > So, perhaps in cxx_eval_call_expression we need: > > if (TREE_CODE (fun) == ADDR_EXPR) > > fun = TREE_OPERAND (fun, 0); > > + else if (TARGET_VTABLE_USES_DESCRIPTORS > > + && TREE_CODE (fun) == POINTER_PLUS_EXPR > > + && ...) > > where we verify that p+ first argument is ADDR_EXPR of a virtual table, > > second arg is INTEGER_CST and just walk the DECL_INITIAL of that, finding > > the FDESC_EXPR at the right offset (therefore, I believe you need following > > rather than the patch you've posted, so that you can actually find it) and > > finally pick the function from the FDESC_EXPR entry. > > Makes me wonder what happens with indirect calls in constexpr evaluation, > > e.g. if I do: > > constexpr int bar () { return 42; } > > constexpr int foo () { int (*fn) () = bar; return fn (); } > > static_assert (foo () == 42); > > but apparently this works. > > > > --- gcc/cp/class.c.jj 2018-09-20 09:56:59.229751895 +0200 > > +++ gcc/cp/class.c 2018-09-20 10:12:17.447370890 +0200 > > @@ -9266,7 +9266,6 @@ build_vtbl_initializer (tree binfo, > > tree vcall_index; > > tree fn, fn_original; > > tree init = NULL_TREE; > > - tree idx = size_int (jx++); > > > > fn = BV_FN (v); > > fn_original = fn; > > @@ -9370,7 +9369,7 @@ build_vtbl_initializer (tree binfo, > > int i; > > if (init == size_zero_node) > > for (i = 0; i < TARGET_VTABLE_USES_DESCRIPTORS; ++i) > > - CONSTRUCTOR_APPEND_ELT (*inits, idx, init); > > + CONSTRUCTOR_APPEND_ELT (*inits, size_int (jx++), init); > > else > > for (i = 0; i < TARGET_VTABLE_USES_DESCRIPTORS; ++i) > > { > > @@ -9378,11 +9377,11 @@ build_vtbl_initializer (tree binfo, > > fn, build_int_cst (NULL_TREE, i)); > > TREE_CONSTANT (fdesc) = 1; > > > > - CONSTRUCTOR_APPEND_ELT (*inits, idx, fdesc); > > + CONSTRUCTOR_APPEND_ELT (*inits, size_int (jx++), fdesc); > > } > > } > > else > > - CONSTRUCTOR_APPEND_ELT (*inits, idx, init); > > + CONSTRUCTOR_APPEND_ELT (*inits, size_int (jx++), init); > > } > > } > > This patch is OK. And your suggestion for cxx_eval_call_expression > sounds right, too. Marek, will you follow up on that? Ok, I will (provided I can get a box that has TARGET_VTABLE_USES_DESCRIPTORS, I think ppc64 BE should be enough). Marek
On Thu, Sep 27, 2018, 7:08 PM Marek Polacek <polacek@redhat.com> wrote: > On Thu, Sep 27, 2018 at 01:15:46AM -0400, Jason Merrill wrote: > > On Thu, Sep 20, 2018 at 5:20 AM, Jakub Jelinek <jakub@redhat.com> wrote: > > > On Thu, Sep 20, 2018 at 09:12:53AM +0200, Andreas Schwab wrote: > > >> On Sep 19 2018, Jason Merrill <jason@redhat.com> wrote: > > >> > > >> > Andreas, do the new testcases pass? That would surprise me, but OK > if so. > > >> > > >> No, they don't. > > >> > > >> > /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:29:26: > error: non-constant condition for static assertion > > >> > /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:29:23: > error: expression '((& X2::_ZTV2X2) + 16)' does not designate a 'constexpr' > function > > >> > /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:33:26: > error: non-constant condition for static assertion > > >> > /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:33:23: > error: expression '((& X2::_ZTV2X2) + 16)' does not designate a 'constexpr' > function > > >> > /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:37:27: > error: non-constant condition for static assertion > > >> > /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:37:24: > error: expression '((& X2::_ZTV2X2) + 16)' does not designate a 'constexpr' > function > > >> > /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:41:26: > error: non-constant condition for static assertion > > >> > /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:41:23: > error: expression '((& X4::_ZTV2X4) + 16)' does not designate a 'constexpr' > function > > >> > /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:45:26: > error: non-constant condition for static assertion > > >> > /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:45:23: > error: expression '((& X4::_ZTV2X4) + 16)' does not designate a 'constexpr' > function > > >> > /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:49:27: > error: non-constant condition for static assertion > > >> > /usr/local/gcc/gcc-20180920/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C:49:24: > error: expression '((& X4::_ZTV2X4) + 16)' does not designate a 'constexpr' > function > > >> compiler exited with status 1 > > >> FAIL: g++.dg/cpp2a/constexpr-virtual2.C (test for excess errors) > > > > > > I think the primary problem here is: > > > /* When using function descriptors, the address of the > > > vtable entry is treated as a function pointer. */ > > > if (TARGET_VTABLE_USES_DESCRIPTORS) > > > e2 = build1 (NOP_EXPR, TREE_TYPE (e2), > > > cp_build_addr_expr (e2, complain)); > > > in typeck.c, on non-descriptor targets we have an INDIRECT_REF where we > > > read the vtable function pointer. On ia64, the above optimizes the > > > INDIRECT_REF away, so what the cxx_eval_call_expression actually gets > > > after constexpr evaluating the CALL_FN is not ADDR_EXPR of a function, > > > but the address of the function descriptor (e.g. &_ZTV2X2 + 16 ). > > > > > > So, perhaps in cxx_eval_call_expression we need: > > > if (TREE_CODE (fun) == ADDR_EXPR) > > > fun = TREE_OPERAND (fun, 0); > > > + else if (TARGET_VTABLE_USES_DESCRIPTORS > > > + && TREE_CODE (fun) == POINTER_PLUS_EXPR > > > + && ...) > > > where we verify that p+ first argument is ADDR_EXPR of a virtual table, > > > second arg is INTEGER_CST and just walk the DECL_INITIAL of that, > finding > > > the FDESC_EXPR at the right offset (therefore, I believe you need > following > > > rather than the patch you've posted, so that you can actually find it) > and > > > finally pick the function from the FDESC_EXPR entry. > > > Makes me wonder what happens with indirect calls in constexpr > evaluation, > > > e.g. if I do: > > > constexpr int bar () { return 42; } > > > constexpr int foo () { int (*fn) () = bar; return fn (); } > > > static_assert (foo () == 42); > > > but apparently this works. > > > > > > --- gcc/cp/class.c.jj 2018-09-20 09:56:59.229751895 +0200 > > > +++ gcc/cp/class.c 2018-09-20 10:12:17.447370890 +0200 > > > @@ -9266,7 +9266,6 @@ build_vtbl_initializer (tree binfo, > > > tree vcall_index; > > > tree fn, fn_original; > > > tree init = NULL_TREE; > > > - tree idx = size_int (jx++); > > > > > > fn = BV_FN (v); > > > fn_original = fn; > > > @@ -9370,7 +9369,7 @@ build_vtbl_initializer (tree binfo, > > > int i; > > > if (init == size_zero_node) > > > for (i = 0; i < TARGET_VTABLE_USES_DESCRIPTORS; ++i) > > > - CONSTRUCTOR_APPEND_ELT (*inits, idx, init); > > > + CONSTRUCTOR_APPEND_ELT (*inits, size_int (jx++), init); > > > else > > > for (i = 0; i < TARGET_VTABLE_USES_DESCRIPTORS; ++i) > > > { > > > @@ -9378,11 +9377,11 @@ build_vtbl_initializer (tree binfo, > > > fn, build_int_cst (NULL_TREE, i)); > > > TREE_CONSTANT (fdesc) = 1; > > > > > > - CONSTRUCTOR_APPEND_ELT (*inits, idx, fdesc); > > > + CONSTRUCTOR_APPEND_ELT (*inits, size_int (jx++), > fdesc); > > > } > > > } > > > else > > > - CONSTRUCTOR_APPEND_ELT (*inits, idx, init); > > > + CONSTRUCTOR_APPEND_ELT (*inits, size_int (jx++), init); > > > } > > > } > > > > This patch is OK. And your suggestion for cxx_eval_call_expression > > sounds right, too. Marek, will you follow up on that? > > Ok, I will (provided I can get a box that has > TARGET_VTABLE_USES_DESCRIPTORS, I > think ppc64 BE should be enough). > Since this is a compile time issue, I would think a cross compiler would do the trick? >
On Thu, Sep 27, 2018 at 07:08:41PM -0400, Marek Polacek wrote: > > This patch is OK. And your suggestion for cxx_eval_call_expression > > sounds right, too. Marek, will you follow up on that? > > Ok, I will (provided I can get a box that has TARGET_VTABLE_USES_DESCRIPTORS, I > think ppc64 BE should be enough). AFAIK only ia64 defines this macro to non-zero, so you need to cross-compile. Given that this is constexpr stuff, all you are looking for is compile time anyway ;) Jakub
On Sep 20 2018, Jakub Jelinek <jakub@redhat.com> wrote: > --- gcc/cp/class.c.jj 2018-09-20 09:56:59.229751895 +0200 > +++ gcc/cp/class.c 2018-09-20 10:12:17.447370890 +0200 > @@ -9266,7 +9266,6 @@ build_vtbl_initializer (tree binfo, > tree vcall_index; > tree fn, fn_original; > tree init = NULL_TREE; > - tree idx = size_int (jx++); > > fn = BV_FN (v); > fn_original = fn; > @@ -9370,7 +9369,7 @@ build_vtbl_initializer (tree binfo, > int i; > if (init == size_zero_node) > for (i = 0; i < TARGET_VTABLE_USES_DESCRIPTORS; ++i) > - CONSTRUCTOR_APPEND_ELT (*inits, idx, init); > + CONSTRUCTOR_APPEND_ELT (*inits, size_int (jx++), init); > else > for (i = 0; i < TARGET_VTABLE_USES_DESCRIPTORS; ++i) > { > @@ -9378,11 +9377,11 @@ build_vtbl_initializer (tree binfo, > fn, build_int_cst (NULL_TREE, i)); > TREE_CONSTANT (fdesc) = 1; > > - CONSTRUCTOR_APPEND_ELT (*inits, idx, fdesc); > + CONSTRUCTOR_APPEND_ELT (*inits, size_int (jx++), fdesc); > } > } > else > - CONSTRUCTOR_APPEND_ELT (*inits, idx, init); > + CONSTRUCTOR_APPEND_ELT (*inits, size_int (jx++), init); > } > } This still doesn't fix the tests. Andreas.
On Mon, Oct 08, 2018 at 03:36:18PM +0200, Andreas Schwab wrote:
> This still doesn't fix the tests.
Sure, one needs to add the constexpr virtual calls for ia64 as I said in the
mail.
Jakub
diff --git gcc/cp/call.c gcc/cp/call.c index 69503ca7920..6c70874af40 100644 --- gcc/cp/call.c +++ gcc/cp/call.c @@ -8401,7 +8401,8 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain) if (DECL_VINDEX (fn) && (flags & LOOKUP_NONVIRTUAL) == 0 /* Don't mess with virtual lookup in instantiate_non_dependent_expr; - virtual functions can't be constexpr. */ + virtual functions can't be constexpr. FIXME Actually, no longer + true in C++2a. */ && !in_template_function ()) { tree t; diff --git gcc/cp/constexpr.c gcc/cp/constexpr.c index 88c73787961..8ebd86b611c 100644 --- gcc/cp/constexpr.c +++ gcc/cp/constexpr.c @@ -2414,16 +2414,27 @@ cxx_eval_array_reference (const constexpr_ctx *ctx, tree t, bool *non_constant_p, bool *overflow_p) { tree oldary = TREE_OPERAND (t, 0); + tree oldidx = TREE_OPERAND (t, 1); + + /* The virtual table isn't constexpr, but has static storage duration and its + initializer is a compile-time constant, so we handle referencing an element + in the table specially. */ + if (TREE_TYPE (t) == vtable_entry_type) + { + VERIFY_CONSTANT (oldidx); + tree ctor = DECL_INITIAL (oldary); + return CONSTRUCTOR_ELT (ctor, tree_to_uhwi (oldidx))->value; + } + tree ary = cxx_eval_constant_expression (ctx, oldary, lval, non_constant_p, overflow_p); - tree index, oldidx; + tree index; HOST_WIDE_INT i = 0; tree elem_type = NULL_TREE; unsigned len = 0, elem_nchars = 1; if (*non_constant_p) return t; - oldidx = TREE_OPERAND (t, 1); index = cxx_eval_constant_expression (ctx, oldidx, false, non_constant_p, overflow_p); @@ -4209,7 +4220,14 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, CONST_DECL for aggregate constants. */ if (lval) return t; + /* is_really_empty_class doesn't take into account _vptr, so initializing + otherwise empty class with { } would overwrite the initializer that + initialize_vtable created for us. */ if (COMPLETE_TYPE_P (TREE_TYPE (t)) + && !(DECL_INITIAL (t) + && TREE_CODE (DECL_INITIAL (t)) == CONSTRUCTOR + /* But if DECL_INITIAL was { }, do mark it as constant. */ + && CONSTRUCTOR_NELTS (DECL_INITIAL (t)) > 0) && is_really_empty_class (TREE_TYPE (t))) { /* If the class is empty, we aren't actually loading anything. */ @@ -4778,7 +4796,6 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, case MODOP_EXPR: /* GCC internal stuff. */ case VA_ARG_EXPR: - case OBJ_TYPE_REF: case NON_DEPENDENT_EXPR: case BASELINK: case OFFSET_REF: @@ -4788,6 +4805,28 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, *non_constant_p = true; break; + case OBJ_TYPE_REF: + { + /* Virtual function call. Let the constexpr machinery figure out + the dynamic type. */ + int token = tree_to_shwi (OBJ_TYPE_REF_TOKEN (t)); + tree obj = OBJ_TYPE_REF_OBJECT (t); + obj = cxx_eval_constant_expression (ctx, obj, lval, non_constant_p, + overflow_p); + /* We expect something in the form of &x.D.2103.D.2094; get x. */ + if (TREE_CODE (obj) != ADDR_EXPR) + return t; + obj = TREE_OPERAND (obj, 0); + while (handled_component_p (obj)) + obj = TREE_OPERAND (obj, 0); + tree objtype = TREE_TYPE (obj); + /* Find the function decl in the virtual functions list. TOKEN is + the DECL_VINDEX that says which function we're looking for. */ + tree virtuals = BINFO_VIRTUALS (TYPE_BINFO (objtype)); + r = TREE_VALUE (chain_index (token, virtuals)); + break; + } + case PLACEHOLDER_EXPR: /* Use of the value or address of the current object. */ if (tree ctor = lookup_placeholder (ctx, lval, TREE_TYPE (t))) @@ -5871,7 +5910,6 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now, case OACC_UPDATE: /* GCC internal stuff. */ case VA_ARG_EXPR: - case OBJ_TYPE_REF: case TRANSACTION_EXPR: case ASM_EXPR: case AT_ENCODE_EXPR: @@ -5880,6 +5918,14 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now, error_at (loc, "expression %qE is not a constant expression", t); return false; + case OBJ_TYPE_REF: + if (cxx_dialect >= cxx2a) + /* In C++2a virtual calls can be constexpr, don't give up yet. */ + return true; + else if (flags & tf_error) + error_at (loc, "virtual functions cannot be constexpr before C++2a"); + return false; + case TYPEID_EXPR: /* -- a typeid expression whose operand is of polymorphic class type; */ diff --git gcc/cp/decl.c gcc/cp/decl.c index 50b60e89df5..da3749254e9 100644 --- gcc/cp/decl.c +++ gcc/cp/decl.c @@ -10854,12 +10854,13 @@ grokdeclarator (const cp_declarator *declarator, storage_class = sc_none; staticp = 0; } - if (constexpr_p) + if (constexpr_p && cxx_dialect < cxx2a) { gcc_rich_location richloc (declspecs->locations[ds_virtual]); richloc.add_range (declspecs->locations[ds_constexpr]); - error_at (&richloc, "member %qD cannot be declared both %<virtual%> " - "and %<constexpr%>", dname); + pedwarn (&richloc, OPT_Wpedantic, "member %qD can be declared both " + "%<virtual%> and %<constexpr%> only in -std=c++2a or " + "-std=gnu++2a", dname); } } friendp = decl_spec_seq_has_spec_p (declspecs, ds_friend); diff --git gcc/testsuite/g++.dg/cpp0x/constexpr-virtual5.C gcc/testsuite/g++.dg/cpp0x/constexpr-virtual5.C index 2465f9d9b4f..5f9ab4d9c28 100644 --- gcc/testsuite/g++.dg/cpp0x/constexpr-virtual5.C +++ gcc/testsuite/g++.dg/cpp0x/constexpr-virtual5.C @@ -2,5 +2,5 @@ // { dg-do compile { target c++11 } } struct S { - constexpr virtual int f() { return 1; } // { dg-error "13:member .f. cannot be declared both .virtual. and .constexpr." } + constexpr virtual int f() { return 1; } // { dg-error "13:member .f. can be declared both .virtual. and .constexpr." "" { target c++17_down } } }; diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-virtual1.C gcc/testsuite/g++.dg/cpp2a/constexpr-virtual1.C index e69de29bb2d..fcf8cac6417 100644 --- gcc/testsuite/g++.dg/cpp2a/constexpr-virtual1.C +++ gcc/testsuite/g++.dg/cpp2a/constexpr-virtual1.C @@ -0,0 +1,8 @@ +// P1064R0 +// { dg-do compile { target c++11 } } +// { dg-options "-pedantic-errors" } + +struct X +{ + constexpr virtual int f() { return 0; } // { dg-error "member .f. can be declared both .virtual. and .constexpr. only" "" { target c++17_down } } +}; diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C index e69de29bb2d..9d82c5c59ac 100644 --- gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C +++ gcc/testsuite/g++.dg/cpp2a/constexpr-virtual2.C @@ -0,0 +1,49 @@ +// P1064R0 +// { dg-do compile } +// { dg-options "-std=c++2a" } + +struct X1 +{ + virtual int f() const = 0; +}; + +struct X2: public X1 +{ + constexpr virtual int f() const { return 2; } +}; + +struct X3: public X2 +{ + virtual int f() const { return 3; } +}; + +struct X4: public X3 +{ + constexpr virtual int f() const { return 4; } +}; + +constexpr int (X1::*pf)() const = &X1::f; + +constexpr X2 x2; +static_assert(x2.f() == 2); +static_assert((x2.*pf)() == 2); + +constexpr X1 const& r2 = x2; +static_assert(r2.f() == 2); +static_assert((r2.*pf)() == 2); + +constexpr X1 const* p2 = &x2; +static_assert(p2->f() == 2); +static_assert((p2->*pf)() == 2); + +constexpr X4 x4; +static_assert(x4.f() == 4); +static_assert((x4.*pf)() == 4); + +constexpr X1 const& r4 = x4; +static_assert(r4.f() == 4); +static_assert((r4.*pf)() == 4); + +constexpr X1 const* p4 = &x4; +static_assert(p4->f() == 4); +static_assert((p4->*pf)() == 4); diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-virtual3.C gcc/testsuite/g++.dg/cpp2a/constexpr-virtual3.C index e69de29bb2d..d71422fc4d0 100644 --- gcc/testsuite/g++.dg/cpp2a/constexpr-virtual3.C +++ gcc/testsuite/g++.dg/cpp2a/constexpr-virtual3.C @@ -0,0 +1,52 @@ +// P1064R0 +// { dg-do compile } +// { dg-options "-std=c++2a" } + +struct X1 +{ + virtual int f() const = 0; +}; + +struct X2: public X1 +{ + int i2 = 42; + constexpr virtual int f() const { return 2; } +}; + +struct X3: public X2 +{ + int i3 = 42; + virtual int f() const { return 3; } +}; + +struct X4: public X3 +{ + int i4 = 42; + constexpr virtual int f() const { return 4; } +}; + +constexpr int (X1::*pf)() const = &X1::f; + +constexpr X2 x2; +static_assert(x2.f() == 2); +static_assert((x2.*pf)() == 2); + +constexpr X1 const& r2 = x2; +static_assert(r2.f() == 2); +static_assert((r2.*pf)() == 2); + +constexpr X1 const* p2 = &x2; +static_assert(p2->f() == 2); +static_assert((p2->*pf)() == 2); + +constexpr X4 x4; +static_assert(x4.f() == 4); +static_assert((x4.*pf)() == 4); + +constexpr X1 const& r4 = x4; +static_assert(r4.f() == 4); +static_assert((r4.*pf)() == 4); + +constexpr X1 const* p4 = &x4; +static_assert(p4->f() == 4); +static_assert((p4->*pf)() == 4); diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-virtual4.C gcc/testsuite/g++.dg/cpp2a/constexpr-virtual4.C index e69de29bb2d..2038bebc6d1 100644 --- gcc/testsuite/g++.dg/cpp2a/constexpr-virtual4.C +++ gcc/testsuite/g++.dg/cpp2a/constexpr-virtual4.C @@ -0,0 +1,57 @@ +// P1064R0 +// { dg-do compile } +// { dg-options "-std=c++2a" } + +struct X1 +{ + virtual int f1() const = 0; + virtual int f2() const = 0; + virtual int f3() const = 0; +}; + +struct X2: public X1 +{ + constexpr virtual int f1() const { return 21; } + constexpr virtual int f2() const { return 22; } + constexpr virtual int f3() const { return 23; } +}; + +struct X3: public X2 +{ + virtual int f1() const { return 31; } + virtual int f2() const { return 32; } + virtual int f3() const { return 33; } +}; + +struct X4: public X3 +{ + constexpr virtual int f1() const { return 41; } + constexpr virtual int f2() const { return 42; } + constexpr virtual int f3() const { return 43; } +}; + +constexpr int (X1::*pf)() const = &X1::f2; + +constexpr X2 x2; +static_assert(x2.f2() == 22); +static_assert((x2.*pf)() == 22); + +constexpr X1 const& r2 = x2; +static_assert(r2.f2() == 22); +static_assert((r2.*pf)() == 22); + +constexpr X1 const* p2 = &x2; +static_assert(p2->f2() == 22); +static_assert((p2->*pf)() == 22); + +constexpr X4 x4; +static_assert(x4.f2() == 42); +static_assert((x4.*pf)() == 42); + +constexpr X1 const& r4 = x4; +static_assert(r4.f2() == 42); +static_assert((r4.*pf)() == 42); + +constexpr X1 const* p4 = &x4; +static_assert(p4->f2() == 42); +static_assert((p4->*pf)() == 42); diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-virtual5.C gcc/testsuite/g++.dg/cpp2a/constexpr-virtual5.C index e69de29bb2d..6d27990a8b6 100644 --- gcc/testsuite/g++.dg/cpp2a/constexpr-virtual5.C +++ gcc/testsuite/g++.dg/cpp2a/constexpr-virtual5.C @@ -0,0 +1,60 @@ +// P1064R0 +// { dg-do compile } +// { dg-options "-std=c++2a" } + +struct X1 +{ + virtual int f1() const = 0; + virtual int f2() const = 0; + virtual int f3() const = 0; +}; + +struct X2: public X1 +{ + int i2 = 42; + constexpr virtual int f1() const { return 21; } + constexpr virtual int f2() const { return 22; } + constexpr virtual int f3() const { return 23; } +}; + +struct X3: public X2 +{ + int i3 = 42; + virtual int f1() const { return 31; } + virtual int f2() const { return 32; } + virtual int f3() const { return 33; } +}; + +struct X4: public X3 +{ + int i4 = 42; + constexpr virtual int f1() const { return 41; } + constexpr virtual int f2() const { return 42; } + constexpr virtual int f3() const { return 43; } +}; + +constexpr int (X1::*pf)() const = &X1::f2; + +constexpr X2 x2; +static_assert(x2.f2() == 22); +static_assert((x2.*pf)() == 22); + +constexpr X1 const& r2 = x2; +static_assert(r2.f2() == 22); +static_assert((r2.*pf)() == 22); + +constexpr X1 const* p2 = &x2; +static_assert(p2->f2() == 22); +static_assert((p2->*pf)() == 22); + +constexpr X4 x4; +static_assert(x4.f2() == 42); +static_assert((x4.*pf)() == 42); + +constexpr X1 const& r4 = x4; +static_assert(r4.f2() == 42); +static_assert((r4.*pf)() == 42); + +constexpr X1 const* p4 = &x4; +static_assert(p4->f2() == 42); +static_assert((p4->*pf)() == 42); diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-virtual6.C gcc/testsuite/g++.dg/cpp2a/constexpr-virtual6.C index e69de29bb2d..ece5e703c32 100644 --- gcc/testsuite/g++.dg/cpp2a/constexpr-virtual6.C +++ gcc/testsuite/g++.dg/cpp2a/constexpr-virtual6.C @@ -0,0 +1,25 @@ +// P1064R0 +// { dg-do compile } +// { dg-options "-std=c++2a" } + +struct X1 +{ + constexpr virtual X1 const *f() const { return this; } +}; + +struct Y +{ + int m = 0; +}; + +struct X2: public Y, public X1 +{ + constexpr virtual X2 const *f() const { return this; } +}; + +constexpr X1 x1; +static_assert(x1.f() == &x1); + +constexpr X2 x2; +constexpr X1 const& r2 = x2; +static_assert(r2.f() == &r2); diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-virtual7.C gcc/testsuite/g++.dg/cpp2a/constexpr-virtual7.C index e69de29bb2d..b0f499608ef 100644 --- gcc/testsuite/g++.dg/cpp2a/constexpr-virtual7.C +++ gcc/testsuite/g++.dg/cpp2a/constexpr-virtual7.C @@ -0,0 +1,87 @@ +// P1064R0 +// { dg-do compile } +// { dg-options "-std=c++2a" } + +struct X1 +{ + virtual int f() const = 0; +}; + +struct X2: public X1 +{ + constexpr virtual int f() const { return 2; } +}; + +struct X3: public X2 +{ + virtual int f() const { return 3; } +}; + +struct X4: public X3 +{ + constexpr virtual int f() const { return 4; } +}; + +constexpr int (X1::*pf)() const = &X1::f; + +constexpr X2 x2; + +struct S +{ + int i, j; + constexpr S() : i(x2.f()), j((x2.*pf)()) { } +}; + +static_assert(S().i == 2); +static_assert(S().j == 2); + +constexpr X1 const& r2 = x2; + +struct S2 +{ + int i, j; + constexpr S2() : i(r2.f()), j((r2.*pf)()) { } +}; + +static_assert(S2().i == 2); +static_assert(S2().j == 2); + +constexpr X1 const* p2 = &x2; +struct S3 +{ + int i, j; + constexpr S3() : i(p2->f()), j((p2->*pf)()) { } +}; + +static_assert(S3().i == 2); +static_assert(S3().j == 2); + +constexpr X4 x4; +struct S4 +{ + int i, j; + constexpr S4() : i(x4.f()), j((x4.*pf)()) { } +}; + +static_assert(S4().i == 4); +static_assert(S4().j == 4); + +constexpr X1 const& r4 = x4; +struct S5 +{ + int i, j; + constexpr S5() : i(r4.f()), j((r4.*pf)()) { } +}; + +static_assert(S5().i == 4); +static_assert(S5().j == 4); + +constexpr X1 const* p4 = &x4; +struct S6 +{ + int i, j; + constexpr S6() : i(p4->f()), j((p4->*pf)()) { } +}; + +static_assert(S6().i == 4); +static_assert(S6().j == 4); diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-virtual8.C gcc/testsuite/g++.dg/cpp2a/constexpr-virtual8.C index e69de29bb2d..4a7cc972a91 100644 --- gcc/testsuite/g++.dg/cpp2a/constexpr-virtual8.C +++ gcc/testsuite/g++.dg/cpp2a/constexpr-virtual8.C @@ -0,0 +1,50 @@ +// P1064R0 +// { dg-do compile } +// { dg-options "-std=c++2a" } + +template<typename T> +struct X1 +{ + virtual T f() const = 0; +}; + +struct X2: public X1<int> +{ + constexpr virtual int f() const { return 2; } +}; + +struct X3: public X2 +{ + virtual int f() const { return 3; } +}; + +struct X4: public X3 +{ + constexpr virtual int f() const { return 4; } +}; + +constexpr int (X1<int>::*pf)() const = &X1<int>::f; + +constexpr X2 x2; +static_assert(x2.f() == 2); +static_assert((x2.*pf)() == 2); + +constexpr X1<int> const& r2 = x2; +static_assert(r2.f() == 2); +static_assert((r2.*pf)() == 2); + +constexpr X1<int> const* p2 = &x2; +static_assert(p2->f() == 2); +static_assert((p2->*pf)() == 2); + +constexpr X4 x4; +static_assert(x4.f() == 4); +static_assert((x4.*pf)() == 4); + +constexpr X1<int> const& r4 = x4; +static_assert(r4.f() == 4); +static_assert((r4.*pf)() == 4); + +constexpr X1<int> const* p4 = &x4; +static_assert(p4->f() == 4); +static_assert((p4->*pf)() == 4); diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-virtual9.C gcc/testsuite/g++.dg/cpp2a/constexpr-virtual9.C index e69de29bb2d..3a12adc2659 100644 --- gcc/testsuite/g++.dg/cpp2a/constexpr-virtual9.C +++ gcc/testsuite/g++.dg/cpp2a/constexpr-virtual9.C @@ -0,0 +1,83 @@ +// P1064R0 +// { dg-do compile } +// { dg-options "-std=c++2a" } + +struct X1 +{ + virtual int f() const = 0; + virtual int f(int) const = 0; + virtual int f(int, int) const = 0; +}; + +struct X2: public X1 +{ + constexpr virtual int f() const { return 2; } + constexpr virtual int f(int) const { return 12; } + constexpr virtual int f(int, int) const { return 22; } +}; + +struct X3: public X2 +{ + virtual int f() const { return 3; } + virtual int f(int) const { return 13; } + virtual int f(int, int) const { return 23; } +}; + +struct X4: public X3 +{ + constexpr virtual int f() const { return 4; } + constexpr virtual int f(int) const { return 14; } + constexpr virtual int f(int, int) const { return 24; } +}; + +constexpr int (X1::*pf)() const = &X1::f; +constexpr int (X1::*pf1)(int) const = &X1::f; +constexpr int (X1::*pf2)(int, int) const = &X1::f; + +constexpr X2 x2; +static_assert(x2.f() == 2); +static_assert((x2.*pf)() == 2); +static_assert(x2.f(1) == 12); +static_assert((x2.*pf1)(1) == 12); +static_assert(x2.f(1, 2) == 22); +static_assert((x2.*pf2)(1, 2) == 22); + +constexpr X1 const& r2 = x2; +static_assert(r2.f() == 2); +static_assert((r2.*pf)() == 2); +static_assert(r2.f(1) == 12); +static_assert((r2.*pf1)(1) == 12); +static_assert(r2.f(1, 2) == 22); +static_assert((r2.*pf2)(1, 2) == 22); + +constexpr X1 const* p2 = &x2; +static_assert(p2->f() == 2); +static_assert((p2->*pf)() == 2); +static_assert(p2->f(1) == 12); +static_assert((p2->*pf1)(1) == 12); +static_assert(p2->f(1, 2) == 22); +static_assert((p2->*pf2)(1, 2) == 22); + +constexpr X4 x4; +static_assert(x4.f() == 4); +static_assert((x4.*pf)() == 4); +static_assert(x4.f(1) == 14); +static_assert((x4.*pf1)(1) == 14); +static_assert(x4.f(1, 2) == 24); +static_assert((x4.*pf2)(1, 2) == 24); + +constexpr X1 const& r4 = x4; +static_assert(r4.f() == 4); +static_assert((r4.*pf)() == 4); +static_assert(r4.f(1) == 14); +static_assert((r4.*pf1)(1) == 14); +static_assert(r4.f(1, 2) == 24); +static_assert((r4.*pf2)(1, 2) == 24); + +constexpr X1 const* p4 = &x4; +static_assert(p4->f() == 4); +static_assert((p4->*pf)() == 4); +static_assert(p4->f(1) == 14); +static_assert((p4->*pf1)(1) == 14); +static_assert(p4->f(1, 2) == 24); +static_assert((p4->*pf2)(1, 2) == 24); diff --git gcc/testsuite/g++.dg/diagnostic/virtual-constexpr.C gcc/testsuite/g++.dg/diagnostic/virtual-constexpr.C index 2c83236cae9..9223c692737 100644 --- gcc/testsuite/g++.dg/diagnostic/virtual-constexpr.C +++ gcc/testsuite/g++.dg/diagnostic/virtual-constexpr.C @@ -1,14 +1,15 @@ -// { dg-options "-fdiagnostics-show-caret" } +// { dg-options "-fdiagnostics-show-caret -pedantic-errors" } // { dg-do compile { target c++11 } } +// { dg-skip-if "virtual constexpr" { *-*-* } { "-std=gnu++2a" } { "" } } struct S { - virtual constexpr void foo(); // { dg-error "3:member .foo. cannot be declared both .virtual. and .constexpr." } + virtual constexpr void foo(); // { dg-error "3:member .foo. can be declared both .virtual. and .constexpr." } /* { dg-begin-multiline-output "" } virtual constexpr void foo(); ^~~~~~~ ~~~~~~~~~ { dg-end-multiline-output "" } */ - constexpr virtual void bar(); // { dg-error "13:member .bar. cannot be declared both .virtual. and .constexpr." } + constexpr virtual void bar(); // { dg-error "13:member .bar. can be declared both .virtual. and .constexpr." } /* { dg-begin-multiline-output "" } constexpr virtual void bar(); ~~~~~~~~~ ^~~~~~~