Compiling code with GCC 8.1 / binutils 2.30 (built locally on GNU/Linux amd64) which previously compiled and worked OK with GCC 6.2 and 7.3. I received a very cryptic error that had me running around reworking class implementations for quite a while before I realized the problem: I had an incomplete type. I don't know if there's anything G++ could do better here, but FYI I had this code: class Bar { public: struct Less { bool operator()(const Bar& lhs, const Bar& rhs) const; bool operator()(const Bar* lhs, const Bar* rhs) const; }; }; class Biz; #include <set> class Foo { std::set<const Biz*, Bar::Less> _map; }; It's not immediately clear that the incomplete Biz class is a problem, especially in my code which is significantly more complex and crosses multiple header files, and G++ doesn't give a very helpful (to me) error: $ g++ -o set.o -c set.cpp In file included from x86_64-generic-linux-gnu/include/c++/8.1.0/set:60, from set.cpp:13: x86_64-generic-linux-gnu/include/c++/8.1.0/bits/stl_tree.h: In instantiation of 'class std::_Rb_tree<const Biz*, const Biz*, std::_Identity<const Biz*>, Bar::Less, std::allocator<const Biz*> >': x86_64-generic-linux-gnu/include/c++/8.1.0/bits/stl_set.h:133:17: required from 'class std::set<const Biz*, Bar::Less>' set.cpp:17:37: required from here x86_64-generic-linux-gnu/include/c++/8.1.0/bits/stl_tree.h:452:21: error: static assertion failed: comparison object must be invocable with two arguments of key type static_assert(__is_invocable<_Compare&, const _Key&, const _Key&>{}, ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ If I had included the complete type for class Biz, the compiler would have seen that Biz is a subclass of Bar and it would have been fine; adding in the header file fixed my problem: class Bar { public: struct Less { bool operator()(const Bar& lhs, const Bar& rhs) const; bool operator()(const Bar* lhs, const Bar* rhs) const; }; }; class Biz : public Bar {} #include <set> class Foo { std::set<const Biz*, Bar::Less> _map; };
There's nothing that can be done here. The error comes from the library (not the compiler) and there's no way the compiler can possibly say "the static assertion would have passed if the __is_invocable trait had been true, which might have happened if the type was complete so that a conversion sequence from const Biz* to const Bar* might be possible". The is_invocable trait asks if that function object can be called with those arguments, and when Biz is incomplete the answer is no. I think sadly I have to remove that static_assertion, or just make it happen later when the comparison function is used (by which point either the call works, or you get an error anyway and the static assertion doesn't add any value).
IMHO, it is very unfortunate that we need to provide the full definition for this to work. It has forced me to include a header file just for this error to go away, which is not desirable. I guess it doesn't compile a completely valid C++ code (at least, I've not found that this is a std::set requirement). Probably, __is_invocable<> should not signal an error if it finds an incomplete type, or it should be replaced with a construct that doesn't. So, if the code using an incomplete type pointer for std::set is a valid C++ code, this is a sever (non-standard conforming) bug in library.
(In reply to Hedayat Vatankhah from comment #2) > Probably, __is_invocable<> should not signal an error if it finds an > incomplete type, or it should be replaced with a construct that doesn't. It's not possible for a type trait to give a different answer depending whether a type is complete or not. That would violate the One-Definition Rule. I'd forgotten about this issue because the component wasn't libstdc++. I will remove the is_invocable checks.
Oops sorry: I guess I'm not familiar enough with the vagaries of the bug trackers :). Thanks Jonathan!
No problem, it's not the reporter's responsibility to categorise it correctly.
Author: redi Date: Tue Mar 26 15:28:48 2019 New Revision: 269949 URL: https://gcc.gnu.org/viewcvs?rev=269949&root=gcc&view=rev Log: PR libstdc++/85965 delay static assertions until types are complete The static assertions added for PR libstdc++/48101 were at class scope and so were evaluated too eagerly, when it might not be possible to determine whether the function objects are invocable with the key types. The problematic cases are where the key type is not known to be convertible to the argument type(s) of the function object until later, after a type has been completed. Specifically, if the key type is a pointer to a derived class and the function object's argument type is a pointer to a base class, then the derived-to-base conversion is only valid once the derived type is complete. By moving the static assertions to the destructor they will only be evaluated when the destructor is instantiated, at which point whether the key type can be passed to the function object should be knowable. The ideal place to do the checks would be only when the function objects are actually invoked, but that would mean adding the checks in numerous places, so the destructor is used instead. The tests need to be adjusted because the "required from here" line is now the location of the destructor, not the point of instantiation in the test file. For the map and multimap tests which check two specializations, the dg-error matching the assertion text matches both cases. Also check the diagnostic output for the template arguments, to ensure both specializations trigger the assertion. PR libstdc++/85965 * include/bits/hashtable.h (_Hashtable): Move static assertions to destructor so they are not evaluated until the _Key type is complete. * include/bits/stl_tree.h (_Rb_tree): Likewise. * testsuite/23_containers/set/85965.cc: New test. * testsuite/23_containers/unordered_set/85965.cc: New test. * testsuite/23_containers/map/48101_neg.cc: Replace "here" errors with regexp matching the corresponding _Rb_tree specialization. * testsuite/23_containers/multimap/48101_neg.cc: Likewise. * testsuite/23_containers/multiset/48101_neg.cc: Remove "here" error. * testsuite/23_containers/set/48101_neg.cc: Likewise. * testsuite/23_containers/unordered_map/48101_neg.cc: Likewise. * testsuite/23_containers/unordered_multimap/48101_neg.cc: Likewise. * testsuite/23_containers/unordered_multiset/48101_neg.cc: Likewise. * testsuite/23_containers/unordered_set/48101_neg.cc: Likewise. Added: trunk/libstdc++-v3/testsuite/23_containers/set/85965.cc - copied, changed from r269947, trunk/libstdc++-v3/testsuite/23_containers/map/48101_neg.cc trunk/libstdc++-v3/testsuite/23_containers/unordered_set/85965.cc - copied, changed from r269947, trunk/libstdc++-v3/testsuite/23_containers/map/48101_neg.cc Modified: trunk/libstdc++-v3/ChangeLog trunk/libstdc++-v3/include/bits/hashtable.h trunk/libstdc++-v3/include/bits/stl_tree.h trunk/libstdc++-v3/testsuite/23_containers/map/48101_neg.cc trunk/libstdc++-v3/testsuite/23_containers/multimap/48101_neg.cc trunk/libstdc++-v3/testsuite/23_containers/multiset/48101_neg.cc trunk/libstdc++-v3/testsuite/23_containers/set/48101_neg.cc trunk/libstdc++-v3/testsuite/23_containers/unordered_map/48101_neg.cc trunk/libstdc++-v3/testsuite/23_containers/unordered_multimap/48101_neg.cc trunk/libstdc++-v3/testsuite/23_containers/unordered_multiset/48101_neg.cc trunk/libstdc++-v3/testsuite/23_containers/unordered_set/48101_neg.cc
This is fixed on trunk now, but still needs to be backported to gcc-8-branch.
Author: redi Date: Tue May 7 15:46:05 2019 New Revision: 270960 URL: https://gcc.gnu.org/viewcvs?rev=270960&root=gcc&view=rev Log: PR libstdc++/85965 delay static assertions until types are complete The static assertions added for PR libstdc++/48101 were at class scope and so were evaluated too eagerly, when it might not be possible to determine whether the function objects are invocable with the key types. The problematic cases are where the key type is not known to be convertible to the argument type(s) of the function object until later, after a type has been completed. Specifically, if the key type is a pointer to a derived class and the function object's argument type is a pointer to a base class, then the derived-to-base conversion is only valid once the derived type is complete. By moving the static assertions to the destructor they will only be evaluated when the destructor is instantiated, at which point whether the key type can be passed to the function object should be knowable. The ideal place to do the checks would be only when the function objects are actually invoked, but that would mean adding the checks in numerous places, so the destructor is used instead. The tests need to be adjusted because the "required from here" line is now the location of the destructor, not the point of instantiation in the test file. For the map and multimap tests which check two specializations, the dg-error matching the assertion text matches both cases. Also check the diagnostic output for the template arguments, to ensure both specializations trigger the assertion. Backport from mainline 2019-03-26 Jonathan Wakely <jwakely@redhat.com> PR libstdc++/85965 * include/bits/hashtable.h (_Hashtable): Move static assertions to destructor so they are not evaluated until the _Key type is complete. * include/bits/stl_tree.h (_Rb_tree): Likewise. * testsuite/23_containers/set/85965.cc: New test. * testsuite/23_containers/unordered_set/85965.cc: New test. * testsuite/23_containers/map/48101_neg.cc: Replace "here" errors with regexp matching the corresponding _Rb_tree specialization. * testsuite/23_containers/multimap/48101_neg.cc: Likewise. * testsuite/23_containers/multiset/48101_neg.cc: Remove "here" error. * testsuite/23_containers/set/48101_neg.cc: Likewise. * testsuite/23_containers/unordered_map/48101_neg.cc: Likewise. * testsuite/23_containers/unordered_multimap/48101_neg.cc: Likewise. * testsuite/23_containers/unordered_multiset/48101_neg.cc: Likewise. * testsuite/23_containers/unordered_set/48101_neg.cc: Likewise. Added: branches/gcc-8-branch/libstdc++-v3/testsuite/23_containers/set/85965.cc - copied, changed from r270959, branches/gcc-8-branch/libstdc++-v3/testsuite/23_containers/map/48101_neg.cc branches/gcc-8-branch/libstdc++-v3/testsuite/23_containers/unordered_set/85965.cc - copied, changed from r270959, branches/gcc-8-branch/libstdc++-v3/testsuite/23_containers/map/48101_neg.cc Modified: branches/gcc-8-branch/libstdc++-v3/ChangeLog branches/gcc-8-branch/libstdc++-v3/include/bits/hashtable.h branches/gcc-8-branch/libstdc++-v3/include/bits/stl_tree.h branches/gcc-8-branch/libstdc++-v3/testsuite/23_containers/map/48101_neg.cc branches/gcc-8-branch/libstdc++-v3/testsuite/23_containers/multimap/48101_neg.cc branches/gcc-8-branch/libstdc++-v3/testsuite/23_containers/multiset/48101_neg.cc branches/gcc-8-branch/libstdc++-v3/testsuite/23_containers/set/48101_neg.cc branches/gcc-8-branch/libstdc++-v3/testsuite/23_containers/unordered_map/48101_neg.cc branches/gcc-8-branch/libstdc++-v3/testsuite/23_containers/unordered_multimap/48101_neg.cc branches/gcc-8-branch/libstdc++-v3/testsuite/23_containers/unordered_multiset/48101_neg.cc branches/gcc-8-branch/libstdc++-v3/testsuite/23_containers/unordered_set/48101_neg.cc
Fixed for 8.4 too.
Isn't it expected to be fixed in Gcc 9.1.1? It seems to still affect GCC 9.1.1 (Fedora 30)
The example reported here is fixed in 9.1.0, if you have a different example maybe there's a different problem.
OK, I found the problem. Yes, the fix is included in 9.1.1. However, the fix assumes that the type must be complete when desctructor is called, but unfortunately, this is a wrong assumption. AFAIK, this is a completely valid C++ code, but cannot be compiled successfully: foo.h: ------------------------------- class Bar { public: struct Less { bool operator()(const Bar& lhs, const Bar& rhs) const; bool operator()(const Bar* lhs, const Bar* rhs) const; }; }; class Biz; #include <set> class Foo { public: void do_something(); std::set<const Biz*, Bar::Less> _map; }; ------------------------------- test.cpp: ------------------------------- void tt() { Foo f; f.do_something(); } ------------------------------- foo.cpp: ------------------------------- #include "foo.h" #include "biz.h" Foo::do_something() { // do an insert... }
(In reply to Hedayat Vatankhah from comment #12) > AFAIK, this is a completely valid C++ code, I don't think that's actually clear. The standard doesn't specify when the comparison object must be invocable, so I think it's conforming (but not very user friendly) to reject that code, and the original example in this bug report.
A new patch to fix this was posted to: https://gcc.gnu.org/ml/gcc-patches/2019-05/msg00863.html
Thanks! Yep, I'm certainly not a C++ standard expert.
Author: redi Date: Fri May 17 14:13:32 2019 New Revision: 271323 URL: https://gcc.gnu.org/viewcvs?rev=271323&root=gcc&view=rev Log: PR libstdc++/85965 move is_invocable assertions again This is another attempt to reduce how often the assertions are evaluated, so that code which doesn't try to use the function objects doesn't need them to be invocable. For _Rb_tree we access the _M_key_compare object directly, so can't put the assertions in an accessor function for it. However, every invocation of _M_key_compare is accompanied by a use of _S_key, so the assertions can be put in there. For _Hashtable there are member functions that are consistently used to obtain a hash code or test for equality, so the assertions can go in those members. PR libstdc++/85965 * include/bits/hashtable.h (_Hashtable::~_Hashtable()): Remove static assertions from the destructor. * include/bits/hashtable_policy.h (_Hash_code_base::_M_hash_code): Move static_assert for hash function to here. (_Hash_table_base::_M_equals): Move static_assert for equality predicate to here. * include/bits/stl_tree.h (_Rb_tree::_S_value(_Const_Link_type)): Remove. (_Rb_tree::_S_key(_Const_Link_type)): Move assertions here. Access the value directly instead of calling _S_value. (_Rb_tree::_S_value(_Const_Base_ptr)): Remove. (_Rb_tree::_S_key(_Const_Base_ptr)): Do downcast and forward to _S_key(_Const_Link_type). * testsuite/23_containers/set/85965.cc: Check construction, destruction, assignment and size() do not trigger the assertions. * testsuite/23_containers/unordered_set/85965.cc: Likewise. * testsuite/23_containers/map/48101_neg.cc: Call find and adjust expected errors. * testsuite/23_containers/multimap/48101_neg.cc: Likewise. * testsuite/23_containers/multiset/48101_neg.cc: Likewise. * testsuite/23_containers/set/48101_neg.cc: Likewise. * testsuite/23_containers/unordered_map/48101_neg.cc: Likewise. * testsuite/23_containers/unordered_multimap/48101_neg.cc: Likewise. * testsuite/23_containers/unordered_multiset/48101_neg.cc: Likewise. * testsuite/23_containers/unordered_set/48101_neg.cc: Likewise. Modified: trunk/libstdc++-v3/ChangeLog trunk/libstdc++-v3/include/bits/hashtable.h trunk/libstdc++-v3/include/bits/hashtable_policy.h trunk/libstdc++-v3/include/bits/stl_tree.h trunk/libstdc++-v3/testsuite/23_containers/map/48101_neg.cc trunk/libstdc++-v3/testsuite/23_containers/multimap/48101_neg.cc trunk/libstdc++-v3/testsuite/23_containers/multiset/48101_neg.cc trunk/libstdc++-v3/testsuite/23_containers/set/48101_neg.cc trunk/libstdc++-v3/testsuite/23_containers/set/85965.cc trunk/libstdc++-v3/testsuite/23_containers/unordered_map/48101_neg.cc trunk/libstdc++-v3/testsuite/23_containers/unordered_multimap/48101_neg.cc trunk/libstdc++-v3/testsuite/23_containers/unordered_multiset/48101_neg.cc trunk/libstdc++-v3/testsuite/23_containers/unordered_set/48101_neg.cc trunk/libstdc++-v3/testsuite/23_containers/unordered_set/85965.cc
Author: redi Date: Fri May 24 16:09:28 2019 New Revision: 271607 URL: https://gcc.gnu.org/viewcvs?rev=271607&root=gcc&view=rev Log: PR libstdc++/85965 move is_invocable assertions again This is another attempt to reduce how often the assertions are evaluated, so that code which doesn't try to use the function objects doesn't need them to be invocable. For _Rb_tree we access the _M_key_compare object directly, so can't put the assertions in an accessor function for it. However, every invocation of _M_key_compare is accompanied by a use of _S_key, so the assertions can be put in there. For _Hashtable there are member functions that are consistently used to obtain a hash code or test for equality, so the assertions can go in those members. Backport from mainline 2019-05-17 Jonathan Wakely <jwakely@redhat.com> PR libstdc++/85965 * include/bits/hashtable.h (_Hashtable::~_Hashtable()): Remove static assertions from the destructor. * include/bits/hashtable_policy.h (_Hash_code_base::_M_hash_code): Move static_assert for hash function to here. (_Hash_table_base::_M_equals): Move static_assert for equality predicate to here. * include/bits/stl_tree.h (_Rb_tree::_S_key(_Const_Link_type)): Move assertions here. Access the value directly instead of calling _S_value. (_Rb_tree::_S_key(_Const_Base_ptr)): Do downcast and forward to _S_key(_Const_Link_type). * testsuite/23_containers/set/85965.cc: Check construction, destruction, assignment and size() do not trigger the assertions. * testsuite/23_containers/unordered_set/85965.cc: Likewise. * testsuite/23_containers/map/48101_neg.cc: Call find and adjust expected errors. * testsuite/23_containers/multimap/48101_neg.cc: Likewise. * testsuite/23_containers/multiset/48101_neg.cc: Likewise. * testsuite/23_containers/set/48101_neg.cc: Likewise. * testsuite/23_containers/unordered_map/48101_neg.cc: Likewise. * testsuite/23_containers/unordered_multimap/48101_neg.cc: Likewise. * testsuite/23_containers/unordered_multiset/48101_neg.cc: Likewise. * testsuite/23_containers/unordered_set/48101_neg.cc: Likewise. Modified: branches/gcc-9-branch/libstdc++-v3/ChangeLog branches/gcc-9-branch/libstdc++-v3/include/bits/hashtable.h branches/gcc-9-branch/libstdc++-v3/include/bits/hashtable_policy.h branches/gcc-9-branch/libstdc++-v3/include/bits/stl_tree.h branches/gcc-9-branch/libstdc++-v3/testsuite/23_containers/map/48101_neg.cc branches/gcc-9-branch/libstdc++-v3/testsuite/23_containers/multimap/48101_neg.cc branches/gcc-9-branch/libstdc++-v3/testsuite/23_containers/multiset/48101_neg.cc branches/gcc-9-branch/libstdc++-v3/testsuite/23_containers/set/48101_neg.cc branches/gcc-9-branch/libstdc++-v3/testsuite/23_containers/set/85965.cc branches/gcc-9-branch/libstdc++-v3/testsuite/23_containers/unordered_map/48101_neg.cc branches/gcc-9-branch/libstdc++-v3/testsuite/23_containers/unordered_multimap/48101_neg.cc branches/gcc-9-branch/libstdc++-v3/testsuite/23_containers/unordered_multiset/48101_neg.cc branches/gcc-9-branch/libstdc++-v3/testsuite/23_containers/unordered_set/48101_neg.cc branches/gcc-9-branch/libstdc++-v3/testsuite/23_containers/unordered_set/85965.cc
Author: redi Date: Fri Aug 30 13:09:42 2019 New Revision: 275166 URL: https://gcc.gnu.org/viewcvs?rev=275166&root=gcc&view=rev Log: PR libstdc++/85965 move is_invocable assertions again This is another attempt to reduce how often the assertions are evaluated, so that code which doesn't try to use the function objects doesn't need them to be invocable. For _Rb_tree we access the _M_key_compare object directly, so can't put the assertions in an accessor function for it. However, every invocation of _M_key_compare is accompanied by a use of _S_key, so the assertions can be put in there. For _Hashtable there are member functions that are consistently used to obtain a hash code or test for equality, so the assertions can go in those members. Backport from mainline 2019-05-17 Jonathan Wakely <jwakely@redhat.com> PR libstdc++/85965 * include/bits/hashtable.h (_Hashtable::~_Hashtable()): Remove static assertions from the destructor. * include/bits/hashtable_policy.h (_Hash_code_base::_M_hash_code): Move static_assert for hash function to here. (_Hash_table_base::_M_equals): Move static_assert for equality predicate to here. * include/bits/stl_tree.h (_Rb_tree::_S_key(_Const_Link_type)): Move assertions here. Access the value directly instead of calling _S_value. (_Rb_tree::_S_key(_Const_Base_ptr)): Do downcast and forward to _S_key(_Const_Link_type). * testsuite/23_containers/set/85965.cc: Check construction, destruction, assignment and size() do not trigger the assertions. * testsuite/23_containers/unordered_set/85965.cc: Likewise. * testsuite/23_containers/map/48101_neg.cc: Call find and adjust expected errors. * testsuite/23_containers/multimap/48101_neg.cc: Likewise. * testsuite/23_containers/multiset/48101_neg.cc: Likewise. * testsuite/23_containers/set/48101_neg.cc: Likewise. * testsuite/23_containers/unordered_map/48101_neg.cc: Likewise. * testsuite/23_containers/unordered_multimap/48101_neg.cc: Likewise. * testsuite/23_containers/unordered_multiset/48101_neg.cc: Likewise. * testsuite/23_containers/unordered_set/48101_neg.cc: Likewise. Modified: branches/gcc-8-branch/libstdc++-v3/ChangeLog branches/gcc-8-branch/libstdc++-v3/include/bits/hashtable.h branches/gcc-8-branch/libstdc++-v3/include/bits/hashtable_policy.h branches/gcc-8-branch/libstdc++-v3/include/bits/stl_tree.h branches/gcc-8-branch/libstdc++-v3/testsuite/23_containers/map/48101_neg.cc branches/gcc-8-branch/libstdc++-v3/testsuite/23_containers/multimap/48101_neg.cc branches/gcc-8-branch/libstdc++-v3/testsuite/23_containers/multiset/48101_neg.cc branches/gcc-8-branch/libstdc++-v3/testsuite/23_containers/set/48101_neg.cc branches/gcc-8-branch/libstdc++-v3/testsuite/23_containers/set/85965.cc branches/gcc-8-branch/libstdc++-v3/testsuite/23_containers/unordered_map/48101_neg.cc branches/gcc-8-branch/libstdc++-v3/testsuite/23_containers/unordered_multimap/48101_neg.cc branches/gcc-8-branch/libstdc++-v3/testsuite/23_containers/unordered_multiset/48101_neg.cc branches/gcc-8-branch/libstdc++-v3/testsuite/23_containers/unordered_set/48101_neg.cc branches/gcc-8-branch/libstdc++-v3/testsuite/23_containers/unordered_set/85965.cc
Fixed for 8.4