From: Marek Polacek Date: Fri, 31 Jul 2026 15:57:14 +0000 (-0400) Subject: c++/reflection: ICE with &template [:members_of():] [PR124794] X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;p=thirdparty%2Fgcc.git c++/reflection: ICE with &template [:members_of():] [PR124794] Given struct C { template void f(T); }; we handle "&template [:^^C::f:]" correctly because the spliced expression is BASELINK>, binfo C> which is fine: we have an OVERLOAD around the TEMPLATE_DECL and lookup_member wrapped the whole thing in a BASELINK. But when we're splicing members_of(^^C, ac)[0], we ended up with OVERLOAD> and then go down the wrong path in cp_parser_splice_expression. splice already correctly adds the missing OVERLOAD but it also needs to (maybe) add a BASELINK. This patch also adjusts baselink_for_fns to gain a parameter controlling if we want to ignore currently_open_derived_class. It matters when we're in a member function of a class derived from C and the object argument of the -> is a different derived class, as exercised in splice17.C: pd->[:g1:] (42); in D2::mfn. There, if we didn't ignore currently_open_derived_class, the BASELINK would use D2 as the access_binfo, which is wrong because it has no derivation relationship to the object type (here D1). With this patch access_binfo will be C, which is what members_of gave us. PR c++/124794 gcc/cp/ChangeLog: * cp-tree.h (baselink_for_fns): Adjust declaration. * parser.cc (cp_parser_reflect_expression): Adjust the call to baselink_for_fns. * reflect.cc (splice): Call baselink_for_fns. * semantics.cc (baselink_for_fns): Add a bool parameter. If it's true, ignore currently_open_derived_class. gcc/testsuite/ChangeLog: * g++.dg/reflect/splice17.C: New test. Reviewed-by: Jason Merrill --- diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 87245184461..a8af4d38945 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -8683,7 +8683,7 @@ extern void finish_transaction_stmt (tree, tree, int, tree); extern tree build_transaction_expr (location_t, tree, int, tree); extern bool cxx_omp_create_clause_info (tree, tree, bool, bool, bool, bool); -extern tree baselink_for_fns (tree); +extern tree baselink_for_fns (tree, bool = false); extern void finish_static_assert (tree, tree, location_t, bool, bool, bool = false); extern tree finish_decltype_type (tree, bool, tsubst_flags_t); diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc index 19918cfa9de..67a1696f026 100644 --- a/gcc/cp/parser.cc +++ b/gcc/cp/parser.cc @@ -10233,7 +10233,7 @@ cp_parser_reflect_expression (cp_parser *parser) && !concept_check_p (t)) t = finish_template_variable (t); else if (is_overloaded_fn (t)) - t = baselink_for_fns (t); + t = baselink_for_fns (t, /*ignore_current_class_p=*/true); if (cp_parser_parse_definitely (parser)) return get_reflection (loc, t); } diff --git a/gcc/cp/reflect.cc b/gcc/cp/reflect.cc index 7b4142f2d4f..d1cfeeacfa5 100644 --- a/gcc/cp/reflect.cc +++ b/gcc/cp/reflect.cc @@ -8790,6 +8790,11 @@ splice (tree refl) it comes from e.g. members_of it is not. */ if (DECL_FUNCTION_TEMPLATE_P (refl)) refl = ovl_make (refl, NULL_TREE); + /* Also add a BASELINK so that we handle &[:R:]. Since R was already + resolved (e.g. via members_of), we don't want to consider the enclosing + class for the access path. */ + if (is_overloaded_fn (refl)) + refl = baselink_for_fns (refl, /*ignore_current_class_p=*/true); return refl; } diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc index 2274c6ab9b5..7907668da36 100644 --- a/gcc/cp/semantics.cc +++ b/gcc/cp/semantics.cc @@ -4564,23 +4564,22 @@ finish_base_specifier (tree base, tree access, bool virtual_p, /* If FNS is a member function, a set of member functions, or a template-id referring to one or more member functions, return a BASELINK for FNS, incorporating the current access context. - Otherwise, return FNS unchanged. */ + Otherwise, return FNS unchanged. If IGNORE_CURRENT_CLASS_P is + true, we do not consider the currently open derived class. */ tree -baselink_for_fns (tree fns) +baselink_for_fns (tree fns, bool ignore_current_class_p/*=false*/) { - tree scope; - tree cl; - - if (BASELINK_P (fns) - || error_operand_p (fns)) + if (BASELINK_P (fns) || error_operand_p (fns)) return fns; - scope = ovl_scope (fns); + tree scope = ovl_scope (fns); if (!CLASS_TYPE_P (scope)) return fns; - cl = currently_open_derived_class (scope); + tree cl = (ignore_current_class_p + ? NULL_TREE + : currently_open_derived_class (scope)); if (!cl) cl = scope; tree access_path = TYPE_BINFO (cl); diff --git a/gcc/testsuite/g++.dg/reflect/splice17.C b/gcc/testsuite/g++.dg/reflect/splice17.C new file mode 100644 index 00000000000..634b5853472 --- /dev/null +++ b/gcc/testsuite/g++.dg/reflect/splice17.C @@ -0,0 +1,72 @@ +// PR c++/124794 +// { dg-do compile { target c++26 } } +// { dg-additional-options "-freflection" } + +#include + +struct C { + template void f(T); + void g (int); + + static constexpr int val = 42; +}; + +constexpr auto ac = std::meta::access_context::current(); +constexpr auto f1 = members_of(^^C, ac)[0]; +constexpr auto f2 = ^^C::f; +void (C::*p1)(int) = &template [:f1:]; +void (C::*p2)(int) = &template [:f2:]; + +constexpr auto g1 = members_of(^^C, ac)[1]; +constexpr auto g2 = ^^C::g; +void (C::*p3)(int) = &[:g1:]; +void (C::*p4)(int) = &[:g2:]; + +void +g (C *pc) +{ + auto p = &pc->[: ^^C::val :]; + auto q = &pc->C::val; + + pc->f (42); + pc->template [:f1:](42); + pc->template [:f2:](42); + pc->g (42); + pc->[:g1:] (42); + pc->[:g2:] (42); +} + +struct D1 : C { + void mfn (D1 *pd) + { + auto p = &pd->[: ^^C::val :]; + auto q = &pd->C::val; + + pd->f (42); + pd->template [:f1:](42); + pd->template [:f2:](42); + pd->g (42); + pd->[:g1:] (42); + pd->[:g2:] (42); + } +}; + +struct D2 : C { + void mfn (D1 *pd) + { + auto p = &pd->[: ^^C::val :]; + auto q = &pd->C::val; + + constexpr auto rg = ^^C::g; + pd->[:rg:] (42); + constexpr auto rf = ^^C::f; + pd->template [:rf:] (42); + + pd->f (42); + pd->template [:f1:](42); + pd->template [:f2:](42); + pd->g (42); + pd->[:g1:] (42); + pd->[:g2:] (42); + } +};