re PR c++/52282 ([C++0x] rejects-valid issues with decltype/constexpr)
authorJason Merrill <jason@redhat.com>
Mon, 17 Nov 2014 17:00:38 +0000 (12:00 -0500)
committerJason Merrill <jason@gcc.gnu.org>
Mon, 17 Nov 2014 17:00:38 +0000 (12:00 -0500)
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
gcc/cp/cp-tree.h
gcc/cp/decl.c
gcc/cp/decl2.c
gcc/cp/tree.c
gcc/testsuite/g++.dg/cpp0x/constexpr-decltype1.C [new file with mode: 0644]

index 0c5e72b2027b0067537d3e37395b86d2f809d1a1..42521c9b767b94d2754093f52d19735e2d2251e5 100644 (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
index b69c73695890c7f5994cb83947ccebda5d0d4877..54f7e9b863396c7342974e8c118204e3222b86f6 100644 (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)' */
index 1ef97637426d859fd79034fad393138b4fe93355..1f22c265b8c93fa37e9ae555a5a6a4bf0c2b8a04 100644 (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);
index 1b686ef95ae10e5d4af6f5e8faa3220828d0e15e..fb8d0c82e40d794d28c3f374b913000949612486 100644 (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
index 21cecc2e39c8da17813be99ab125c2607475564d..4502273fb572ca440af5d5324bb3cb2fbc6d143d 100644 (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:
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-decltype1.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-decltype1.C
new file mode 100644 (file)
index 0000000..1ff8350
--- /dev/null
@@ -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;
+}