https://gcc.gnu.org/g:3e2077d8c7a0acba2d54bd0666ae578fe114cd72
commit r16-3543-g3e2077d8c7a0acba2d54bd0666ae578fe114cd72 Author: Patrick Palka <ppa...@redhat.com> Date: Wed Sep 3 10:10:00 2025 -0400 c++: constant non-dep init folding vs FIELD_DECL access [PR97740] Here although the local templated variables x and y have the same reduced constant value, only x's initializer {a.get()} is well-formed as written since A::m has private access. We correctly reject y's initializer {&a.m} (at instantiation time), but we also reject x's initializer because we happen to constant fold it ahead of time, which means at instantiation time it's already represented as a COMPONENT_REF to a FIELD_DECL, and so when substituting this COMPONENT_REF we naively double check that the given FIELD_DECL is accessible, which fails. This patch sidesteps around this particular issue by not checking access when substituting a COMPONENT_REF to a FIELD_DECL. If the target of a COMPONENT_REF is already a FIELD_DECL (i.e. before substitution), then I think we can assume access has been already checked appropriately. PR c++/97740 gcc/cp/ChangeLog: * pt.cc (tsubst_expr) <case COMPONENT_REF>: Don't check access when the given member is already a FIELD_DECL. gcc/testsuite/ChangeLog: * g++.dg/cpp0x/constexpr-97740a.C: New test. * g++.dg/cpp0x/constexpr-97740b.C: New test. Reviewed-by: Jason Merrill <ja...@redhat.com> Diff: --- gcc/cp/pt.cc | 6 ++++++ gcc/testsuite/g++.dg/cpp0x/constexpr-97740a.C | 18 ++++++++++++++++++ gcc/testsuite/g++.dg/cpp0x/constexpr-97740b.C | 20 ++++++++++++++++++++ 3 files changed, 44 insertions(+) diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc index 65de1cff0e64..365a6c55a837 100644 --- a/gcc/cp/pt.cc +++ b/gcc/cp/pt.cc @@ -22092,8 +22092,14 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl) } else if (TREE_CODE (member) == FIELD_DECL) { + /* Assume access of this FIELD_DECL has already been checked; we + don't recheck it to avoid bogus access errors when substituting + a reduced constant initializer (97740). */ + gcc_checking_assert (TREE_CODE (TREE_OPERAND (t, 1)) == FIELD_DECL); + push_deferring_access_checks (dk_deferred); r = finish_non_static_data_member (member, object, NULL_TREE, complain); + pop_deferring_access_checks (); if (REF_PARENTHESIZED_P (t)) r = force_paren_expr (r); RETURN (r); diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-97740a.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-97740a.C new file mode 100644 index 000000000000..7cb65c5eb535 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-97740a.C @@ -0,0 +1,18 @@ +// PR c++/97740 +// { dg-do compile { target c++11 } } + +struct A { + constexpr const int* get() const { return &m; } +private: + int m; +} a; + +struct B { const int* p; }; + +template<class T> +void f() { + constexpr B x = {a.get()}; // { dg-bogus "private" } + constexpr B y = {&a.m}; // { dg-error "private" } +} + +template void f<int>(); diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-97740b.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-97740b.C new file mode 100644 index 000000000000..0ea767d94449 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-97740b.C @@ -0,0 +1,20 @@ +// PR c++/97740 +// { dg-do compile { target c++14 } } + +struct A { + constexpr const int* get() const { return &m; } +private: + int m; +} a; + +struct B { const int* p; }; + +template<A* arg> +void f() { + [] (auto) { + constexpr B x = {arg->get()}; // { dg-bogus "private" } + constexpr B y = {&arg->m}; // { dg-error "private" } + }(0); +} + +template void f<&a>();