Weak pointer support on 32-bit platforms

Hi All,

I'm trying to port Apple's libobjc to Windows using clang. I'm hoping
to have Objective C with ARC and weak pointers support on both 32 and
64-bit Windows.

Most part of the port is done, I have ARC working for 32-bit Windows
binaries. I'm now facing two problems: exceptions, which is a separate
story (you probably know what this means :wink: and weak pointers.

Now, I can't find a combination of compiler flags that would enable
weak pointer support for the 32-bit Windows target. Clang refuses to
do weakptrs on OSX in 32-bit mode either, but let's say that's not
required today. Bitness itself is not an issue, because after all
weakptrs exist on 32-bit iOS systems and libobjc itself is fine with
32 bits.

So what happens when you use a weak pointer, say in a program like this:

    #include <Foundation/NSObject.h>

    NSObject __weak* weakObj;

    int main()
    {
        weakObj = nil;
        return 0;
    }

is as follows: when the target is OSX with -arch i386 the compiler
says "the current deployment target does not support automated __weak
references" pointing at the __weak keyword. However, when the target
is Windows (i686-pc-win32 or x86_64-pc-win32) the compiler silently
ignores the __weak keyword and emits a objc_storeStrong call instead
of objc_storeWeak!

Let alone that potentially this may result in colossal memory leaks in
apps. On top of that, why wouldn't clang either give an error or emit
correct calls?

Here is my command that compiles the program above but produces
incorrect runtime calls for the program above (on OSX):

clang++ -target i686-pc-win32 \
        -isystem /usr/local/i686-w64-mingw32/include
-I<path-to-Foundation-headers> \
        -DTARGET_CPU_X86 -fobjc-arc -fobjc-runtime=macosx -S wtest.mm

This setup is very fragile: you change anything in these flags and
something goes wrong. I tried both the "canonical" clang and Apple's,
same effect.

I'm not familiar with clang internals, but I'd appreciate if someone
pointed me in the right direction as to what can be done. Even
patching the compiler would be acceptable for me, because in any case
it will take time until this port of libobjc matures and becomes part
of a bigger effort of emulating Cocoa on Windows.

And finally, the GNU compiler is not an option for me because it lags
in some crucial aspects of the ObjC language. I really, really want to
get clang to compile libobjc and then the entire Foundation/Cocoa
emulation layers. Ideally, clang should be able cross-compile this
stuff on OSX.

(The reason I'm writing to cfe-dev is that I thought a runtime lib is
kind of closer to the compiler itself.)

Thanks!

Hi,

And finally, the GNU compiler is not an option for me because it lags
in some crucial aspects of the ObjC language. I really, really want to
get clang to compile libobjc and then the entire Foundation/Cocoa
emulation layers. Ideally, clang should be able cross-compile this
stuff on OSX.

You seem to be conflating many parts of the stack. For all of the stuff that you want, you need:

- A compiler
- An Objective-C runtime
- A Foundation / AppKit implementation

Clang is the compiler, and supports several Objective-C runtimes. The only supported configurations on Windows, however, are the GNU family of runtimes (the Apple runtime works on Windows, but requires code that Apple has not open sourced to actually be useful). The GCC and GNUstep runtimes both work on Windows and the GNUstep runtime has supported ARC for several years and is used by a number of commercial products on Windows, Android, and so on.

Once you have the compiler and runtime, you need a Foundation implementation that supports the runtime. Again, GNUstep provides this for the GCC and GNUstep runtimes.

I'm not sure what you mean by 'the entire Foundation/Cocoa emulation layers'. Foundation and Cocoa are just libraries. You can not use Apple's implementation on Windows[1], because Apple doesn't license their library implementations for third parties.

David

[1] Unless you can find a copy of Yellow Box for Windows, in which case you can but you'll have a *really* old version.

Hi David,

(I should have made it clear that I do understand there's GNUstep
(whose GUI is so horrible and 1990-ish that makes me want to switch to
an entirely different industry altogether :slight_smile: and I do understand I
can't use Apple's code unless it's covered by a more or less
permissive license. Libobjc is APSL so we're fine with that. As for
the rest, Foundation & Cocoa, there is at least one open source
implementation named Cocotron that's pretty good but is a bit behind
in terms of the runtime and language features. One possibility, for
example, would be to adapt Cocotron to the latest libobjc, that's all.
But let's forget about higher layers for now.)

My point is, theoretically there's nothing that stops clang from
making Apple's libobjc compile and work on Windows, less exceptions.
We can wait until clang brings exceptions to Windows, that's fine,
because exceptions are kind of not critical to F/Cocoa.

But weak pointers, I think what I found is a bug. You declare a __weak
pointer and the compiler treats it as __strong for a particular
target. The consequences of this bug for an app can be catastrophic.

Let's say my setup is a bit unusual, but if there is one little thing
that possibly needs a little fix, why not? I was hoping to hear some
ideas as to what I can do: file a bug? suggest a patch? use some
compiler flag that I didn't know of?

Filing a bug with a reduced test-case would be helpful to get this fixed.

Apologies, my bad, there is no bug. Turns out in certain circumstances
MinGW can redefine __attribute__ to do nothing, and as a result __weak
becomes either a simple "unsafe" ptr or for certain setups it may
become __strong.

In any case, I decided to go the route of patching clang to accept a
new type of -fobjc-runtime. For the time being the patch will be
internal of course.

Apologies, my bad, there is no bug. Turns out in certain circumstances
MinGW can redefine __attribute__ to do nothing, and as a result __weak
becomes either a simple "unsafe" ptr or for certain setups it may
become __strong.

Hi Hovik,

If that's the case there's still a bug in clang:

The documentation describes __autoreleasing, __strong and __weak as attribute spellings but the implementation provides them as preprocessor macro definitions.

I haven't examined why this is the case but one of the two could do with fixing (probably the implementation given that this is causing trouble on a supported platform). Aaron, any idea what's happened here?

Alp.

Hi Alp,

The documentation describes __autoreleasing, __strong and __weak as
attribute spellings but the implementation provides them as preprocessor
macro definitions.

Actually no, the documentation says: "The names of the ownership
qualifiers are reserved for the implementation. A program may not
assume that they are or are not implemented with macros, or what those
macros expand to."

http://clang.llvm.org/docs/AutomaticReferenceCounting.html#spelling

This may lead to unpleasant surprises though, like with MinGW. More
specifically, when clang is in -fms-compatibility mode it doesn't
define __GNUC__ and that in turn makes MinGW think __attribute__ and
some other things should be masked.

So the source of confusion here might be that __attribute__ is a "GNU
thing" (correct?), at the same time __weak is not, but relies on it.

__weak, __strong and __autoreleasing are not keywords, they are
preprocessor macros that result in an objc_gc attribute, which only
works with GNU-style attributes. There are some almost-keyword
versions (strong, weak, autoreleasing and several others) which are
contextual-ish keywords in ParseObjC, but they only relate to ObjC
properties.

If MinGW fiddles with __attribute__ in -fms-compatibility mode, that
would be problematic. But I'm not certain whether that would be
classified as a documentation bug, compiler bug or a MinGW bug (or
some combination thereof).

~Aaron

Hi Alp,

>
> The documentation describes __autoreleasing, __strong and __weak as
> attribute spellings but the implementation provides them as preprocessor
> macro definitions.
>

Actually no, the documentation says: "The names of the ownership
qualifiers are reserved for the implementation. A program may not
assume that they are or are not implemented with macros, or what those
macros expand to."

http://clang.llvm.org/docs/AutomaticReferenceCounting.html#spelling

This may lead to unpleasant surprises though, like with MinGW. More
specifically, when clang is in -fms-compatibility mode it doesn't
define __GNUC__ and that in turn makes MinGW think __attribute__ and
some other things should be masked.

You can probably work around this with -D__GNUC__=4.

However, why do you need -fms-compatibility if you are using MinGW?

Clang puts itself into MS compatibility mode when the target is e.g.
i686-pc-win32. A better workaround for me was to use
-fno-ms-compatibility.

In any case, I don't think this

    Builder.defineMacro("__weak", "__attribute__((objc_ownership(weak)))");

was a good idea, as it makes some of the most critical attributes in
Objective C dependent on a compiler extension (__attribute__) which is
not always available at compile time!

It's plausible that we could implement them in terms of contextual
keywords which create the attributes, if there was sufficient desire.

~Aaron

>
> You can probably work around this with -D__GNUC__=4.
>
> However, why do you need -fms-compatibility if you are using MinGW?
>

Clang puts itself into MS compatibility mode when the target is e.g.
i686-pc-win32. A better workaround for me was to use
-fno-ms-compatibility.

So, you're using MinGW headers with i686-pc-win32 instead of
i686-pc-mingw32. The former target aims for MSVC compatibility and the
latter MinGW/GCC compatibility.

Using MinGW headers with -win32 is crossing the streams, and I don't
recommend doing it, unless you have a really good reason. You are probably
going to have problems linking C++ binaries or passing structs across ABI
boundaries.

In any case, I don't think this

    Builder.defineMacro("__weak", "__attribute__((objc_ownership(weak)))");

was a good idea, as it makes some of the most critical attributes in
Objective C dependent on a compiler extension (__attribute__) which is
not always available at compile time!

Well, Clang always provides __attribute__, so it's perfectly reasonable to
implement __weak this way. MinGW shouldn't be messing with the
implementors namespace this way.

That said, I'm in favor of making these contextual keywords. We have the
infrastructure to do it, so let's avoid the pre-processor cruftiness.

Certainly plausible -- the fun part is in figuring out what attribute
to create when. __weak is defined as:

* __attribute__((objc_gc(weak))) when the frontend is rewriting ObjC
* __attribute__((objc_ownership(weak))) when ObjCAutoRefCount is true
* __attribute__((objc_gc(weak))) when ObjCAutoRefCount is false and
building for Darwin
* not defined otherwise

Similar shenanigans for __strong, et al. So it's not that this isn't
possible to do, but it may have some surprises too.

~Aaron