From: Marek Polacek Date: Wed, 3 Jun 2026 19:01:33 +0000 (-0400) Subject: c++: CWG 1704, type checking in explicit inst of var tmpl [PR125575] X-Git-Url: http://git.ipfire.org/gitweb/index.cgi?a=commitdiff_plain;h=f213d6591640f76eb86cafdb4366d87b87747038;p=thirdparty%2Fgcc.git c++: CWG 1704, type checking in explicit inst of var tmpl [PR125575] While working on something else I noticed that we compile template constexpr int vt = 42; template float vt; but we shouldn't due to the float/int mismatch. Andrew found 98524 which is the same problem. This turned out to be CWG 1704. For explicit specialization of functions, determine_specialization already performs the checking; see the various same_type_p, compparms, comp_template_parms etc. checks. Except I do believe it doesn't check exception specification as it should: template void (fn)(T) {} template void (fn)(int) noexcept; // ill-formed, we accept The fix should be rather easy. PR c++/125575 PR c++/98524 gcc/cp/ChangeLog: * pt.cc (check_explicit_inst_of_var_template): New. (check_explicit_specialization): Use it. gcc/testsuite/ChangeLog: * g++.dg/cpp1y/var-templ90.C: New test. * g++.dg/cpp1y/var-templ91.C: New test. * g++.dg/cpp1y/var-templ92.C: New test. * g++.dg/cpp1y/var-templ93.C: New test. Reviewed-by: Jason Merrill --- diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc index 4683b57f34b..3802b3771b6 100644 --- a/gcc/cp/pt.cc +++ b/gcc/cp/pt.cc @@ -2851,6 +2851,46 @@ warn_spec_missing_attributes (tree tmpl, tree spec, tree attrlist) } } +/* Perform type checking in explicit instantiation of variable templates + as per CWG 1704. That is, detect + template T var = {}; + template int *var; + where there's a mismatch 'int' x 'int *'. INST is the instantiation + of the variable template, DECL is the VAR_DECL from the template-id + used in the explicit instantiation. */ + +static bool +check_explicit_inst_of_var_template (tree inst, tree decl) +{ + if (!inst || inst == error_mark_node) + return true; + + tree type1 = TREE_TYPE (inst); + tree type2 = TREE_TYPE (decl); + + /* Redeclaration with type auto is OK. */ + if (is_auto (type1) || is_auto (type2)) + return true; + + /* Absence of major array bound is permitted. */ + if (TREE_CODE (type1) == ARRAY_TYPE + && TREE_CODE (type2) == ARRAY_TYPE + && (!TYPE_DOMAIN (type1) || !TYPE_DOMAIN (type2))) + { + type1 = TREE_TYPE (type1); + type2 = TREE_TYPE (type2); + } + + if (same_type_p (type1, type2)) + /* All good. */ + return true; + + error ("type %qT for explicit instantiation %qD does not match declared " + "type %qT", type2, decl, type1); + inform (DECL_SOURCE_LOCATION (inst), "variable template declared here"); + return false; +} + /* Check to see if the function just declared, as indicated in DECLARATOR, and in DECL, is a specialization of a function template. We may also discover that the declaration is an explicit @@ -3235,7 +3275,11 @@ check_explicit_specialization (tree declarator, targs = new_targs; } - return instantiate_template (tmpl, targs, tf_error); + tree inst = instantiate_template (tmpl, targs, tf_error); + if (variable_template_p (tmpl) + && !check_explicit_inst_of_var_template (inst, decl)) + return error_mark_node; + return inst; } /* If we thought that the DECL was a member function, but it diff --git a/gcc/testsuite/g++.dg/cpp1y/var-templ90.C b/gcc/testsuite/g++.dg/cpp1y/var-templ90.C new file mode 100644 index 00000000000..65cc88f2634 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1y/var-templ90.C @@ -0,0 +1,53 @@ +// PR c++/125575 +// { dg-do compile { target c++14 } } + +template +constexpr T v1 = 42; +template const int v1; + +template +constexpr T v2 = 42; +template int v2; // { dg-error "type .int. for explicit instantiation .v2. does not match declared type .const int." } + +template +constexpr T v3 = 42; +template int *v3; // { dg-error "type .int\\*. for explicit instantiation .v3. does not match declared type .const int." } + +template +constexpr const T *const *v4 = nullptr; +template int *const *v4; // { dg-error "type .int\\* const\\*. for explicit instantiation .v4. does not match declared type .const int\\* const\\* const." } + +template +constexpr T v5 = 42; +template float v5; // { dg-error "type .float. for explicit instantiation .v5. does not match declared type .const int." } + +template +T v6 = 42; +template volatile int v6; // { dg-error "type .volatile int. for explicit instantiation .v6. does not match declared type .int." } + +template +constexpr auto v7 = 42; +template int v7; // { dg-error "type .int. for explicit instantiation .v7. does not match declared type .const int." } + +template +auto v8 = 42; +template const int v8; // { dg-error "type .const int. for explicit instantiation .v8. does not match declared type .int." } + +template +T v9{}; +template int v9[5]; // { dg-error "type .int \\\[5\\\]. for explicit instantiation .v9. does not match declared type .int \\\[10\\\]." } + +template +T v10{}; +template int v10[10]; + +template +T v11{}; +template int v11[10][10]; + +template +T v12{}; +template int v12[][10]; + +template int arr[sizeof(T)] = {}; +template int arr[]; diff --git a/gcc/testsuite/g++.dg/cpp1y/var-templ91.C b/gcc/testsuite/g++.dg/cpp1y/var-templ91.C new file mode 100644 index 00000000000..e7bdef1b486 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1y/var-templ91.C @@ -0,0 +1,18 @@ +// PR c++/125575 +// CWG 1704, Type checking in explicit instantiation of variable templates +// { dg-do compile { target c++14 } } +// Test from [temp.explicit]. + +template T var = {}; +template float var; // OK, instantiated variable has type float +template int var[]; // OK, absence of major array bound is permitted +template int *var; // error: instantiated variable has type int +// { dg-error "type .int\\*. for explicit instantiation .var. does not match declared type .int." "" { target *-*-* } .-1 } + +template auto av = T(); +template int av; // OK, variable with type auto can be redeclared with type int + +template auto f() {} +template void f(); // error: function with deduced return type + // redeclared with non-deduced return type ([dcl.spec.auto]) +// { dg-error "does not match any template declaration" "" { target *-*-* } .-2 } diff --git a/gcc/testsuite/g++.dg/cpp1y/var-templ92.C b/gcc/testsuite/g++.dg/cpp1y/var-templ92.C new file mode 100644 index 00000000000..125284ec424 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1y/var-templ92.C @@ -0,0 +1,20 @@ +// PR c++/98524 +// { dg-do compile { target c++14 } } + +template void (*f1)(); +template void (*f1)(int); // { dg-error "type .void \\(\\*\\)\\(int\\). for explicit instantiation .f1. does not match declared type .void \\(\\*\\)\\(\\)." } + +template void (*f2)(int) noexcept; +template void (*f2)(int); // { dg-error "type .void \\(\\*\\)\\(int\\). for explicit instantiation .f2. does not match declared type .void \\(\\*\\)\\(int\\) noexcept." "" { target c++17 } } + +template void (*f3)(int) ; +template void (*f3)(int) noexcept; // { dg-error "type .void \\(\\*\\)\\(int\\) noexcept. for explicit instantiation .f3. does not match declared type .void \\(\\*\\)\\(int\\)." "" { target c++17 } } + +template void (*f4)(int) noexcept; +template void (*f4)(int) noexcept; + +template void (*f5)(int); +template void (*f5)(int); + +template void (*f6)(int); +template int (*f6)(int); // { dg-error "type .int \\(\\*\\)\\(int\\). for explicit instantiation .f6. does not match declared type .void \\(\\*\\)\\(int\\)." } diff --git a/gcc/testsuite/g++.dg/cpp1y/var-templ93.C b/gcc/testsuite/g++.dg/cpp1y/var-templ93.C new file mode 100644 index 00000000000..d55c1f02ac3 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1y/var-templ93.C @@ -0,0 +1,8 @@ +// PR c++/125575 +// CWG 1728, Type of an explicit instantiation of a variable template +// { dg-do compile { target c++14 } } + +template T var = T(); +template float var; +template int* var; // { dg-error "type .int\\*. for explicit instantiation .var. does not match declared type .int." } +template auto var; // { dg-error "has no initializer" }