Skip to content

Commit 419a0bb

Browse files
committed
[lldb] Fix dynamic type resolution for ObjC protocol existentials
An existential whose protocol type is an Objective-C protocol is not passed in an existential container, but as a plain old instance pointer. This patch dynamic type resolution failing for those types. rdar://144028358
1 parent 263d6fa commit 419a0bb

File tree

8 files changed

+124
-12
lines changed

8 files changed

+124
-12
lines changed

lldb/source/Plugins/LanguageRuntime/Swift/SwiftLanguageRuntimeDynamicTypeResolution.cpp

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2865,10 +2865,15 @@ bool SwiftLanguageRuntime::GetDynamicTypeAndAddress(
28652865
else if (type_info.AllSet(eTypeIsMetatype | eTypeIsProtocol))
28662866
success = GetDynamicTypeAndAddress_ExistentialMetatype(
28672867
in_value, val_type, use_dynamic, class_type_or_name, address);
2868-
else if (type_info.AnySet(eTypeIsProtocol))
2869-
success = GetDynamicTypeAndAddress_Existential(in_value, val_type, use_dynamic,
2870-
class_type_or_name, address);
2871-
else {
2868+
else if (type_info.AnySet(eTypeIsProtocol)) {
2869+
if (type_info.AnySet(eTypeIsObjC))
2870+
success = GetDynamicTypeAndAddress_Class(in_value, val_type, use_dynamic,
2871+
class_type_or_name, address,
2872+
static_value_type, local_buffer);
2873+
else
2874+
success = GetDynamicTypeAndAddress_Existential(
2875+
in_value, val_type, use_dynamic, class_type_or_name, address);
2876+
} else {
28722877
CompilerType bound_type;
28732878
if (type_info.AnySet(eTypeHasUnboundGeneric | eTypeHasDynamicSelf)) {
28742879
// Perform generic type resolution.

lldb/source/Plugins/TypeSystem/Swift/SwiftASTContext.cpp

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6188,11 +6188,18 @@ SwiftASTContext::GetTypeInfo(opaque_compiler_type_t type,
61886188
eTypeInstanceIsPointer;
61896189
break;
61906190

6191-
case swift::TypeKind::Protocol:
6191+
case swift::TypeKind::Protocol: {
6192+
auto *protocol =
6193+
llvm::cast<swift::ProtocolType>(swift_can_type.getPointer());
6194+
swift::ProtocolDecl *decl = protocol->getDecl();
6195+
if (decl->isObjC())
6196+
swift_flags |= eTypeIsObjC | eTypeHasValue;
6197+
LLVM_FALLTHROUGH;
6198+
}
61926199
case swift::TypeKind::ProtocolComposition:
6193-
case swift::TypeKind::Existential:
6200+
case swift::TypeKind::Existential: {
61946201
swift_flags |= eTypeHasChildren | eTypeIsStructUnion | eTypeIsProtocol;
6195-
break;
6202+
} break;
61966203
case swift::TypeKind::ExistentialMetatype:
61976204
swift_flags |= eTypeIsProtocol;
61986205
LLVM_FALLTHROUGH;

lldb/source/Plugins/TypeSystem/Swift/TypeSystemSwiftTypeRef.cpp

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1575,22 +1575,47 @@ swift::Demangle::NodePointer TypeSystemSwiftTypeRef::GetDemangleTreeForPrinting(
15751575
return GetNodeForPrintingImpl(dem, node, flavor, resolve_objc_module);
15761576
}
15771577

1578-
/// Determine wether this demangle tree contains a node of kind \c kind.
1578+
/// Determine wether this demangle tree contains a node of kind \c kind and with
1579+
/// text \c text (if provided).
15791580
static bool Contains(swift::Demangle::NodePointer node,
1580-
swift::Demangle::Node::Kind kind) {
1581+
swift::Demangle::Node::Kind kind,
1582+
llvm::StringRef text = "") {
15811583
if (!node)
15821584
return false;
15831585

1584-
if (node->getKind() == kind)
1585-
return true;
1586+
if (node->getKind() == kind) {
1587+
if (text.empty())
1588+
return true;
1589+
if (!node->hasText())
1590+
return false;
1591+
return node->getText() == text;
1592+
}
15861593

15871594
for (swift::Demangle::NodePointer child : *node)
1588-
if (Contains(child, kind))
1595+
if (Contains(child, kind, text))
15891596
return true;
15901597

15911598
return false;
15921599
}
15931600

1601+
static bool ProtocolCompositionContainsSingleObjcProtocol(
1602+
swift::Demangle::NodePointer node) {
1603+
// Kind=ProtocolList
1604+
// Kind=TypeList
1605+
// Kind=Type
1606+
// Kind=Protocol
1607+
// Kind=Module, text="__C"
1608+
// Kind=Identifier, text="SomeIdentifier"
1609+
if (node->getKind() != Node::Kind::ProtocolList)
1610+
return false;
1611+
NodePointer type_list = node->getFirstChild();
1612+
if (type_list->getKind() != Node::Kind::TypeList ||
1613+
type_list->getNumChildren() != 1)
1614+
return false;
1615+
NodePointer type = type_list->getFirstChild();
1616+
return Contains(type, Node::Kind::Module, swift::MANGLING_MODULE_OBJC);
1617+
}
1618+
15941619
/// Determine wether this demangle tree contains a generic type parameter.
15951620
static bool ContainsGenericTypeParameter(swift::Demangle::NodePointer node) {
15961621
return Contains(node, swift::Demangle::Node::Kind::DependentGenericParamType);
@@ -1777,6 +1802,8 @@ uint32_t TypeSystemSwiftTypeRef::CollectTypeInfo(
17771802
swift_flags |= eTypeIsProtocol;
17781803
// Bug-for-bug-compatibility.
17791804
swift_flags |= eTypeHasChildren | eTypeIsStructUnion;
1805+
if (ProtocolCompositionContainsSingleObjcProtocol(node))
1806+
swift_flags |= eTypeIsObjC | eTypeHasValue;
17801807
break;
17811808

17821809
case Node::Kind::ExistentialMetatype:
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
SWIFT_SOURCES := main.swift
2+
SWIFT_BRIDGING_HEADER := bridging-header.h
3+
OBJC_SOURCES := objc.m
4+
SWIFT_OBJC_INTEROP := 1
5+
6+
include Makefile.rules
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import lldb
2+
from lldbsuite.test.lldbtest import *
3+
from lldbsuite.test.decorators import *
4+
import lldbsuite.test.lldbutil as lldbutil
5+
6+
7+
class TestSwiftObjcProtocol(TestBase):
8+
@skipUnlessDarwin
9+
@swiftTest
10+
def test(self):
11+
"""Tests that dynamic type resolution works for an Objective-C protocol existential"""
12+
self.build()
13+
(target, process, thread, breakpoint) = lldbutil.run_to_source_breakpoint(
14+
self, "break here", lldb.SBFileSpec("main.swift")
15+
)
16+
17+
self.runCmd("settings set symbols.swift-enable-ast-context false")
18+
self.expect("frame variable v", substrs=["SwiftClass", "a = 42", "b = 938"])
19+
lldbutil.continue_to_breakpoint(process, breakpoint)
20+
self.expect(
21+
"frame variable v",
22+
substrs=["ObjcClass", "_someString", '"The objc string"'],
23+
)
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
2+
#include <Foundation/Foundation.h>
3+
4+
@protocol ObjcProtocol <NSObject>
5+
@end
6+
7+
@interface ObjcClass : NSObject <ObjcProtocol>
8+
@property NSString *someString;
9+
- (instancetype)init;
10+
+ (id<ObjcProtocol>)getP;
11+
@end
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
class SwiftClass: NSObject, ObjcProtocol {
2+
let a = 42
3+
let b = 938
4+
}
5+
6+
func foo(v: (any ObjcProtocol)) {
7+
print(v) // break here
8+
}
9+
10+
func f() {
11+
let swiftClass = SwiftClass()
12+
foo(v: swiftClass)
13+
let objcClass = ObjcClass()!
14+
foo(v: objcClass)
15+
}
16+
f()
17+
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#include "bridging-header.h"
2+
3+
@implementation ObjcClass
4+
5+
- (instancetype)init {
6+
self = [super init];
7+
if (self) {
8+
self.someString = @"The objc string";
9+
}
10+
return self;
11+
}
12+
13+
+ (id<ObjcProtocol>)getP {
14+
return [ObjcClass new];
15+
}
16+
@end

0 commit comments

Comments
 (0)