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:
Jason Merrill 2014-11-17 12:00:38 -05:00 committed by Jason Merrill
parent 72a4a8b0bc
commit 35abb8ed23
6 changed files with 123 additions and 54 deletions

View File

@ -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

View File

@ -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)' */

View File

@ -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);

View File

@ -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

View File

@ -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:

View File

@ -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;
}