Bug 97740 - [13/14 Regression] Weird error message about accessing a private member of my own class inside of std::string_view inside of constexpr
Summary: [13/14 Regression] Weird error message about accessing a private member of my...
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 11.0
: P2 normal
Target Milestone: 13.5
Assignee: Patrick Palka
URL:
Keywords: rejects-valid
: 96716 109576 121707 (view as bug list)
Depends on:
Blocks: constexpr
  Show dependency treegraph
 
Reported: 2020-11-06 09:03 UTC by Markus Böck
Modified: 2026-03-30 15:14 UTC (History)
6 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2020-11-09 00:00:00


Attachments
Reproduction of the bug. Simply compile with -std=c++17 (3.31 KB, text/plain)
2020-11-06 09:03 UTC, Markus Böck
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Markus Böck 2020-11-06 09:03:24 UTC
Created attachment 49513 [details]
Reproduction of the bug. Simply compile with -std=c++17

I want to apologize for the code you are about to see already. I am one of those people who likes to constexpr and templates at times. In one of my recent code that does some constexpr string parsing and validation I encountered a very very weird error message:

/opt/compiler-explorer/gcc-10.2.0/include/c++/10.2.0/string_view: In instantiation of 'cld::detail::CommandLine::parseOptions<cld::detail::CommandLine::Pack<detail::INCLUDESarg0, detail::INCLUDESarg1, detail::INCLUDESarg2>, {detail::INCLUDESbind0, std::in_place_type<std::basic_string_view<char, std::char_traits<char> > >}>::<lambda(auto:7&& ...)> [with auto:7 = {const std::pair<cld::detail::CommandLine::Pointer<(& detail::INCLUDESbind0)>, std::in_place_type_t<std::basic_string_view<char, std::char_traits<char> > > >&}]':
/opt/compiler-explorer/gcc-10.2.0/include/c++/10.2.0/type_traits:2506:26:   required by substitution of 'template<class _Fn, class ... _Args> static std::__result_of_success<decltype (declval<_Fn>()((declval<_Args>)()...)), std::__invoke_other> std::__result_of_other_impl::_S_test(int) [with _Fn = cld::detail::CommandLine::parseOptions<cld::detail::CommandLine::Pack<detail::INCLUDESarg0, detail::INCLUDESarg1, detail::INCLUDESarg2>, {detail::INCLUDESbind0, std::in_place_type<std::basic_string_view<char, std::char_traits<char> > >}>::<lambda(auto:7&& ...)>; _Args = {const std::pair<cld::detail::CommandLine::Pointer<(& detail::INCLUDESbind0)>, std::in_place_type_t<std::basic_string_view<char, std::char_traits<char> > > >&}]'
/opt/compiler-explorer/gcc-10.2.0/include/c++/10.2.0/type_traits:2517:55:   required from 'struct std::__result_of_impl<false, false, cld::detail::CommandLine::parseOptions<cld::detail::CommandLine::Pack<detail::INCLUDESarg0, detail::INCLUDESarg1, detail::INCLUDESarg2>, {detail::INCLUDESbind0, std::in_place_type<std::basic_string_view<char, std::char_traits<char> > >}>::<lambda(auto:7&& ...)>, const std::pair<cld::detail::CommandLine::Pointer<(& detail::INCLUDESbind0)>, std::in_place_type_t<std::basic_string_view<char, std::char_traits<char> > > >&>'
/opt/compiler-explorer/gcc-10.2.0/include/c++/10.2.0/type_traits:138:12:   recursively required by substitution of 'template<class _Result, class _Ret> struct std::__is_invocable_impl<_Result, _Ret, true, std::__void_t<typename _CTp::type> > [with _Result = std::__invoke_result<cld::detail::CommandLine::parseOptions<cld::detail::CommandLine::Pack<detail::INCLUDESarg0, detail::INCLUDESarg1, detail::INCLUDESarg2>, {detail::INCLUDESbind0, std::in_place_type<std::basic_string_view<char, std::char_traits<char> > >}>::<lambda(auto:7&& ...)>, const std::pair<cld::detail::CommandLine::Pointer<(& detail::INCLUDESbind0)>, std::in_place_type_t<std::basic_string_view<char, std::char_traits<char> > > >&>; _Ret = void]'
/opt/compiler-explorer/gcc-10.2.0/include/c++/10.2.0/type_traits:138:12:   required from 'struct std::__and_<std::__is_invocable_impl<std::__invoke_result<cld::detail::CommandLine::parseOptions<cld::detail::CommandLine::Pack<detail::INCLUDESarg0, detail::INCLUDESarg1, detail::INCLUDESarg2>, {detail::INCLUDESbind0, std::in_place_type<std::basic_string_view<char, std::char_traits<char> > >}>::<lambda(auto:7&& ...)>, const std::pair<cld::detail::CommandLine::Pointer<(& detail::INCLUDESbind0)>, std::in_place_type_t<std::basic_string_view<char, std::char_traits<char> > > >&>, void, true, void>, std::__call_is_nothrow<std::__invoke_result<cld::detail::CommandLine::parseOptions<cld::detail::CommandLine::Pack<detail::INCLUDESarg0, detail::INCLUDESarg1, detail::INCLUDESarg2>, {detail::INCLUDESbind0, std::in_place_type<std::basic_string_view<char, std::char_traits<char> > >}>::<lambda(auto:7&& ...)>, const std::pair<cld::detail::CommandLine::Pointer<(& detail::INCLUDESbind0)>, std::in_place_type_t<std::basic_string_view<char, std::char_traits<char> > > >&>, cld::detail::CommandLine::parseOptions<cld::detail::CommandLine::Pack<detail::INCLUDESarg0, detail::INCLUDESarg1, detail::INCLUDESarg2>, {detail::INCLUDESbind0, std::in_place_type<std::basic_string_view<char, std::char_traits<char> > >}>::<lambda(auto:7&& ...)>, const std::pair<cld::detail::CommandLine::Pointer<(& detail::INCLUDESbind0)>, std::in_place_type_t<std::basic_string_view<char, std::char_traits<char> > > >&> >'
/opt/compiler-explorer/gcc-10.2.0/include/c++/10.2.0/type_traits:2979:12:   required from 'struct std::is_nothrow_invocable<cld::detail::CommandLine::parseOptions<cld::detail::CommandLine::Pack<detail::INCLUDESarg0, detail::INCLUDESarg1, detail::INCLUDESarg2>, {detail::INCLUDESbind0, std::in_place_type<std::basic_string_view<char, std::char_traits<char> > >}>::<lambda(auto:7&& ...)>, const std::pair<cld::detail::CommandLine::Pointer<(& detail::INCLUDESbind0)>, std::in_place_type_t<std::basic_string_view<char, std::char_traits<char> > > >&>'
/opt/compiler-explorer/gcc-10.2.0/include/c++/10.2.0/tuple:1715:37:   required from 'constexpr const bool std::__unpack_std_tuple<template<class _Fn, class ... _ArgTypes> struct std::is_nothrow_invocable, cld::detail::CommandLine::parseOptions<cld::detail::CommandLine::Pack<detail::INCLUDESarg0, detail::INCLUDESarg1, detail::INCLUDESarg2>, {detail::INCLUDESbind0, std::in_place_type<std::basic_string_view<char, std::char_traits<char> > >}>::<lambda(auto:7&& ...)>, const std::tuple<std::pair<cld::detail::CommandLine::Pointer<(& detail::INCLUDESbind0)>, std::in_place_type_t<std::basic_string_view<char, std::char_traits<char> > > > >&>'
/opt/compiler-explorer/gcc-10.2.0/include/c++/10.2.0/tuple:1730:14:   required from 'constexpr decltype(auto) std::apply(_Fn&&, _Tuple&&) [with _Fn = cld::detail::CommandLine::parseOptions<cld::detail::CommandLine::Pack<detail::INCLUDESarg0, detail::INCLUDESarg1, detail::INCLUDESarg2>, {detail::INCLUDESbind0, std::in_place_type<std::basic_string_view<char, std::char_traits<char> > >}>::<lambda(auto:7&& ...)>; _Tuple = const std::tuple<std::pair<cld::detail::CommandLine::Pointer<(& detail::INCLUDESbind0)>, std::in_place_type_t<std::basic_string_view<char, std::char_traits<char> > > > >&]'
<source>:456:36:   required from 'constexpr auto cld::detail::CommandLine::parseOptions(std::string_view, cld::CLIMultiArg) [with T = cld::detail::CommandLine::Pack<detail::INCLUDESarg0, detail::INCLUDESarg1, detail::INCLUDESarg2>; auto& ...args = {detail::INCLUDESbind0, std::in_place_type<std::basic_string_view<char, std::char_traits<char> > >}; std::string_view = std::basic_string_view<char>]'
<source>:502:62:   required from here
/opt/compiler-explorer/gcc-10.2.0/include/c++/10.2.0/string_view:287:34: error: 'char cld::Constexpr::basic_fixed_string<char, 7>::m_data [7]' is private within this context
  287 |  return basic_string_view{_M_str + __pos, __rlen};
      |                           ~~~~~~~^~~~~~~
<source>:18:5: note: declared private here
   18 |   T m_data[N] = {};
      | 

I tried to create a minimum reproducible from scratch but failed to so. Instead I now used my code and tried to minimize that as much as possible which sadly still amounts to 500 lines of code but does reproduce the issue. I have attached the file.

To my knowledge the code is correct and clang and MSVC compile it without issues. Changing m_data in basic_fixed_string from private to public makes the build succeed. Every version of GCC 8 to trunk fail with the same error. GCC 7 instead has an internal compiler error.

Alternatively see this compiler explorer link: https://godbolt.org/z/snnfo7
Comment 1 Jonathan Wakely 2020-11-09 15:32:34 UTC
N.B. the "diagnostic" keyword is for poor quality diagnostics, not ones that shouldn't happen at all. Code that shouldn't give an error at all should use "rejects-valid" instead.
Comment 2 Drea Pinski 2021-08-28 22:16:01 UTC
The trunk produces:
Output of x86-64 gcc (trunk) (Compiler #2)
<source>: In instantiation of 'cld::detail::CommandLine::Pack<detail::INCLUDESarg0, detail::INCLUDESarg1, detail::INCLUDESarg2>::parseOptionsImpl()::<lambda(auto:6&& ...)> [with auto:6 = {const cld::detail::CommandLine::Arg&, const cld::detail::CommandLine::Arg&, const cld::detail::CommandLine::Arg&}]':
/opt/compiler-explorer/gcc-trunk-20210828/include/c++/12.0.0/type_traits:2555:26:   required by substitution of 'template<class _Fn, class ... _Args> static std::__result_of_success<decltype (declval<_Fn>()((declval<_Args>)()...)), std::__invoke_other> std::__result_of_other_impl::_S_test(int) [with _Fn = cld::detail::CommandLine::Pack<detail::INCLUDESarg0, detail::INCLUDESarg1, detail::INCLUDESarg2>::parseOptionsImpl()::<lambda(auto:6&& ...)>; _Args = {const cld::detail::CommandLine::Arg&, const cld::detail::CommandLine::Arg&, const cld::detail::CommandLine::Arg&}]'
/opt/compiler-explorer/gcc-trunk-20210828/include/c++/12.0.0/type_traits:2566:55:   required from 'struct std::__result_of_impl<false, false, cld::detail::CommandLine::Pack<detail::INCLUDESarg0, detail::INCLUDESarg1, detail::INCLUDESarg2>::parseOptionsImpl()::<lambda(auto:6&& ...)>, const cld::detail::CommandLine::Arg&, const cld::detail::CommandLine::Arg&, const cld::detail::CommandLine::Arg&>'
/opt/compiler-explorer/gcc-trunk-20210828/include/c++/12.0.0/type_traits:151:12:   recursively required by substitution of 'template<class _Result, class _Ret> struct std::__is_invocable_impl<_Result, _Ret, true, std::__void_t<typename _CTp::type> > [with _Result = std::__invoke_result<cld::detail::CommandLine::Pack<detail::INCLUDESarg0, detail::INCLUDESarg1, detail::INCLUDESarg2>::parseOptionsImpl()::<lambda(auto:6&& ...)>, const cld::detail::CommandLine::Arg&, const cld::detail::CommandLine::Arg&, const cld::detail::CommandLine::Arg&>; _Ret = void]'
/opt/compiler-explorer/gcc-trunk-20210828/include/c++/12.0.0/type_traits:151:12:   required from 'struct std::__and_<std::__is_invocable_impl<std::__invoke_result<cld::detail::CommandLine::Pack<detail::INCLUDESarg0, detail::INCLUDESarg1, detail::INCLUDESarg2>::parseOptionsImpl()::<lambda(auto:6&& ...)>, const cld::detail::CommandLine::Arg&, const cld::detail::CommandLine::Arg&, const cld::detail::CommandLine::Arg&>, void, true, void>, std::__call_is_nothrow<std::__invoke_result<cld::detail::CommandLine::Pack<detail::INCLUDESarg0, detail::INCLUDESarg1, detail::INCLUDESarg2>::parseOptionsImpl()::<lambda(auto:6&& ...)>, const cld::detail::CommandLine::Arg&, const cld::detail::CommandLine::Arg&, const cld::detail::CommandLine::Arg&>, cld::detail::CommandLine::Pack<detail::INCLUDESarg0, detail::INCLUDESarg1, detail::INCLUDESarg2>::parseOptionsImpl()::<lambda(auto:6&& ...)>, const cld::detail::CommandLine::Arg&, const cld::detail::CommandLine::Arg&, const cld::detail::CommandLine::Arg&> >'
/opt/compiler-explorer/gcc-trunk-20210828/include/c++/12.0.0/type_traits:3054:12:   required from 'struct std::is_nothrow_invocable<cld::detail::CommandLine::Pack<detail::INCLUDESarg0, detail::INCLUDESarg1, detail::INCLUDESarg2>::parseOptionsImpl()::<lambda(auto:6&& ...)>, const cld::detail::CommandLine::Arg&, const cld::detail::CommandLine::Arg&, const cld::detail::CommandLine::Arg&>'
/opt/compiler-explorer/gcc-trunk-20210828/include/c++/12.0.0/tuple:1872:37:   required from 'constexpr const bool std::__unpack_std_tuple<template<class _Fn, class ... _ArgTypes> struct std::is_nothrow_invocable, cld::detail::CommandLine::Pack<detail::INCLUDESarg0, detail::INCLUDESarg1, detail::INCLUDESarg2>::parseOptionsImpl()::<lambda(auto:6&& ...)>, const std::tuple<cld::detail::CommandLine::Arg, cld::detail::CommandLine::Arg, cld::detail::CommandLine::Arg>&>'
/opt/compiler-explorer/gcc-trunk-20210828/include/c++/12.0.0/tuple:1887:14:   required from 'constexpr decltype(auto) std::apply(_Fn&&, _Tuple&&) [with _Fn = cld::detail::CommandLine::Pack<detail::INCLUDESarg0, detail::INCLUDESarg1, detail::INCLUDESarg2>::parseOptionsImpl()::<lambda(auto:6&& ...)>; _Tuple = const std::tuple<cld::detail::CommandLine::Arg, cld::detail::CommandLine::Arg, cld::detail::CommandLine::Arg>&]'
<source>:408:49:   required from 'static constexpr auto cld::detail::CommandLine::Pack<args>::parseOptionsImpl() [with const auto& ...args = {detail::INCLUDESarg0, detail::INCLUDESarg1, detail::INCLUDESarg2}]'
<source>:444:49:   required from 'constexpr const auto cld::detail::CommandLine::parsedTuple<cld::detail::CommandLine::Pack<detail::INCLUDESarg0, detail::INCLUDESarg1, detail::INCLUDESarg2> >'
<source>:458:48:   required from 'constexpr auto cld::detail::CommandLine::parseOptions(std::string_view, cld::CLIMultiArg) [with T = cld::detail::CommandLine::Pack<detail::INCLUDESarg0, detail::INCLUDESarg1, detail::INCLUDESarg2>; auto& ...args = {detail::INCLUDESbind0, std::in_place_type<std::basic_string_view<char, std::char_traits<char> > >}; std::string_view = std::basic_string_view<char>]'
<source>:502:5:   required from here
<source>:411:31: error: too many initializers for 'std::array<std::basic_string_view<char>, 3>'
  411 |                 input.value...};
      |                               ^


clang accepts the code using GCC's libstdc++ while rejects it using LLVM's libc++:
<source>:410:20: error: implicit instantiation of undefined template 'std::array<std::string_view, 3>'
            return std::array<std::string_view, sizeof...(input)>{
                   ^
Comment 3 Patrick Palka 2023-03-24 16:08:25 UTC
Here's a reduced C++11 testcase that became rejects-valid after r10-6416-g8fda2c274ac66d:

struct A {
  constexpr const int* get() const { return &m; }
private:
  int m = 42;
} a;

struct B { const int* p; };

template<class>
void f() {
  constexpr B b = {a.get()};
}

template void f<int>();

<stdin>: In function ‘void f()’:
<stdin>:11:25: error: ‘int A::m’ is private within this context
<stdin>:4:7: note: declared private here
<stdin>: In instantiation of ‘void f() [with <template-parameter-1-1> = int]’:
<stdin>:14:22:   required from here
<stdin>:11:25: error: ‘int A::m’ is private within this context
<stdin>:4:7: note: declared private here
Comment 4 Patrick Palka 2023-03-24 16:10:27 UTC
A C++14 version that's more similar to the original testcase using a generic lambda and dependent initializer:

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 b = {arg->get()};
  }(0);
}

template void f<&a>();
Comment 5 Richard Biener 2023-07-07 10:38:22 UTC
GCC 10 branch is being closed.
Comment 6 Richard Biener 2024-07-19 13:09:15 UTC
GCC 11 branch is being closed.
Comment 7 Richard Biener 2025-07-11 09:03:40 UTC
GCC 12 branch is being closed.
Comment 8 Patrick Palka 2025-09-02 20:59:12 UTC
*** Bug 121707 has been marked as a duplicate of this bug. ***
Comment 9 GCC Commits 2025-09-03 14:10:39 UTC
The master branch has been updated by Patrick Palka <ppalka@gcc.gnu.org>:

https://gcc.gnu.org/g:3e2077d8c7a0acba2d54bd0666ae578fe114cd72

commit r16-3543-g3e2077d8c7a0acba2d54bd0666ae578fe114cd72
Author: Patrick Palka <ppalka@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 <jason@redhat.com>
Comment 10 Patrick Palka 2025-09-03 14:11:24 UTC
Fixed on trunk so far
Comment 11 Patrick Palka 2025-09-03 14:12:45 UTC
*** Bug 96716 has been marked as a duplicate of this bug. ***
Comment 12 Patrick Palka 2025-09-03 14:13:48 UTC
*** Bug 109576 has been marked as a duplicate of this bug. ***
Comment 13 GCC Commits 2025-09-11 20:33:36 UTC
The releases/gcc-15 branch has been updated by Patrick Palka <ppalka@gcc.gnu.org>:

https://gcc.gnu.org/g:1669dbb9363dadf7a52d9f1090f13fa33c680c6a

commit r15-10317-g1669dbb9363dadf7a52d9f1090f13fa33c680c6a
Author: Patrick Palka <ppalka@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 <jason@redhat.com>
    (cherry picked from commit 3e2077d8c7a0acba2d54bd0666ae578fe114cd72)
Comment 14 Patrick Palka 2025-09-11 20:34:05 UTC
Also fixed for 15.3 so far
Comment 15 GCC Commits 2026-03-30 14:25:06 UTC
The releases/gcc-14 branch has been updated by Patrick Palka <ppalka@gcc.gnu.org>:

https://gcc.gnu.org/g:d84dd6434adf8808901a74ded3db64590ae650bc

commit r14-12488-gd84dd6434adf8808901a74ded3db64590ae650bc
Author: Patrick Palka <ppalka@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 <jason@redhat.com>
    (cherry picked from commit 3e2077d8c7a0acba2d54bd0666ae578fe114cd72)
Comment 16 GCC Commits 2026-03-30 15:13:51 UTC
The releases/gcc-13 branch has been updated by Patrick Palka <ppalka@gcc.gnu.org>:

https://gcc.gnu.org/g:1b640aabd28e7dcf30423d54072d48f60a1e6378

commit r13-10235-g1b640aabd28e7dcf30423d54072d48f60a1e6378
Author: Patrick Palka <ppalka@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 <jason@redhat.com>
    (cherry picked from commit 3e2077d8c7a0acba2d54bd0666ae578fe114cd72)
Comment 17 Patrick Palka 2026-03-30 15:14:18 UTC
Fixed for GCC 13.5 / 14.4 / 15.3 / 16