Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/expr.c
Original file line number Diff line number Diff line change
Expand Up @@ -54667,7 +54667,7 @@ is TRUE if the expression is the immediate operand of an "&" operator.
if (csp != NULL && symbol_is(csp, sk_constant)) {
make_sym_constant_operand(csp, result);
} else {
make_constant_operand(cp, result);
make_template_param_object_variable_operand(var, result);
} /* if */
} else {
sym = symbol_for(var);
Expand Down
14 changes: 13 additions & 1 deletion src/exprutil.c
Original file line number Diff line number Diff line change
Expand Up @@ -8540,6 +8540,18 @@ allocated in the file scope memory region.
template_param_objects->map(ch, vp);
sym->variant.variable.ptr = vp;
} /* if */
make_template_param_object_variable_operand(vp, operand);
} /* make_template_param_object_operand */


void make_template_param_object_variable_operand(a_variable_ptr vp,
an_operand *operand)
/*
Make operand a (const) lvalue expression operand for the template parameter
object vp.
*/
{
check_assertion(vp->is_template_param_object);
/* Making an operand for the object is a use of it and because the object
is an inline variable its definition must then be emitted here. The
usual symbol reference path does not record that because the object is
Expand All @@ -8553,7 +8565,7 @@ allocated in the file scope memory region.
check_assertion(is_expression_operand(operand));
operand->variant.expression->type = operand->type;
(void)make_node_from_operand(operand);
} /* make_template_param_object_operand */
} /* make_template_param_object_variable_operand */


void make_sym_constant_operand(a_symbol_ptr sym,
Expand Down
4 changes: 4 additions & 0 deletions src/exprutil.h
Original file line number Diff line number Diff line change
Expand Up @@ -2147,6 +2147,10 @@ extern void make_constant_operand(a_constant *constant,
extern void make_sym_constant_operand(a_symbol_ptr sym,
an_operand *operand);

extern void make_template_param_object_variable_operand(
a_variable_ptr vp,
an_operand *operand);

extern void make_string_constant_operand(a_constant *constant,
an_operand *operand);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
[GH #423]
Class-type template parameters of enclosing templates in member templates

When an enclosing template (e.g., a class template) was instantiated, a
reference to one of its nontype template parameters of class type in the
signature of a member template was replaced by the template parameter
object. When the member template's own template arguments were
substituted, that reference was then treated as a prvalue instead of as a
const lvalue. As a result, taking its address caused deduction to fail, and
other uses (e.g., in a decltype operand) caused a spurious "unexpected
function type" error. That is now fixed. For example, with --c++20:

struct A { int i = 0; };
constexpr A a1{1};
template <A V> struct S {
template <typename T> static auto f(T t) -> decltype(t, &V);
template <typename T> static auto g(T t) -> decltype(t, V);
};
using R1 = decltype(S<a1>::f(0)); // Previously, no instance matched.
using R2 = decltype(S<a1>::g(0)); // Previously, "unexpected function
// type" error.
1 change: 1 addition & 0 deletions tests/tests/regressions/.gh-423.rto/default.1.1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
fe_only -DTEST_NUMBER=1 --c++20 Test_name.c
1 change: 1 addition & 0 deletions tests/tests/regressions/.gh-423.rto/default.2.1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
fe_only -DTEST_NUMBER=2 --c++20 --g++ --gnu_version=150200 Test_name.c
1 change: 1 addition & 0 deletions tests/tests/regressions/.gh-423.rto/default.3.1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
fe_only -DTEST_NUMBER=3 --c++20 --clang --clang_version=210000 Test_name.c
1 change: 1 addition & 0 deletions tests/tests/regressions/.gh-423.rto/default.4.1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
fe_only -DTEST_NUMBER=4 --ms_c++20 --microsoft_version=1950 Test_name.c
53 changes: 53 additions & 0 deletions tests/tests/regressions/gh-423.sft.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
//type:fp
//options:--c++20:--c++20 --g++ --gnu_version=150200:--c++20 --clang --clang_version=210000:--ms_c++20 --microsoft_version=1950
//remark:class-type template parameter of an enclosing template in a signature

// When an enclosing template was instantiated, references to a class-type
// template parameter of that template in the signature of a member template
// were replaced by the template parameter object. Substituting the member
// template's own template arguments then turned the object into a prvalue:
// Taking its address made deduction fail, and other uses resulted in a
// function type inconsistent with that of the template.

struct A { int i = 0; };
constexpr A a1{1};

template <typename T, typename U> constexpr bool is_same = false;
template <typename T> constexpr bool is_same<T, T> = true;

// A member template of a class template.
template <A V> struct S {
static constexpr const A *p = &V;
template <typename T> static constexpr auto f(T t) -> decltype(t, &V) {
return &V;
}
template <typename T> static auto g(T t) -> decltype(t, V);
template <typename T> static auto h(T t) -> decltype(t, V.i);
};
static_assert(is_same<decltype(S<a1>::f(0)), const A *>);
static_assert(S<a1>::f(0) == S<a1>::p);
static_assert(is_same<decltype(S<a1>::g(0)), const A &>);
static_assert(is_same<decltype(S<a1>::h(0)), const int &>);

// A generic lambda in a function template.
template <A V> constexpr auto mk() {
return [](auto t) -> decltype(t, &V) { return &V; };
}
static_assert(mk<a1>()(0) == mk<a1>()(0L));

// A member template of a class template partial specialization.
template <typename T, typename U> struct X;
template <A V> struct Tag { };
template <A V, typename U> struct X<Tag<V>, U> {
template <typename T> static auto f(T t) -> decltype(t, U(), &V);
};
static_assert(is_same<decltype(X<Tag<a1>, int>::f(0)), const A *>);

// The type of the template parameter is deduced.
struct NoConst {
int operator()() { return 0; }
};
template <auto F> struct D {
template <typename T> static auto g(T t) -> decltype(t, F);
};
static_assert(is_same<decltype(D<NoConst{}>::g(0)), const NoConst &>);
Loading