diff options
author | Jason Merrill <jason@redhat.com> | 2021-04-14 17:27:19 -0400 |
---|---|---|
committer | Jason Merrill <jason@redhat.com> | 2021-04-16 01:16:03 -0400 |
commit | 89c863488bc8c7315596bcb753173aa2fd8be727 (patch) | |
tree | 23f643de494414e8cbb18cf5d22fc3007daac151 /gcc | |
parent | ee351f7fdbd82f8947fe9a0e74cea65d216a8549 (diff) | |
download | gcc-89c863488bc8c7315596bcb753173aa2fd8be727.tar.gz |
c++: C++20 class NTTP trailing zero-init [PR100079]
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.
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.
Diffstat (limited to 'gcc')
-rw-r--r-- | gcc/cp/cp-tree.h | 1 | ||||
-rw-r--r-- | gcc/cp/decl.c | 2 | ||||
-rw-r--r-- | gcc/cp/mangle.c | 40 | ||||
-rw-r--r-- | gcc/cp/pt.c | 3 | ||||
-rw-r--r-- | gcc/cp/tree.c | 28 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/abi/mangle71.C | 12 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/abi/mangle77.C | 31 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/cpp2a/nontype-class-equiv1.C | 25 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/cpp2a/nontype-class-union1.C | 2 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/cpp2a/nontype-class44.C | 25 |
10 files changed, 117 insertions, 52 deletions
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{}; |