diff --git a/src/exprutil.c b/src/exprutil.c
index db22dd4b7b..2181e28d1f 100644
--- a/src/exprutil.c
+++ b/src/exprutil.c
@@ -73,6 +73,9 @@ static a_boolean is_unmodifiable_initonly_field_operand(
static void mark_init_component_as_permanently_allocated(
an_init_component *icp);
static void prep_generic_init_component_list(an_arg_list_elem_ptr list);
+static void make_template_param_object_operand(a_constant_ptr cp,
+ an_operand *operand,
+ a_boolean copy_constant);
/*
@@ -4913,6 +4916,7 @@ in *bound_function_selector.
an_expr_node_ptr orig_expr = expr;
an_expr_node_ptr expr_copy = NULL;
a_boolean rescanned_case = FALSE;
+ a_boolean is_template_param_object = FALSE;
a_constant_ptr constant = local_constant();
a_constant_ptr alloc_con = NULL;
an_expr_rescan_info_entry_ptr eriep;
@@ -4964,11 +4968,35 @@ in *bound_function_selector.
/* The operand was already built above. */
} else {
if (expr_copy == NULL) {
- /* The result is a constant, so return a constant operand. */
- if (alloc_con != NULL) {
- make_constant_operand(alloc_con, operand);
+ a_constant_ptr cp = alloc_con != NULL ? alloc_con : constant;
+ if (is_constant_node(expr) &&
+ constant_is(node_constant(expr), ck_template_param) &&
+ tpck_is(node_constant(expr), tpck_param) &&
+ constant_is(cp, ck_aggregate)) {
+ /* A C++20 nontype template parameter of class type has been
+ replaced by its argument value. As when such a parameter is
+ referenced in an instantiation (see make_sym_constant_operand),
+ the result is an lvalue designating the template parameter
+ object. That object is identified by the argument value
+ itself, i.e., without the "const" that substitution adds to
+ the value. */
+ if (cp != constant) {
+ /* cp may be the template argument itself (if the argument value
+ was produced by an earlier substitution), so it must not be
+ modified. */
+ copy_constant(cp, constant);
+ cp = constant;
+ } /* if */
+ if (cp->variant.aggregate.added_const_for_template_param) {
+ cp->type = remove_qualifiers(cp->type, TQ_CONST);
+ cp->variant.aggregate.added_const_for_template_param = FALSE;
+ } /* if */
+ make_template_param_object_operand(cp, operand,
+ /*copy_constant=*/TRUE);
+ is_template_param_object = TRUE;
} else {
- make_constant_operand(constant, operand);
+ /* The result is a constant, so return a constant operand. */
+ make_constant_operand(cp, operand);
} /* if */
} else {
/* Return an expression operand. */
@@ -4992,6 +5020,15 @@ in *bound_function_selector.
/* Normal case: restore everything. */
restore_operand_info_from_expr_rescan_info_entry(operand, eriep);
} /* if */
+ if (is_template_param_object && orig_expr->extra.rescan_info != NULL) {
+ /* The rescan information on the template parameter constant was
+ saved before the reference was identified as an id-expression
+ (possibly parenthesized). That is recorded on the node that makes
+ the reference an lvalue (see make_sym_constant_operand), and it
+ matters, e.g., for decltype. */
+ restore_operand_id_details(operand,
+ &orig_expr->extra.rescan_info->saved_operand);
+ } /* if */
} /* if */
release_local_constant(&constant);
} /* make_rescan_operand_full */
@@ -8467,11 +8504,15 @@ STATIC_THREAD a_template_param_object_map
object. */
static void make_template_param_object_operand(a_constant_ptr cp,
- an_operand *operand)
+ an_operand *operand,
+ a_boolean copy_constant)
/*
Make operand an lvalue expression operand for the template parameter object
associated with the constant cp. If needed, create that object (as a variable
-entry).
+entry). copy_constant is TRUE if cp is a local constant (rather than an
+allocated IL constant): If the object is created, its value is then a copy of
+cp, including its subconstants (which may be shared with other constants),
+allocated in the file scope memory region.
*/
{
a_constant_handle ch = { cp };
@@ -8480,6 +8521,15 @@ entry).
if (vp == NULL) {
a_symbol_ptr sym = make_template_param_object_sym(&error_position);
a_type_ptr vtp = cp->type;
+ if (copy_constant) {
+ a_memory_region_number region_to_switch_back_to;
+ switch_to_file_scope_region(®ion_to_switch_back_to);
+ cp = copy_constant_full(cp, (a_constant *)NULL,
+ CE_SRC_CONSTANT_IS_NOT_ALLOC_IN_IL);
+ switch_back_to_original_region(region_to_switch_back_to);
+ break_constant_source_corresp(cp);
+ ch.ptr = cp;
+ } /* if */
vp = make_variable(vtp, sc_unspecified, DEPTH_OF_FILE_SCOPE);
vp->is_template_param_object = TRUE;
vp->is_constexpr = TRUE;
@@ -8541,7 +8591,8 @@ The position of the current token will be used as the operand position.
/* A C++20 nontype template parameter instantiated with a class type
constant. Create a variable operand referring to the underlying
template parameter object. */
- make_template_param_object_operand(con_ptr, operand);
+ make_template_param_object_operand(con_ptr, operand,
+ /*copy_constant=*/FALSE);
} else {
/* Normal (non-reference) case. */
make_constant_operand(&constant, operand);
diff --git a/src/pending_changes/gh-414-class-type-template-param-object.chng.txt b/src/pending_changes/gh-414-class-type-template-param-object.chng.txt
new file mode 100644
index 0000000000..4cf8b2eab2
--- /dev/null
+++ b/src/pending_changes/gh-414-class-type-template-param-object.chng.txt
@@ -0,0 +1,21 @@
+[GH #414]
+Class-type template parameter objects in function template signatures
+
+A nontype template parameter of class type denotes a template parameter
+object, which is a const lvalue. When such a parameter was used in the
+signature of a function template, substituting template arguments treated
+it as a prvalue instead. As a result, decltype applied to the
+parenthesized parameter name produced the class type rather than a const
+lvalue reference to it (causing a spurious "unexpected function type"
+error), member accesses through the parameter had the wrong value category,
+and taking the address of the parameter caused substitution to fail. That
+is now fixed. For example, with --c++20:
+
+ struct A { };
+ template void f(decltype((V)) v) { }
+ template auto g() -> decltype(&V);
+ void h() {
+ constexpr A a;
+ f(a); // Previously an "unexpected function type" error.
+ g(); // Previously, no instance of g matched.
+ }
diff --git a/tests/tests/imported/clang/cpp/SemaTemplate/.concepts.rto/default.1.1.txt b/tests/tests/imported/clang/cpp/SemaTemplate/.concepts.rto/default.1.1.txt
index 35a69da7f7..1f7a1172ed 100644
--- a/tests/tests/imported/clang/cpp/SemaTemplate/.concepts.rto/default.1.1.txt
+++ b/tests/tests/imported/clang/cpp/SemaTemplate/.concepts.rto/default.1.1.txt
@@ -749,23 +749,6 @@ fe_only -DTEST_NUMBER=1 --clang -tused -e 200 --no_wrap_diagnostics --clang_vers
static_assert( requires {{ &f } -> C;} ); // expected-error {{reference to overloaded function could not be resolved;}}
^
-"Test_name.c", line 1692: error: the deduced placeholder type "GH170856::absolute_zero" failed the type constraint
- consteval PointOriginFor auto default_point_origin(R) {
- ^
-"Test_name.c", line 1681: note: concept is false for arguments ", GH170856::{{{}}}>"
- concept PointOriginFor = QuantitySpecOf;
- ^
-"Test_name.c", line 1679: note: concept is false for arguments ">"
- concept QuantitySpecOf = QuantitySpec;
- ^
-"Test_name.c", line 1677: note: atomic constraint evaluates to false
- concept QuantitySpec = __is_convertible(T*, quantity_spec_interface_base*);
- ^
- detected during:
- instantiation of "GH170856::PointOriginFor<...> auto GH170856::default_point_origin(R) [with R=struct GH170856::]" at line 1697
- instantiation of class "GH170856::point_ [with R=struct GH170856::]" at line 1705
- instantiation of "GH170856::point_)> GH170856::point [with R=GH170856::{{}}]" at line 1705
-
"Test_name.c", line 41: error: a requires-clause is not allowed here (not a templated function)
([] () requires C {} ()); // expected-error {{expression contains unexpanded parameter pack 'Ts'}}
^
@@ -1000,4 +983,4 @@ fe_only -DTEST_NUMBER=1 --clang -tused -e 200 --no_wrap_diagnostics --clang_vers
F* f = l; // expected-error {{no viable conversion from}}
^
-145 errors detected in the compilation of "Test_name.c".
+144 errors detected in the compilation of "Test_name.c".
diff --git a/tests/tests/imported/clang/cpp/SemaTemplate/.concepts.rto/default.2.1.txt b/tests/tests/imported/clang/cpp/SemaTemplate/.concepts.rto/default.2.1.txt
index 177141856e..98c3adff53 100644
--- a/tests/tests/imported/clang/cpp/SemaTemplate/.concepts.rto/default.2.1.txt
+++ b/tests/tests/imported/clang/cpp/SemaTemplate/.concepts.rto/default.2.1.txt
@@ -741,23 +741,6 @@ fe_only -DTEST_NUMBER=2 --clang -tused -e 200 --no_wrap_diagnostics --clang_vers
static_assert( requires {{ &f } -> C;} ); // expected-error {{reference to overloaded function could not be resolved;}}
^
-"Test_name.c", line 1692: error: the deduced placeholder type "GH170856::absolute_zero" failed the type constraint
- consteval PointOriginFor auto default_point_origin(R) {
- ^
-"Test_name.c", line 1681: note: concept is false for arguments ", GH170856::{{{}}}>"
- concept PointOriginFor = QuantitySpecOf;
- ^
-"Test_name.c", line 1679: note: concept is false for arguments ">"
- concept QuantitySpecOf = QuantitySpec;
- ^
-"Test_name.c", line 1677: note: atomic constraint evaluates to false
- concept QuantitySpec = __is_convertible(T*, quantity_spec_interface_base*);
- ^
- detected during:
- instantiation of "GH170856::PointOriginFor<...> auto GH170856::default_point_origin(R) [with R=struct GH170856::]" at line 1697
- instantiation of class "GH170856::point_ [with R=struct GH170856::]" at line 1705
- instantiation of "GH170856::point_)> GH170856::point [with R=GH170856::{{}}]" at line 1705
-
"Test_name.c", line 41: error: a requires-clause is not allowed here (not a templated function)
([] () requires C {} ()); // expected-error {{expression contains unexpanded parameter pack 'Ts'}}
^
@@ -992,4 +975,4 @@ fe_only -DTEST_NUMBER=2 --clang -tused -e 200 --no_wrap_diagnostics --clang_vers
F* f = l; // expected-error {{no viable conversion from}}
^
-145 errors detected in the compilation of "Test_name.c".
+144 errors detected in the compilation of "Test_name.c".
diff --git a/tests/tests/imported/clang/cpp/SemaTemplate/.concepts.rto/edg_x86_32_cp.1.1.txt b/tests/tests/imported/clang/cpp/SemaTemplate/.concepts.rto/edg_x86_32_cp.1.1.txt
index b4854b78cf..155259c307 100644
--- a/tests/tests/imported/clang/cpp/SemaTemplate/.concepts.rto/edg_x86_32_cp.1.1.txt
+++ b/tests/tests/imported/clang/cpp/SemaTemplate/.concepts.rto/edg_x86_32_cp.1.1.txt
@@ -846,23 +846,6 @@ fe_only -DTEST_NUMBER=1 --clang -tused -e 200 --no_wrap_diagnostics --clang_vers
static_assert( requires {{ &f } -> C;} ); // expected-error {{reference to overloaded function could not be resolved;}}
^
-"Test_name.c", line 1692: error: the deduced placeholder type "GH170856::absolute_zero" failed the type constraint
- consteval PointOriginFor auto default_point_origin(R) {
- ^
-"Test_name.c", line 1681: note: concept is false for arguments ", GH170856::{{{}}}>"
- concept PointOriginFor = QuantitySpecOf;
- ^
-"Test_name.c", line 1679: note: concept is false for arguments ">"
- concept QuantitySpecOf = QuantitySpec;
- ^
-"Test_name.c", line 1677: note: atomic constraint evaluates to false
- concept QuantitySpec = __is_convertible(T*, quantity_spec_interface_base*);
- ^
- detected during:
- instantiation of "GH170856::PointOriginFor<...> auto GH170856::default_point_origin(R) [with R=struct GH170856::]" at line 1697
- instantiation of class "GH170856::point_ [with R=struct GH170856::]" at line 1705
- instantiation of "GH170856::point_)> GH170856::point [with R=GH170856::{{}}]" at line 1705
-
"Test_name.c", line 46: error: no instance of function template "lambda []()->auto" matches the argument list
object type is: lambda []()->auto
([] requires C () {} (), ...);
@@ -1014,4 +997,4 @@ fe_only -DTEST_NUMBER=1 --clang -tused -e 200 --no_wrap_diagnostics --clang_vers
^
detected during instantiation of "void DeferredInstantiationInstScope::LocalStructMemberVar(T) [with T=int]" at line 590
-151 errors detected in the compilation of "Test_name.c".
+150 errors detected in the compilation of "Test_name.c".
diff --git a/tests/tests/imported/clang/cpp/SemaTemplate/.concepts.rto/edg_x86_32_cp.2.1.txt b/tests/tests/imported/clang/cpp/SemaTemplate/.concepts.rto/edg_x86_32_cp.2.1.txt
index 0addde9c02..468346a8cd 100644
--- a/tests/tests/imported/clang/cpp/SemaTemplate/.concepts.rto/edg_x86_32_cp.2.1.txt
+++ b/tests/tests/imported/clang/cpp/SemaTemplate/.concepts.rto/edg_x86_32_cp.2.1.txt
@@ -838,23 +838,6 @@ fe_only -DTEST_NUMBER=2 --clang -tused -e 200 --no_wrap_diagnostics --clang_vers
static_assert( requires {{ &f } -> C;} ); // expected-error {{reference to overloaded function could not be resolved;}}
^
-"Test_name.c", line 1692: error: the deduced placeholder type "GH170856::absolute_zero" failed the type constraint
- consteval PointOriginFor auto default_point_origin(R) {
- ^
-"Test_name.c", line 1681: note: concept is false for arguments ", GH170856::{{{}}}>"
- concept PointOriginFor = QuantitySpecOf;
- ^
-"Test_name.c", line 1679: note: concept is false for arguments ">"
- concept QuantitySpecOf = QuantitySpec;
- ^
-"Test_name.c", line 1677: note: atomic constraint evaluates to false
- concept QuantitySpec = __is_convertible(T*, quantity_spec_interface_base*);
- ^
- detected during:
- instantiation of "GH170856::PointOriginFor<...> auto GH170856::default_point_origin(R) [with R=struct GH170856::]" at line 1697
- instantiation of class "GH170856::point_ [with R=struct GH170856::]" at line 1705
- instantiation of "GH170856::point_)> GH170856::point [with R=GH170856::{{}}]" at line 1705
-
"Test_name.c", line 46: error: no instance of function template "lambda []()->auto" matches the argument list
object type is: lambda []()->auto
([] requires C () {} (), ...);
@@ -1006,4 +989,4 @@ fe_only -DTEST_NUMBER=2 --clang -tused -e 200 --no_wrap_diagnostics --clang_vers
^
detected during instantiation of "void DeferredInstantiationInstScope::LocalStructMemberVar(T) [with T=int]" at line 590
-151 errors detected in the compilation of "Test_name.c".
+150 errors detected in the compilation of "Test_name.c".
diff --git a/tests/tests/imported/clang/cpp/SemaTemplate/.concepts.rto/edg_x86_64_cp.1.1.txt b/tests/tests/imported/clang/cpp/SemaTemplate/.concepts.rto/edg_x86_64_cp.1.1.txt
index b4854b78cf..155259c307 100644
--- a/tests/tests/imported/clang/cpp/SemaTemplate/.concepts.rto/edg_x86_64_cp.1.1.txt
+++ b/tests/tests/imported/clang/cpp/SemaTemplate/.concepts.rto/edg_x86_64_cp.1.1.txt
@@ -846,23 +846,6 @@ fe_only -DTEST_NUMBER=1 --clang -tused -e 200 --no_wrap_diagnostics --clang_vers
static_assert( requires {{ &f } -> C;} ); // expected-error {{reference to overloaded function could not be resolved;}}
^
-"Test_name.c", line 1692: error: the deduced placeholder type "GH170856::absolute_zero" failed the type constraint
- consteval PointOriginFor auto default_point_origin(R) {
- ^
-"Test_name.c", line 1681: note: concept is false for arguments ", GH170856::{{{}}}>"
- concept PointOriginFor = QuantitySpecOf;
- ^
-"Test_name.c", line 1679: note: concept is false for arguments ">"
- concept QuantitySpecOf = QuantitySpec;
- ^
-"Test_name.c", line 1677: note: atomic constraint evaluates to false
- concept QuantitySpec = __is_convertible(T*, quantity_spec_interface_base*);
- ^
- detected during:
- instantiation of "GH170856::PointOriginFor<...> auto GH170856::default_point_origin(R) [with R=struct GH170856::]" at line 1697
- instantiation of class "GH170856::point_ [with R=struct GH170856::]" at line 1705
- instantiation of "GH170856::point_)> GH170856::point [with R=GH170856::{{}}]" at line 1705
-
"Test_name.c", line 46: error: no instance of function template "lambda []()->auto" matches the argument list
object type is: lambda []()->auto
([] requires C () {} (), ...);
@@ -1014,4 +997,4 @@ fe_only -DTEST_NUMBER=1 --clang -tused -e 200 --no_wrap_diagnostics --clang_vers
^
detected during instantiation of "void DeferredInstantiationInstScope::LocalStructMemberVar(T) [with T=int]" at line 590
-151 errors detected in the compilation of "Test_name.c".
+150 errors detected in the compilation of "Test_name.c".
diff --git a/tests/tests/imported/clang/cpp/SemaTemplate/.concepts.rto/edg_x86_64_cp.2.1.txt b/tests/tests/imported/clang/cpp/SemaTemplate/.concepts.rto/edg_x86_64_cp.2.1.txt
index 0addde9c02..468346a8cd 100644
--- a/tests/tests/imported/clang/cpp/SemaTemplate/.concepts.rto/edg_x86_64_cp.2.1.txt
+++ b/tests/tests/imported/clang/cpp/SemaTemplate/.concepts.rto/edg_x86_64_cp.2.1.txt
@@ -838,23 +838,6 @@ fe_only -DTEST_NUMBER=2 --clang -tused -e 200 --no_wrap_diagnostics --clang_vers
static_assert( requires {{ &f } -> C;} ); // expected-error {{reference to overloaded function could not be resolved;}}
^
-"Test_name.c", line 1692: error: the deduced placeholder type "GH170856::absolute_zero" failed the type constraint
- consteval PointOriginFor auto default_point_origin(R) {
- ^
-"Test_name.c", line 1681: note: concept is false for arguments ", GH170856::{{{}}}>"
- concept PointOriginFor = QuantitySpecOf;
- ^
-"Test_name.c", line 1679: note: concept is false for arguments ">"
- concept QuantitySpecOf = QuantitySpec;
- ^
-"Test_name.c", line 1677: note: atomic constraint evaluates to false
- concept QuantitySpec = __is_convertible(T*, quantity_spec_interface_base*);
- ^
- detected during:
- instantiation of "GH170856::PointOriginFor<...> auto GH170856::default_point_origin(R) [with R=struct GH170856::]" at line 1697
- instantiation of class "GH170856::point_ [with R=struct GH170856::]" at line 1705
- instantiation of "GH170856::point_)> GH170856::point [with R=GH170856::{{}}]" at line 1705
-
"Test_name.c", line 46: error: no instance of function template "lambda []()->auto" matches the argument list
object type is: lambda []()->auto
([] requires C () {} (), ...);
@@ -1006,4 +989,4 @@ fe_only -DTEST_NUMBER=2 --clang -tused -e 200 --no_wrap_diagnostics --clang_vers
^
detected during instantiation of "void DeferredInstantiationInstScope::LocalStructMemberVar(T) [with T=int]" at line 590
-151 errors detected in the compilation of "Test_name.c".
+150 errors detected in the compilation of "Test_name.c".
diff --git a/tests/tests/regressions/.gh-414.rto/default.1.1.txt b/tests/tests/regressions/.gh-414.rto/default.1.1.txt
new file mode 100644
index 0000000000..32fc82e916
--- /dev/null
+++ b/tests/tests/regressions/.gh-414.rto/default.1.1.txt
@@ -0,0 +1 @@
+fe_only -DTEST_NUMBER=1 --c++20 Test_name.c
diff --git a/tests/tests/regressions/.gh-414.rto/default.2.1.txt b/tests/tests/regressions/.gh-414.rto/default.2.1.txt
new file mode 100644
index 0000000000..2f4f598fac
--- /dev/null
+++ b/tests/tests/regressions/.gh-414.rto/default.2.1.txt
@@ -0,0 +1 @@
+fe_only -DTEST_NUMBER=2 --c++20 --g++ --gnu_version=150100 Test_name.c
diff --git a/tests/tests/regressions/.gh-414.rto/default.3.1.txt b/tests/tests/regressions/.gh-414.rto/default.3.1.txt
new file mode 100644
index 0000000000..2a35d8f546
--- /dev/null
+++ b/tests/tests/regressions/.gh-414.rto/default.3.1.txt
@@ -0,0 +1 @@
+fe_only -DTEST_NUMBER=3 --c++20 --clang --clang_version=190000 Test_name.c
diff --git a/tests/tests/regressions/.gh-414.rto/default.4.1.txt b/tests/tests/regressions/.gh-414.rto/default.4.1.txt
new file mode 100644
index 0000000000..6e33f463c5
--- /dev/null
+++ b/tests/tests/regressions/.gh-414.rto/default.4.1.txt
@@ -0,0 +1 @@
+fe_only -DTEST_NUMBER=4 --ms_c++20 --microsoft_version=1950 Test_name.c
diff --git a/tests/tests/regressions/gh-414.sft.cpp b/tests/tests/regressions/gh-414.sft.cpp
new file mode 100644
index 0000000000..a59478fff6
--- /dev/null
+++ b/tests/tests/regressions/gh-414.sft.cpp
@@ -0,0 +1,113 @@
+//type:fp
+//options:--c++20:--c++20 --g++ --gnu_version=150100:--c++20 --clang --clang_version=190000:--ms_c++20 --microsoft_version=1950
+
+// A nontype template parameter of class type denotes a template parameter
+// object, which is a const lvalue. When such a parameter was used in a
+// function template signature, substitution treated it as a prvalue: The
+// decltype of the parenthesized name became the class type rather than a
+// const reference to it (resulting in a spurious "unexpected function type"
+// error), and taking its address caused substitution to fail.
+
+template constexpr bool is_same = false;
+template constexpr bool is_same = true;
+
+struct A { int i = 0; };
+constexpr A a1{1}, a2{2};
+
+template void test1(decltype((V)) v) { }
+#if __GNUC__
+template void test2(__decltype((V)) v) { }
+#endif /* __GNUC__ */
+
+void g() {
+ constexpr A a;
+ test1(a);
+#if __GNUC__
+ test2(a);
+#endif /* __GNUC__ */
+}
+
+template auto paren() -> decltype((V));
+static_assert(is_same()), const A&>);
+
+template auto no_paren() -> decltype(V);
+static_assert(is_same()), A>);
+
+template auto member() -> decltype((V.i));
+static_assert(is_same()), const int&>);
+
+template void dependent(decltype((V))) { }
+void h() { dependent(a1); }
+
+template struct S { };
+template auto deduced(S) -> decltype((V));
+static_assert(is_same{})), const A&>);
+
+// The object designated in a rescanned signature is the same one designated
+// in the instantiated function body.
+template constexpr const A* addr() { return &V; }
+template constexpr auto addr_in_sig() -> decltype(&V) { return &V; }
+static_assert(addr() == addr_in_sig());
+static_assert(addr_in_sig() != addr_in_sig());
+static_assert(addr_in_sig()->i == 2);
+
+template requires (&V != nullptr) constexpr int constrained() {
+ return V.i;
+}
+static_assert(constrained() == 2);
+
+template struct X {
+ template static auto f() -> decltype((V));
+};
+static_assert(is_same::f()), const A&>);
+
+template struct Y {
+ template static auto f(T) -> decltype(&V);
+};
+static_assert(is_same::f(0)), const A*>);
+
+// A closure-type argument that is itself the result of an earlier
+// substitution (into the concept's template argument list).
+template concept Callable = (F() == 1);
+template constexpr int call() requires Callable { return 1; }
+template constexpr int call() { return 0; }
+static_assert(call<[]{ return 1; }>() == 1);
+static_assert(call<[]{ return 2; }>() == 0);
+
+// Template parameter objects with subaggregates (whose values must not be
+// shared with the template argument value), designated in a template argument
+// list in the signature.
+namespace subaggregates {
+ struct B { int x[3]; };
+ struct C { B b; int y; };
+ struct D { B bs[2]; };
+
+ template struct P { };
+ template auto f() -> P<&V>;
+ using R = decltype(f());
+
+ template struct Q {
+ static constexpr const B* ptr = p;
+ static constexpr int value = p->x[2];
+ };
+ template constexpr auto g() -> Q<&V> { return {}; }
+ template constexpr auto g2() -> Q<&V> { return {}; }
+ template constexpr const B* addr() { return &V; }
+ static_assert(decltype(g())::value == 3);
+ static_assert(decltype(g())::ptr == addr());
+ static_assert(is_same()),
+ decltype(g2())>);
+ static_assert(decltype(g())::ptr !=
+ decltype(g())::ptr);
+ static_assert(g().value == 9);
+
+ template struct QC { static constexpr int value = p->b.x[1]; };
+ template auto h() -> QC<&V>;
+ static_assert(decltype(h())::value == 2);
+
+ template struct QD {
+ static constexpr int value = p->bs[1].x[0];
+ };
+ template auto k() -> QD<&V>;
+ static_assert(decltype(k())::value == 4);
+} // namespace subaggregates