public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [pushed] c++: C++20 class NTTP trailing zero-init [PR100079]
@ 2021-04-16  5:16 Jason Merrill
  2021-04-29 18:29 ` [pushed] c++: unset COMPOUND_LITERAL_P [PR100079] Jason Merrill
  0 siblings, 1 reply; 2+ messages in thread
From: Jason Merrill @ 2021-04-16  5:16 UTC (permalink / raw)
  To: gcc-patches

The new testcase was breaking because constexpr evaluation was simplifying
Bar{Baz<42>{}} to Bar{empty}, but then we weren't treating them as
equivalent.  Poking at this revealed that the code for eliding trailing
zero-initialization in class non-type template argument mangling was pretty
broken, including the test, mangle71.

I dealt with the FIXME to support RANGE_EXPR, and fixed the confusion
between a list-initialized temporary mangled as written (i.e. in the
signature of a function template) and a template parameter object mangled as
the value representation of the object.  I'm distinguishing between these
using COMPOUND_LITERAL_P.  A later patch will adjust the use of
COMPOUND_LITERAL_P to be more useful for this distinction, but it works now
for distinguishing these cases in mangling.

Tested x86_64-pc-linux-gnu, applying to trunk.

gcc/cp/ChangeLog:

	PR c++/100079
	* cp-tree.h (first_field): Declare.
	* mangle.c (range_expr_nelts): New.
	(write_expression): Improve class NTTP mangling.
	* pt.c (get_template_parm_object): Clear TREE_HAS_CONSTRUCTOR.
	* tree.c (zero_init_expr_p): Improve class NTTP handling.
	* decl.c: Adjust comment.

gcc/testsuite/ChangeLog:

	PR c++/100079
	* g++.dg/abi/mangle71.C: Fix expected mangling.
	* g++.dg/abi/mangle77.C: New test.
	* g++.dg/cpp2a/nontype-class-union1.C: Likewise.
	* g++.dg/cpp2a/nontype-class-equiv1.C: Removed.
	* g++.dg/cpp2a/nontype-class44.C: New test.
---
 gcc/cp/cp-tree.h                              |  1 +
 gcc/cp/decl.c                                 |  2 +-
 gcc/cp/mangle.c                               | 40 ++++++++++++++-----
 gcc/cp/pt.c                                   |  3 ++
 gcc/cp/tree.c                                 | 28 ++++++++-----
 gcc/testsuite/g++.dg/abi/mangle71.C           | 12 +++---
 gcc/testsuite/g++.dg/abi/mangle77.C           | 31 ++++++++++++++
 .../g++.dg/cpp2a/nontype-class-equiv1.C       | 25 ------------
 .../g++.dg/cpp2a/nontype-class-union1.C       |  2 +-
 gcc/testsuite/g++.dg/cpp2a/nontype-class44.C  | 25 ++++++++++++
 10 files changed, 117 insertions(+), 52 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/abi/mangle77.C
 delete mode 100644 gcc/testsuite/g++.dg/cpp2a/nontype-class-equiv1.C
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/nontype-class44.C

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index e42b82ae5a4..23a77a2b2e0 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -6695,6 +6695,7 @@ extern void initialize_artificial_var		(tree, vec<constructor_elt, va_gc> *);
 extern tree check_var_type			(tree, tree, location_t);
 extern tree reshape_init                        (tree, tree, tsubst_flags_t);
 extern tree next_initializable_field (tree);
+extern tree first_field				(const_tree);
 extern tree fndecl_declared_return_type		(tree);
 extern bool undeduced_auto_decl			(tree);
 extern bool require_deduced_type		(tree, tsubst_flags_t = tf_warning_or_error);
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index 1cb47313923..d40b7a7da5f 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -6152,7 +6152,7 @@ struct reshape_iter
 
 static tree reshape_init_r (tree, reshape_iter *, tree, tsubst_flags_t);
 
-/* FIELD is a FIELD_DECL or NULL.  In the former case, the value
+/* FIELD is an element of TYPE_FIELDS or NULL.  In the former case, the value
    returned is the next FIELD_DECL (possibly FIELD itself) that can be
    initialized.  If there are no more such fields, the return value
    will be NULL.  */
diff --git a/gcc/cp/mangle.c b/gcc/cp/mangle.c
index 4399165ee23..49f1266bef3 100644
--- a/gcc/cp/mangle.c
+++ b/gcc/cp/mangle.c
@@ -2940,6 +2940,16 @@ write_base_ref (tree expr, tree base = NULL_TREE)
   return true;
 }
 
+/* The number of elements spanned by a RANGE_EXPR.  */
+
+unsigned HOST_WIDE_INT
+range_expr_nelts (tree expr)
+{
+  tree lo = TREE_OPERAND (expr, 0);
+  tree hi = TREE_OPERAND (expr, 1);
+  return tree_to_uhwi (hi) - tree_to_uhwi (lo) + 1;
+}
+
 /* <expression> ::= <unary operator-name> <expression>
 		::= <binary operator-name> <expression> <expression>
 		::= <expr-primary>
@@ -3284,8 +3294,14 @@ write_expression (tree expr)
 	  write_type (etype);
 	}
 
-      bool nontriv = !trivial_type_p (etype);
-      if (nontriv || !zero_init_expr_p (expr))
+      /* If this is an undigested initializer, mangle it as written.
+	 COMPOUND_LITERAL_P doesn't actually distinguish between digested and
+	 undigested braced casts, but it should work to use it to distinguish
+	 between braced casts in a template signature (undigested) and template
+	 parm object values (digested), and all CONSTRUCTORS that get here
+	 should be one of those two cases.  */
+      bool undigested = braced_init || COMPOUND_LITERAL_P (expr);
+      if (undigested || !zero_init_expr_p (expr))
 	{
 	  /* Convert braced initializer lists to STRING_CSTs so that
 	     A<"Foo"> mangles the same as A<{'F', 'o', 'o', 0}> while
@@ -3296,28 +3312,32 @@ write_expression (tree expr)
 	  if (TREE_CODE (expr) == CONSTRUCTOR)
 	    {
 	      vec<constructor_elt, va_gc> *elts = CONSTRUCTOR_ELTS (expr);
-	      unsigned last_nonzero = UINT_MAX, i;
+	      unsigned last_nonzero = UINT_MAX;
 	      constructor_elt *ce;
-	      tree val;
 
-	      if (!nontriv)
-		FOR_EACH_CONSTRUCTOR_VALUE (elts, i, val)
-		  if (!zero_init_expr_p (val))
+	      if (!undigested)
+		for (HOST_WIDE_INT i = 0; vec_safe_iterate (elts, i, &ce); ++i)
+		  if ((TREE_CODE (etype) == UNION_TYPE
+		       && ce->index != first_field (etype))
+		      || !zero_init_expr_p (ce->value))
 		    last_nonzero = i;
 
-	      if (nontriv || last_nonzero != UINT_MAX)
+	      if (undigested || last_nonzero != UINT_MAX)
 		for (HOST_WIDE_INT i = 0; vec_safe_iterate (elts, i, &ce); ++i)
 		  {
 		    if (i > last_nonzero)
 		      break;
-		    /* FIXME handle RANGE_EXPR */
 		    if (TREE_CODE (etype) == UNION_TYPE)
 		      {
 			/* Express the active member as a designator.  */
 			write_string ("di");
 			write_unqualified_name (ce->index);
 		      }
-		    write_expression (ce->value);
+		    unsigned reps = 1;
+		    if (ce->index && TREE_CODE (ce->index) == RANGE_EXPR)
+		      reps = range_expr_nelts (ce->index);
+		    for (unsigned j = 0; j < reps; ++j)
+		      write_expression (ce->value);
 		  }
 	    }
 	  else
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 2190f83882a..a0ca65cfa0d 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -7150,6 +7150,9 @@ get_template_parm_object (tree expr, tsubst_flags_t complain)
   if (invalid_tparm_referent_p (TREE_TYPE (expr), expr, complain))
     return error_mark_node;
 
+  /* This is no longer a compound literal.  */
+  TREE_HAS_CONSTRUCTOR (expr) = 0;
+
   tree name = mangle_template_parm_object (expr);
   tree decl = get_global_binding (name);
   if (decl)
diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
index dca947bf52a..a8bfd5fc053 100644
--- a/gcc/cp/tree.c
+++ b/gcc/cp/tree.c
@@ -4680,20 +4680,30 @@ zero_init_expr_p (tree t)
   tree type = TREE_TYPE (t);
   if (!type || uses_template_parms (type))
     return false;
-  if (zero_init_p (type))
-    return initializer_zerop (t);
   if (TYPE_PTRMEM_P (type))
     return null_member_pointer_value_p (t);
-  if (TREE_CODE (t) == CONSTRUCTOR
-      && CP_AGGREGATE_TYPE_P (type))
+  if (TREE_CODE (t) == CONSTRUCTOR)
     {
-      tree elt_init;
-      unsigned HOST_WIDE_INT i;
-      FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (t), i, elt_init)
-	if (!zero_init_expr_p (elt_init))
-	  return false;
+      if (CONSTRUCTOR_IS_DEPENDENT (t)
+	  || BRACE_ENCLOSED_INITIALIZER_P (t))
+	/* Undigested, conversions might change the zeroness.
+
+	   Other COMPOUND_LITERAL_P in template context are also undigested,
+	   but there isn't currently a way to distinguish between them and
+	   COMPOUND_LITERAL_P from non-template context that are digested.  */
+	return false;
+      for (constructor_elt &elt : CONSTRUCTOR_ELTS (t))
+	{
+	  if (TREE_CODE (type) == UNION_TYPE
+	      && elt.index != first_field (type))
+	    return false;
+	  if (!zero_init_expr_p (elt.value))
+	    return false;
+	}
       return true;
     }
+  if (zero_init_p (type))
+    return initializer_zerop (t);
   return false;
 }
 
diff --git a/gcc/testsuite/g++.dg/abi/mangle71.C b/gcc/testsuite/g++.dg/abi/mangle71.C
index cb9d7d3a1d8..038befa8f7d 100644
--- a/gcc/testsuite/g++.dg/abi/mangle71.C
+++ b/gcc/testsuite/g++.dg/abi/mangle71.C
@@ -1,4 +1,4 @@
-// Verify manglinng of class literals of types with ctors.
+// Verify mangling of class literals of types with ctors.
 // { dg-do compile { target c++2a } }
 
 struct A
@@ -13,16 +13,16 @@ struct B { A a[3]; };
 template <B> struct X { };
 
 void f___ (X<B{{ }}>) { }
-// { dg-final { scan-assembler "_Z4f___1XIXtl1BtlA3_1AtlS1_Lc1EEEEEE" } }
+// { dg-final { scan-assembler "_Z4f0001XIXtl1BEEE" } }
 
 void f0__ (X<B{{ 0 }}>) { }
-// { dg-final { scan-assembler "_Z4f0__1XIXtl1BtlA3_1AtlS1_Lc0EEtlS1_Lc1EEEEEE" } }
+// { dg-final { scan-assembler "_Z4f0__1XIXtl1BtlA3_1AtlS1_EtlS1_Lc1EEtlS1_Lc1EEEEEE" } }
 
 void f00_ (X<B{{ 0, 0 }}>) { }
-// { dg-final { scan-assembler "_Z4f00_1XIXtl1BtlA3_1AtlS1_Lc0EEtlS1_Lc0EEtlS1_Lc1EEEEEE" } }
+// { dg-final { scan-assembler "_Z4f00_1XIXtl1BtlA3_1AtlS1_EtlS1_EtlS1_Lc1EEEEEE" } }
 
 void f000 (X<B{{ 0, 0, 0 }}>) { }
-// { dg-final { scan-assembler "_Z4f0001XIXtl1BtlA3_1AtlS1_Lc0EEtlS1_Lc0EEtlS1_Lc0EEEEEE" } }
+// { dg-final { scan-assembler "_Z4f0001XIXtl1BEEE" } }
 
 void f1__ (X<B{{ 1 }}>) { }
-// { dg-final { scan-assembler "_Z4f1__1XIXtl1BtlA3_1AtlS1_Lc1EEtlS1_Lc1EEEEEE" } }
+// { dg-final { scan-assembler "_Z4f1__1XIXtl1BtlA3_1AtlS1_Lc1EEtlS1_Lc1EEtlS1_Lc1EEEEEE" } }
diff --git a/gcc/testsuite/g++.dg/abi/mangle77.C b/gcc/testsuite/g++.dg/abi/mangle77.C
new file mode 100644
index 00000000000..1181dc82f56
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/mangle77.C
@@ -0,0 +1,31 @@
+// Test that we handle T{} differently between class non-type template
+// arguments and other expressions in the signature.
+
+// { dg-do compile { target c++20 } }
+
+struct B
+{
+  int i;
+  constexpr B(int i): i(i+1) {}
+};
+
+struct A
+{
+  B b;
+};
+
+template <class T, class... Ts> T sink(T&&, Ts&&...);
+
+// Here A{1} is mangled as A{1}, the source representation, because expressions
+// involving template parameters are compared by ODR (token-based) equivalence
+// [temp.over.link].
+// { dg-final { scan-assembler "_Z1fIiEDTcl4sinktl1ALi1EEcvT__EEES1_" } }
+template <class T>
+decltype(sink(A{1},T())) f(T) { return A{1}; }
+int main() { f(42); }
+
+template <auto> struct C { };
+// Here A{1} is mangled as A{B{2}}, the value representation, because template
+// arguments are compared by value.
+// { dg-final { scan-assembler "_Z1g1CIXtl1Atl1BLi2EEEEE" } }
+void g(C<A{1}>) { }
diff --git a/gcc/testsuite/g++.dg/cpp2a/nontype-class-equiv1.C b/gcc/testsuite/g++.dg/cpp2a/nontype-class-equiv1.C
deleted file mode 100644
index 038d46fdac8..00000000000
--- a/gcc/testsuite/g++.dg/cpp2a/nontype-class-equiv1.C
+++ /dev/null
@@ -1,25 +0,0 @@
-// { 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)>))
-
-union U {
-  int i; int j;
-  constexpr U(int i): i(i) {}
-  constexpr U(unsigned u): j(u) {}
-};
-
-TEQ(U(0),U(0));
-
-// Calling the other constructor initializes a different member with the same
-// value.  We need to distinguish these.
-TNEQ(U(0),U(0u));
-
-// { dg-final { scan-assembler "_Z1f1AIXtl1Udi1iLi0EEEE" } }
-void f(A<U(0)>) { }
-// { dg-final { scan-assembler "_Z1g1AIXtl1Udi1jLi0EEEE" } }
-void g(A<U(0u)>) { }
diff --git a/gcc/testsuite/g++.dg/cpp2a/nontype-class-union1.C b/gcc/testsuite/g++.dg/cpp2a/nontype-class-union1.C
index 038d46fdac8..df913256a79 100644
--- a/gcc/testsuite/g++.dg/cpp2a/nontype-class-union1.C
+++ b/gcc/testsuite/g++.dg/cpp2a/nontype-class-union1.C
@@ -19,7 +19,7 @@ TEQ(U(0),U(0));
 // value.  We need to distinguish these.
 TNEQ(U(0),U(0u));
 
-// { dg-final { scan-assembler "_Z1f1AIXtl1Udi1iLi0EEEE" } }
+// { dg-final { scan-assembler "_Z1f1AIXtl1UEEE" } }
 void f(A<U(0)>) { }
 // { dg-final { scan-assembler "_Z1g1AIXtl1Udi1jLi0EEEE" } }
 void g(A<U(0u)>) { }
diff --git a/gcc/testsuite/g++.dg/cpp2a/nontype-class44.C b/gcc/testsuite/g++.dg/cpp2a/nontype-class44.C
new file mode 100644
index 00000000000..0316f79d212
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/nontype-class44.C
@@ -0,0 +1,25 @@
+// PR c++/100079
+// { dg-do compile { target c++20 } }
+
+template <auto value>
+struct Foo {
+    using SomeTypeAlias = int;
+
+    Foo() {}
+};
+
+template <class T>
+struct Bar {
+    T value;
+
+    constexpr Bar(const T& value)
+        : value{value}
+    {}
+};
+
+template <int N>
+struct Baz {};
+
+constexpr auto baz = Baz<42>{};
+
+const Foo<Bar<Baz<42>>{baz}> test{};

base-commit: ee351f7fdbd82f8947fe9a0e74cea65d216a8549
-- 
2.27.0


^ permalink raw reply	[flat|nested] 2+ messages in thread

* [pushed] c++: unset COMPOUND_LITERAL_P [PR100079]
  2021-04-16  5:16 [pushed] c++: C++20 class NTTP trailing zero-init [PR100079] Jason Merrill
@ 2021-04-29 18:29 ` Jason Merrill
  0 siblings, 0 replies; 2+ messages in thread
From: Jason Merrill @ 2021-04-29 18:29 UTC (permalink / raw)
  To: gcc-patches

Once a CONSTRUCTOR has been digested and used as an initializer, it no
longer represents a compound literal by itself, so we can clear the flag,
letting us use it consistently to distinguish between digested and
undigested initializer-lists.

Tested x86_64-pc-linux-gnu, applying to trunk.

gcc/cp/ChangeLog:

	* pt.c (get_template_parm_object): Add assert.
	* semantics.c (finish_compound_literal): Clear TREE_HAS_CONSTRUCTOR.
	* tree.c (zero_init_expr_p): Check TREE_HAS_CONSTRUCTOR.
	* typeck2.c (store_init_value): Likewise.
---
 gcc/cp/cp-tree.h   | 6 ++++--
 gcc/cp/pt.c        | 2 +-
 gcc/cp/semantics.c | 8 ++++++--
 gcc/cp/tree.c      | 8 ++------
 gcc/cp/typeck2.c   | 6 ++++++
 5 files changed, 19 insertions(+), 11 deletions(-)

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index cb254e0adea..e80902a2139 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -4407,7 +4407,7 @@ more_aggr_init_expr_args_p (const aggr_init_expr_arg_iterator *iter)
    When appearing in a SAVE_EXPR, it means that underneath
    is a call to a constructor.
 
-   When appearing in a CONSTRUCTOR, the expression is a
+   When appearing in a CONSTRUCTOR, the expression is an unconverted
    compound literal.
 
    When appearing in a FIELD_DECL, it means that this field
@@ -4419,7 +4419,9 @@ more_aggr_init_expr_args_p (const aggr_init_expr_arg_iterator *iter)
   (TREE_CODE (NODE) == CONSTRUCTOR && TREE_TYPE (NODE) == init_list_type_node)
 
 /* True if NODE is a compound-literal, i.e., a brace-enclosed
-   initializer cast to a particular type.  */
+   initializer cast to a particular type.  This is mostly only set during
+   template parsing; once the initializer has been digested into an actual
+   value of the type, the expression is represented by a TARGET_EXPR.  */
 #define COMPOUND_LITERAL_P(NODE) \
   (TREE_CODE (NODE) == CONSTRUCTOR && TREE_HAS_CONSTRUCTOR (NODE))
 
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index eaf46659f85..3cd803f2f81 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -7157,7 +7157,7 @@ get_template_parm_object (tree expr, tsubst_flags_t complain)
     return error_mark_node;
 
   /* This is no longer a compound literal.  */
-  TREE_HAS_CONSTRUCTOR (expr) = 0;
+  gcc_assert (!TREE_HAS_CONSTRUCTOR (expr));
 
   tree name = mangle_template_parm_object (expr);
   tree decl = get_global_binding (name);
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index 3a6468fd5f3..319a3a816ed 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -3177,9 +3177,13 @@ finish_compound_literal (tree type, tree compound_literal,
     }
 
   /* Represent other compound literals with TARGET_EXPR so we produce
-     an lvalue, but can elide copies.  */
+     a prvalue, and can elide copies.  */
   if (!VECTOR_TYPE_P (type))
-    compound_literal = get_target_expr_sfinae (compound_literal, complain);
+    {
+      /* The CONSTRUCTOR is now an initializer, not a compound literal.  */
+      TREE_HAS_CONSTRUCTOR (compound_literal) = false;
+      compound_literal = get_target_expr_sfinae (compound_literal, complain);
+    }
 
   return compound_literal;
 }
diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
index a8bfd5fc053..3a20cd33fdc 100644
--- a/gcc/cp/tree.c
+++ b/gcc/cp/tree.c
@@ -4684,13 +4684,9 @@ zero_init_expr_p (tree t)
     return null_member_pointer_value_p (t);
   if (TREE_CODE (t) == CONSTRUCTOR)
     {
-      if (CONSTRUCTOR_IS_DEPENDENT (t)
+      if (COMPOUND_LITERAL_P (t)
 	  || BRACE_ENCLOSED_INITIALIZER_P (t))
-	/* Undigested, conversions might change the zeroness.
-
-	   Other COMPOUND_LITERAL_P in template context are also undigested,
-	   but there isn't currently a way to distinguish between them and
-	   COMPOUND_LITERAL_P from non-template context that are digested.  */
+	/* Undigested, conversions might change the zeroness.  */
 	return false;
       for (constructor_elt &elt : CONSTRUCTOR_ELTS (t))
 	{
diff --git a/gcc/cp/typeck2.c b/gcc/cp/typeck2.c
index 4e9632f6a7d..ce3016c780d 100644
--- a/gcc/cp/typeck2.c
+++ b/gcc/cp/typeck2.c
@@ -818,6 +818,12 @@ store_init_value (tree decl, tree init, vec<tree, va_gc>** cleanups, int flags)
   /* Handle aggregate NSDMI in non-constant initializers, too.  */
   value = replace_placeholders (value, decl);
 
+  /* A COMPOUND_LITERAL_P CONSTRUCTOR is the syntactic form; by the time we get
+     here it should have been digested into an actual value for the type.  */
+  gcc_checking_assert (TREE_CODE (value) != CONSTRUCTOR
+		       || processing_template_decl
+		       || !TREE_HAS_CONSTRUCTOR (value));
+
   /* If the initializer is not a constant, fill in DECL_INITIAL with
      the bits that are constant, and then return an expression that
      will perform the dynamic initialization.  */

base-commit: 449d7b40f6f6be8d7f9aa7232c73b0371f0963bf
-- 
2.27.0


^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2021-04-29 18:29 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-04-16  5:16 [pushed] c++: C++20 class NTTP trailing zero-init [PR100079] Jason Merrill
2021-04-29 18:29 ` [pushed] c++: unset COMPOUND_LITERAL_P [PR100079] Jason Merrill

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).