Skip to content

Commit 04f2723

Browse files
Use template parameter objects for class-type NTTPs in signatures [GH #414]
1 parent 4cc57d4 commit 04f2723

11 files changed

Lines changed: 200 additions & 79 deletions

File tree

‎src/exprutil.c‎

Lines changed: 58 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,9 @@ static a_boolean is_unmodifiable_initonly_field_operand(
7373
static void mark_init_component_as_permanently_allocated(
7474
an_init_component *icp);
7575
static void prep_generic_init_component_list(an_arg_list_elem_ptr list);
76+
static void make_template_param_object_operand(a_constant_ptr cp,
77+
an_operand *operand,
78+
a_boolean copy_constant);
7679

7780

7881
/*
@@ -4913,6 +4916,7 @@ in *bound_function_selector.
49134916
an_expr_node_ptr orig_expr = expr;
49144917
an_expr_node_ptr expr_copy = NULL;
49154918
a_boolean rescanned_case = FALSE;
4919+
a_boolean is_template_param_object = FALSE;
49164920
a_constant_ptr constant = local_constant();
49174921
a_constant_ptr alloc_con = NULL;
49184922
an_expr_rescan_info_entry_ptr eriep;
@@ -4964,11 +4968,35 @@ in *bound_function_selector.
49644968
/* The operand was already built above. */
49654969
} else {
49664970
if (expr_copy == NULL) {
4967-
/* The result is a constant, so return a constant operand. */
4968-
if (alloc_con != NULL) {
4969-
make_constant_operand(alloc_con, operand);
4971+
a_constant_ptr cp = alloc_con != NULL ? alloc_con : constant;
4972+
if (is_constant_node(expr) &&
4973+
constant_is(node_constant(expr), ck_template_param) &&
4974+
tpck_is(node_constant(expr), tpck_param) &&
4975+
constant_is(cp, ck_aggregate)) {
4976+
/* A C++20 nontype template parameter of class type has been
4977+
replaced by its argument value. As when such a parameter is
4978+
referenced in an instantiation (see make_sym_constant_operand),
4979+
the result is an lvalue designating the template parameter
4980+
object. That object is identified by the argument value
4981+
itself, i.e., without the "const" that substitution adds to
4982+
the value. */
4983+
if (cp != constant) {
4984+
/* cp may be the template argument itself (if the argument value
4985+
was produced by an earlier substitution), so it must not be
4986+
modified. */
4987+
copy_constant(cp, constant);
4988+
cp = constant;
4989+
} /* if */
4990+
if (cp->variant.aggregate.added_const_for_template_param) {
4991+
cp->type = remove_qualifiers(cp->type, TQ_CONST);
4992+
cp->variant.aggregate.added_const_for_template_param = FALSE;
4993+
} /* if */
4994+
make_template_param_object_operand(cp, operand,
4995+
/*copy_constant=*/TRUE);
4996+
is_template_param_object = TRUE;
49704997
} else {
4971-
make_constant_operand(constant, operand);
4998+
/* The result is a constant, so return a constant operand. */
4999+
make_constant_operand(cp, operand);
49725000
} /* if */
49735001
} else {
49745002
/* Return an expression operand. */
@@ -4992,6 +5020,15 @@ in *bound_function_selector.
49925020
/* Normal case: restore everything. */
49935021
restore_operand_info_from_expr_rescan_info_entry(operand, eriep);
49945022
} /* if */
5023+
if (is_template_param_object && orig_expr->extra.rescan_info != NULL) {
5024+
/* The rescan information on the template parameter constant was
5025+
saved before the reference was identified as an id-expression
5026+
(possibly parenthesized). That is recorded on the node that makes
5027+
the reference an lvalue (see make_sym_constant_operand), and it
5028+
matters, e.g., for decltype. */
5029+
restore_operand_id_details(operand,
5030+
&orig_expr->extra.rescan_info->saved_operand);
5031+
} /* if */
49955032
} /* if */
49965033
release_local_constant(&constant);
49975034
} /* make_rescan_operand_full */
@@ -8467,11 +8504,15 @@ STATIC_THREAD a_template_param_object_map
84678504
object. */
84688505

84698506
static void make_template_param_object_operand(a_constant_ptr cp,
8470-
an_operand *operand)
8507+
an_operand *operand,
8508+
a_boolean copy_constant)
84718509
/*
84728510
Make operand an lvalue expression operand for the template parameter object
84738511
associated with the constant cp. If needed, create that object (as a variable
8474-
entry).
8512+
entry). copy_constant is TRUE if cp is a local constant (rather than an
8513+
allocated IL constant): If the object is created, its value is then a copy of
8514+
cp, including its subconstants (which may be shared with other constants),
8515+
allocated in the file scope memory region.
84758516
*/
84768517
{
84778518
a_constant_handle ch = { cp };
@@ -8480,6 +8521,15 @@ entry).
84808521
if (vp == NULL) {
84818522
a_symbol_ptr sym = make_template_param_object_sym(&error_position);
84828523
a_type_ptr vtp = cp->type;
8524+
if (copy_constant) {
8525+
a_memory_region_number region_to_switch_back_to;
8526+
switch_to_file_scope_region(&region_to_switch_back_to);
8527+
cp = copy_constant_full(cp, (a_constant *)NULL,
8528+
CE_SRC_CONSTANT_IS_NOT_ALLOC_IN_IL);
8529+
switch_back_to_original_region(region_to_switch_back_to);
8530+
break_constant_source_corresp(cp);
8531+
ch.ptr = cp;
8532+
} /* if */
84838533
vp = make_variable(vtp, sc_unspecified, DEPTH_OF_FILE_SCOPE);
84848534
vp->is_template_param_object = TRUE;
84858535
vp->is_constexpr = TRUE;
@@ -8541,7 +8591,8 @@ The position of the current token will be used as the operand position.
85418591
/* A C++20 nontype template parameter instantiated with a class type
85428592
constant. Create a variable operand referring to the underlying
85438593
template parameter object. */
8544-
make_template_param_object_operand(con_ptr, operand);
8594+
make_template_param_object_operand(con_ptr, operand,
8595+
/*copy_constant=*/FALSE);
85458596
} else {
85468597
/* Normal (non-reference) case. */
85478598
make_constant_operand(&constant, operand);
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
[GH #414]
2+
Class-type template parameter objects in function template signatures
3+
4+
A nontype template parameter of class type denotes a template parameter
5+
object, which is a const lvalue. When such a parameter was used in the
6+
signature of a function template, substituting template arguments treated
7+
it as a prvalue instead. As a result, decltype applied to the
8+
parenthesized parameter name produced the class type rather than a const
9+
lvalue reference to it (causing a spurious "unexpected function type"
10+
error), member accesses through the parameter had the wrong value category,
11+
and taking the address of the parameter caused substitution to fail. That
12+
is now fixed. For example, with --c++20:
13+
14+
struct A { };
15+
template <A V> void f(decltype((V)) v) { }
16+
template <A V> auto g() -> decltype(&V);
17+
void h() {
18+
constexpr A a;
19+
f<a>(a); // Previously an "unexpected function type" error.
20+
g<a>(); // Previously, no instance of g matched.
21+
}

‎tests/tests/imported/clang/cpp/SemaTemplate/.concepts.rto/default.1.1.txt‎

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -749,23 +749,6 @@ fe_only -DTEST_NUMBER=1 --clang -tused -e 200 --no_wrap_diagnostics --clang_vers
749749
static_assert( requires {{ &f } -> C;} ); // expected-error {{reference to overloaded function could not be resolved;}}
750750
^
751751

752-
"Test_name.c", line 1692: error: the deduced placeholder type "GH170856::absolute_zero" failed the type constraint
753-
consteval PointOriginFor<R{}> auto default_point_origin(R) {
754-
^
755-
"Test_name.c", line 1681: note: concept is false for arguments "<struct GH170856::<unnamed>, GH170856::<unnamed>{{{}}}>"
756-
concept PointOriginFor = QuantitySpecOf<decltype(QS), T::_quantity_spec_>;
757-
^
758-
"Test_name.c", line 1679: note: concept is false for arguments "<const struct GH170856::<unnamed>>"
759-
concept QuantitySpecOf = QuantitySpec<decltype(QS)>;
760-
^
761-
"Test_name.c", line 1677: note: atomic constraint evaluates to false
762-
concept QuantitySpec = __is_convertible(T*, quantity_spec_interface_base*);
763-
^
764-
detected during:
765-
instantiation of "GH170856::PointOriginFor<...> auto GH170856::default_point_origin(R) [with R=struct GH170856::<unnamed>]" at line 1697
766-
instantiation of class "GH170856::point_<R> [with R=struct GH170856::<unnamed>]" at line 1705
767-
instantiation of "GH170856::point_<decltype(<expression>)> GH170856::point [with R=GH170856::<unnamed>{{}}]" at line 1705
768-
769752
"Test_name.c", line 41: error: a requires-clause is not allowed here (not a templated function)
770753
([] () requires C<Ts> {} ()); // expected-error {{expression contains unexpanded parameter pack 'Ts'}}
771754
^
@@ -1000,4 +983,4 @@ fe_only -DTEST_NUMBER=1 --clang -tused -e 200 --no_wrap_diagnostics --clang_vers
1000983
F* f = l; // expected-error {{no viable conversion from}}
1001984
^
1002985

1003-
145 errors detected in the compilation of "Test_name.c".
986+
144 errors detected in the compilation of "Test_name.c".

‎tests/tests/imported/clang/cpp/SemaTemplate/.concepts.rto/default.2.1.txt‎

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -741,23 +741,6 @@ fe_only -DTEST_NUMBER=2 --clang -tused -e 200 --no_wrap_diagnostics --clang_vers
741741
static_assert( requires {{ &f } -> C;} ); // expected-error {{reference to overloaded function could not be resolved;}}
742742
^
743743

744-
"Test_name.c", line 1692: error: the deduced placeholder type "GH170856::absolute_zero" failed the type constraint
745-
consteval PointOriginFor<R{}> auto default_point_origin(R) {
746-
^
747-
"Test_name.c", line 1681: note: concept is false for arguments "<struct GH170856::<unnamed>, GH170856::<unnamed>{{{}}}>"
748-
concept PointOriginFor = QuantitySpecOf<decltype(QS), T::_quantity_spec_>;
749-
^
750-
"Test_name.c", line 1679: note: concept is false for arguments "<const struct GH170856::<unnamed>>"
751-
concept QuantitySpecOf = QuantitySpec<decltype(QS)>;
752-
^
753-
"Test_name.c", line 1677: note: atomic constraint evaluates to false
754-
concept QuantitySpec = __is_convertible(T*, quantity_spec_interface_base*);
755-
^
756-
detected during:
757-
instantiation of "GH170856::PointOriginFor<...> auto GH170856::default_point_origin(R) [with R=struct GH170856::<unnamed>]" at line 1697
758-
instantiation of class "GH170856::point_<R> [with R=struct GH170856::<unnamed>]" at line 1705
759-
instantiation of "GH170856::point_<decltype(<expression>)> GH170856::point [with R=GH170856::<unnamed>{{}}]" at line 1705
760-
761744
"Test_name.c", line 41: error: a requires-clause is not allowed here (not a templated function)
762745
([] () requires C<Ts> {} ()); // expected-error {{expression contains unexpanded parameter pack 'Ts'}}
763746
^
@@ -992,4 +975,4 @@ fe_only -DTEST_NUMBER=2 --clang -tused -e 200 --no_wrap_diagnostics --clang_vers
992975
F* f = l; // expected-error {{no viable conversion from}}
993976
^
994977

995-
145 errors detected in the compilation of "Test_name.c".
978+
144 errors detected in the compilation of "Test_name.c".

‎tests/tests/imported/clang/cpp/SemaTemplate/.concepts.rto/edg_x86_64_cp.1.1.txt‎

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -846,23 +846,6 @@ fe_only -DTEST_NUMBER=1 --clang -tused -e 200 --no_wrap_diagnostics --clang_vers
846846
static_assert( requires {{ &f } -> C;} ); // expected-error {{reference to overloaded function could not be resolved;}}
847847
^
848848

849-
"Test_name.c", line 1692: error: the deduced placeholder type "GH170856::absolute_zero" failed the type constraint
850-
consteval PointOriginFor<R{}> auto default_point_origin(R) {
851-
^
852-
"Test_name.c", line 1681: note: concept is false for arguments "<struct GH170856::<unnamed>, GH170856::<unnamed>{{{}}}>"
853-
concept PointOriginFor = QuantitySpecOf<decltype(QS), T::_quantity_spec_>;
854-
^
855-
"Test_name.c", line 1679: note: concept is false for arguments "<const struct GH170856::<unnamed>>"
856-
concept QuantitySpecOf = QuantitySpec<decltype(QS)>;
857-
^
858-
"Test_name.c", line 1677: note: atomic constraint evaluates to false
859-
concept QuantitySpec = __is_convertible(T*, quantity_spec_interface_base*);
860-
^
861-
detected during:
862-
instantiation of "GH170856::PointOriginFor<...> auto GH170856::default_point_origin(R) [with R=struct GH170856::<unnamed>]" at line 1697
863-
instantiation of class "GH170856::point_<R> [with R=struct GH170856::<unnamed>]" at line 1705
864-
instantiation of "GH170856::point_<decltype(<expression>)> GH170856::point [with R=GH170856::<unnamed>{{}}]" at line 1705
865-
866849
"Test_name.c", line 46: error: no instance of function template "lambda []()->auto" matches the argument list
867850
object type is: lambda []()->auto
868851
([]<int = 0> requires C<Ts> () {} (), ...);
@@ -1014,4 +997,4 @@ fe_only -DTEST_NUMBER=1 --clang -tused -e 200 --no_wrap_diagnostics --clang_vers
1014997
^
1015998
detected during instantiation of "void DeferredInstantiationInstScope::LocalStructMemberVar(T) [with T=int]" at line 590
1016999

1017-
151 errors detected in the compilation of "Test_name.c".
1000+
150 errors detected in the compilation of "Test_name.c".

‎tests/tests/imported/clang/cpp/SemaTemplate/.concepts.rto/edg_x86_64_cp.2.1.txt‎

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -838,23 +838,6 @@ fe_only -DTEST_NUMBER=2 --clang -tused -e 200 --no_wrap_diagnostics --clang_vers
838838
static_assert( requires {{ &f } -> C;} ); // expected-error {{reference to overloaded function could not be resolved;}}
839839
^
840840

841-
"Test_name.c", line 1692: error: the deduced placeholder type "GH170856::absolute_zero" failed the type constraint
842-
consteval PointOriginFor<R{}> auto default_point_origin(R) {
843-
^
844-
"Test_name.c", line 1681: note: concept is false for arguments "<struct GH170856::<unnamed>, GH170856::<unnamed>{{{}}}>"
845-
concept PointOriginFor = QuantitySpecOf<decltype(QS), T::_quantity_spec_>;
846-
^
847-
"Test_name.c", line 1679: note: concept is false for arguments "<const struct GH170856::<unnamed>>"
848-
concept QuantitySpecOf = QuantitySpec<decltype(QS)>;
849-
^
850-
"Test_name.c", line 1677: note: atomic constraint evaluates to false
851-
concept QuantitySpec = __is_convertible(T*, quantity_spec_interface_base*);
852-
^
853-
detected during:
854-
instantiation of "GH170856::PointOriginFor<...> auto GH170856::default_point_origin(R) [with R=struct GH170856::<unnamed>]" at line 1697
855-
instantiation of class "GH170856::point_<R> [with R=struct GH170856::<unnamed>]" at line 1705
856-
instantiation of "GH170856::point_<decltype(<expression>)> GH170856::point [with R=GH170856::<unnamed>{{}}]" at line 1705
857-
858841
"Test_name.c", line 46: error: no instance of function template "lambda []()->auto" matches the argument list
859842
object type is: lambda []()->auto
860843
([]<int = 0> requires C<Ts> () {} (), ...);
@@ -1006,4 +989,4 @@ fe_only -DTEST_NUMBER=2 --clang -tused -e 200 --no_wrap_diagnostics --clang_vers
1006989
^
1007990
detected during instantiation of "void DeferredInstantiationInstScope::LocalStructMemberVar(T) [with T=int]" at line 590
1008991

1009-
151 errors detected in the compilation of "Test_name.c".
992+
150 errors detected in the compilation of "Test_name.c".
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
fe_only -DTEST_NUMBER=1 --c++20 Test_name.c
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
fe_only -DTEST_NUMBER=2 --c++20 --g++ --gnu_version=150100 Test_name.c
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
fe_only -DTEST_NUMBER=3 --c++20 --clang --clang_version=190000 Test_name.c
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
fe_only -DTEST_NUMBER=4 --ms_c++20 --microsoft_version=1950 Test_name.c

0 commit comments

Comments
 (0)