From 35abb8ed23a3d2d1d4e0ab95c2da4f197089ce17 Mon Sep 17 00:00:00 2001 From: Jason Merrill Date: Mon, 17 Nov 2014 12:00:38 -0500 Subject: [PATCH] re PR c++/52282 ([C++0x] rejects-valid issues with decltype/constexpr) PR c++/52282 * decl.c (build_ptrmemfunc_type): Don't build a different RECORD_TYPE for a qualified PMF. * cp-tree.h (TYPE_PTRMEMFUNC_FN_TYPE): Merge cv-quals. (TYPE_PTRMEMFUNC_FN_TYPE_RAW): New. * decl2.c (cplus_decl_attributes): Use TYPE_PTRMEMFUNC_FN_TYPE_RAW. * tree.c (cp_walk_subtrees): Likewise. (cp_build_qualified_type_real): Remove special PMF handling. From-SVN: r217660 --- gcc/cp/ChangeLog | 11 +++ gcc/cp/cp-tree.h | 6 ++ gcc/cp/decl.c | 25 +---- gcc/cp/decl2.c | 2 +- gcc/cp/tree.c | 34 +------ .../g++.dg/cpp0x/constexpr-decltype1.C | 99 +++++++++++++++++++ 6 files changed, 123 insertions(+), 54 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp0x/constexpr-decltype1.C diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 0c5e72b2027..42521c9b767 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,14 @@ +2014-11-17 Jason Merrill + + PR c++/52282 + * decl.c (build_ptrmemfunc_type): Don't build a different + RECORD_TYPE for a qualified PMF. + * cp-tree.h (TYPE_PTRMEMFUNC_FN_TYPE): Merge cv-quals. + (TYPE_PTRMEMFUNC_FN_TYPE_RAW): New. + * decl2.c (cplus_decl_attributes): Use TYPE_PTRMEMFUNC_FN_TYPE_RAW. + * tree.c (cp_walk_subtrees): Likewise. + (cp_build_qualified_type_real): Remove special PMF handling. + 2014-11-15 Jason Merrill * parser.c (cp_parser_omp_declare_reduction_exprs): A block is not diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index b69c7369589..54f7e9b8633 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -3623,6 +3623,12 @@ more_aggr_init_expr_args_p (const aggr_init_expr_arg_iterator *iter) pointer to member function. TYPE_PTRMEMFUNC_P _must_ be true, before using this macro. */ #define TYPE_PTRMEMFUNC_FN_TYPE(NODE) \ + (cp_build_qualified_type (TREE_TYPE (TYPE_FIELDS (NODE)),\ + cp_type_quals (NODE))) + +/* As above, but can be used in places that want an lvalue at the expense + of not necessarily having the correct cv-qualifiers. */ +#define TYPE_PTRMEMFUNC_FN_TYPE_RAW(NODE) \ (TREE_TYPE (TYPE_FIELDS (NODE))) /* Returns `A' for a type like `int (A::*)(double)' */ diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index 1ef97637426..1f22c265b8c 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -8131,7 +8131,6 @@ build_ptrmemfunc_type (tree type) { tree field, fields; tree t; - tree unqualified_variant = NULL_TREE; if (type == error_mark_node) return type; @@ -8145,9 +8144,11 @@ build_ptrmemfunc_type (tree type) /* Make sure that we always have the unqualified pointer-to-member type first. */ - if (cp_type_quals (type) != TYPE_UNQUALIFIED) - unqualified_variant - = build_ptrmemfunc_type (TYPE_MAIN_VARIANT (type)); + if (cp_cv_quals quals = cp_type_quals (type)) + { + tree unqual = build_ptrmemfunc_type (TYPE_MAIN_VARIANT (type)); + return cp_build_qualified_type (unqual, quals); + } t = make_node (RECORD_TYPE); @@ -8168,22 +8169,6 @@ build_ptrmemfunc_type (tree type) information for this anonymous RECORD_TYPE. */ TYPE_NAME (t) = NULL_TREE; - /* If this is not the unqualified form of this pointer-to-member - type, set the TYPE_MAIN_VARIANT for this type to be the - unqualified type. Since they are actually RECORD_TYPEs that are - not variants of each other, we must do this manually. - As we just built a new type there is no need to do yet another copy. */ - if (cp_type_quals (type) != TYPE_UNQUALIFIED) - { - int type_quals = cp_type_quals (type); - TYPE_READONLY (t) = (type_quals & TYPE_QUAL_CONST) != 0; - TYPE_VOLATILE (t) = (type_quals & TYPE_QUAL_VOLATILE) != 0; - TYPE_RESTRICT (t) = (type_quals & TYPE_QUAL_RESTRICT) != 0; - TYPE_MAIN_VARIANT (t) = unqualified_variant; - TYPE_NEXT_VARIANT (t) = TYPE_NEXT_VARIANT (unqualified_variant); - TYPE_NEXT_VARIANT (unqualified_variant) = t; - } - /* Cache this pointer-to-member type so that we can find it again later. */ TYPE_SET_PTRMEMFUNC_TYPE (type, t); diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c index 1b686ef95ae..fb8d0c82e40 100644 --- a/gcc/cp/decl2.c +++ b/gcc/cp/decl2.c @@ -1474,7 +1474,7 @@ cplus_decl_attributes (tree *decl, tree attributes, int flags) { attributes = decl_attributes (decl, attributes, flags | ATTR_FLAG_FUNCTION_NEXT); - decl_attributes (&TYPE_PTRMEMFUNC_FN_TYPE (TREE_TYPE (*decl)), + decl_attributes (&TYPE_PTRMEMFUNC_FN_TYPE_RAW (TREE_TYPE (*decl)), attributes, flags); } else diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c index 21cecc2e39c..4502273fb57 100644 --- a/gcc/cp/tree.c +++ b/gcc/cp/tree.c @@ -1082,18 +1082,6 @@ cp_build_qualified_type_real (tree type, = TYPE_HAS_NONTRIVIAL_DESTRUCTOR (TYPE_MAIN_VARIANT (element_type)); return t; } - else if (TYPE_PTRMEMFUNC_P (type)) - { - /* For a pointer-to-member type, we can't just return a - cv-qualified version of the RECORD_TYPE. If we do, we - haven't changed the field that contains the actual pointer to - a method, and so TYPE_PTRMEMFUNC_FN_TYPE will be wrong. */ - tree t; - - t = TYPE_PTRMEMFUNC_FN_TYPE (type); - t = cp_build_qualified_type_real (t, type_quals, complain); - return build_ptrmemfunc_type (t); - } else if (TREE_CODE (type) == TYPE_PACK_EXPANSION) { tree t = PACK_EXPANSION_PATTERN (type); @@ -1154,26 +1142,6 @@ cp_build_qualified_type_real (tree type, result = build_ref_qualified_type (result, type_memfn_rqual (type)); } - /* If this was a pointer-to-method type, and we just made a copy, - then we need to unshare the record that holds the cached - pointer-to-member-function type, because these will be distinct - between the unqualified and qualified types. */ - if (result != type - && TYPE_PTR_P (type) - && TREE_CODE (TREE_TYPE (type)) == METHOD_TYPE - && TYPE_LANG_SPECIFIC (result) == TYPE_LANG_SPECIFIC (type)) - TYPE_LANG_SPECIFIC (result) = NULL; - - /* We may also have ended up building a new copy of the canonical - type of a pointer-to-method type, which could have the same - sharing problem described above. */ - if (TYPE_CANONICAL (result) != TYPE_CANONICAL (type) - && TYPE_PTR_P (type) - && TREE_CODE (TREE_TYPE (type)) == METHOD_TYPE - && (TYPE_LANG_SPECIFIC (TYPE_CANONICAL (result)) - == TYPE_LANG_SPECIFIC (TYPE_CANONICAL (type)))) - TYPE_LANG_SPECIFIC (TYPE_CANONICAL (result)) = NULL; - return result; } @@ -3705,7 +3673,7 @@ cp_walk_subtrees (tree *tp, int *walk_subtrees_p, walk_tree_fn func, case RECORD_TYPE: if (TYPE_PTRMEMFUNC_P (*tp)) - WALK_SUBTREE (TYPE_PTRMEMFUNC_FN_TYPE (*tp)); + WALK_SUBTREE (TYPE_PTRMEMFUNC_FN_TYPE_RAW (*tp)); break; case TYPE_ARGUMENT_PACK: diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-decltype1.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-decltype1.C new file mode 100644 index 00000000000..1ff835036c8 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-decltype1.C @@ -0,0 +1,99 @@ +// PR c++/52282 +// { dg-do run { target c++11 } } + +template +struct W { static constexpr T value() { return V; } }; + +template +struct X { typedef T type; static constexpr type value() { return V; } }; + +template +struct Y { using type = T; static constexpr type value() { return V; } }; + +template +struct Z { static constexpr decltype(V) value() { return V; } }; + +template +struct W_ { static constexpr T value = V; }; + +template +struct X_ { typedef T type; static constexpr type value = V; }; + +template +struct Y_ { using type = T; static constexpr type value = V; }; + +template +struct Z_ { static constexpr decltype(V) value = V; }; + + +static_assert(W::value() == 10, "oops"); +static_assert(X::value() == 10, "oops"); +static_assert(Y::value() == 10, "oops"); +static_assert(Z::value() == 10, "oops"); +static_assert(W_::value == 10, "oops"); +static_assert(X_::value == 10, "oops"); +static_assert(Y_::value == 10, "oops"); +static_assert(Z_::value == 10, "oops"); + +extern constexpr int a = 10; +static_assert(*W::value() == 10, "oops"); +static_assert(*X::value() == 10, "oops"); +static_assert(*Y::value() == 10, "oops"); +static_assert(*Z::value() == 10, "oops"); // ICE +static_assert(*W_::value == 10, "oops"); +static_assert(*X_::value == 10, "oops"); +static_assert(*Y_::value == 10, "oops"); +static_assert(*Z_::value == 10, "oops"); // ICE + +template constexpr int b() { return V; } +static_assert((W>::value())() == 10, "oops"); +static_assert((X>::value())() == 10, "oops"); // incorrect evaluation +static_assert((Y>::value())() == 10, "oops"); // incorrect evaluation +static_assert((Z>::value())() == 10, "oops"); // ICE +static_assert(W_>::value() == 10, "oops"); +static_assert(X_>::value() == 10, "oops"); +static_assert(Y_>::value() == 10, "oops"); +static_assert(Z_>::value() == 10, "oops"); // ICE + +constexpr struct C { + constexpr int c1() const { return 10; } + static constexpr int c2() { return 10; } +} c; + +static_assert((c.*W::value())() == 10, "oops"); +static_assert((c.*X::value())() == 10, "oops"); +static_assert((c.*Y::value())() == 10, "oops"); +static_assert((c.*Z::value())() == 10, "oops"); +static_assert((c.*W_::value)() == 10, "oops"); // incorrect evaluation +static_assert((c.*X_::value)() == 10, "oops"); // incorrect evaluation +static_assert((c.*Y_::value)() == 10, "oops"); // incorrect evaluation +static_assert((c.*Z_::value)() == 10, "oops"); // incorrect evaluation + +static_assert((W::value())() == 10, "oops"); +static_assert((X::value())() == 10, "oops"); // incorrect evaluation +static_assert((Y::value())() == 10, "oops"); // incorrect evaluation +static_assert((Z::value())() == 10, "oops"); // ICE +static_assert(W_::value() == 10, "oops"); +static_assert(X_::value() == 10, "oops"); +static_assert(Y_::value() == 10, "oops"); +static_assert(Z_::value() == 10, "oops"); // ICE + + +#include + +template +constexpr typename X_::type X_::value; + +int main() { + C c; + + // correctly evaluates inside method scope + int t1 = X>::value()(); + int t2 = (c.*X_::value)(); + int t3 = X::value()(); + + assert(t1 == 10); + assert(t2 == 10); + assert(t3 == 10); + return 0; +}