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);
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. */
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>
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
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
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)
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;
}
-// 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
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" } }
--- /dev/null
+// 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}>) { }
+++ /dev/null
-// { 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)>) { }
// 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)>) { }
--- /dev/null
+// 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{};