]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
c++: CWG 1704, type checking in explicit inst of var tmpl [PR125575]
authorMarek Polacek <polacek@redhat.com>
Wed, 3 Jun 2026 19:01:33 +0000 (15:01 -0400)
committerMarek Polacek <polacek@redhat.com>
Thu, 4 Jun 2026 22:06:47 +0000 (18:06 -0400)
While working on something else I noticed that we compile

  template<typename T>
  constexpr int vt = 42;
  template float vt<int>;

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<typename T> void (fn)(T) {}
  template void (fn<int>)(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 <jason@redhat.com>
gcc/cp/pt.cc
gcc/testsuite/g++.dg/cpp1y/var-templ90.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp1y/var-templ91.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp1y/var-templ92.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp1y/var-templ93.C [new file with mode: 0644]

index 4683b57f34b63ec23c62bb42ab61bc7fac8d9276..3802b3771b6fc7639f3cc8a9672079a3a54c8887 100644 (file)
@@ -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<typename T> T var = {};
+     template int *var<int>;
+   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 (file)
index 0000000..65cc88f
--- /dev/null
@@ -0,0 +1,53 @@
+// PR c++/125575
+// { dg-do compile { target c++14 } }
+
+template<typename T>
+constexpr T v1 = 42;
+template const int v1<int>;
+
+template<typename T>
+constexpr T v2 = 42;
+template int v2<int>; // { dg-error "type .int. for explicit instantiation .v2. does not match declared type .const int." }
+
+template<typename T>
+constexpr T v3 = 42;
+template int *v3<int>; // { dg-error "type .int\\*. for explicit instantiation .v3. does not match declared type .const int." }
+
+template<typename T>
+constexpr const T *const *v4 = nullptr;
+template int *const *v4<int>; // { dg-error "type .int\\* const\\*. for explicit instantiation .v4. does not match declared type .const int\\* const\\* const." }
+
+template<typename T>
+constexpr T v5 = 42;
+template float v5<int>; // { dg-error "type .float. for explicit instantiation .v5. does not match declared type .const int." }
+
+template<typename T>
+T v6 = 42;
+template volatile int v6<int>; // { dg-error "type .volatile int. for explicit instantiation .v6. does not match declared type .int." }
+
+template<typename>
+constexpr auto v7 = 42;
+template int v7<int>; // { dg-error "type .int. for explicit instantiation .v7. does not match declared type .const int." }
+
+template<typename>
+auto v8 = 42;
+template const int v8<int>; // { dg-error "type .const int. for explicit instantiation .v8. does not match declared type .int." }
+
+template<typename T>
+T v9{};
+template int v9<int[10]>[5]; // { dg-error "type .int \\\[5\\\]. for explicit instantiation .v9. does not match declared type .int \\\[10\\\]." }
+
+template<typename T>
+T v10{};
+template int v10<int[10]>[10];
+
+template<typename T>
+T v11{};
+template int v11<int[10][10]>[10][10];
+
+template<typename T>
+T v12{};
+template int v12<int[10][10]>[][10];
+
+template<typename T> int arr[sizeof(T)] = {};
+template int arr<int>[];
diff --git a/gcc/testsuite/g++.dg/cpp1y/var-templ91.C b/gcc/testsuite/g++.dg/cpp1y/var-templ91.C
new file mode 100644 (file)
index 0000000..e7bdef1
--- /dev/null
@@ -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<typename T> T var = {};
+template float var<float>;      // OK, instantiated variable has type float
+template int var<int[16]>[];    // OK, absence of major array bound is permitted
+template int *var<int>;         // error: instantiated variable has type int
+// { dg-error "type .int\\*. for explicit instantiation .var. does not match declared type .int." "" { target *-*-* } .-1 }
+
+template<typename T> auto av = T();
+template int av<int>;           // OK, variable with type auto can be redeclared with type int
+
+template<typename T> auto f() {}
+template void f<int>();         // 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 (file)
index 0000000..125284e
--- /dev/null
@@ -0,0 +1,20 @@
+// PR c++/98524
+// { dg-do compile { target c++14 } }
+
+template<typename T> void (*f1)();
+template void (*f1<int>)(int); // { dg-error "type .void \\(\\*\\)\\(int\\). for explicit instantiation .f1. does not match declared type .void \\(\\*\\)\\(\\)." }
+
+template<typename T> void (*f2)(int) noexcept;
+template void (*f2<int>)(int);  // { dg-error "type .void \\(\\*\\)\\(int\\). for explicit instantiation .f2. does not match declared type .void \\(\\*\\)\\(int\\) noexcept." "" { target c++17 } }
+
+template<typename T> void (*f3)(int) ;
+template void (*f3<int>)(int) noexcept; // { dg-error "type .void \\(\\*\\)\\(int\\) noexcept. for explicit instantiation .f3. does not match declared type .void \\(\\*\\)\\(int\\)." "" { target c++17 } }
+
+template<typename T> void (*f4)(int) noexcept;
+template void (*f4<int>)(int) noexcept;
+
+template<typename T> void (*f5)(int);
+template void (*f5<int>)(int);
+
+template<typename T> void (*f6)(int);
+template int (*f6<int>)(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 (file)
index 0000000..d55c1f0
--- /dev/null
@@ -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<typename T> T var = T();
+template float var<float>;
+template int* var<int>;          // { dg-error "type .int\\*. for explicit instantiation .var. does not match declared type .int." }
+template auto var<char>;  // { dg-error "has no initializer" }