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
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.
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)>{ ^
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
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>();
GCC 10 branch is being closed.
GCC 11 branch is being closed.
GCC 12 branch is being closed.
*** Bug 121707 has been marked as a duplicate of this bug. ***
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>
Fixed on trunk so far
*** Bug 96716 has been marked as a duplicate of this bug. ***
*** Bug 109576 has been marked as a duplicate of this bug. ***
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)
Also fixed for 15.3 so far
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)
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)
Fixed for GCC 13.5 / 14.4 / 15.3 / 16