From 0860ccbd0c13f8abceccfb6541df9082cc4086ae Mon Sep 17 00:00:00 2001 From: Daveed Vandevoorde Date: Fri, 6 Nov 2026 03:30:25 +0000 Subject: [PATCH] Accept template-dependent variable template-ids in C++03 constant expressions [GH #489] --- src/exprutil.c | 15 ++++++- src/pending_changes/gh-489-var-tmpl-const.txt | 19 +++++++++ .../.gh-489-neg.rto/default.1.1.txt | 11 +++++ .../.gh-489-neg.rto/default.2.1.txt | 11 +++++ .../.gh-489-neg.rto/default.3.1.txt | 14 +++++++ .../regressions/.gh-489.rto/default.1.1.txt | 13 ++++++ .../regressions/.gh-489.rto/default.2.1.txt | 13 ++++++ .../regressions/.gh-489.rto/default.3.1.txt | 13 ++++++ tests/tests/regressions/gh-489-neg.sft.cpp | 15 +++++++ tests/tests/regressions/gh-489.sft.cpp | 41 +++++++++++++++++++ 10 files changed, 163 insertions(+), 2 deletions(-) create mode 100644 src/pending_changes/gh-489-var-tmpl-const.txt create mode 100644 tests/tests/regressions/.gh-489-neg.rto/default.1.1.txt create mode 100644 tests/tests/regressions/.gh-489-neg.rto/default.2.1.txt create mode 100644 tests/tests/regressions/.gh-489-neg.rto/default.3.1.txt create mode 100644 tests/tests/regressions/.gh-489.rto/default.1.1.txt create mode 100644 tests/tests/regressions/.gh-489.rto/default.2.1.txt create mode 100644 tests/tests/regressions/.gh-489.rto/default.3.1.txt create mode 100644 tests/tests/regressions/gh-489-neg.sft.cpp create mode 100644 tests/tests/regressions/gh-489.sft.cpp diff --git a/src/exprutil.c b/src/exprutil.c index f27d414f79..16b8214eda 100644 --- a/src/exprutil.c +++ b/src/exprutil.c @@ -17760,7 +17760,7 @@ be returned for a C mode const variable. function-local as well). */ con_val = initializer_constant(var); if (con_val == NULL && gpp_mode && - var->init_kind == (an_init_kind)initk_none && + var->init_kind == initk_none && var->source_corresp.is_class_member && !var->is_member_constant && parent_class_of(var)->variant.class_struct_union.is_nonreal_class && @@ -17769,7 +17769,18 @@ be returned for a C mode const variable. in-class initializer as a constant-expression during the prototype instantiation of its class. In a real instantiation, the out-of-class definition must be present. */ - con_val = alloc_constant((a_constant_repr_kind)ck_template_param); + con_val = alloc_constant(ck_template_param); + make_template_param_expr_constant(var_rvalue_expr(var), con_val); + } else if (con_val == NULL && !constexpr_enabled && var->is_nonreal && + var->init_kind == initk_none && + var->template_info != NULL && + var->template_info->template_arg_list != NULL) { + /* A variable template instance with template-dependent arguments + (e.g., "vt" in a template), whose value is not known until + instantiation. A pre-C++11 constant expression (where variable + templates are an extension) requires a constant here, so treat + the reference as a template-dependent constant. */ + con_val = alloc_constant(ck_template_param); make_template_param_expr_constant(var_rvalue_expr(var), con_val); } /* if */ if (!C_mode() && !constexpr_enabled && strict_ansi_mode && diff --git a/src/pending_changes/gh-489-var-tmpl-const.txt b/src/pending_changes/gh-489-var-tmpl-const.txt new file mode 100644 index 0000000000..362d7757f4 --- /dev/null +++ b/src/pending_changes/gh-489-var-tmpl-const.txt @@ -0,0 +1,19 @@ +[GH #489] +Template-dependent variable template-ids in C++03 constant expressions + +In C++03 GNU and Clang modes, where variable templates are accepted as an +extension, a variable template-id with template-dependent template arguments +was rejected as nonconstant where a constant expression is required (e.g., as +a template argument, an array bound, or an enumerator value). That is now +fixed. Such a reference to a constant variable template is treated as a +value-dependent constant, and its value is checked when the template is +instantiated. For example, with --g++ --c++03: + + template const bool is_fp = false; + template <> const bool is_fp = true; + template struct integral_constant { + static const bool value = B; + }; + template + struct is_floating_point + : integral_constant > {}; // Previously an error. diff --git a/tests/tests/regressions/.gh-489-neg.rto/default.1.1.txt b/tests/tests/regressions/.gh-489-neg.rto/default.1.1.txt new file mode 100644 index 0000000000..a5d6269086 --- /dev/null +++ b/tests/tests/regressions/.gh-489-neg.rto/default.1.1.txt @@ -0,0 +1,11 @@ +fe_only -DTEST_NUMBER=1 --g++ --c++03 --gnu_version=150200 Test_name.c +"Test_name.c", line 13: warning: variable templates are a C++14 feature + template bool nonconst = false; + ^ + +"Test_name.c", line 14: error: expression must have a constant value + template struct Z : integral_constant > {}; + ^ + detected during instantiation of class "Z [with T=int]" at line 15 + +1 error detected in the compilation of "Test_name.c". diff --git a/tests/tests/regressions/.gh-489-neg.rto/default.2.1.txt b/tests/tests/regressions/.gh-489-neg.rto/default.2.1.txt new file mode 100644 index 0000000000..eca5af1c3f --- /dev/null +++ b/tests/tests/regressions/.gh-489-neg.rto/default.2.1.txt @@ -0,0 +1,11 @@ +fe_only -DTEST_NUMBER=2 --clang --c++03 --clang_version=220100 Test_name.c +"Test_name.c", line 13: warning: variable templates are a C++14 feature + template bool nonconst = false; + ^ + +"Test_name.c", line 14: error: expression must have a constant value + template struct Z : integral_constant > {}; + ^ + detected during instantiation of class "Z [with T=int]" at line 15 + +1 error detected in the compilation of "Test_name.c". diff --git a/tests/tests/regressions/.gh-489-neg.rto/default.3.1.txt b/tests/tests/regressions/.gh-489-neg.rto/default.3.1.txt new file mode 100644 index 0000000000..1ace6417b5 --- /dev/null +++ b/tests/tests/regressions/.gh-489-neg.rto/default.3.1.txt @@ -0,0 +1,14 @@ +fe_only -DTEST_NUMBER=3 --g++ --c++11 --gnu_version=150200 Test_name.c +"Test_name.c", line 13: warning: variable templates are a C++14 feature + template bool nonconst = false; + ^ + +"Test_name.c", line 14: error: expression must have a constant value + template struct Z : integral_constant > {}; + ^ +"Test_name.c", line 14: note: the value of variable "nonconst" (declared at line 13) cannot be used as a constant + template struct Z : integral_constant > {}; + ^ + detected during instantiation of class "Z [with T=int]" at line 15 + +1 error detected in the compilation of "Test_name.c". diff --git a/tests/tests/regressions/.gh-489.rto/default.1.1.txt b/tests/tests/regressions/.gh-489.rto/default.1.1.txt new file mode 100644 index 0000000000..aa8e120e9d --- /dev/null +++ b/tests/tests/regressions/.gh-489.rto/default.1.1.txt @@ -0,0 +1,13 @@ +fe_only -DTEST_NUMBER=1 --g++ --c++03 --gnu_version=150200 Test_name.c +"Test_name.c", line 13: warning: variable templates are a C++14 feature + template inline const bool is_fp_impl = false; + ^ + +"Test_name.c", line 14: warning: inline variables are a C++17 feature + template <> inline const bool is_fp_impl = true; + ^ + +"Test_name.c", line 25: warning: variable templates are a C++14 feature + template const bool positive = N > 0; + ^ + diff --git a/tests/tests/regressions/.gh-489.rto/default.2.1.txt b/tests/tests/regressions/.gh-489.rto/default.2.1.txt new file mode 100644 index 0000000000..0f36a80559 --- /dev/null +++ b/tests/tests/regressions/.gh-489.rto/default.2.1.txt @@ -0,0 +1,13 @@ +fe_only -DTEST_NUMBER=2 --clang --c++03 --clang_version=220100 Test_name.c +"Test_name.c", line 13: warning: variable templates are a C++14 feature + template inline const bool is_fp_impl = false; + ^ + +"Test_name.c", line 14: warning: inline variables are a C++17 feature + template <> inline const bool is_fp_impl = true; + ^ + +"Test_name.c", line 25: warning: variable templates are a C++14 feature + template const bool positive = N > 0; + ^ + diff --git a/tests/tests/regressions/.gh-489.rto/default.3.1.txt b/tests/tests/regressions/.gh-489.rto/default.3.1.txt new file mode 100644 index 0000000000..d4f5bb10fa --- /dev/null +++ b/tests/tests/regressions/.gh-489.rto/default.3.1.txt @@ -0,0 +1,13 @@ +fe_only -DTEST_NUMBER=3 --g++ --c++11 --gnu_version=150200 Test_name.c +"Test_name.c", line 13: warning: variable templates are a C++14 feature + template inline const bool is_fp_impl = false; + ^ + +"Test_name.c", line 14: warning: inline variables are a C++17 feature + template <> inline const bool is_fp_impl = true; + ^ + +"Test_name.c", line 25: warning: variable templates are a C++14 feature + template const bool positive = N > 0; + ^ + diff --git a/tests/tests/regressions/gh-489-neg.sft.cpp b/tests/tests/regressions/gh-489-neg.sft.cpp new file mode 100644 index 0000000000..06222423fd --- /dev/null +++ b/tests/tests/regressions/gh-489-neg.sft.cpp @@ -0,0 +1,15 @@ +//type:fn +//options:--g++ --c++03 --gnu_version=150200:--clang --c++03 --clang_version=220100:--g++ --c++11 --gnu_version=150200 +//remark:Non-constant variable template-id rejected in a constant expression + +// A variable template that is not const is still not usable where a constant +// expression is required. The error is issued when the template is +// instantiated. + +template struct integral_constant { + static const bool value = B; +}; + +template bool nonconst = false; +template struct Z : integral_constant > {}; +Z z; diff --git a/tests/tests/regressions/gh-489.sft.cpp b/tests/tests/regressions/gh-489.sft.cpp new file mode 100644 index 0000000000..ad56ec9419 --- /dev/null +++ b/tests/tests/regressions/gh-489.sft.cpp @@ -0,0 +1,41 @@ +//type:fp +//options:--g++ --c++03 --gnu_version=150200:--clang --c++03 --clang_version=220100:--g++ --c++11 --gnu_version=150200 +//remark:Template-dependent variable template-ids in C++03 constant expressions + +// In C++03 GNU and Clang modes (where variable templates are an extension), +// a variable template-id with template-dependent arguments was rejected as +// nonconstant where a constant expression is required. + +template struct integral_constant { + static const bool value = B; +}; + +template inline const bool is_fp_impl = false; +template <> inline const bool is_fp_impl = true; +template <> inline const bool is_fp_impl = true; +template <> inline const bool is_fp_impl = true; + +template +struct is_floating_point : integral_constant > {}; + +int a1[is_floating_point::value ? 1 : -1]; +int a2[is_floating_point::value ? -1 : 1]; + +// Value-dependent template argument; other constant-expression contexts. +template const bool positive = N > 0; +template struct X : integral_constant > { + int arr[positive ? 1 : 2]; + enum { e = positive }; +}; +int b1[X<1>::value ? 1 : -1]; +int b2[sizeof(X<1>().arr) == sizeof(int) ? 1 : -1]; +int b3[X<0>::e == 0 ? 1 : -1]; + +// Member variable template. +struct S { + template static const bool v = false; +}; +template <> const bool S::v = true; +template struct Y : integral_constant > {}; +int c1[Y::value ? 1 : -1]; +int c2[Y::value ? -1 : 1];