Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions src/Changes
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
56 changes: 45 additions & 11 deletions src/cp_gen_be.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)) {
Expand Down Expand Up @@ -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:;
Comment thread
wchilders-nvidia marked this conversation as resolved.
#endif /* DEFAULT_RECORD_FORM_OF_NAME_REFERENCE */
} /* gen_type_reference */


Expand Down
1 change: 1 addition & 0 deletions tests/tests/regressions/.gh-191.rto/default.1.1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
cpfe -DTEST_NUMBER=1 --c++17 --gnu_version=150200 Test_name.c
1 change: 1 addition & 0 deletions tests/tests/regressions/.gh-191b.rto/default.1.1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
fe_only -DTEST_NUMBER=1 --c++20 Test_name.c
48 changes: 48 additions & 0 deletions tests/tests/regressions/gh-191.sft.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
//options_all:--c++17 --gnu_version=150200
//type:cp
template <bool, typename, typename> using a = int;
template <bool, typename, typename> using b = int;
template <typename...> using i = unsigned long;
template <typename> constexpr bool d = false;
template <typename e, typename f> constexpr bool j = __is_same(e, f);
template <typename e, e...> struct h {};
template <typename e, e ad> using ae = h<e, __integer_pack(ad)...>;
template <long... af> using ag = h<unsigned long, af...>;
template <long ad> using ah = ae<unsigned long, ad>;
template <int ai, typename... aj> struct k {
using g = __type_pack_element<ai, aj...>;
};
typedef unsigned long al;
template <typename...> class l;
template <typename> long ao;
template <typename... aj> constexpr long ao<l<aj...>> = sizeof...(aj);
template <unsigned long, typename> struct m;
template <unsigned long ai, typename... aj> struct m<ai, l<aj...>> {
using g = typename k<ai, aj...>::g;
};
template <long ai, typename aq> using ar = typename m<ai, aq>::g;
template <typename... aj> l<aj...> as(l<aj...>);
template <long ai, typename aq, typename at = decltype(aq()),
typename e = ar<ai, at>>
using au = a<d<aq>, e, e>;
template <typename e, typename... aj> bool ay = (j<e, aj> && ...);
template <typename av, typename aq, unsigned long... az>
constexpr bool ba(ag<az...>) {
(void)ay<i<av, au<az, aq>>...>;
return false;
}
template <typename...> class l {};
template <typename av, typename... aw> constexpr av bb(av x, aw...) {
using bc = decltype(as(aw()...));
ba<av, bc>(ah<ao<bc>>());
return x;
}
struct {
using bd = l<al, long>;
bd c;
} be;
void e() {
struct n {};
bb(n{}, be.c);
}
using bh = b<j<unsigned long, al>, int, long>;
12 changes: 12 additions & 0 deletions tests/tests/regressions/gh-191b.sft.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
//type:fp
//options:--c++20
template<typename T> class S
{ };

class C {
private:
struct N;
S<N> f();
};

extern template class S<C::N>;
Loading