When selecting a member class template partial specialization,
most_specialized_partial_spec strips the target arguments to the
innermost level and substitutes the enclosing class template arguments
into each candidate before matching. That is fine when the enclosing
arguments are non-dependent.
When they are still dependent, substituting them can lower template
parameter levels and make an outer parameter look innermost.
get_partial_spec_bindings then works with inconsistent template
parameter and argument vectors, so later tsubst or unify can index past
the end of an innermost argument vector.
Keep that path only for non-dependent enclosing arguments. For dependent
enclosing arguments, leave args multi-level and match the original
candidate directly, preserving template parameter levels.
The testcase covers the original -Wmismatched-tags crash and a related
case that reaches the same underlying issue through unify.
gcc/cp/ChangeLog:
* pt.cc (most_specialized_partial_spec): Keep full multi-level
arguments when enclosing template arguments are dependent.
gcc/testsuite/ChangeLog:
* g++.dg/warn/Wmismatched-tags-ice1.C: New test.
Testing:
* Built cc1plus with all-gcc.
* Ran g++.dg/warn/Wmismatched-tags-ice1.C.
* Ran g++.dg/template/mem-partial1.C.
* Ran g++.dg/template/mem-partial2.C.
* Ran g++.dg/cpp2a/concepts-partial-spec7.C.
* Verified the VCMI preprocessed reproducer no longer ICEs.
Signed-off-by: Stefan Strogin <[email protected]>
---
gcc/cp/pt.cc | 14 ++++++++++----
gcc/testsuite/g++.dg/warn/Wmismatched-tags-ice1.C | 14 ++++++++++++++
2 files changed, 24 insertions(+), 4 deletions(-)
create mode 100644 gcc/testsuite/g++.dg/warn/Wmismatched-tags-ice1.C
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index b7f9ce4cb0a..fc2c54ba4e9 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -27974,13 +27974,19 @@ most_specialized_partial_spec (tree target,
tsubst_flags_t complain,
push_access_scope_guard pas (decl);
deferring_access_check_sentinel acs (dk_no_deferred);
- /* For determining which partial specialization to use, only the
- innermost args are interesting. */
tree outer_args = NULL_TREE;
if (TMPL_ARGS_HAVE_MULTIPLE_LEVELS (args))
{
- outer_args = strip_innermost_template_args (args, 1);
- args = INNERMOST_TEMPLATE_ARGS (args);
+ tree stripped_args = strip_innermost_template_args (args, 1);
+ /* If the outer args are still dependent, keep ARGS multi-level so
+ matching preserves their template parameter levels. */
+ if (!uses_template_parms (stripped_args))
+ {
+ /* The non-dependent outer args get substituted into each candidate
+ below; only the innermost args are needed for matching. */
+ outer_args = stripped_args;
+ args = INNERMOST_TEMPLATE_ARGS (args);
+ }
}
/* The caller hasn't called push_to_top_level yet, but we need
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..5df546b2f49
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wmismatched-tags-ice1.C
@@ -0,0 +1,14 @@
+// { 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>;
+};
+
+template <class X, class Y> struct C {
+ template <class, class = void> struct D;
+ template <class T> struct D<Y, T>;
+ struct D<Y, int>;
+};
--
2.54.0