Skip to content

Support expression evaluation inside of actors. #2904

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
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 30 additions & 42 deletions lldb/source/Plugins/ExpressionParser/Swift/SwiftASTManipulator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ extension %s$__lldb_context {
%s
}
}
%s
@LLDBDebuggerFunction %s
func $__lldb_expr(_ $__lldb_arg : UnsafeMutablePointer<Any>) {
do {
$__lldb_injected_self.$__lldb_wrapped_expr_%u(
Expand Down Expand Up @@ -255,63 +255,51 @@ void SwiftASTManipulatorBase::DoInitialization() {
if (m_repl)
return;

static llvm::StringRef s_func_prefix_str("$__lldb_expr");
// First pass: find whether we're dealing with a wrapped function or not.

// First pass: find whether we're dealing with a wrapped function or not

class FuncAndExtensionFinder : public swift::ASTWalker {
public:
swift::FuncDecl *m_function_decl = nullptr; // This is the function in which
// the expression code is
// inserted.
// It is always marked with the DebuggerFunction attribute.
swift::ExtensionDecl *m_extension_decl =
nullptr; // This is an optional extension holding the function
swift::FuncDecl *m_wrapper_decl = nullptr; // This is an optional wrapper
// function that calls
// m_function_decl.
llvm::StringRef m_wrapper_func_prefix; // This is the prefix name for the
// wrapper function. One tricky bit
// is that in the case where there is no wrapper, the m_function_decl
// has this name. That's why we check first for the debugger attribute.

FuncAndExtensionFinder(llvm::StringRef &wrapped_func_prefix)
: m_wrapper_func_prefix(wrapped_func_prefix) {}
struct FuncAndExtensionFinder : public swift::ASTWalker {
/// This is the toplevel entry function for the expression. It may
/// call into \c ext_method_decl or hold the entire expression.
swift::FuncDecl *toplevel_decl = nullptr;
/// This is optional.
swift::FuncDecl *ext_method_decl = nullptr;
/// This is an optional extension holding the above function.
swift::ExtensionDecl *extension_decl = nullptr;

bool walkToDeclPre(swift::Decl *D) override {
auto *FD = llvm::dyn_cast<swift::FuncDecl>(D);
// Traverse into any non-function-decls.
if (!FD)
return true;

if (FD->getAttrs().hasAttribute<swift::LLDBDebuggerFunctionAttr>()) {
m_function_decl = FD;
if (!FD->getAttrs().hasAttribute<swift::LLDBDebuggerFunctionAttr>())
return false;

// Now walk back up the containing DeclContexts, and if we find an
// extension Decl, that's our extension:
for (swift::DeclContext *DC = m_function_decl->getDeclContext(); DC;
DC = DC->getParent()) {
if (auto *extension_decl = llvm::dyn_cast<swift::ExtensionDecl>(DC)) {
m_extension_decl = extension_decl;
break;
}
// Walk up the DeclContext chain, searching for an extension.
for (auto *DC = FD->getDeclContext(); DC; DC = DC->getParent()) {
if (auto *extension = llvm::dyn_cast<swift::ExtensionDecl>(DC)) {
extension_decl = extension;
ext_method_decl = FD;
return false;
}
} else if (FD->hasName() && FD->getBaseIdentifier().str().startswith(
m_wrapper_func_prefix)) {
m_wrapper_decl = FD;
}

// There's nothing buried in a function that we need to find in this
// search.
// Not in an extenstion,
toplevel_decl = FD;
return false;
}
};

FuncAndExtensionFinder func_finder(s_func_prefix_str);
FuncAndExtensionFinder func_finder;
m_source_file.walk(func_finder);

m_function_decl = func_finder.m_function_decl;
m_wrapper_decl = func_finder.m_wrapper_decl;
m_extension_decl = func_finder.m_extension_decl;
m_extension_decl = func_finder.extension_decl;
if (m_extension_decl) {
m_function_decl = func_finder.ext_method_decl;
m_wrapper_decl = func_finder.toplevel_decl;
} else {
m_function_decl = func_finder.toplevel_decl;
m_wrapper_decl = nullptr;
}

assert(m_function_decl);

Expand Down
3 changes: 3 additions & 0 deletions lldb/test/API/lang/swift/async/expr/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
SWIFT_SOURCES := main.swift
SWIFTFLAGS_EXTRAS := -Xfrontend -enable-experimental-concurrency -parse-as-library
include Makefile.rules
21 changes: 21 additions & 0 deletions lldb/test/API/lang/swift/async/expr/TestSwiftAsyncExpressions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import lldb
from lldbsuite.test.decorators import *
import lldbsuite.test.lldbtest as lldbtest
import lldbsuite.test.lldbutil as lldbutil
import unittest2


class TestSwiftAsyncExpressions(lldbtest.TestBase):

mydir = lldbtest.TestBase.compute_mydir(__file__)

@swiftTest
@skipIfWindows
@skipIfLinux
@skipIf(archs=no_match(["arm64", "arm64e", "arm64_32", "x86_64"]))
def test_actor(self):
"""Test async unwind"""
self.build()
target, process, thread, main_bkpt = lldbutil.run_to_source_breakpoint(
self, 'break here', lldb.SBFileSpec("main.swift"))
self.expect("expr n", substrs=["42"])
13 changes: 13 additions & 0 deletions lldb/test/API/lang/swift/async/expr/main.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
actor Actor {
func f(_ n : Int) async {
print(n) // break here
}
}


@main struct Main {
static func main() async {
let A = Actor()
await A.f(42)
}
}