Skip to content
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

[SR-10947] Dynamic Method Replacement does not work if there are replacements in different files #53338

Closed
0xpablo opened this issue Jun 17, 2019 · 3 comments
Assignees
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. compiler The Swift compiler in itself crash Bug: A crash, i.e., an abnormal termination of software

Comments

@0xpablo
Copy link

0xpablo commented Jun 17, 2019

Previous ID SR-10947
Radar None
Original Reporter @0xpablo
Type Bug
Status Resolved
Resolution Done

Attachment: Download

Environment

This is happening at least on:

Swift 5.1 (Xcode 10.11 b1)
macOS 10.14.5 and 10.15 b1

Additional Detail from JIRA
Votes 0
Component/s Compiler
Labels Bug, CompilerCrash
Assignee @aschwaighofer
Priority Medium

md5: 718db125b745e51289b765edac1e3086

Issue Description:

The Dynamic Method Replacement feature only works if the replacements are in the same file.
Even if there are two unrelated types with replacements in different files, the compiler seems to only pick up the first replacement.

Additionally, if Whole Module Optimization is enabled and there are replacements in different files, the compiler crashes.

STR:

  1. Create a new Command Line Tool (this happens in frameworks as well)
  2. Create a type A in A.swift with a method foo()
  3. Create a replacement of A.foo() inside A.swift
  4. Create a type B in B.swift with a method bar()
  5. Create a replacement of B.bar() inside B.swift
  6. In main.swift, call A().foo() and B().bar().
  7. Run the program and observe how only one replacement will be called.

(Additional steps to reproduce the crash)

8. Change the SWIFT_COMPILATION_MODE build setting to wholemodule
9. Compile the project

Compilation will fail with the following error:

<unknown>:0: error: fatal error encountered during compilation; please file a bug report with your project and the crash log
<unknown>:0: note: unsupported relocation with subtraction expression, symbol '_$s21DynamicReplacementBug1BC15replacement_baryyF' can not be undefined in a subtraction expression
Command CompileSwift failed with a nonzero exit code

I attach a very simple Xcode project that shows the issue. wmo is not enabled so you can see that A's replacement does not work (the original implementation gets called).

@0xpablo
Copy link
Author

0xpablo commented Jun 17, 2019

Hi aschwaighofer@apple.com (JIRA User), this is the issue a colleague of mine approached you for at WWDC. At that time we didn't know how to reproduce but we have been able to reduce the issue to a trivial example. We hope it's useful!

If you need us to dupe this in Radar or if we can do anything else to help let us know.

Thanks in advance! 🙂

@0xpablo
Copy link
Author

0xpablo commented Jun 17, 2019

Some additional info:

It looks like if I move the replacements together to a single file (but leave one of the class declarations in a different file, I also get a compiler crash and this time I got a stack dump):

1.  While type-checking extension of A (at /Users/user/Desktop/DynamicReplacementBug/DynamicReplacementBug/B.swift:21:1)
2.  While type-checking 'replacement_foo()' (at /Users/user/Desktop/DynamicReplacementBug/DynamicReplacementBug/B.swift:23:2)
3.  While evaluating request IsObjCRequest(DynamicReplacementBug.(file).A extension.replacement_foo()@/Users/user/Desktop/DynamicReplacementBug/DynamicReplacementBug/B.swift:23:7)
0  swift                    0x000000010905d8a3 PrintStackTraceSignalHandler(void*) + 51
1  swift                    0x000000010905d076 SignalHandler(int) + 358
2  libsystem_platform.dylib 0x00007fff594fdb5d _sigtramp + 29
3  libsystem_platform.dylib 0x0000000000000040 _sigtramp + 2796561664
4  swift                    0x0000000105825b91 shouldMarkAsObjC(swift::ValueDecl const*, bool) + 1249
5  swift                    0x00000001058935ed swift::SimpleRequest<swift::IsObjCRequest, (swift::CacheKind)2, bool, swift::ValueDecl*>::evaluateRequest(swift::IsObjCRequest const&, swift::Evaluator&) + 109
6  swift                    0x0000000105b9a78b swift::IsObjCRequest::OutputType swift::evaluateOrDefault<swift::IsObjCRequest>(swift::Evaluator&, swift::IsObjCRequest, swift::IsObjCRequest::OutputType) + 1179
7  swift                    0x00000001057c1de1 swift::TypeChecker::checkDynamicReplacementAttribute(swift::ValueDecl*) + 401
8  swift                    0x0000000105810f64 (anonymous namespace)::DeclChecker::visitFuncDecl(swift::FuncDecl*) + 548
9  swift                    0x00000001058048b4 (anonymous namespace)::DeclChecker::visit(swift::Decl*) + 276
10 swift                    0x0000000105807b3b (anonymous namespace)::DeclChecker::visit(swift::Decl*) + 13211
11 swift                    0x00000001058c30b6 swift::performTypeChecking(swift::SourceFile&, swift::TopLevelContext&, swift::OptionSet<swift::TypeCheckingFlags, unsigned int>, unsigned int, unsigned int, unsigned int, unsigned int, unsigned int) + 1702
12 swift                    0x0000000104fcd5b5 swift::CompilerInstance::performSemaUpTo(swift::SourceFile::ASTStage_t) + 6021
13 swift                    0x0000000104cc903e performCompile(swift::CompilerInstance&, swift::CompilerInvocation&, llvm::ArrayRef<char const*>, int&, swift::FrontendObserver*, swift::UnifiedStatsReporter*) + 798
14 swift                    0x0000000104cc5424 swift::performFrontend(llvm::ArrayRef<char const*>, char const*, void*, swift::FrontendObserver*) + 6868
15 swift                    0x0000000104c546b3 main + 1219
16 libdyld.dylib            0x00007fff593123d5 start + 1
17 libdyld.dylib            0x0000000000000049 start + 2798574709
 

The content of the files are:

A.swift:

public class A {
    dynamic func foo() {
        print("Original (A)")
    }
} 

B.swift:

public class B {
    dynamic func bar() {
        print("Original (B)")
    }
}


extension B {
    @_dynamicReplacement(for: bar)
    func replacement_bar() {
        print("Replacement (B)")
    }
}


extension A {
    @_dynamicReplacement(for: foo)
    func replacement_foo() {
        print("Replacement (A)")
    }
} 

@aschwaighofer
Copy link
Member

The first issue should get fixed by: #25616

When there are multiple object files with replacements the replacement sections from the individual object files get concatenated. We were only considering the first entry in that concatenation.

@swift-ci swift-ci transferred this issue from apple/swift-issues Apr 25, 2022
@AnthonyLatsis AnthonyLatsis added the crash Bug: A crash, i.e., an abnormal termination of software label Dec 12, 2022
This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. compiler The Swift compiler in itself crash Bug: A crash, i.e., an abnormal termination of software
Projects
None yet
Development

No branches or pull requests

3 participants