ExistentialMetatypeType assertion failure on Linux

I’m seeing an assertion failure when I try to compile the following on Linux:

  typealias TypeMetadataAccessor = @convention(c) () -> AnyClass?

The assertion failing is:

    assert(getASTContext().LangOpts.EnableObjCInterop ||
           *repr != MetatypeRepresentation::ObjC);

in ExistentialMetatypeType::ExistentialMetatypeType(). Commenting it out and the code compiles and works.

Can someone that understands the compiler suggest the correct fix?

— Luke

It looks like you're trying to poke at private runtime metadata structures; please don't do that. What are you trying to do?

-Joe

···

On Dec 29, 2015, at 9:04 PM, Luke Howard via swift-dev <swift-dev@swift.org> wrote:

I’m seeing an assertion failure when I try to compile the following on Linux:

  typealias TypeMetadataAccessor = @convention(c) () -> AnyClass?

The assertion failing is:

   assert(getASTContext().LangOpts.EnableObjCInterop ||
          *repr != MetatypeRepresentation::ObjC);

in ExistentialMetatypeType::ExistentialMetatypeType(). Commenting it out and the code compiles and works.

Can someone that understands the compiler suggest the correct fix?

That is the asset in the compiler that is being hit when the code unsafeBitCasts to a c function.

typealias TypeMetadataAccessor = @convention(c) () -> AnyClass?
let accessor = unsafeBitCast(symbol, TypeMetadataAccessor.self) // <— this causes the compiler to assert there.

···

On Dec 30, 2015, at 10:03 AM, Joe Groff via swift-dev <swift-dev@swift.org> wrote:

On Dec 29, 2015, at 9:04 PM, Luke Howard via swift-dev <swift-dev@swift.org> wrote:

I’m seeing an assertion failure when I try to compile the following on Linux:

  typealias TypeMetadataAccessor = @convention(c) () -> AnyClass?

The assertion failing is:

  assert(getASTContext().LangOpts.EnableObjCInterop ||
         *repr != MetatypeRepresentation::ObjC);

in ExistentialMetatypeType::ExistentialMetatypeType(). Commenting it out and the code compiles and works.

Can someone that understands the compiler suggest the correct fix?

It looks like you're trying to poke at private runtime metadata structures; please don't do that. What are you trying to do?

-Joe
_______________________________________________
swift-dev mailing list
swift-dev@swift.org
https://lists.swift.org/mailman/listinfo/swift-dev

Under the ObjC runtime model, C and ObjC APIs represent class metatypes as pointers to their ObjC class representation, but that distinction doesn't matter in non-ObjC interop. This is easy to fix, but I'll reiterate that type metadata accessors are something you almost certainly have no business directly interacting with. What are you trying to do?

-Joe

···

On Dec 30, 2015, at 10:05 AM, Philippe Hausler <phausler@apple.com> wrote:

That is the asset in the compiler that is being hit when the code unsafeBitCasts to a c function.

typealias TypeMetadataAccessor = @convention(c) () -> AnyClass?
let accessor = unsafeBitCast(symbol, TypeMetadataAccessor.self) // <— this causes the compiler to assert there.

Per our exchange yesterday – implement NSStringFromClass() heuristics for NSKeyedArchiver

A better solution would be to provide a proposed API/patch to the runtime but I was just trying to get the NSCoding branch testable on Linux first – can’t tackle everything at once.

More:

  [SR-381] API to lookup a Type given a top-level class name · Issue #42998 · apple/swift · GitHub

— Luke

···

On 31 Dec 2015, at 5:03 AM, Joe Groff <jgroff@apple.com> wrote:

On Dec 29, 2015, at 9:04 PM, Luke Howard via swift-dev <swift-dev@swift.org> wrote:

I’m seeing an assertion failure when I try to compile the following on Linux:

  typealias TypeMetadataAccessor = @convention(c) () -> AnyClass?

The assertion failing is:

  assert(getASTContext().LangOpts.EnableObjCInterop ||
         *repr != MetatypeRepresentation::ObjC);

in ExistentialMetatypeType::ExistentialMetatypeType(). Commenting it out and the code compiles and works.

Can someone that understands the compiler suggest the correct fix?

It looks like you're trying to poke at private runtime metadata structures; please don't do that. What are you trying to do?

It looks like you're trying to poke at private runtime metadata structures; please don't do that. What are you trying to do?

Per our exchange yesterday – implement NSStringFromClass() heuristics for NSKeyedArchiver

Sorry I mean NSClassFromString(). I thought indirecting the lookup through the metadata accessor was at least a little bit less bad than accessing it directly. :)

— Luke

If you're experimenting privately that's OK, but I'd prefer to get the runtime functionality in place before committing anything. We already have our hands full trying to clean up and stabilize the standard library/runtime interfaces.

-Joe

···

On Dec 30, 2015, at 2:41 PM, Luke Howard <lukeh@padl.com> wrote:

It looks like you're trying to poke at private runtime metadata structures; please don't do that. What are you trying to do?

Per our exchange yesterday – implement NSStringFromClass() heuristics for NSKeyedArchiver

Sorry I mean NSClassFromString(). I thought indirecting the lookup through the metadata accessor was at least a little bit less bad than accessing it directly. :)

Yep and that is why I am holding off on merging this just yet until we can get something up and rolling from the stdlib to support this.
For now we could just limit these to the list of hand coded class to string conversions and omit the NSStringFromClass/NSClassFromString transform; that way we give the stdlib a bit of time to get back on their feet from the break and get this code in.

···

On Dec 30, 2015, at 3:21 PM, Joe Groff via swift-dev <swift-dev@swift.org> wrote:

On Dec 30, 2015, at 2:41 PM, Luke Howard <lukeh@padl.com> wrote:

It looks like you're trying to poke at private runtime metadata structures; please don't do that. What are you trying to do?

Per our exchange yesterday – implement NSStringFromClass() heuristics for NSKeyedArchiver

Sorry I mean NSClassFromString(). I thought indirecting the lookup through the metadata accessor was at least a little bit less bad than accessing it directly. :)

If you're experimenting privately that's OK, but I'd prefer to get the runtime functionality in place before committing anything. We already have our hands full trying to clean up and stabilize the standard library/runtime interfaces.

-Joe
_______________________________________________
swift-dev mailing list
swift-dev@swift.org
https://lists.swift.org/mailman/listinfo/swift-dev

Your call; I know perfect is the enemy of good enough, but I’m definitely interested in helping get something in stdlib once we decide on what to do re: mangling etc.

— Luke

···

On 31 Dec 2015, at 10:24 AM, Philippe Hausler <phausler@apple.com> wrote:

Yep and that is why I am holding off on merging this just yet until we can get something up and rolling from the stdlib to support this.
For now we could just limit these to the list of hand coded class to string conversions and omit the NSStringFromClass/NSClassFromString transform; that way we give the stdlib a bit of time to get back on their feet from the break and get this code in.

I really want to merge in the rest of the coders; so lets go ahead and omit the mangling stuff and leave it just as the Foundation classes . That will be a huge win in my book, it gives us something that is testable and functional enough to do some pretty impressive demonstrations of archiving.

···

On Dec 30, 2015, at 3:55 PM, Luke Howard <lukeh@padl.com> wrote:

On 31 Dec 2015, at 10:24 AM, Philippe Hausler <phausler@apple.com> wrote:

Yep and that is why I am holding off on merging this just yet until we can get something up and rolling from the stdlib to support this.
For now we could just limit these to the list of hand coded class to string conversions and omit the NSStringFromClass/NSClassFromString transform; that way we give the stdlib a bit of time to get back on their feet from the break and get this code in.

Your call; I know perfect is the enemy of good enough, but I’m definitely interested in helping get something in stdlib once we decide on what to do re: mangling etc.

— Luke

I really want to merge in the rest of the coders; so lets go ahead and omit the mangling stuff and leave it just as the Foundation classes . That will be a huge win in my book, it gives us something that is testable and functional enough to do some pretty impressive demonstrations of archiving.

OK, the version I’ve pushed now removes the code that was poking into runtime structures but still uses dlopen()/the metadata accessor for NSClassFromString(). Tests verify on OS X and Linux (with compiler fix).

If you want to change this further to use a static mapping table, go for it, but I’d rather focus my time on getting the appropriate API into stdlib.

— Luke

As a compromise how about a:

* swift_getTypeByName() API that only takes demangled names
* NSClassFromString()/NSStringFromClass() remain internal and hard fail with nested classes/generics
* We decide what to do about nested class interop later (is changing the Darwin behaviour something that would even be on the table?)

— Luke

···

On 31 Dec 2015, at 11:25 AM, Luke Howard via swift-dev <swift-dev@swift.org> wrote:

I really want to merge in the rest of the coders; so lets go ahead and omit the mangling stuff and leave it just as the Foundation classes . That will be a huge win in my book, it gives us something that is testable and functional enough to do some pretty impressive demonstrations of archiving.

OK, the version I’ve pushed now removes the code that was poking into runtime structures but still uses dlopen()/the metadata accessor for NSClassFromString(). Tests verify on OS X and Linux (with compiler fix).

If you want to change this further to use a static mapping table, go for it, but I’d rather focus my time on getting the appropriate API into stdlib.

I think those are all pretty reasonable. The pipeline for changes is bi-directional; however it will have to get reviewed by the component owner and undergo our process to change API behavior internally. I am fairly certain that this is an edge case that hasn’t even been thought of and I would think that the rest of the Foundation team will be very apt to nail this down so we don’t have to worry about backwards compatibility problems that using the mangled name might incur in the future.

···

On Dec 30, 2015, at 6:57 PM, Luke Howard <lukeh@padl.com> wrote:

On 31 Dec 2015, at 11:25 AM, Luke Howard via swift-dev <swift-dev@swift.org> wrote:

I really want to merge in the rest of the coders; so lets go ahead and omit the mangling stuff and leave it just as the Foundation classes . That will be a huge win in my book, it gives us something that is testable and functional enough to do some pretty impressive demonstrations of archiving.

OK, the version I’ve pushed now removes the code that was poking into runtime structures but still uses dlopen()/the metadata accessor for NSClassFromString(). Tests verify on OS X and Linux (with compiler fix).

If you want to change this further to use a static mapping table, go for it, but I’d rather focus my time on getting the appropriate API into stdlib.

As a compromise how about a:

* swift_getTypeByName() API that only takes demangled names
* NSClassFromString()/NSStringFromClass() remain internal and hard fail with nested classes/generics
* We decide what to do about nested class interop later (is changing the Darwin behaviour something that would even be on the table?)

— Luke

Right, NB the behaviour is really in libobjc’s objc_lookUpClass/class_getName.

— Luke

···

On 31 Dec 2015, at 2:10 PM, Philippe Hausler <phausler@apple.com> wrote:

I think those are all pretty reasonable. The pipeline for changes is bi-directional; however it will have to get reviewed by the component owner and undergo our process to change API behavior internally. I am fairly certain that this is an edge case that hasn’t even been thought of and I would think that the rest of the Foundation team will be very apt to nail this down so we don’t have to worry about backwards compatibility problems that using the mangled name might incur in the future.

I think those are all pretty reasonable. The pipeline for changes is bi-directional; however it will have to get reviewed by the component owner and undergo our process to change API behavior internally. I am fairly certain that this is an edge case that hasn’t even been thought of and I would think that the rest of the Foundation team will be very apt to nail this down so we don’t have to worry about backwards compatibility problems that using the mangled name might incur in the future.

If we can get _typeByName() into stdlib (taking an unmangled qualified name), then we could do mangling for nested type names in Foundation and get Darwin archive compatibility that way. It still breaks if the name mangling algorithm changes, but that may be a problem on Darwin too.

— Luke