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
parent 4ab89d8bbb
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);
}
void visit(Cell& cell)
void visit(Cell& cell) SWIFT_NAME(visitRef(_:))
{
visit_impl(cell);
}
void visit(Cell const* cell)
void visit(Cell const* cell) SWIFT_NAME(visitConst(_:))
{
visit(const_cast<Cell*>(cell));
}
void visit(Cell const& cell)
void visit(Cell const& cell) SWIFT_NAME(visitConstRef(_:))
{
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
// 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 {
static func allocate(on heap: GC.Heap) -> UnsafeMutablePointer<Self>
init(cell: OpaquePointer)
init(cell: GC.Cell)
func finalize()
func visitEdges(_ visitor: OpaquePointer)
func visitEdges(_ visitor: GC.Cell.Visitor)
var cell: OpaquePointer { 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
var cell: GC.Cell { get }
}
// Here be dragons
@ -64,7 +41,7 @@ func asHeapAllocatableType(_ typeMetadata: UnsafeMutableRawPointer) -> any HeapA
}
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))
}
@ -76,7 +53,7 @@ extension HeapAllocatable {
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)
}
@ -101,5 +78,5 @@ extension HeapAllocatable {
}
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)
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_LINK_OPTIONS "-load-plugin-library;${_SWIFT_TESTING_TARGETLESS_DIR}/host/plugins/libTestingMacros.so"
)
endif()
endif()

View file

@ -6,12 +6,21 @@ if (ENABLE_SWIFT)
TestHeap.cpp
TestInterop.cpp
)
# FIXME: Swift doesn't seem to like object libraries for @main
target_sources(TestGCSwift PRIVATE ../Resources/SwiftTestMain.swift)
generate_clang_module_map(TestGCSwift)
set_target_properties(TestGCSwift PROPERTIES SUFFIX .swift-testing)
target_include_directories(TestGCSwift PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
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)
endif()

View file

@ -6,14 +6,28 @@
import AK
import GC
import GCTesting
@_exported import TestGCSwiftCxx
import Testing
// FIXME: We want a type declared *here* for HeapString, but it gives a compiler warning:
// error: type 'GCString' cannot conform to protocol 'HeapAllocatable' because it has requirements that cannot be satisfied
// 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
// be re-exported by the GC module.
public struct HeapString: HeapAllocatable {
public var string: Swift.String
public init(cell: GC.Cell) {
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)
struct TestGCSwiftBindings {

View file

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

View file

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