Skip to content

Make Objective-C interoperability configurable in the runtime #40843

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 19, 2022

Conversation

adrian-prantl
Copy link
Contributor

In order to be able to debug, for example, a Linux process from a macOS host, we need to be able to initialize a ReflectionContext without Objective-C interoperability. This patch turns ObjCInterOp into another template trait, so it's possible to instantiate a non-ObjC MetadataReader on a system built with ObjC-interop (but not vice versa).

Posting this early to get feedback on my amateurish C++ templating.

rdar://87179578

@adrian-prantl
Copy link
Contributor Author

Corresponding LLDB patch is in swiftlang/llvm-project#3789

@adrian-prantl
Copy link
Contributor Author

This patch changes the class hierarchy to


                          TargetMetadata<Runtime>
                                    | 
                          TargetHeapMetadata<Runtime>
                                    |
                          TargetAnyClassMetadata<Runtime>
                                   /                \
                                  /               TargetAnyClassMetadataObjCInterop<Runtime>
                                 /                              \
TargetClassMetadata<Runtime, TargetAnyClassMetadata<Runtime>>    \
                                                                  \
                    TargetClassMetadata<Runtime, TargetAnyClassMetadataObjCInterop<Runtime>>

TargetAnyClassMetadataObjCInterop inherits from TargetAnyClassMetadata because most of the implementation is the same. This choice makes TargetClassMetadata a bit tricky. In this patch I went with templating the parent class.

@@ -540,7 +580,7 @@ struct TargetMetadata {

#if SWIFT_OBJC_INTEROP
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is great! As a followup, can we eliminate some or all of the #if SWIFT_OBJC_INTEROP conditional compilations in these headers? (It would make sense for runtime implementation to still use #if SWIFT_OBJC_INTEROP, to implement operations that fundamentally rely on the ObjC runtime, but it'd be cool to make as much functionality as possible dependent only on the template.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One thing that wasn't clear to me is how much of the code depends on definitions in objc system headers, but I'm happy to eliminate as much of the conditionals as possible.

In order to be able to debug, for example, a Linux process from a macOS host, we
need to be able to initialize a ReflectionContext without Objective-C
interoperability. This patch turns ObjCInterOp into another template trait, so
it's possible to instantiate a non-ObjC MetadataReader on a system built with
ObjC-interop (but not vice versa).

This patch changes the class hierarchy to

                          TargetMetadata<Runtime>
                                    |
                          TargetHeapMetadata<Runtime>
                                    |
                          TargetAnyClassMetadata<Runtime>
                                   /                \
                                  /               TargetAnyClassMetadataObjCInterop<Runtime>
                                 /                              \
TargetClassMetadata<Runtime, TargetAnyClassMetadata<Runtime>>    \
                                                                  \
                    TargetClassMetadata<Runtime, TargetAnyClassMetadataObjCInterop<Runtime>>

TargetAnyClassMetadataObjCInterop inherits from TargetAnyClassMetadata because
most of the implementation is the same. This choice makes TargetClassMetadata a
bit tricky. In this patch I went with templating the parent class.

rdar://87179578
@adrian-prantl adrian-prantl changed the title WIP: Make Objective-C interoperability configurable in the runtime Make Objective-C interoperability configurable in the runtime Jan 14, 2022
@adrian-prantl
Copy link
Contributor Author

Removing the [WIP] tag. This patch is ready for review now and passes Swift and LLDB tests on the Mac.

@adrian-prantl
Copy link
Contributor Author

@swift-ci test

@adrian-prantl
Copy link
Contributor Author

test with swiftlang/llvm-project#3789
@swift-ci test

@adrian-prantl adrian-prantl requested a review from mikeash January 14, 2022 20:53
@swift-ci
Copy link
Contributor

Build failed
Swift Test OS X Platform
Git Sha - e84c6cc

@swift-ci
Copy link
Contributor

Build failed
Swift Test Linux Platform
Git Sha - e84c6cc

@adrian-prantl
Copy link
Contributor Author

Swift-CI test failures are unrelated.

@@ -120,14 +150,44 @@ struct ExternalPointer {
StoredPointer PointerValue;
};

/// An external process's runtime target, which may be a different architecture.
template <typename Runtime> struct WithObjCInterop {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems a little awkward, especially with the nesting at the point of use. Would it be reasonable to make ObjC interop another parameter of RuntimeTarget instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's possible to sink this into a

template <unsigned PointerSize, bool WithObjCInterOp> struct RuntimeTarget

the downside is that since we need to conditionally define TargetAnyClassMetadata, we'd end up with 4 template specializations:

template <> struct RuntimeTarget<4, false>
template <> struct RuntimeTarget<4, true>
template <> struct RuntimeTarget<8, false>
template <> struct RuntimeTarget<8, true>

which take roughly the same amount of lines as the current definition with its reexporting using declarations. I like the separate nested template a little more since it doesn't duplicate any definitions and it would scale better if we have to add more dimensions, but I don't feel very strongly about it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking it could be a partial specialization like template <bool WithObjCInterOp> struct RuntimeTarget<4>, then a little more magic to resolve TargetAnyClassMetadata based on the remaining template parameter. Not entirely sure if that's possible, though. If not then I agree with you that the separate nested template is better.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not a very experienced template metaprogrammer, but in my experiments it looked like template specialization and using separate nested templates (like in the patch) are the only ways to make a using type declaration configurable. When I discovered std::conditional I thought looked like a good fit, but I could not actually get it to work without instantiation failures deep in the ClassMetadata hierarchy.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

std::conditional ought to work, but this stuff is really difficult to debug if you don't get it right.

Given all of that, let's go ahead and keep it as-is, and then if something ever comes up where we really want it to be in RuntimeTarget we can try again then.

@adrian-prantl
Copy link
Contributor Author

test with swiftlang/llvm-project#3789
@swift-ci test

@swift-ci
Copy link
Contributor

Build failed
Swift Test Linux Platform
Git Sha - e84c6cc

@swift-ci
Copy link
Contributor

Build failed
Swift Test OS X Platform
Git Sha - e84c6cc

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants