Bootstrapped and regtested on x86_64-cp-linux-gnu, does this look OK for trunk and eventual backports?
-- >8 -- Here although the local templated variables x and y have the same reduced constant value, only x's initializer is well-formed as written since A::m has private access. We correctly reject y's initializer (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 (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. --- 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(+) create mode 100644 gcc/testsuite/g++.dg/cpp0x/constexpr-97740a.C create mode 100644 gcc/testsuite/g++.dg/cpp0x/constexpr-97740b.C diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc index d628744dbfd1..31e88887e1aa 100644 --- a/gcc/cp/pt.cc +++ b/gcc/cp/pt.cc @@ -22091,8 +22091,14 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl) } else if (TREE_CODE (member) == FIELD_DECL) { + /* Assume access has already been checked appropriately; we don't + want to recheck it to avoid false negatives when substituting a + constant non-dependent 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..92902ff24a85 --- /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 = 42; +} 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..c51590944d7e --- /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 = 42; +} 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>(); -- 2.51.0.167.g6ad8021821