From e560a91f4de1490fbe2e584116e4190ad0243c8b Mon Sep 17 00:00:00 2001 From: Jakub Labenski Date: Mon, 19 Oct 2026 12:33:27 +0200 Subject: [PATCH] fix(templates): avoid reusing specialization without rescan info in CTAD [] compare_expressions checked, under CC_EXACT_EQUIVALENCE, that both nodes either have or lack rescan info. That check only ran when the type comparison was done, and the type comparison is skipped for variable references. As a result, a specialization such as P (b a dependent static data member) created by a member declaration, with no rescan info, could be reused for the same specialization in a constructor parameter type. Then, building the implicit deduction guide for class template argument deduction failed with the internal error "missing default rescan info". Move the rescan info check out of the type comparison block so it applies to all equivalent nodes. Add regression test regressions/ctad-dependent-member-template-arg-reuse.sft.cpp and a Changes entry. --- src/il.c | 25 ++++++++------- .../ctad-missing-rescan-info.txt | 17 ++++++++++ .../default.1.1.txt | 1 + ...ependent-member-template-arg-reuse.sft.cpp | 31 +++++++++++++++++++ 4 files changed, 63 insertions(+), 11 deletions(-) create mode 100644 src/pending_changes/ctad-missing-rescan-info.txt create mode 100644 tests/tests/regressions/.ctad-dependent-member-template-arg-reuse.rto/default.1.1.txt create mode 100644 tests/tests/regressions/ctad-dependent-member-template-arg-reuse.sft.cpp diff --git a/src/il.c b/src/il.c index 11db2ca050..f45f06c880 100644 --- a/src/il.c +++ b/src/il.c @@ -8665,19 +8665,22 @@ are done. } /* if */ if (!identical_types_full(node1->type, node2->type, itf_options)) { eq = FALSE; - } else if ((options & CC_EXACT_EQUIVALENCE) && - (node1->extra.rescan_info != NULL) != - (node2->extra.rescan_info != NULL)) { - /* The two expressions are equivalent, but one has associated rescan - information and the other not. With the given option, the two - should not be considered equivalent because it could cause the - version with rescan information to be replaced by the other version, - thereby causing spurious rescan failures. (If the two nodes have - associated rescan information, that information can be assumed to - be equivalent since the expressions are equivalent.) */ - eq = FALSE; } /* if */ } /* if */ + if (eq && (options & CC_EXACT_EQUIVALENCE) && + (node1->extra.rescan_info != NULL) != + (node2->extra.rescan_info != NULL)) { + /* The two expressions are equivalent, but one has associated rescan + information and the other not. With the given option, the two + should not be considered equivalent because it could cause the + version with rescan information to be replaced by the other version, + thereby causing spurious rescan failures. (If the two nodes have + associated rescan information, that information can be assumed to + be equivalent since the expressions are equivalent.) This check is + done even when the type comparison is skipped (e.g., for variable + references). */ + eq = FALSE; + } /* if */ } /* if */ done: #if DEBUG diff --git a/src/pending_changes/ctad-missing-rescan-info.txt b/src/pending_changes/ctad-missing-rescan-info.txt new file mode 100644 index 0000000000..d90b834015 --- /dev/null +++ b/src/pending_changes/ctad-missing-rescan-info.txt @@ -0,0 +1,17 @@ +[GH #335] +Fix CTAD rescan info for reused specializations + +An internal error ("missing default rescan info") could occur when building +an implicit deduction guide for a constructor whose parameter type is a +class template specialization with a nontype template argument referring to +a dependent static data member, if that same specialization was already used +earlier in the class (e.g., in a member declaration). For example, with +--c++17: + + template struct P {}; + template struct A { + static constexpr bool b = sizeof(T) == 1; + P m; + A(T, P); + }; + void g() { A{char{}, {}}; } // Previously caused an internal error. diff --git a/tests/tests/regressions/.ctad-dependent-member-template-arg-reuse.rto/default.1.1.txt b/tests/tests/regressions/.ctad-dependent-member-template-arg-reuse.rto/default.1.1.txt new file mode 100644 index 0000000000..43bb995b7b --- /dev/null +++ b/tests/tests/regressions/.ctad-dependent-member-template-arg-reuse.rto/default.1.1.txt @@ -0,0 +1 @@ +fe_only -DTEST_NUMBER=1 --c++17 Test_name.c diff --git a/tests/tests/regressions/ctad-dependent-member-template-arg-reuse.sft.cpp b/tests/tests/regressions/ctad-dependent-member-template-arg-reuse.sft.cpp new file mode 100644 index 0000000000..d26cab8579 --- /dev/null +++ b/tests/tests/regressions/ctad-dependent-member-template-arg-reuse.sft.cpp @@ -0,0 +1,31 @@ +//type:fp +//options_all:--c++17 + +// A class template specialization whose nontype template argument refers to +// a dependent static data member (P below) was first created by a member +// declaration and later reused for the same specialization in a constructor +// parameter type. The first argument expression lacked the information +// needed to redo semantic analysis, so building the implicit deduction guide +// for class template argument deduction caused an internal error ("missing +// default rescan info"). + +template constexpr bool f() { return true; } + +template struct P {}; + +template struct A { + static constexpr bool b = f(); + P m; + A(T, P); +}; + +template struct B { + static constexpr bool b = sizeof(T) == 1; + P m; + B(T, P); +}; + +void g() { + A{char{}, {}}; + B{char{}, {}}; +}