Quuxplusone / LLVMBugzillaTest

0 stars 0 forks source link

Strong enums not shareable between C++ / ObjC++ #22422

Open Quuxplusone opened 9 years ago

Quuxplusone commented 9 years ago
Bugzilla Link PR22423
Status NEW
Importance P normal
Reported by Peter Griess (pgriess@gmail.com)
Reported on 2015-01-30 21:57:57 -0800
Last modified on 2015-02-19 12:15:02 -0800
Version unspecified
Hardware Other All
CC eric@efcs.ca, llvm-bugs@lists.llvm.org, mclow.lists@gmail.com, rnk@google.com
Fixed by commit(s)
Attachments
Blocks
Blocked by
See also
In __config, we have the following

#if !(__has_feature(cxx_strong_enums))
#define _LIBCPP_HAS_NO_STRONG_ENUMS
#endif

...

#if __has_feature(objc_arc_weak)
#define _LIBCPP_HAS_OBJC_ARC_WEAK
#define _LIBCPP_HAS_NO_STRONG_ENUMS
#endif

As a result, C++ code will have strong enums enabled, while ObjC++ code will
not. This causes problems with classes like std::cv_status which are used as
return values, as they become composite types. The ARM calling conventions
(see:
http://infocenter.arm.com/help/topic/com.arm.doc.ihi0042e/IHI0042E_aapcs.pdf
section 5.4) mandates different calling conventions for such return types. As a
result, if caller and callee have different values for
_LIBCPP_HAS_NO_STRONG_ENUMS, registers are allocated differently (e.g. r0 is
used to specify the address of the return value in one, where as r0 is the
value of 'this' in the other).

Practically, this occurs when instantiating templates in different compilation
units, as the definitions are different, but are coalesced in linking due to
ODR. Linking the two files into a single binary will result in a crash:

  Foo.cpp
    void foo() {
      std::mutex m;
      std::unique_lock<std::mutex> lk(m);
      std::condition_variable cv;
      std::chrono::seconds dur(3);
      cv.wait_for(lk, dur);
    }

  Bar.mm:
    void bar() {
      std::mutex m;
      std::unique_lock<std::mutex> lk(m);
      std::condition_variable cv;
      std::chrono::seconds dur(3);
      cv.wait_for(lk, dur);
    }

  main.c:
    foo();
    bar();

Application developers can work around this by building mixed C++/ObjC++ apps
with _LIBCPP_HAS_NO_STRONG_ENUMS=1.
Quuxplusone commented 9 years ago

Why are strong enums composite types instead of being passed as their underlying integer type would be passed? Low-level ABI documents like the one you linked to typically specify the C ABI for the platform, and then we layer the Itanium C++ ABI rules on top of it. One obvious implementation of C++11 strong enums is to treat them like their underlying integral type.

Quuxplusone commented 9 years ago
http://llvm.org/bugs/show_bug.cgi?id=11428 provides some context for this
decision. It mentions portability as motivating this decision, but that seems
to suggest that libcxx aims to support compilers that do not support C++11
syntax, as otherwise the enum class syntax could just be used directly.

In addition, I'm not clear on why _LIBCPP_HAS_NO_STRONG_ENUMS is set if ARC
weak references are supported. From __config:

 #if __has_feature(objc_arc_weak)
 #define _LIBCPP_HAS_OBJC_ARC_WEAK
 #define _LIBCPP_HAS_NO_STRONG_ENUMS
 #endif

This makes ObjC++ effectively un-usable on ARM with libc++.

As you may be aware, C++ development (including ObjC++ bindings) is gaining
importance on mobile platforms as a portable alternative to re-implementing the
same code in Java and ObjC for Android and iOS. Fixing this would solve a nasty
gotcha in this use-case.
Quuxplusone commented 9 years ago
Marshal or Eric, is there any reason for this define? It looks like an accident
of patch merging. There is nothing in the commit message or bug to suggest that
this was intentional.

By the way, I still fail to understand why strong enums are being classified as
ARM ABI composite types. That doesn't make sense.

How would I go about testing the following patch?

diff --git a/include/__config b/include/__config
index b109ad7..03ed25d 100644
--- a/include/__config
+++ b/include/__config
@@ -315,7 +315,6 @@ typedef __char32_t char32_t;

 #if __has_feature(objc_arc_weak)
 #define _LIBCPP_HAS_OBJC_ARC_WEAK
-#define _LIBCPP_HAS_NO_STRONG_ENUMS
 #endif

 #if !(__has_feature(cxx_constexpr))