swiftlang / swift

The Swift Programming Language
https://swift.org
Apache License 2.0
67.32k stars 10.34k forks source link

_dynamicReplacement could not call origin async method #62214

Open Whirlwind opened 1 year ago

Whirlwind commented 1 year ago

Xcode 14.1, swift-driver version: 1.62.15 Apple Swift version 5.7.1 (swiftlang-5.7.1.135.3 clang-1400.0.29.51)


dynamic
public func runCommander() async {
    printf("1")
}

@_dynamicReplacement(for: runCommander())
public func core_runCommander() async {
    await runCommander()
}

I get a deadloop:

image

I try other methods, same wrong.

LucianoPAlmeida commented 1 year ago

This looks like correct behavior, by telling that core_runCommander is a dynamic replacement for runCommander calling runCommander means calling core_runCommander which make this function a recursive call.

Whirlwind commented 1 year ago

This looks like correct behavior, by telling that core_runCommander is a dynamic replacement for runCommander calling runCommander means calling core_runCommander which make this function a recursive call.

I don't think so. If I remove the async, call the runCommander will call the origin method:

dynamic
public func runCommander() {
    printf("1")
}

@_dynamicReplacement(for: runCommander())
public func core_runCommander() {
    runCommander()
}

It will print the 1.

If you are right, what should I do when calling the origin method?

I found a test: https://github.com/apple/swift/blob/7123d2614b5f222d03b3762cb110d27a9dd98e24/test/Interpreter/Inputs/dynamic_replacement_chaining_B.swift It will call the origin method by origin name.

LucianoPAlmeida commented 1 year ago

Ok, looks like I misunderstood the feature behavior

LucianoPAlmeida commented 1 year ago

Yeah this looks indeed a bug

Whirlwind commented 1 year ago

Any process?

LucianoPAlmeida commented 1 year ago

cc @aschwaighofer

Whirlwind commented 1 year ago

Any process?

pbartolome commented 10 months ago

Still reproducible with swift 5.9 swift-driver version: 1.87.1 Apple Swift version 5.9 (swiftlang-5.9.0.128.108 clang-1500.0.40.1)

The following sample code:

dynamic func original() {
    print("original")
}

@_dynamicReplacement(for: original)
func replacement() {
    print("replacement")
    original()
}

original()

correctly prints:

replacement
original

But adding async to the original method forces the use of await in the replacement in order to call it, this produces a call to the replacement instead of the original implementation, ending with an infinte loop

dynamic func original() async {
    print("original")
}

@_dynamicReplacement(for: original)
func replacement() async {
    print("replacement")
    await original()
}

await original()
replacement
replacement
replacement
replacement
replacement
replacement
replacement
replacement
replacement
replacement
...
maximkrouk commented 9 months ago

Also reproducible for non-async objc methods: https://github.com/apple/swift/issues/53916

Whirlwind commented 1 month ago

Any process?

Whirlwind commented 1 month ago

Still reproducible with Apple Swift version 6.0 (swiftlang-6.0.0.7.6 clang-1600.0.24.1) Target: arm64-apple-macosx15.0