LibGC: Teach Swift bindings about Cell and Cell::Visitor

Add the proper annotations for the Cell and Cell::Visitor classes to be
visible in Swift. This lets us remove some OpaquePointer shinangians in
the Swift bindings.
This commit is contained in:
Andrew Kaster 2025-03-22 19:01:41 -06:00 committed by Andrew Kaster
commit 8554ee386e
Notes: github-actions[bot] 2025-04-03 22:49:27 +00:00
7 changed files with 43 additions and 48 deletions

View file

@ -72,17 +72,17 @@ public:
visit_impl(*cell); visit_impl(*cell);
} }
void visit(Cell& cell) void visit(Cell& cell) SWIFT_NAME(visitRef(_:))
{ {
visit_impl(cell); visit_impl(cell);
} }
void visit(Cell const* cell) void visit(Cell const* cell) SWIFT_NAME(visitConst(_:))
{ {
visit(const_cast<Cell*>(cell)); visit(const_cast<Cell*>(cell));
} }
void visit(Cell const& cell) void visit(Cell const& cell) SWIFT_NAME(visitConstRef(_:))
{ {
visit(const_cast<Cell&>(cell)); visit(const_cast<Cell&>(cell));
} }
@ -157,7 +157,7 @@ public:
} }
} }
void visit(NanBoxedValue const& value); void visit(NanBoxedValue const& value) SWIFT_NAME(visitValue(_:));
// Allow explicitly ignoring a GC-allocated member in a visit_edges implementation instead // Allow explicitly ignoring a GC-allocated member in a visit_edges implementation instead
// of just not using it. // of just not using it.

View file

@ -15,38 +15,15 @@ extension GC.Heap {
} }
} }
// FIXME: Cell and Cell::Visitor are not imported properly, so we have to treat them as OpaquePointer
public protocol HeapAllocatable { public protocol HeapAllocatable {
static func allocate(on heap: GC.Heap) -> UnsafeMutablePointer<Self> static func allocate(on heap: GC.Heap) -> UnsafeMutablePointer<Self>
init(cell: OpaquePointer) init(cell: GC.Cell)
func finalize() func finalize()
func visitEdges(_ visitor: OpaquePointer) func visitEdges(_ visitor: GC.Cell.Visitor)
var cell: OpaquePointer { get } var cell: GC.Cell { get }
}
// FIXME: Figure out why other modules can't conform to HeapAllocatable
public struct HeapString: HeapAllocatable {
public var string: Swift.String
public init(cell: OpaquePointer) {
self.cell = cell
self.string = ""
}
// FIXME: HeapAllocatable cannot be exposed to C++ yet, so we're off to void* paradise
public static func create(on heap: GC.Heap, string: Swift.String) -> OpaquePointer {
// NOTE: GC must be deferred so that a collection during allocation doesn't get tripped
// up looking for the Cell pointer on the stack or in a register when it might only exist in the heap
precondition(heap.is_gc_deferred())
let heapString = allocate(on: heap)
heapString.pointee.string = string
return heapString.pointee.cell
}
public var cell: OpaquePointer
} }
// Here be dragons // Here be dragons
@ -64,7 +41,7 @@ func asHeapAllocatableType(_ typeMetadata: UnsafeMutableRawPointer) -> any HeapA
} }
extension HeapAllocatable { extension HeapAllocatable {
fileprivate static func initializeFromFFI(at this: UnsafeMutableRawPointer, cell: OpaquePointer) { fileprivate static func initializeFromFFI(at this: UnsafeMutableRawPointer, cell: GC.Cell) {
this.assumingMemoryBound(to: Self.self).initialize(to: Self.self.init(cell: cell)) this.assumingMemoryBound(to: Self.self).initialize(to: Self.self.init(cell: cell))
} }
@ -76,7 +53,7 @@ extension HeapAllocatable {
this.assumingMemoryBound(to: Self.self).pointee.finalize() this.assumingMemoryBound(to: Self.self).pointee.finalize()
} }
fileprivate static func visitEdgesFromFFI(at this: UnsafeMutableRawPointer, visitor: OpaquePointer) { fileprivate static func visitEdgesFromFFI(at this: UnsafeMutableRawPointer, visitor: GC.Cell.Visitor) {
this.assumingMemoryBound(to: Self.self).pointee.visitEdges(visitor) this.assumingMemoryBound(to: Self.self).pointee.visitEdges(visitor)
} }
@ -101,5 +78,5 @@ extension HeapAllocatable {
} }
public func finalize() {} public func finalize() {}
public func visitEdges(_ visitor: OpaquePointer) {} public func visitEdges(_ visitor: GC.Cell.Visitor) {}
} }

View file

@ -17,6 +17,7 @@ if (SWIFT_TESTING)
cmake_path(GET _SWIFT_TESTING_DIR PARENT_PATH _SWIFT_TESTING_TARGETLESS_DIR) cmake_path(GET _SWIFT_TESTING_DIR PARENT_PATH _SWIFT_TESTING_TARGETLESS_DIR)
set_target_properties(SwiftTesting::SwiftTesting PROPERTIES set_target_properties(SwiftTesting::SwiftTesting PROPERTIES
INTERFACE_COMPILE_OPTIONS "$<$<COMPILE_LANGUAGE:Swift>:SHELL:-load-plugin-library ${_SWIFT_TESTING_TARGETLESS_DIR}/host/plugins/libTestingMacros.so>" INTERFACE_COMPILE_OPTIONS "$<$<COMPILE_LANGUAGE:Swift>:SHELL:-load-plugin-library ${_SWIFT_TESTING_TARGETLESS_DIR}/host/plugins/libTestingMacros.so>"
INTERFACE_LINK_OPTIONS "-load-plugin-library;${_SWIFT_TESTING_TARGETLESS_DIR}/host/plugins/libTestingMacros.so"
) )
endif() endif()
endif() endif()

View file

@ -6,12 +6,21 @@ if (ENABLE_SWIFT)
TestHeap.cpp TestHeap.cpp
TestInterop.cpp TestInterop.cpp
) )
# FIXME: Swift doesn't seem to like object libraries for @main # FIXME: Swift doesn't seem to like object libraries for @main
target_sources(TestGCSwift PRIVATE ../Resources/SwiftTestMain.swift) target_sources(TestGCSwift PRIVATE ../Resources/SwiftTestMain.swift)
generate_clang_module_map(TestGCSwift)
set_target_properties(TestGCSwift PROPERTIES SUFFIX .swift-testing) set_target_properties(TestGCSwift PROPERTIES SUFFIX .swift-testing)
target_include_directories(TestGCSwift PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) target_include_directories(TestGCSwift PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(TestGCSwift PRIVATE AK LibGC SwiftTesting::SwiftTesting) target_link_libraries(TestGCSwift PRIVATE AK LibGC SwiftTesting::SwiftTesting)
get_property(testing_compile_options TARGET SwiftTesting::SwiftTesting PROPERTY INTERFACE_LINK_OPTIONS)
add_swift_target_properties(TestGCSwift
LAGOM_LIBRARIES AK LibGC
COMPILE_DEFINITIONS LIBGC_WORKAROUND_BOOL_BITFIELD
COMPILE_OPTIONS ${testing_compile_options} -enable-experimental-feature Extern
)
add_test(NAME TestGCSwift COMMAND TestGCSwift) add_test(NAME TestGCSwift COMMAND TestGCSwift)
endif() endif()

View file

@ -6,14 +6,28 @@
import AK import AK
import GC import GC
import GCTesting @_exported import TestGCSwiftCxx
import Testing import Testing
// FIXME: We want a type declared *here* for HeapString, but it gives a compiler warning: public struct HeapString: HeapAllocatable {
// error: type 'GCString' cannot conform to protocol 'HeapAllocatable' because it has requirements that cannot be satisfied public var string: Swift.String
// Even using the same exact code from LibGC/Heap+Swift.swift
// This is likely because one of the required types for HeapAllocatable is not fully imported from C++ and thus can't public init(cell: GC.Cell) {
// be re-exported by the GC module. self.cell = cell
self.string = ""
}
public static func create(on heap: GC.Heap, string: Swift.String) -> GC.Cell {
// NOTE: GC must be deferred so that a collection during allocation doesn't get tripped
// up looking for the Cell pointer on the stack or in a register when it might only exist in the heap
precondition(heap.is_gc_deferred())
let heapString = allocate(on: heap)
heapString.pointee.string = string
return heapString.pointee.cell
}
public var cell: GC.Cell
}
@Suite(.serialized) @Suite(.serialized)
struct TestGCSwiftBindings { struct TestGCSwiftBindings {

View file

@ -7,9 +7,9 @@
#include "TestInterop.h" #include "TestInterop.h"
#include "TestHeap.h" #include "TestHeap.h"
#include <AK/TypeCasts.h> #include <AK/TypeCasts.h>
#include <LibGC-Swift.h>
#include <LibGC/ForeignCell.h> #include <LibGC/ForeignCell.h>
#include <LibGC/Heap.h> #include <LibGC/Heap.h>
#include <TestGCSwift-Swift.h>
#define COLLECT heap.collect_garbage(GC::Heap::CollectionType::CollectGarbage) #define COLLECT heap.collect_garbage(GC::Heap::CollectionType::CollectGarbage)
#define COLLECT_ALL heap.collect_garbage(GC::Heap::CollectionType::CollectEverything) #define COLLECT_ALL heap.collect_garbage(GC::Heap::CollectionType::CollectEverything)
@ -20,7 +20,7 @@ void test_interop()
COLLECT_ALL; COLLECT_ALL;
auto string = GC::ForeignRef<GC::HeapString>::allocate(heap, "Hello, World!"); auto string = GC::ForeignRef<TestGCSwift::HeapString>::allocate(heap, "Hello, World!");
COLLECT; COLLECT;

View file

@ -1,6 +0,0 @@
module GCTesting {
header "TestHeap.h"
header "TestInterop.h"
requires cplusplus
export *
}