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
This commit is contained in:
parent
72a4a8b0bc
commit
35abb8ed23
|
@ -1,3 +1,14 @@
|
|||
2014-11-17 Jason Merrill <jason@redhat.com>
|
||||
|
||||
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 <jason@redhat.com>
|
||||
|
||||
* parser.c (cp_parser_omp_declare_reduction_exprs): A block is not
|
||||
|
|
|
@ -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)' */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
// PR c++/52282
|
||||
// { dg-do run { target c++11 } }
|
||||
|
||||
template <typename T, T V>
|
||||
struct W { static constexpr T value() { return V; } };
|
||||
|
||||
template <typename T, T V>
|
||||
struct X { typedef T type; static constexpr type value() { return V; } };
|
||||
|
||||
template <typename T, T V>
|
||||
struct Y { using type = T; static constexpr type value() { return V; } };
|
||||
|
||||
template <typename T, T V>
|
||||
struct Z { static constexpr decltype(V) value() { return V; } };
|
||||
|
||||
template <typename T, T V>
|
||||
struct W_ { static constexpr T value = V; };
|
||||
|
||||
template <typename T, T V>
|
||||
struct X_ { typedef T type; static constexpr type value = V; };
|
||||
|
||||
template <typename T, T V>
|
||||
struct Y_ { using type = T; static constexpr type value = V; };
|
||||
|
||||
template <typename T, T V>
|
||||
struct Z_ { static constexpr decltype(V) value = V; };
|
||||
|
||||
|
||||
static_assert(W<int, 10>::value() == 10, "oops");
|
||||
static_assert(X<int, 10>::value() == 10, "oops");
|
||||
static_assert(Y<int, 10>::value() == 10, "oops");
|
||||
static_assert(Z<int, 10>::value() == 10, "oops");
|
||||
static_assert(W_<int, 10>::value == 10, "oops");
|
||||
static_assert(X_<int, 10>::value == 10, "oops");
|
||||
static_assert(Y_<int, 10>::value == 10, "oops");
|
||||
static_assert(Z_<int, 10>::value == 10, "oops");
|
||||
|
||||
extern constexpr int a = 10;
|
||||
static_assert(*W<const int*, &a>::value() == 10, "oops");
|
||||
static_assert(*X<const int*, &a>::value() == 10, "oops");
|
||||
static_assert(*Y<const int*, &a>::value() == 10, "oops");
|
||||
static_assert(*Z<const int*, &a>::value() == 10, "oops"); // ICE
|
||||
static_assert(*W_<const int*, &a>::value == 10, "oops");
|
||||
static_assert(*X_<const int*, &a>::value == 10, "oops");
|
||||
static_assert(*Y_<const int*, &a>::value == 10, "oops");
|
||||
static_assert(*Z_<const int*, &a>::value == 10, "oops"); // ICE
|
||||
|
||||
template <int V> constexpr int b() { return V; }
|
||||
static_assert((W<int(*)(), &b<10>>::value())() == 10, "oops");
|
||||
static_assert((X<int(*)(), &b<10>>::value())() == 10, "oops"); // incorrect evaluation
|
||||
static_assert((Y<int(*)(), &b<10>>::value())() == 10, "oops"); // incorrect evaluation
|
||||
static_assert((Z<int(*)(), &b<10>>::value())() == 10, "oops"); // ICE
|
||||
static_assert(W_<int(*)(), &b<10>>::value() == 10, "oops");
|
||||
static_assert(X_<int(*)(), &b<10>>::value() == 10, "oops");
|
||||
static_assert(Y_<int(*)(), &b<10>>::value() == 10, "oops");
|
||||
static_assert(Z_<int(*)(), &b<10>>::value() == 10, "oops"); // ICE
|
||||
|
||||
constexpr struct C {
|
||||
constexpr int c1() const { return 10; }
|
||||
static constexpr int c2() { return 10; }
|
||||
} c;
|
||||
|
||||
static_assert((c.*W<int(C::*)()const, &C::c1>::value())() == 10, "oops");
|
||||
static_assert((c.*X<int(C::*)()const, &C::c1>::value())() == 10, "oops");
|
||||
static_assert((c.*Y<int(C::*)()const, &C::c1>::value())() == 10, "oops");
|
||||
static_assert((c.*Z<int(C::*)()const, &C::c1>::value())() == 10, "oops");
|
||||
static_assert((c.*W_<int(C::*)()const, &C::c1>::value)() == 10, "oops"); // incorrect evaluation
|
||||
static_assert((c.*X_<int(C::*)()const, &C::c1>::value)() == 10, "oops"); // incorrect evaluation
|
||||
static_assert((c.*Y_<int(C::*)()const, &C::c1>::value)() == 10, "oops"); // incorrect evaluation
|
||||
static_assert((c.*Z_<int(C::*)()const, &C::c1>::value)() == 10, "oops"); // incorrect evaluation
|
||||
|
||||
static_assert((W<int(*)(), &C::c2>::value())() == 10, "oops");
|
||||
static_assert((X<int(*)(), &C::c2>::value())() == 10, "oops"); // incorrect evaluation
|
||||
static_assert((Y<int(*)(), &C::c2>::value())() == 10, "oops"); // incorrect evaluation
|
||||
static_assert((Z<int(*)(), &C::c2>::value())() == 10, "oops"); // ICE
|
||||
static_assert(W_<int(*)(), &C::c2>::value() == 10, "oops");
|
||||
static_assert(X_<int(*)(), &C::c2>::value() == 10, "oops");
|
||||
static_assert(Y_<int(*)(), &C::c2>::value() == 10, "oops");
|
||||
static_assert(Z_<int(*)(), &C::c2>::value() == 10, "oops"); // ICE
|
||||
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
template <typename T, T V>
|
||||
constexpr typename X_<T, V>::type X_<T, V>::value;
|
||||
|
||||
int main() {
|
||||
C c;
|
||||
|
||||
// correctly evaluates inside method scope
|
||||
int t1 = X<int(*)(), &b<10>>::value()();
|
||||
int t2 = (c.*X_<int(C::*)()const, &C::c1>::value)();
|
||||
int t3 = X<int(*)(), &C::c2>::value()();
|
||||
|
||||
assert(t1 == 10);
|
||||
assert(t2 == 10);
|
||||
assert(t3 == 10);
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue