From e7e486436a316d11b30f3a84d17db49814cf8e94 Mon Sep 17 00:00:00 2001 From: Wyatt Childers Date: Thu, 1 Oct 2026 16:08:32 -0400 Subject: [PATCH] Fix C++-generating back end issue with inaccessible or out-of-scope template arguments [GH #191] --- src/Changes | 10 ++++ src/cp_gen_be.c | 56 +++++++++++++++---- .../regressions/.gh-191.rto/default.1.1.txt | 1 + .../regressions/.gh-191b.rto/default.1.1.txt | 1 + tests/tests/regressions/gh-191.sft.cpp | 48 ++++++++++++++++ tests/tests/regressions/gh-191b.sft.cpp | 12 ++++ 6 files changed, 117 insertions(+), 11 deletions(-) create mode 100644 tests/tests/regressions/.gh-191.rto/default.1.1.txt create mode 100644 tests/tests/regressions/.gh-191b.rto/default.1.1.txt create mode 100644 tests/tests/regressions/gh-191.sft.cpp create mode 100644 tests/tests/regressions/gh-191b.sft.cpp diff --git a/src/Changes b/src/Changes index 79bcd59a6c..1d62033f92 100644 --- a/src/Changes +++ b/src/Changes @@ -8,6 +8,16 @@ behavior standard-mandated. It is now the default behavior, except in Microsoft mode with microsoft_version < 1951. +Version 7.1 + +10/1/26 [GH #191] +C++-generating back end: inaccessible or out-of-scope template arguments + +In certain complex cases, the C++-generating back end could put out a +template argument that uses an inaccessible or out-of-scope name, making +the generated code uncompilable. This is now fixed. + + Version 7.0, September 28, 2026 9/15/26 [EDGcpfe/28958] diff --git a/src/cp_gen_be.c b/src/cp_gen_be.c index 80536553a5..10de37b872 100644 --- a/src/cp_gen_be.c +++ b/src/cp_gen_be.c @@ -9911,6 +9911,29 @@ elaborated-type-specifier, even if it would be required in some contexts. specified in the reference; a null template argument list implies an empty list, "<>", not that it was omitted. */ arg_list = trp->variant.typeref.extra_info->template_arg_list; + if (arg_list != NULL) { + a_template_arg_ptr tap; + a_boolean for_all_scopes = TRUE; + begin_template_arg_list_traversal_simple(arg_list, &tap); + while (tap != NULL) { + if (!template_arg_is_accessible( + tap, /*ignore_context=*/FALSE, + /*check_related_types=*/FALSE, + &for_all_scopes, /*check_visibility=*/TRUE)) { + /* This template argument uses a name that is inaccessible or + out of scope, so the trk_template_arg_list cannot be used. + Recursively put out the underlying type (which might + include a trk_name_qualifier that should be preserved, so + we use the direct underlying type and not + skip_lexical_typerefs). */ + gen_type_reference(trp->variant.typeref.type, + suppress_typename_kwd, is_declaration, + suppress_elab_type_spec); + goto done; + } /* if */ + advance_to_next_template_arg_simple(&tap); + } /* while */ + } /* if */ if (type_is(refp, tk_typeref) && is_typeref_kind(refp, trk_name_qualifier)) { /* The target of the typeref specifies the qualifiers used in @@ -10179,17 +10202,25 @@ elaborated-type-specifier, even if it would be required in some contexts. use_elab_type_spec = TRUE; } /* if */ } else if (!suppress_elab_type_spec) { - /* See if the type is a member of a class or namespace. If so, we - need to push the hidden name information for that scope to see if - the elaborated type specifier is needed. */ - a_scope_ptr scope = parent_scope_of(type); - if (scope != NULL) { - /* This type is a member of a class or namespace, so push the - hidden name information for that name context. */ - push_name_context(scope); - } /* if */ - use_elab_type_spec = type->elaborated_type_specifier_needed; - if (scope != NULL) pop_name_context(); + if (is_tag_type(type) && is_declaration) { + /* The tag keyword is required in a declaration. Normally the recorded + form of the name reference puts out the tag keyword, but that path + may be bypassed when the recorded template argument list cannot be + used. */ + use_elab_type_spec = TRUE; + } else { + /* See if the type is a member of a class or namespace. If so, we + need to push the hidden name information for that scope to see if + the elaborated type specifier is needed. */ + a_scope_ptr scope = parent_scope_of(type); + if (scope != NULL) { + /* This type is a member of a class or namespace, so push the + hidden name information for that name context. */ + push_name_context(scope); + } /* if */ + use_elab_type_spec = type->elaborated_type_specifier_needed; + if (scope != NULL) pop_name_context(); + } /* if */ } /* if */ if (!has_name_before_mangling(orig_type) && synthesize_decltype_specifier(orig_type)) { @@ -10218,6 +10249,9 @@ elaborated-type-specifier, even if it would be required in some contexts. /* A fundamental type. */ form_type(type, &octl); } /* if */ +#if DEFAULT_RECORD_FORM_OF_NAME_REFERENCE +done:; +#endif /* DEFAULT_RECORD_FORM_OF_NAME_REFERENCE */ } /* gen_type_reference */ diff --git a/tests/tests/regressions/.gh-191.rto/default.1.1.txt b/tests/tests/regressions/.gh-191.rto/default.1.1.txt new file mode 100644 index 0000000000..bb74fe0441 --- /dev/null +++ b/tests/tests/regressions/.gh-191.rto/default.1.1.txt @@ -0,0 +1 @@ +cpfe -DTEST_NUMBER=1 --c++17 --gnu_version=150200 Test_name.c diff --git a/tests/tests/regressions/.gh-191b.rto/default.1.1.txt b/tests/tests/regressions/.gh-191b.rto/default.1.1.txt new file mode 100644 index 0000000000..32fc82e916 --- /dev/null +++ b/tests/tests/regressions/.gh-191b.rto/default.1.1.txt @@ -0,0 +1 @@ +fe_only -DTEST_NUMBER=1 --c++20 Test_name.c diff --git a/tests/tests/regressions/gh-191.sft.cpp b/tests/tests/regressions/gh-191.sft.cpp new file mode 100644 index 0000000000..6413d0fa7b --- /dev/null +++ b/tests/tests/regressions/gh-191.sft.cpp @@ -0,0 +1,48 @@ +//options_all:--c++17 --gnu_version=150200 +//type:cp +template using a = int; +template using b = int; +template using i = unsigned long; +template constexpr bool d = false; +template constexpr bool j = __is_same(e, f); +template struct h {}; +template using ae = h; +template using ag = h; +template using ah = ae; +template struct k { + using g = __type_pack_element; +}; +typedef unsigned long al; +template class l; +template long ao; +template constexpr long ao> = sizeof...(aj); +template struct m; +template struct m> { + using g = typename k::g; +}; +template using ar = typename m::g; +template l as(l); +template > +using au = a, e, e>; +template bool ay = (j && ...); +template +constexpr bool ba(ag) { + (void)ay>...>; + return false; +} +template class l {}; +template constexpr av bb(av x, aw...) { + using bc = decltype(as(aw()...)); + ba(ah>()); + return x; +} +struct { + using bd = l; + bd c; +} be; +void e() { + struct n {}; + bb(n{}, be.c); +} +using bh = b, int, long>; diff --git a/tests/tests/regressions/gh-191b.sft.cpp b/tests/tests/regressions/gh-191b.sft.cpp new file mode 100644 index 0000000000..e4b3ffa58b --- /dev/null +++ b/tests/tests/regressions/gh-191b.sft.cpp @@ -0,0 +1,12 @@ +//type:fp +//options:--c++20 +template class S +{ }; + +class C { +private: + struct N; + S f(); +}; + +extern template class S;