c++: Allow subobject references in C++20.
authorJason Merrill <jason@redhat.com>
Tue, 7 Jul 2020 05:41:35 +0000 (01:41 -0400)
committerJason Merrill <jason@redhat.com>
Mon, 20 Jul 2020 21:54:18 +0000 (17:54 -0400)
The last new thing allowed by P1907R1: subobject addresses as template
arguments.  The ABI group has discussed mangling for this; there has been
some talk of a compressed subobject mangling, but it hasn't been finalized,
so for now I'm using normal expression mangling.  In order for two array
subobject references to compare as equal template arguments, the offsets
need to have the same type, so I convert them to always be the same type,
currently ptrdiff_t.  Base class conversions are represented as a cast to
reference type, only if necessary to resolve an ambiguity.

This patch also updates the value of __cpp_nontype_template_args, since
the paper is fully implemented.

gcc/cp/ChangeLog:

* mangle.c (write_base_ref): New.
(write_expression): Use it for base field COMPONENT_REFs.
* pt.c (invalid_tparm_referent_p): Canonicalize the type
of array offsets.  Allow subobjects.

gcc/c-family/ChangeLog:

* c-cppbuiltin.c (c_cpp_builtins): Update
__cpp_nontype_template_args for C++20.

gcc/testsuite/ChangeLog:

* g++.dg/cpp1z/nontype2.C: No error in C++20.
* g++.dg/template/nontype25.C: No error in C++20.
* g++.dg/template/nontype8.C: No error in C++20.
* g++.dg/cpp2a/nontype-subob1.C: New test.
* g++.dg/cpp2a/nontype-subob2.C: New test.
* g++.dg/cpp1z/nontype3.C: Now C++17-only.
* g++.dg/cpp2a/feat-cxx2a.C: Adjust expected value.

gcc/c-family/c-cppbuiltin.c
gcc/cp/mangle.c
gcc/cp/pt.c
gcc/testsuite/g++.dg/cpp1z/nontype2.C
gcc/testsuite/g++.dg/cpp1z/nontype3.C
gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C
gcc/testsuite/g++.dg/cpp2a/nontype-subob1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp2a/nontype-subob2.C [new file with mode: 0644]
gcc/testsuite/g++.dg/template/nontype25.C
gcc/testsuite/g++.dg/template/nontype8.C

index 83f52fdf5d872a250aef5fdbad51510188e0ca47..74ecca8de8e08610c43249047e0be1a3bab6eb2e 100644 (file)
@@ -967,7 +967,8 @@ c_cpp_builtins (cpp_reader *pfile)
          cpp_define (pfile, "__cpp_enumerator_attributes=201411L");
          cpp_define (pfile, "__cpp_nested_namespace_definitions=201411L");
          cpp_define (pfile, "__cpp_fold_expressions=201603L");
-         cpp_define (pfile, "__cpp_nontype_template_args=201411L");
+         if (cxx_dialect <= cxx17)
+           cpp_define (pfile, "__cpp_nontype_template_args=201411L");
          cpp_define (pfile, "__cpp_range_based_for=201603L");
          if (cxx_dialect <= cxx17)
            cpp_define (pfile, "__cpp_constexpr=201603L");
@@ -998,6 +999,7 @@ c_cpp_builtins (cpp_reader *pfile)
          cpp_define (pfile, "__cpp_consteval=201811L");
          cpp_define (pfile, "__cpp_constinit=201907L");
          cpp_define (pfile, "__cpp_deduction_guides=201907L");
+         cpp_define (pfile, "__cpp_nontype_template_args=201911L");
          cpp_define (pfile, "__cpp_nontype_template_parameter_class=201806L");
          cpp_define (pfile, "__cpp_impl_destroying_delete=201806L");
          cpp_define (pfile, "__cpp_constexpr_dynamic_alloc=201907L");
index ab2d8ecf2f27727c5fd820dac041c677a0ea2e14..43ff2e84db5850e831b89f796e6f90a1b2e400a0 100644 (file)
@@ -2850,6 +2850,60 @@ write_member_name (tree member)
     write_expression (member);
 }
 
+/* EXPR is a base COMPONENT_REF; write the minimized base conversion path for
+   converting to BASE, or just the conversion of EXPR if BASE is null.
+
+   "Given a fully explicit base path P := C_n -> ... -> C_0, the minimized base
+   path Min(P) is defined as follows: let C_i be the last element for which the
+   conversion to C_0 is unambiguous; if that element is C_n, the minimized path
+   is C_n -> C_0; otherwise, the minimized path is Min(C_n -> ... -> C_i) ->
+   C_0."
+
+   We mangle the conversion to C_i if it's different from C_n.  */
+
+static bool
+write_base_ref (tree expr, tree base = NULL_TREE)
+{
+  if (TREE_CODE (expr) != COMPONENT_REF)
+    return false;
+
+  tree field = TREE_OPERAND (expr, 1);
+
+  if (TREE_CODE (field) != FIELD_DECL || !DECL_FIELD_IS_BASE (field))
+    return false;
+
+  tree object = TREE_OPERAND (expr, 0);
+
+  tree binfo = NULL_TREE;
+  if (base)
+    {
+      tree cur = TREE_TYPE (object);
+      binfo = lookup_base (cur, base, ba_unique, NULL, tf_none);
+    }
+  else
+    /* We're at the end of the base conversion chain, so it can't be
+       ambiguous.  */
+    base = TREE_TYPE (field);
+
+  if (binfo == error_mark_node)
+    {
+      /* cur->base is ambiguous, so make the conversion to
+        last explicit, expressed as a cast (last&)object.  */
+      tree last = TREE_TYPE (expr);
+      write_string (OVL_OP_INFO (false, CAST_EXPR)->mangled_name);
+      write_type (build_reference_type (last));
+      write_expression (object);
+    }
+  else if (write_base_ref (object, base))
+    /* cur->base is unambiguous, but we had another base conversion
+       underneath and wrote it out.  */;
+  else
+    /* No more base conversions, just write out the object.  */
+    write_expression (object);
+
+  return true;
+}
+
 /* <expression> ::= <unary operator-name> <expression>
                ::= <binary operator-name> <expression> <expression>
                ::= <expr-primary>
@@ -3268,6 +3322,8 @@ write_expression (tree expr)
              ob = TREE_OPERAND (ob, 0);
              write_expression (ob);
            }
+         else if (write_base_ref (expr))
+           return;
          else if (!is_dummy_object (ob))
            {
              write_string ("dt");
index 5f43e9c5c69779f3c7b74d0906336766daa9f1b2..cfe5dcd59cf11f9aa1c8af7a4af79341f5ee84cc 100644 (file)
@@ -6907,7 +6907,9 @@ template_parm_object_p (const_tree t)
 }
 
 /* Subroutine of convert_nontype_argument, to check whether EXPR, as an
-   argument for TYPE, points to an unsuitable object.  */
+   argument for TYPE, points to an unsuitable object.
+
+   Also adjust the type of the index in C++20 array subobject references.  */
 
 static bool
 invalid_tparm_referent_p (tree type, tree expr, tsubst_flags_t complain)
@@ -6935,6 +6937,19 @@ invalid_tparm_referent_p (tree type, tree expr, tsubst_flags_t complain)
       {
        tree decl = TREE_OPERAND (expr, 0);
 
+       if (cxx_dialect >= cxx20)
+         while (TREE_CODE (decl) == COMPONENT_REF
+                || TREE_CODE (decl) == ARRAY_REF)
+           {
+             tree &op = TREE_OPERAND (decl, 1);
+             if (TREE_CODE (decl) == ARRAY_REF
+                 && TREE_CODE (op) == INTEGER_CST)
+               /* Canonicalize array offsets to ptrdiff_t; how they were
+                  written doesn't matter for subobject identity.  */
+               op = fold_convert (ptrdiff_type_node, op);
+             decl = TREE_OPERAND (decl, 0);
+           }
+
        if (!VAR_P (decl))
          {
            if (complain & tf_error)
@@ -6976,9 +6991,10 @@ invalid_tparm_referent_p (tree type, tree expr, tsubst_flags_t complain)
                     decl);
            return true;
          }
-       else if (!same_type_ignoring_top_level_qualifiers_p
-                (strip_array_types (TREE_TYPE (type)),
-                 strip_array_types (TREE_TYPE (decl))))
+       else if (cxx_dialect < cxx20
+                && !(same_type_ignoring_top_level_qualifiers_p
+                     (strip_array_types (TREE_TYPE (type)),
+                      strip_array_types (TREE_TYPE (decl)))))
          {
            if (complain & tf_error)
              error ("the address of the %qT subobject of %qD is not a "
index 75dc7600379f9209036f3b2fc8b0af26f9338d10..cf73166dc3a5bba5775551348ccc5ce45e996b55 100644 (file)
@@ -8,7 +8,7 @@ template<int* p> class X { };
 template<const char *s> class Y {};
 template<const std::type_info &> class Z {};
 
-X<&s.m> x7;                    // { dg-error "3:.& s.S::m. is not a valid template argument" }
+X<&s.m> x7; // { dg-error "3:.& s.S::m. is not a valid template argument" "" { target c++17_down } }
 Y<"foo"> y1;                   // { dg-error "string literal" }
 Z<typeid(p)> z1;               // { dg-error "" }
 
index 80f1e98ab76bbc477cb22ee2e04559f56fcbe144..1e02f1e1f5b4899d9022f370c9393ab1617783c3 100644 (file)
@@ -1,4 +1,4 @@
-// { dg-do compile { target c++17 } }
+// { dg-do compile { target c++17_only } }
 
 #ifndef __cpp_nontype_template_args
 #error __cpp_nontype_template_args not defined
index 82fd602f9f1d592cc4deafb22af2dd176d259d9b..7f1fe34ad7fa8aa9b2335e269343ccea6dfc69e3 100644 (file)
 
 #ifndef __cpp_nontype_template_args
 #  error "__cpp_nontype_template_args"
-#elif __cpp_nontype_template_args != 201411
-#  error "__cpp_nontype_template_args != 201411"
+#elif __cpp_nontype_template_args != 201911
+#  error "__cpp_nontype_template_args != 201911"
 #endif
 
 #ifndef __cpp_hex_float
diff --git a/gcc/testsuite/g++.dg/cpp2a/nontype-subob1.C b/gcc/testsuite/g++.dg/cpp2a/nontype-subob1.C
new file mode 100644 (file)
index 0000000..4c1633e
--- /dev/null
@@ -0,0 +1,25 @@
+// { dg-do compile { target c++20 } }
+
+template <auto N> struct A {};
+template <class,class> struct assert_same;
+template <class T> struct assert_same<T,T> {};
+
+#define TEQ(X,Y) static_assert(__is_same(A<(X)>,A<(Y)>))
+#define TNEQ(X,Y) static_assert(!__is_same(A<(X)>,A<(Y)>))
+
+struct C { int i; };
+
+struct B: C
+{
+  int j[3];
+} b;
+
+// { dg-final { scan-assembler _Z1f1AIXaddtL_Z1bE1iEE } }
+void f(A<&b.i>) {}
+TEQ(&b.i,&((C*)&b)->i);
+
+// { dg-final { scan-assembler _Z1g1AIXadixdtL_Z1bE1jLl1EEE } }
+void g(A<&b.j[0]+1>) {}
+TEQ(&b.j[1],&b.j[1]);
+TEQ(&b.j[1],&b.j[0]+1);
+TNEQ(&b.j[1],&b.j[0]);
diff --git a/gcc/testsuite/g++.dg/cpp2a/nontype-subob2.C b/gcc/testsuite/g++.dg/cpp2a/nontype-subob2.C
new file mode 100644 (file)
index 0000000..a5768f6
--- /dev/null
@@ -0,0 +1,13 @@
+// { dg-do compile { target c++20 } }
+// { dg-additional-options -Wno-inaccessible-base }
+
+struct Base { int i; };
+template <int N> struct Derived : Derived<N-1>, Base {};
+template <> struct Derived<0> : Base {};
+
+template <int* P> struct A { };
+
+Derived<4> d;
+void f(A<&((Derived<0>&)d).i>) {}
+
+// { dg-final { scan-assembler _Z1f1AIXaddtcvR7DerivedILi0EEL_Z1dE1iEE } }
index 3918bea4246b32e7e9b13a36fdf71e2626c853c0..aba85092794bbec39b8fc5e9236229721d4da633 100644 (file)
@@ -7,16 +7,16 @@ template<const A* a> class C {};
 template<const B* b> class D {};
 template<B* b> class E {};
 
-template<const B* b> void f(D<b> &, C<static_cast<const A*>(b)> &) {} // { dg-error "" }
+template<const B* b> void f(D<b> &, C<static_cast<const A*>(b)> &) {} // { dg-error "" "" { target { ! c++20 } } }
 template<const B* b> void g(D<b> &, E<const_cast<B*>(b)> &) {} // { dg-error "" "" { target { ! c++11 } } }
 
 B b;
 
 int main()
 {
-  C<static_cast<const A*>(&b)> c; // { dg-error "" }
+  C<static_cast<const A*>(&b)> c; // { dg-error "" "" { target c++17_down } }
   D<&b> d;
   E<const_cast<B*>(&b)> e; // { dg-error "" "" { target { ! c++11 } } }
-  f(d, c);                // { dg-error "" "" { target c++11 } }
+  f(d, c);                // { dg-error "" "" { target { c++11 && { ! c++20 } } } }
   g(d, e);
 }
index b4fbeaed2d8228c79db593d61505260b88b2cd15..a0458b91ed4831a0f6567be09693c7c4ecf3aa6f 100644 (file)
@@ -6,9 +6,9 @@ template<int* p> class X { };
 int a[10];
 struct S { int m; static int s; } s;
 
-X<&a[2]> x3;                    // { dg-error "3:.& a\\\[2\\\]. is not a valid template argument" "" { target c++17 } }
+X<&a[2]> x3;                    // { dg-error "3:.& a\\\[2\\\]. is not a valid template argument" "" { target c++17_only } }
 // { dg-error "" "" { target c++14_down } .-1 }
-X<&s.m> x4;                     // { dg-error "3:.& s.S::m. is not a valid template argument" "" { target c++17 } }
+X<&s.m> x4;                     // { dg-error "3:.& s.S::m. is not a valid template argument" "" { target c++17_only } }
 // { dg-error "" "" { target c++14_down } .-1 }
 X<&s.s> x5;                     // { dg-error "" "" { target { ! c++17 } } } &S::s must be used
 X<&S::s> x6;                    // OK: address of static member