http://llvm.org/bugs/show_bug.cgi?id=11462

I would like to draw clang developer's attention to:

http://llvm.org/bugs/show_bug.cgi?id=11462

This is a feature request for a new intrinsic __is_final(T).

This intrinsic has become necessary due to the recent C++ language addition "final". The problem is that many libraries (such as libc++) will derive from types they know nothing about except that the type is a class and is eligible for the empty base optimization.

libc++ uses this technique in no less than 5 places to optimize away space for allocators, deleters, predicates, etc. It makes all containers smaller, and is what enables sizeof(unique_ptr) == sizeof(void*). I.e. it is a critically important optimization.

Now that our customers can write:

class final A {};

we find ourselves in the position that libc++ will attempt to derive from A in a surprising number of places (because it is empty), only to result in a compile time error because A is also final. Thus libc++ needs a way to check classes for final.

Ganesh has proposed a patch to clang to implement __is_final, complete with a __has_feature check. libc++ really needs this so that our clients can write:

   std::tuple<A> t;

Comments? Questions?

Thanks,
Howard

It seems reasonable, and I can't think of any other usable solution.

-Eli

FWIW, I think a better solution for the library issue would be for
clang to provide an attribute saying "ignore the rule in
[intro.object]p6, and allow this member to have 0 size." Doing this
would allow possibly-empty types to be used directly as members,
rather than obfuscating the code by forcing them into base classes or
tuples. Perhaps spell the attribute [[may_be_empty]] or
[[may_have_same_address_as_other_members]].

As c++std-lib-31670 indicates, there are non-final classes that can't
be derived from:

  struct B // cannot be a base class
  {
      virtual ~B() final {}
  };

So __is_final is probably not the right trait even for use cases
beyond the empty-base-class optimization.

FWIW, I think a better solution for the library issue would be for
clang to provide an attribute saying "ignore the rule in
[intro.object]p6, and allow this member to have 0 size." Doing this
would allow possibly-empty types to be used directly as members,
rather than obfuscating the code by forcing them into base classes or
tuples. Perhaps spell the attribute [[may_be_empty]] or
[[may_have_same_address_as_other_members]].

I wish C++ would allow such things. However my ambition for today is to just get the library to work with final, as opposed to change the C++ language.

As c++std-lib-31670 indicates, there are non-final classes that can't
be derived from:

struct B // cannot be a base class
{
     virtual ~B() final {}
};

So __is_final is probably not the right trait even for use cases
beyond the empty-base-class optimization.

I'll be checking both is_empty and __is_final. B will fail is_empty, and thus I won't try to derive from it.

Howard

FWIW, I think a better solution for the library issue would be for
clang to provide an attribute saying "ignore the rule in
[intro.object]p6, and allow this member to have 0 size." Doing this
would allow possibly-empty types to be used directly as members,
rather than obfuscating the code by forcing them into base classes or
tuples. Perhaps spell the attribute [[may_be_empty]] or
[[may_have_same_address_as_other_members]].

If we did that, the user could observe two final objects with the same
address... which shouldn't be possible, I think.

-Eli

Is that more important for final objects than other kinds of objects?
Arguably, even the attribute shouldn't be enough to put two objects of
the same type at the same address, which is the restriction used for
base classes.

FWIW, I think a better solution for the library issue would be for
clang to provide an attribute saying "ignore the rule in [intro.object]p6,
and allow this member to have 0 size." Doing this would allow
possibly-empty types to be used directly as members, rather than
obfuscating the code by forcing them into base classes or tuples. Perhaps
spell the attribute [[may_be_empty]] or
[[may_have_same_address_as_other_members]].

If we did that, the user could observe two final objects with the same
address... which shouldn't be possible, I think.

It is possible.

struct A final { int n; };
struct B final { A a; };

Is that more important for final objects than other kinds of objects?
Arguably, even the attribute shouldn't be enough to put two objects of
the same type at the same address, which is the restriction used for base
classes.

Technically, doing this appears to violate [intro.object]p6. However, the
footnote to that paragraph allows the implementation (and hence the standard
library) to break the rule in some cases: "Under the “as-if” rule an
implementation is allowed to store two objects at the same machine address or
not store an object at all if the program cannot observe the difference." If
the program can't legitimately form a pointer to the other member of the
container with which the empty allocator would share an address, the
optimization seems to be conforming.

Richard

FWIW, I've committed Ganesh's patch as Clang r145775. Thanks, Ganesh!

  - Doug