Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GCC 10: Compilation error when including any before including json header in C++17 mode #2129

Closed
2 of 5 tasks
gracicot opened this issue May 20, 2020 · 10 comments
Closed
2 of 5 tasks

Comments

@gracicot
Copy link
Contributor

gracicot commented May 20, 2020

What is the issue you have?

A compilation error just including parts of the standard library and this library in a specific order when using C++17 and GCC 10.

Please describe the steps to reproduce the issue.

Consider the following file:

#include <any>
#include <nlohmann/json>

When compiling it in C++17 mode, it won't compile with GCC 10

Here's a compile explorer link that shows the issue.

When changing <any> to <variant>, there is no issue. I don't know if any other standard header causes this bug.

When putting the #include <any> after #include <nlohmann/json>, it compiles. DEMO.

What is the expected behavior?

It should compile.

And what is the actual behavior instead?

It shows very strange errors and fail to compile.

Which compiler and operating system are you using?

  • Compiler: GCC 10
  • Operating system: Arch Linux

Which version of the library did you use?

  • latest release version 3.7.3
  • other release - please state the version: ___
  • the develop branch

If you experience a compilation error: can you compile and run the unit tests?

  • yes
  • no - please copy/paste the error message below

I had issues with some CMake related tests but it seem unrelated to this bug.

@nlohmann
Copy link
Owner

For reference, here is the error message from godbolt:

In file included from /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/bits/move.h:57,
                 from /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/bits/nested_exception.h:40,
                 from /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/exception:148,
                 from /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/new:41,
                 from /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/any:37,
                 from <source>:1:
/opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/type_traits: In instantiation of 'struct std::is_constructible<nlohmann::basic_json<>, const nlohmann::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool, long int, long unsigned int, double, std::allocator, nlohmann::adl_serializer>&>':
#### json.nlohmann/json.hpp:11120:79:   required by substitution of 'template<class ... Args, typename std::enable_if<std::is_constructible<nlohmann::basic_json<>, Args ...>::value, int>::type <anonymous> > nlohmann::detail::json_ref<nlohmann::basic_json<> >::json_ref(Args&& ...) [with Args = {const nlohmann::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool, long int, long unsigned int, double, std::allocator, nlohmann::adl_serializer>&}; typename std::enable_if<std::is_constructible<nlohmann::basic_json<>, Args ...>::value, int>::type <anonymous> = <missing>]'
/opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/type_traits:901:30:   required from 'struct std::__is_constructible_impl<nlohmann::basic_json<>, const nlohmann::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool, long int, long unsigned int, double, std::allocator, nlohmann::adl_serializer>&>'
/opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/type_traits:930:12:   required from 'struct std::__is_copy_constructible_impl<nlohmann::basic_json<>, true>'
/opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/type_traits:936:12:   required from 'struct std::is_copy_constructible<nlohmann::basic_json<> >'
/opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/any:185:49:   required by substitution of 'template<class _Tp, class _VTp, class _Mgr, typename std::enable_if<(std::is_copy_constructible<_Tp>::value && (! std::__is_in_place_type<_VTp>::value)), bool>::type <anonymous> > std::any::any(_Tp&&) [with _Tp = nlohmann::basic_json<>&; _VTp = nlohmann::basic_json<>; _Mgr = std::any::_Manager_external<nlohmann::basic_json<> >; typename std::enable_if<(std::is_copy_constructible<_Tp>::value && (! std::__is_in_place_type<_VTp>::value)), bool>::type <anonymous> = <missing>]'
/opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/type_traits:2670:31:   required by substitution of 'template<class _Tp, class> static std::true_type std::__swappable_details::__do_is_swappable_impl::__test(int) [with _Tp = nlohmann::basic_json<>; <template-parameter-1-2> = <missing>]'
/opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/type_traits:2694:35:   required from 'struct std::__is_swappable_impl<nlohmann::basic_json<> >'
/opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/type_traits:2720:12:   required from 'struct std::is_swappable<nlohmann::basic_json<> >'
/opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/type_traits:2740:26:   required from 'constexpr const bool std::is_swappable_v<nlohmann::basic_json<> >'
/opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/optional:1201:51:   required by substitution of 'template<class _Tp> std::enable_if_t<(!(is_move_constructible_v<_Tp> && is_swappable_v<_Tp>))> std::swap(std::optional<_Tp>&, std::optional<_Tp>&) [with _Tp = nlohmann::basic_json<>]'
#### json.nlohmann/json.hpp:22677:1:   required from here
/opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/type_traits:906:12: error: invalid use of incomplete type 'struct std::__is_constructible_impl<nlohmann::basic_json<>, const nlohmann::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool, long int, long unsigned int, double, std::allocator, nlohmann::adl_serializer>&>'
  906 |     struct is_constructible
      |            ^~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/type_traits:900:12: note: declaration of 'struct std::__is_constructible_impl<nlohmann::basic_json<>, const nlohmann::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool, long int, long unsigned int, double, std::allocator, nlohmann::adl_serializer>&>'
  900 |     struct __is_constructible_impl
      |            ^~~~~~~~~~~~~~~~~~~~~~~
In file included from /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/vector:66,
                 from /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/functional:62,
                 from /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/pstl/glue_algorithm_defs.h:13,
                 from /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/algorithm:74,
                 from #### json.nlohmann/json.hpp:37:
/opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/bits/stl_uninitialized.h: In instantiation of '_ForwardIterator std::uninitialized_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = __gnu_cxx::__normal_iterator<const nlohmann::basic_json<>*, std::vector<nlohmann::basic_json<>, std::allocator<nlohmann::basic_json<> > > >; _ForwardIterator = nlohmann::basic_json<>*]':
/opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/bits/stl_uninitialized.h:325:37:   required from '_ForwardIterator std::__uninitialized_copy_a(_InputIterator, _InputIterator, _ForwardIterator, std::allocator<_Tp>&) [with _InputIterator = __gnu_cxx::__normal_iterator<const nlohmann::basic_json<>*, std::vector<nlohmann::basic_json<>, std::allocator<nlohmann::basic_json<> > > >; _ForwardIterator = nlohmann::basic_json<>*; _Tp = nlohmann::basic_json<>]'
/opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/bits/stl_vector.h:558:31:   required from 'std::vector<_Tp, _Alloc>::vector(const std::vector<_Tp, _Alloc>&) [with _Tp = nlohmann::basic_json<>; _Alloc = std::allocator<nlohmann::basic_json<> >]'
/opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/ext/new_allocator.h:150:4:   required from 'void __gnu_cxx::new_allocator<_Tp>::construct(_Up*, _Args&& ...) [with _Up = std::vector<nlohmann::basic_json<>, std::allocator<nlohmann::basic_json<> > >; _Args = {const std::vector<nlohmann::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool, long int, long unsigned int, double, std::allocator, nlohmann::adl_serializer>, std::allocator<nlohmann::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool, long int, long unsigned int, double, std::allocator, nlohmann::adl_serializer> > >&}; _Tp = std::vector<nlohmann::basic_json<>, std::allocator<nlohmann::basic_json<> > >]'
/opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/bits/alloc_traits.h:512:17:   required from 'static void std::allocator_traits<std::allocator<_Tp1> >::construct(std::allocator_traits<std::allocator<_Tp1> >::allocator_type&, _Up*, _Args&& ...) [with _Up = std::vector<nlohmann::basic_json<>, std::allocator<nlohmann::basic_json<> > >; _Args = {const std::vector<nlohmann::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool, long int, long unsigned int, double, std::allocator, nlohmann::adl_serializer>, std::allocator<nlohmann::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool, long int, long unsigned int, double, std::allocator, nlohmann::adl_serializer> > >&}; _Tp = std::vector<nlohmann::basic_json<>, std::allocator<nlohmann::basic_json<> > >; std::allocator_traits<std::allocator<_Tp1> >::allocator_type = std::allocator<std::vector<nlohmann::basic_json<>, std::allocator<nlohmann::basic_json<> > > >]'
#### json.nlohmann/json.hpp:15381:35:   [ skipping 2 instantiation contexts, use -ftemplate-backtrace-limit=0 to disable ]
#### json.nlohmann/json.hpp:16365:25:   required from 'nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer>::basic_json(const nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer>&) [with ObjectType = std::map; ArrayType = std::vector; StringType = std::__cxx11::basic_string<char>; BooleanType = bool; NumberIntegerType = long int; NumberUnsignedType = long unsigned int; NumberFloatType = double; AllocatorType = std::allocator; JSONSerializer = nlohmann::adl_serializer]'
#### json.nlohmann/json.hpp:4737:31:   required from 'bool nlohmann::detail::json_sax_dom_callback_parser<BasicJsonType>::end_object() [with BasicJsonType = nlohmann::basic_json<>]'
#### json.nlohmann/json.hpp:8847:33:   required from 'bool nlohmann::detail::parser<BasicJsonType>::sax_parse_internal(SAX*) [with SAX = nlohmann::detail::json_sax_dom_callback_parser<nlohmann::basic_json<> >; BasicJsonType = nlohmann::basic_json<>]'
#### json.nlohmann/json.hpp:8738:31:   required from 'void nlohmann::detail::parser<BasicJsonType>::parse(bool, BasicJsonType&) [with BasicJsonType = nlohmann::basic_json<>]'
#### json.nlohmann/json.hpp:20882:79:   required from 'static nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer> nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer>::parse(IteratorType, IteratorType, nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer>::parser_callback_t, bool) [with IteratorType = const char*; typename std::enable_if<std::is_base_of<std::random_access_iterator_tag, typename std::iterator_traits<_InputIterator>::iterator_category>::value, int>::type <anonymous> = 0; ObjectType = std::map; ArrayType = std::vector; StringType = std::__cxx11::basic_string<char>; BooleanType = bool; NumberIntegerType = long int; NumberUnsignedType = long unsigned int; NumberFloatType = double; AllocatorType = std::allocator; JSONSerializer = nlohmann::adl_serializer; nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer>::parser_callback_t = std::function<bool(int, nlohmann::detail::parser<nlohmann::basic_json<> >::parse_event_t, nlohmann::basic_json<>&)>]'
#### json.nlohmann/json.hpp:22700:42:   required from here
/opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/bits/stl_uninitialized.h:137:72: error: 'value' is not a member of 'std::is_constructible<nlohmann::basic_json<>, const nlohmann::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool, long int, long unsigned int, double, std::allocator, nlohmann::adl_serializer>&>'
  137 |       static_assert(is_constructible<_ValueType2, decltype(*__first)>::value,
      |                                                                        ^~~~~
ASM generation compiler returned: 1
In file included from /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/bits/move.h:57,
                 from /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/bits/nested_exception.h:40,
                 from /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/exception:148,
                 from /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/new:41,
                 from /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/any:37,
                 from <source>:1:
/opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/type_traits: In instantiation of 'struct std::is_constructible<nlohmann::basic_json<>, const nlohmann::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool, long int, long unsigned int, double, std::allocator, nlohmann::adl_serializer>&>':
#### json.nlohmann/json.hpp:11120:79:   required by substitution of 'template<class ... Args, typename std::enable_if<std::is_constructible<nlohmann::basic_json<>, Args ...>::value, int>::type <anonymous> > nlohmann::detail::json_ref<nlohmann::basic_json<> >::json_ref(Args&& ...) [with Args = {const nlohmann::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool, long int, long unsigned int, double, std::allocator, nlohmann::adl_serializer>&}; typename std::enable_if<std::is_constructible<nlohmann::basic_json<>, Args ...>::value, int>::type <anonymous> = <missing>]'
/opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/type_traits:901:30:   required from 'struct std::__is_constructible_impl<nlohmann::basic_json<>, const nlohmann::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool, long int, long unsigned int, double, std::allocator, nlohmann::adl_serializer>&>'
/opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/type_traits:930:12:   required from 'struct std::__is_copy_constructible_impl<nlohmann::basic_json<>, true>'
/opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/type_traits:936:12:   required from 'struct std::is_copy_constructible<nlohmann::basic_json<> >'
/opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/any:185:49:   required by substitution of 'template<class _Tp, class _VTp, class _Mgr, typename std::enable_if<(std::is_copy_constructible<_Tp>::value && (! std::__is_in_place_type<_VTp>::value)), bool>::type <anonymous> > std::any::any(_Tp&&) [with _Tp = nlohmann::basic_json<>&; _VTp = nlohmann::basic_json<>; _Mgr = std::any::_Manager_external<nlohmann::basic_json<> >; typename std::enable_if<(std::is_copy_constructible<_Tp>::value && (! std::__is_in_place_type<_VTp>::value)), bool>::type <anonymous> = <missing>]'
/opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/type_traits:2670:31:   required by substitution of 'template<class _Tp, class> static std::true_type std::__swappable_details::__do_is_swappable_impl::__test(int) [with _Tp = nlohmann::basic_json<>; <template-parameter-1-2> = <missing>]'
/opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/type_traits:2694:35:   required from 'struct std::__is_swappable_impl<nlohmann::basic_json<> >'
/opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/type_traits:2720:12:   required from 'struct std::is_swappable<nlohmann::basic_json<> >'
/opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/type_traits:2740:26:   required from 'constexpr const bool std::is_swappable_v<nlohmann::basic_json<> >'
/opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/optional:1201:51:   required by substitution of 'template<class _Tp> std::enable_if_t<(!(is_move_constructible_v<_Tp> && is_swappable_v<_Tp>))> std::swap(std::optional<_Tp>&, std::optional<_Tp>&) [with _Tp = nlohmann::basic_json<>]'
#### json.nlohmann/json.hpp:22677:1:   required from here
/opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/type_traits:906:12: error: invalid use of incomplete type 'struct std::__is_constructible_impl<nlohmann::basic_json<>, const nlohmann::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool, long int, long unsigned int, double, std::allocator, nlohmann::adl_serializer>&>'
  906 |     struct is_constructible
      |            ^~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/type_traits:900:12: note: declaration of 'struct std::__is_constructible_impl<nlohmann::basic_json<>, const nlohmann::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool, long int, long unsigned int, double, std::allocator, nlohmann::adl_serializer>&>'
  900 |     struct __is_constructible_impl
      |            ^~~~~~~~~~~~~~~~~~~~~~~
In file included from /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/vector:66,
                 from /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/functional:62,
                 from /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/pstl/glue_algorithm_defs.h:13,
                 from /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/algorithm:74,
                 from #### json.nlohmann/json.hpp:37:
/opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/bits/stl_uninitialized.h: In instantiation of '_ForwardIterator std::uninitialized_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = __gnu_cxx::__normal_iterator<const nlohmann::basic_json<>*, std::vector<nlohmann::basic_json<>, std::allocator<nlohmann::basic_json<> > > >; _ForwardIterator = nlohmann::basic_json<>*]':
/opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/bits/stl_uninitialized.h:325:37:   required from '_ForwardIterator std::__uninitialized_copy_a(_InputIterator, _InputIterator, _ForwardIterator, std::allocator<_Tp>&) [with _InputIterator = __gnu_cxx::__normal_iterator<const nlohmann::basic_json<>*, std::vector<nlohmann::basic_json<>, std::allocator<nlohmann::basic_json<> > > >; _ForwardIterator = nlohmann::basic_json<>*; _Tp = nlohmann::basic_json<>]'
/opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/bits/stl_vector.h:558:31:   required from 'std::vector<_Tp, _Alloc>::vector(const std::vector<_Tp, _Alloc>&) [with _Tp = nlohmann::basic_json<>; _Alloc = std::allocator<nlohmann::basic_json<> >]'
/opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/ext/new_allocator.h:150:4:   required from 'void __gnu_cxx::new_allocator<_Tp>::construct(_Up*, _Args&& ...) [with _Up = std::vector<nlohmann::basic_json<>, std::allocator<nlohmann::basic_json<> > >; _Args = {const std::vector<nlohmann::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool, long int, long unsigned int, double, std::allocator, nlohmann::adl_serializer>, std::allocator<nlohmann::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool, long int, long unsigned int, double, std::allocator, nlohmann::adl_serializer> > >&}; _Tp = std::vector<nlohmann::basic_json<>, std::allocator<nlohmann::basic_json<> > >]'
/opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/bits/alloc_traits.h:512:17:   required from 'static void std::allocator_traits<std::allocator<_Tp1> >::construct(std::allocator_traits<std::allocator<_Tp1> >::allocator_type&, _Up*, _Args&& ...) [with _Up = std::vector<nlohmann::basic_json<>, std::allocator<nlohmann::basic_json<> > >; _Args = {const std::vector<nlohmann::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool, long int, long unsigned int, double, std::allocator, nlohmann::adl_serializer>, std::allocator<nlohmann::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool, long int, long unsigned int, double, std::allocator, nlohmann::adl_serializer> > >&}; _Tp = std::vector<nlohmann::basic_json<>, std::allocator<nlohmann::basic_json<> > >; std::allocator_traits<std::allocator<_Tp1> >::allocator_type = std::allocator<std::vector<nlohmann::basic_json<>, std::allocator<nlohmann::basic_json<> > > >]'
#### json.nlohmann/json.hpp:15381:35:   [ skipping 2 instantiation contexts, use -ftemplate-backtrace-limit=0 to disable ]
#### json.nlohmann/json.hpp:16365:25:   required from 'nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer>::basic_json(const nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer>&) [with ObjectType = std::map; ArrayType = std::vector; StringType = std::__cxx11::basic_string<char>; BooleanType = bool; NumberIntegerType = long int; NumberUnsignedType = long unsigned int; NumberFloatType = double; AllocatorType = std::allocator; JSONSerializer = nlohmann::adl_serializer]'
#### json.nlohmann/json.hpp:4737:31:   required from 'bool nlohmann::detail::json_sax_dom_callback_parser<BasicJsonType>::end_object() [with BasicJsonType = nlohmann::basic_json<>]'
#### json.nlohmann/json.hpp:8847:33:   required from 'bool nlohmann::detail::parser<BasicJsonType>::sax_parse_internal(SAX*) [with SAX = nlohmann::detail::json_sax_dom_callback_parser<nlohmann::basic_json<> >; BasicJsonType = nlohmann::basic_json<>]'
#### json.nlohmann/json.hpp:8738:31:   required from 'void nlohmann::detail::parser<BasicJsonType>::parse(bool, BasicJsonType&) [with BasicJsonType = nlohmann::basic_json<>]'
#### json.nlohmann/json.hpp:20882:79:   required from 'static nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer> nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer>::parse(IteratorType, IteratorType, nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer>::parser_callback_t, bool) [with IteratorType = const char*; typename std::enable_if<std::is_base_of<std::random_access_iterator_tag, typename std::iterator_traits<_InputIterator>::iterator_category>::value, int>::type <anonymous> = 0; ObjectType = std::map; ArrayType = std::vector; StringType = std::__cxx11::basic_string<char>; BooleanType = bool; NumberIntegerType = long int; NumberUnsignedType = long unsigned int; NumberFloatType = double; AllocatorType = std::allocator; JSONSerializer = nlohmann::adl_serializer; nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer>::parser_callback_t = std::function<bool(int, nlohmann::detail::parser<nlohmann::basic_json<> >::parse_event_t, nlohmann::basic_json<>&)>]'
#### json.nlohmann/json.hpp:22700:42:   required from here
/opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/bits/stl_uninitialized.h:137:72: error: 'value' is not a member of 'std::is_constructible<nlohmann::basic_json<>, const nlohmann::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool, long int, long unsigned int, double, std::allocator, nlohmann::adl_serializer>&>'
  137 |       static_assert(is_constructible<_ValueType2, decltype(*__first)>::value,
      |                                                                        ^~~~~
Execution build compiler returned: 1

@gracicot
Copy link
Contributor Author

It seem like it doesn't happen with the develop branch. Here's a compiler explorer like that shows this.

I'm quite unsure what caused this. I'm glad this is fixed now. I would be ready to close this issue unless someone would like to take the time to explain what happened there. I tried to find an explanation but didn't found any.

@nlohmann
Copy link
Owner

I neither have a clue nor could reproduce it with the develop branch either...

@deepbluev7
Copy link

deepbluev7 commented May 25, 2020

Was this maybe fixed by #2034 ? I experienced that issue also without including , most standard library headers caused this, but it was fixed by #2034.

EDIT: godbolt to prove my point: https://godbolt.org/z/4Pswt6

Left is the version without #2034 merged, right is after the merge.

@nlohmann
Copy link
Owner

As I have no clue and this is a nice explanation, I am happy to close this issue. Thanks for digging into this!

@kuzkry
Copy link

kuzkry commented Jun 9, 2020

I suppose I found the answer to that mind-boggling question. I'm not a compiler expert though so take that into consideration.

MINIMAL CODE EXAMPLE

First of all, enclosed is the minimal code of single header where I removed everything that wasn't necessary to reproduce the bug:

file json.hpp

#include <string>
#include <type_traits>
#include <utility>

template<typename>
class basic_json;

using json = basic_json<std::string>;

class json_ref
{
  public:
    template <typename T,
              bool = std::is_constructible<json, T>::value>
    json_ref(T &&){}
};

template<typename>
class basic_json
{
  public:
    basic_json(json_ref) {}
};

namespace std {

template<>
void swap<json>(json&, json&) noexcept {}

} // namespace std

file main.cpp

#include <any>
#include "json.hpp"

int main() {}

OBSERVATIONS

Then I started playing with it and I discovered strange relations and how the code could be fixed by:

  1. swapping around includes of "json.hpp" and <any>
  2. removing #include <any>
  3. changing #include <any> to #include <variant>
  4. changing using json = basic_json<std::string>; to using json = basic_json<int>;
  5. removing bool = std::is_constructible<json, T>::value>
  6. chaning json_ref(T &&){} to json_ref(T) / json_ref(const T&) / json_ref(const T&&)
  7. moving the json_ref class to the bottom of json.hpp (but only to the bottom, doesn't work between basic_json and namespace std extension)
  8. removing the external swap (i.e. extension of std)
  9. removing the contructor of basic_json(json_ref) {}
    and much more :D

ANALYSIS AND EXPLANATION

From std::swap specialization to std::any

GCC always starts its ranting with the external swap function (our std::swap specialization) so it's clearly the source of the error. This explicit specialization forces checking all other existing swaps to be inspected and eventually leads to the bug.

Now, as I followed a GCC stacktrace, I found that along the chain, it performs some SFINAE of form:

    struct __do_is_swappable_impl
    {
      template<typename _Tp, typename
               = decltype(swap(std::declval<_Tp&>(), std::declval<_Tp&>()))>
    // (...)

Note: Clang also reaches this code, but I lose the track of where it goes after

But from here it "magically" goes to std::any constructor. Why? Check out std::any external swap function signature; it is:

  /// Exchange the states of two @c any objects.
  inline void swap(any& __x, any& __y) noexcept { __x.swap(__y); }

So, the previously mentioned decltype(swap(std::declval<_Tp&>(), std::declval<_Tp&>()))> considers converting json to std::any implicitly (through one of std::any non-explicit constructors) as a better match than our std::swap specialization.

From std::any constructor to std::__is_constructible_impl

As the compiler goes further, it has to check SFINAE requirements of std::any contructor:

    /// Construct with a copy of @p __value as the contained object.
    template <typename _Tp, typename _VTp = _Decay_if_not_any<_Tp>,
	      typename _Mgr = _Manager<_VTp>,
	      enable_if_t<is_copy_constructible<_VTp>::value
			  && !__is_in_place_type<_VTp>::value, bool> = true>
      any(_Tp&& __value)
      // (...)

It calls the metafunction of std::is_copy_constructible onto our json type which is later dispatched to the mysterious std::__is_constructible_impl.

From std::__is_constructible_impl to std::__is_constructible_impl (indirect incompleteness)

What we soon get is that std::__is_constructible_impl is incomplete. Why? Because inserting json to that mysterious std::__is_constructible_impl involves inspecting all constructors of json obviously, but since we have such a constructor:

basic_json(json_ref) {}

we need to check constructors of json_ref too (as we depend on that type). Compiler just tries everything, e.g. it looks for some implicit conversions here. The catch here is that the dependent constructor

template <typename T,
              bool = std::is_constructible<json, T>::value>
    json_ref(T &&){}

instantiates std::is_constructible, but the latter derives from std::__is_constructible_impl.

I mean:
std::__is_constructible_impl depends on basic_json
basic_json depends on json_ref
json_ref depends on std::is_constructible
std::is_constructible depends on std::__is_constructible_impl

AFTERWORD

IMO it's hard to tell where and what to fix. From user POV it is bollocks that #include <any> rejects the code for seemingly no reason.

Though, I'm gonna be a devil's advocate here and I defend GCC as I reckon that GCC works fine here. IMHO, it's rather the defect of C++17 standard that requires us to write std::swap specializations (at least until C++20) and forbids us from overloading std::swap for our own types. This makes the compiler think that swap<json>(json&, json&) is worse than swap(any&, any&). As we see, this results in convoluted scenarios which take hours of analysis and return no clear error to the user.

Last but not least, unfortunately I have no clue what clang does after inspecting __do_is_swappable_impl. Actually, on my system GCC and Clang share the same header files but they split up in this point. Who knows, maybe clang is more right or wrong here.

@nlohmann
Copy link
Owner

nlohmann commented Jun 9, 2020

Maybe #2176 is related to this.

@gracicot
Copy link
Contributor Author

gracicot commented Jun 9, 2020

Thank you for this explanation. This is why I stumbled across the std::swap specialization while investigating.

@kuzkry
Copy link

kuzkry commented Jun 9, 2020

:)
FYI, I've reported a regression bug in GCC bugzilla as I understand how it behaves but I don't know whether this is how it should be.

@himikof
Copy link
Contributor

himikof commented Jul 27, 2020

Please note the following resolution from Jonathan Wakely on the GCC issue:

The json code is incorrect and should be fixed.

The std::swap specialization is wrong and should be replaced by a normal (non-template) overload in the same namespace as the json type:

void swap(json&, json&) noexcept;

The json_ref constructor should be constrained to avoid recursive instantiations with incomplete types, e.g.

template<typename T, typename U>
  using is_not = std::is_same<std::remove_cv_t<std::remove_reference_t<T>>, U>;

class json_ref
{
  public:
    template <typename T,
              typename = std::enable_if_t<!is_not<T, json_ref>::value>,
              typename = std::enable_if_t<!is_not<T, json>::value>,
              bool = std::is_constructible<json, T>::value>
    json_ref(T &&){}
};

I think I failed to think about a possible "infinite"/incomplete template instantiation here when I wrote the initial version of the json_ref code. Before GCC 10 the stdlib metafunctions did not check that and just returned false, making the code work.

WojciechMigda added a commit to WojciechMigda/Tsetlini that referenced this issue Sep 16, 2023
Resolves nlohmann::json conflict with g++-10:
nlohmann/json#2129
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants