swiftlang / llvm-project

The LLVM Project is a collection of modular and reusable compiler and toolchain technologies. This fork is used to manage Swift’s stable releases of Clang as well as support the Swift project.
https://llvm.org
Other
1.12k stars 331 forks source link

lldb: Unable to bind generic parameters in context with weakly captured self #9194

Open mikolasstuchlik opened 2 months ago

mikolasstuchlik commented 2 months ago

This issue was originally observed in codebase with async sequence, but I strongly suspect this was the underlying issue.

LLDB seems to be unable to bind generic variables, when suspended in weakly captured context. See example code below.

LLDB version

This example was tested using Xcode toolchain in both Xcode and VSCode:

(lldb) version
lldb-1500.0.404.7
Apple Swift version 5.10 (swiftlang-5.10.0.13 clang-1500.3.9.4)

Behavior

For context, where self was captured either strongly or as unowned, following expression will be evaluated successfully, referencing example 3, 4 and 5:

(lldb) e -- T.self
(Int.Type) $R0 = Int

However, example 1 results in following output:

(lldb) e -- T.self
error: <EXPR>:2:43: error: cannot find type '$__lldb_context' in scope
extension Swift.Optional where Wrapped == $__lldb_context {
                                          ^~~~~~~~~~~~~~~

error: <EXPR>:19:27: error: instance method '$__lldb_user_expr_0' requires the types 'Bridge<Int>' and '<<error type>>' be equivalent
    $__lldb_injected_self.$__lldb_user_expr_0(
                          ^

<EXPR>:4:17: note: where 'Wrapped' = 'Bridge<Int>'
  mutating func $__lldb_user_expr_0(_ $__lldb_arg : UnsafeMutablePointer<Any>) {
                ^

Meanwhile, example 2 produces slightly different output:

(lldb) e -- T.self
error: Expression evaluation failed. Retrying without binding generic parameters
error: Could not evaluate the expression without binding generic types.

Notice, that for examples 1 and 2 any expression calls (even interpretable expressions like e -- 1 + 1) end in errors described above.

Example code

import Dispatch

final class Bridge<T> {
    var current: T

    init(c: T) { current = c }

    func foo() {
        // does not work
        DispatchQueue.global(qos: .userInitiated).async { [weak self] in
            print(self?.current) // example 1
        }
        Task { [weak self] in
            print(self?.current) // example 2
        }

        // works
        DispatchQueue.global(qos: .userInitiated).async {
            print(self.current) // example 3
        }
        Task {
            print(self.current) // example 4
        }

        // also works
        Task { [unowned self] in
            print(self.current) // example 5
        }
    } 
}
let a = Bridge(c: 1)
a.foo()

readLine()