Bug 51365 - cannot use final empty class in std::tuple
Summary: cannot use final empty class in std::tuple
Alias: None
Product: gcc
Classification: Unclassified
Component: libstdc++ (show other bugs)
Version: 4.7.0
: P3 normal
Target Milestone: 4.7.0
Assignee: Jonathan Wakely
Keywords: rejects-valid
Depends on:
Blocks: 60921
  Show dependency treegraph
Reported: 2011-11-30 19:01 UTC by Jonathan Wakely
Modified: 2014-04-22 14:36 UTC (History)
1 user (show)

See Also:
Known to work:
Known to fail:
Last reconfirmed: 2011-11-30 00:00:00

front end patch (missing docs and tests) (1.26 KB, patch)
2011-11-30 21:06 UTC, Jonathan Wakely
Details | Diff
library patch (missing tests) (666 bytes, patch)
2011-11-30 21:07 UTC, Jonathan Wakely
Details | Diff
fix other parts of library (1.37 KB, patch)
2011-11-30 23:35 UTC, Jonathan Wakely
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Jonathan Wakely 2011-11-30 19:01:13 UTC
#include <tuple>
struct F final { };
std::tuple<F> t;

This should work. I don't know how to make it work without a compiler-assisted is_final trait.
Comment 1 Jonathan Wakely 2011-11-30 19:08:57 UTC
I'll have a go at implementing __is_final, using Paolo's work for PR 26099 as a starting point.
Comment 2 Paolo Carlini 2011-11-30 19:22:32 UTC
Yes, should be pretty trivial given the infrastructure already in place. In semantics should be just matter of calling CLASS_TYPE_P && CLASSTYPE_FINAL
Comment 3 Jonathan Wakely 2011-11-30 21:06:25 UTC
Created attachment 25961 [details]
front end patch (missing docs and tests)
Comment 4 Jonathan Wakely 2011-11-30 21:07:00 UTC
Created attachment 25962 [details]
library patch (missing tests)
Comment 5 Jonathan Wakely 2011-11-30 21:19:28 UTC
Thanks for the pointer, the hardest part was naming the new intrinsic ;)

We can't have the front-end define __is_final() as an intrinsic and also define std::__is_final as a class template, so the patches above define __is_final_class() and std::__is_final<>

It might be better for the front end to define __is_final() then use that directly in the library and not provide a type trait unless std::is_final gets added to the library by a DR.

i.e. in <tuple>

template<typename _Tp>
  using __empty_base 
    = typename conditional<__is_final(_Tp), false_type, is_empty<_Tp>>::type;
Comment 6 Jonathan Wakely 2011-11-30 23:35:00 UTC
Created attachment 25963 [details]
fix other parts of library

here's a proof of concept patch showing how to fix existing code that inherits from allocators so that it is ABI-compatible for non-final allocators and also supports final allocators (by inheriting from a wrapper class instead).  Something like this needs to be done for the containers so they stay backwards compatible but also work with final types.

(I decided to rename the intrinsic to __is_final and that's what this patch uses)
Comment 7 Jonathan Wakely 2011-12-01 01:07:27 UTC
Here's another testcase:

#include <future>
#include <memory>

template<typename T>
struct final_allocator final : std::allocator<T>
  final_allocator() = default;

  template<typename U>
    final_allocator(const final_allocator<U>&) { }

  template<typename U>
    struct rebind { typedef final_allocator<U> other; };

final_allocator<int> a;

std::promise<int> p(std::allocator_arg, a);
Comment 8 Jonathan Wakely 2011-12-03 12:06:08 UTC
patch implementing __is_final submitted for approval:
Comment 9 Marc Glisse 2011-12-11 12:12:29 UTC

the recent discussion "Structure alignment changes when a constructor or destructor is added" on gcc-help made me wonder whether it would make sense to not only derive for empty classes, but actually derive by default and use members only when necessary (builtin types, final classes). The advantage would be that derivation allows for a slightly more compact representation in some cases with the g++ ABI. I really haven't thought much about the consequences.

Sorry for hijacking this bug with this wild idea, but in case it makes sense it might change slightly the way you want to fix it.
Comment 10 Jonathan Wakely 2011-12-11 13:39:20 UTC
I haven't really thought about it either, but one advantage of depending on is_empty is that it prevents using the EBO for polymorphic classes, where derivation could have undesirable effects.  That *shouldn't* be a problem as long as user-defined types don't have virtual functions using reserved names such as _M_get.

Another problem could be user-defined types with a virtual base, if the tuple node type (which would be the most-derived type) doesn't know how to construct the virtual base.

struct A { A(int) { } };

struct B : virtual A { B() : A(1) { } };

std::tuple<B> t;

If tuple derived from B it couldn't construct the A subobject correctly.
Comment 11 Marc Glisse 2011-12-11 13:51:20 UTC
(In reply to comment #10)
> I haven't really thought about it either, but one advantage of depending on
> is_empty is that it prevents using the EBO for polymorphic classes, where
> derivation could have undesirable effects.

Ah, right, the set of types that can safely be derived from is rather restricted: non-builtin, non-final, non-virtual (and possibly more constraints). And to benefit from additional packing: non-C++03-POD. Probably not worth the trouble...
Comment 12 Jonathan Wakely 2011-12-15 10:02:50 UTC
Author: redi
Date: Thu Dec 15 10:02:45 2011
New Revision: 182360

URL: http://gcc.gnu.org/viewcvs?root=gcc&view=rev&rev=182360
	PR libstdc++/51365
	* c-common.c (RID_IS_FINAL): Add.
	* c-common.h (RID_IS_FINAL): Add.
	PR libstdc++/51365
	* cp-tree.h (CPTK_IS_FINAL): Add.
	* parser.c (cp_parser_translation_unit): Handle RID_IS_FINAL.
	(cp_parser_primary_expression, cp_parser_trait_expr): Likewise.
	* semantics.c (trait_expr_value, finish_trait_expr): Handle
	* cxx-pretty-print.c (pp_cxx_trait_expression): Likewise.
	PR libstdc++/51365
	* g++.dg/ext/is_final.C: New.

Comment 13 Jonathan Wakely 2011-12-15 10:06:19 UTC
front end changes done, I'll work on the library parts this weekend
Comment 14 Jonathan Wakely 2011-12-20 09:09:56 UTC
Author: redi
Date: Tue Dec 20 09:09:50 2011
New Revision: 182523

URL: http://gcc.gnu.org/viewcvs?root=gcc&view=rev&rev=182523
	PR libstdc++/51365
	* include/std/tuple (_Tuple_impl): Check __is_final as well as
	* testsuite/20_util/tuple/51365.cc: New.

Comment 15 Jonathan Wakely 2012-02-04 13:53:44 UTC
Given the number of other PRs I'm working on and that we're in stage4 I'm not going to handle 'final' allocators for 4.7 so I'll come back to it for 4.8 (when I also plan to update all the containers to use C++11 allocators)
Comment 16 Jakub Jelinek 2013-03-22 14:44:18 UTC
GCC 4.8.0 is being released, adjusting target milestone.
Comment 17 Jonathan Wakely 2013-04-28 11:40:24 UTC
shared_ptr fixed to handle final allocators by:

Author: redi
Date: Sun Apr 28 11:38:21 2013
New Revision: 198367

URL: http://gcc.gnu.org/viewcvs?rev=198367&root=gcc&view=rev
	PR libstdc++/51365
	* include/bits/shared_ptr_base (_Sp_ebo_helper): Helper class to
	implement EBO safely.
	(_Sp_counted_base::_M_get_deleter): Add noexcept.
	(_Sp_counter_ptr): Use noexcept instead of comments.
	(_Sp_counted_deleter): Likewise. Use _Sp_ebo_helper.
	(_Sp_counted_ptr_inplace): Likewise.
	* testsuite/20_util/shared_ptr/cons/51365.cc: New.
	* testsuite/20_util/shared_ptr/cons/52924.cc: Add rebind member to
	custom allocator and test construction with custom allocator.
	* testsuite/20_util/shared_ptr/cons/43820_neg.cc: Adjust dg-error
	line number.

      - copied, changed from r198365, trunk/libstdc++-v3/testsuite/20_util/shared_ptr/cons/52924.cc
Comment 18 Jakub Jelinek 2014-04-22 11:36:33 UTC
GCC 4.9.0 has been released
Comment 19 Jonathan Wakely 2014-04-22 14:36:17 UTC
I'm closing this as fixed, I've created Bug 60921 to deal with the remaining final allocator issues.