Bug 80187 - C++ variant should be trivially copy constructible if possible
Summary: C++ variant should be trivially copy constructible if possible
Status: ASSIGNED
Alias: None
Product: gcc
Classification: Unclassified
Component: libstdc++ (show other bugs)
Version: 7.0
: P3 normal
Target Milestone: ---
Assignee: Tim Shen
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2017-03-25 21:08 UTC by Ambroz Bizjak
Modified: 2017-06-27 18:19 UTC (History)
3 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2017-03-26 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Ambroz Bizjak 2017-03-25 21:08:53 UTC
Presumably because std::variant<int,char> is not trivially copy constructible, the ABI will be unable to pass it by value in a register in a call to a non-inlined function taking the variant by value, but will have to pass it by reference.

Test case:

#include <variant>
struct TaggedUnion { union { int i; char c; }; int tag; };
void f1(std::variant<int,char>);
void f2(TaggedUnion);
int main ()
{
    f1(42);
    f2({42, 0});
    return 0;
}

Result on Linux x86-64: f1 is called passing the argument by address, f2 is called passing by register.

See the standard proposal:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0602r0.html

however I think this can be fixed in gcc regardless of when/whether the proposal is accepted.
Comment 1 Tim Shen 2017-03-26 14:39:01 UTC
Assign to myself.

Implementation idea:
  https://godbolt.org/g/ulh4V7

The idea is to avoid multiple-layer hierarchy that libc++ uses, while remains easy to extend to fine-grained triviality (one for each of big five). It requires more or less a rewrite of the storage part. It's not hard for now, because we don't have ABI compatibility constraints.
Comment 2 Tim Shen 2017-05-30 06:39:59 UTC
(In reply to Tim Shen from comment #1)
> Assign to myself.
> 
> Implementation idea:
>   https://godbolt.org/g/ulh4V7

The experiment failed, therefore I posted a patch (see the list) to do the 4-layer inheritance (yikes) hierarchy.

The experiment idea was to use CRTP (mixin) bases to dispatch on the triviality, e.g.

  template<typename... _Types>
  struct variant :
      _Variant_storage<_Types...>,
      _Copy_ctor_mixin<(is_trivially_copy_constructible<_Types> && ...),
      ...

Such that:
1) _Variant_storage holds all memory, and is guaranteed to be trivial on all SMFs.
2) Mixins out-live _Variant_storage, and apply rich SMF semantics, if non-trivial; else forward the triviality.

The triviality of variant is decided by the triviality of both the storage. However, the storage is always trivial, so the triviality is controlled by mixins, which is intended.

The problem is that once storage is trivial, it has trivial behaviors, e.g. memcpy on copy ctor and copy assign. It's hard to get rid of the memcpys without getting rid of the storage triviality.
Comment 3 Ambroz Bizjak 2017-05-30 20:30:25 UTC
Hey,
Does this idea help: https://godbolt.org/g/3Iqp2c ?
The storage is an array of "special" byte classes which have empty constructors/assign when those would be implemented by one of the mixins.
Comment 4 Ambroz Bizjak 2017-05-30 20:38:52 UTC
Oh wait sorry, that doesn't solve it (yet), the variant_storage_byte would still have a default copy constructor that copies the byte member.
Comment 5 Tim Shen 2017-05-30 21:56:34 UTC
(In reply to Ambroz Bizjak from comment #4)
> Oh wait sorry, that doesn't solve it (yet), the variant_storage_byte would
> still have a default copy constructor that copies the byte member.

Yeah, your solution seems to move the triviality forwarding down to the individual elements, not in the variant level. However, I'm not sure if it helps on the forwarding itself. You may still need the 4-layer inheritance on each byte.
Comment 6 Tim Shen 2017-06-27 18:19:35 UTC
Author: timshen
Date: Tue Jun 27 18:19:03 2017
New Revision: 249706

URL: https://gcc.gnu.org/viewcvs?rev=249706&root=gcc&view=rev
Log:
	PR libstdc++/80187
	* include/std/variant (variant::variant, variant::~variant,
	variant::operator=): Implement triviality forwarding for four
	special member functions.
	* testsuite/20_util/variant/compile.cc: Tests.

Modified:
    trunk/libstdc++-v3/ChangeLog
    trunk/libstdc++-v3/include/std/variant
    trunk/libstdc++-v3/testsuite/20_util/variant/compile.cc