While investigating an ICE in VCMI with Qt 6 headers and
-Wmismatched-tags, I found that tsubst can read past the end of a
template argument `level` during partial specialization matching.
The failing path is:
class_decl_loc_t::diag_mismatched_tags
most_specialized_partial_spec
get_partial_spec_bindings
tsubst
In tsubst, template_parm_level_and_index determines both `level` and
`idx` from the template parameter. The current code checks that the
argument `level` is non-empty before using TMPL_ARG, but that does not
mean that `idx` is a valid element of the `level`.
In my case GCC tries to read element 3 from a TREE_VEC with 2 elements.
The code below already handles the case where no argument was found by
returning `t` unchanged for `level == 1`. So check that the requested
index exists, not only that the level is non-empty.
gcc/cp/ChangeLog:
* pt.cc (tsubst): Check the template argument index before
reading from a template argument level.
gcc/testsuite/ChangeLog:
* g++.dg/warn/Wmismatched-tags-ice1.C: New test.
Testing:
* Built all-gcc on x86_64-pc-linux-gnu.
* make -k check-g++ RUNTESTFLAGS="dg.exp=Wmismatched-tags-ice1.C"
* Verified that the original preprocessed VCMI/Qt reproducer no
longer ICEs with -Wmismatched-tags.
Signed-off-by: Stefan Strogin <[email protected]>
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index b7f9ce4cb0a..c65db71fee0 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -17389,7 +17389,7 @@ tsubst (tree t, tree args, tsubst_flags_t complain,
tree in_decl)
levels = TMPL_ARGS_DEPTH (args);
if (level <= levels
- && TREE_VEC_LENGTH (TMPL_ARGS_LEVEL (args, level)) > 0)
+ && idx < TREE_VEC_LENGTH (TMPL_ARGS_LEVEL (args, level)))
{
arg = TMPL_ARG (args, level, idx);
diff --git a/gcc/testsuite/g++.dg/warn/Wmismatched-tags-ice1.C
b/gcc/testsuite/g++.dg/warn/Wmismatched-tags-ice1.C
new file mode 100644
index 00000000000..debde11130c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wmismatched-tags-ice1.C
@@ -0,0 +1,8 @@
+// { dg-do compile }
+// { dg-options "-Wmismatched-tags" }
+
+template <class, class> struct A {
+ template <class, class = void> struct B;
+ template <class T> struct B<T>;
+ struct B<int>;
+};