Hi All,
I'm trying to reference a extern const
C struct in Swift code to pass to another C function which expects a pointer to the struct, but have been unable to do so.
The definition is:
extern const struct wl_interface wl_compositor_interface;
and the function header is:
static void* wl_registry_bind(
struct wl_registry *wl_registry,
uint32_t name,
const struct wl_interface *interface,
uint32_t version
)
My code is:
window._compositor = wl_registry_bind(registry, name, &wl_compositor_interface, 1)
but I get a cannot pass immutable value as inout argument: 'wl_compositor_interface' is a 'let' constant
error.
I've posted a bug about this ([SR-9589] Clang crash when calling external C library function with UnsafePointer to C struct as argument (was Can't pass an imported C extern const struct to a C function which expects a struct pointer) · Issue #52036 · apple/swift · GitHub ) explaining the specifics due to a compiler crash I uncovered while trying to figure out how to achieve this, but thought I'd post here to see if there was a way to achieve it curently.
1 Like
lukasa
(Cory Benfield)
January 3, 2019, 9:55am
2
Does it expect the pointer value to be constant, or does it just want to dereference it? If it just wants to dereference it, you can get the pointer by doing:
window._compositor = withUnsafePointer(to: wl_compositor_interface) {
wl_registry_bind(registry, name, $0, 1)
}
I have tried that but then I get the compiler crash I mentioned in my original post - Swift compiler crash trying to pass C extern const struct by reference · GitHub
lukasa
(Cory Benfield)
January 3, 2019, 10:06am
4
In that case, until that crash is resolved, you may just need to take a local copy of the struct:
var myCopy = wl_compositor_interface
window._compositor = wl_registry_bind(registry, name, &myCopy, 1)
That gives me the same compiler crash unfortunately. You're right, I may just have to wait till that crash is resolved.
wiml
(Wim Lewis)
January 30, 2020, 7:20am
6
Did you ever discover a way to do this? In Swift 5.1.3, I don't get a compiler crash, but I haven't been able to discover a way to get a pointer to a C "extern const struct thingtype athing
".
Calling withUnsafePointer(to:)
gives me a pointer to a stack-allocated copy of the struct (which is not what I need); using an &
gives me the spurious warning about passing an immutable value as an inout
parameter.
My best workaround has been to introduce a whole lot of static inline C functions into a header which Swift can see — one for each struct variable, returning a pointer to that variable — which does compile to good code in release
mode, but is ugly.
@Andrew_Trick Please help.
Having exactly same problem.
Output:
$ swift build
/home/ailion/swift-hello/Sources/swift-hello/main.swift:10:53: error: cannot pass immutable value as inout argument: 'wl_compositor_interface' is a 'let' constant
compositor = wl_registry_bind(registry, id, &wl_compositor_interface, 1)
$ swift build
swift: /home/build-user/llvm-project/clang/lib/CodeGen/CGRecordLayout.h:186: unsigned int clang::CodeGen::CGRecordLayout::getLLVMFieldNo(const clang::FieldDecl *) const: Assertion `FieldInfo.count(FD) && "Invalid field for record!"' failed.
Stack dump:
0. Program arguments: /usr/lib/swift/bin/swift -frontend -c -primary-file /home/ailion/swift-hello/Sources/swift-hello/main.swift -emit-module-path /home/ailion/swift-hello/.build/x86_64-unknown-linux-gnu/debug/swift_hello.build/main~partial.swiftmodule -emit-module-doc-path /home/ailion/swift-hello/.build/x86_64-unknown-linux-gnu/debug/swift_hello.build/main~partial.swiftdoc -emit-module-source-info-path /home/ailion/swift-hello/.build/x86_64-unknown-linux-gnu/debug/swift_hello.build/main~partial.swiftsourceinfo -emit-dependencies-path /home/ailion/swift-hello/.build/x86_64-unknown-linux-gnu/debug/swift_hello.build/main.d -emit-reference-dependencies-path /home/ailion/swift-hello/.build/x86_64-unknown-linux-gnu/debug/swift_hello.build/main.swiftdeps -target x86_64-unknown-linux-gnu -disable-objc-interop -I /home/ailion/swift-hello/.build/x86_64-unknown-linux-gnu/debug -color-diagnostics -enable-testing -g -module-cache-path /home/ailion/swift-hello/.build/x86_64-unknown-linux-gnu/debug/ModuleCache -swift-version 5 -Onone -D SWIFT_PACKAGE -D DEBUG -enable-anonymous-context-mangled-names -Xcc -fmodule-map-file=/home/ailion/swift-hello/Sources/CWaylandEGL/module.modulemap -Xcc -fmodule-map-file=/home/ailion/swift-hello/Sources/CWaylandClient/module.modulemap -module-name swift_hello -o /home/ailion/swift-hello/.build/x86_64-unknown-linux-gnu/debug/swift_hello.build/main.swift.o -index-store-path /home/ailion/swift-hello/.build/x86_64-unknown-linux-gnu/debug/index/store -index-system-modules
1. Swift version 5.2.4 (swift-5.2.4-RELEASE)
2. /usr/include/wayland-client-protocol.h:1095:1: Generating code for declaration 'wl_registry_bind'
/usr/lib/swift/bin/swift[0x4b9a6d4]
/usr/lib/swift/bin/swift[0x4b9838e]
/usr/lib/swift/bin/swift[0x4b9a996]
/usr/lib/libpthread.so.0(+0x14960)[0x7f932f8b9960]
/usr/lib/libc.so.6(gsignal+0x145)[0x7f932f2fd355]
/usr/lib/libc.so.6(abort+0x127)[0x7f932f2e6853]
/usr/lib/libc.so.6(+0x25727)[0x7f932f2e6727]
/usr/lib/libc.so.6(+0x34936)[0x7f932f2f5936]
/usr/lib/swift/bin/swift[0x2524d4f]
/usr/lib/swift/bin/swift[0x2525393]
/usr/lib/swift/bin/swift[0x250e942]
/usr/lib/swift/bin/swift[0x251a2bc]
/usr/lib/swift/bin/swift[0x250adc1]
/usr/lib/swift/bin/swift[0x2513f15]
/usr/lib/swift/bin/swift[0x2567ac9]
/usr/lib/swift/bin/swift[0x256f0bc]
/usr/lib/swift/bin/swift[0x255a435]
/usr/lib/swift/bin/swift[0x250a780]
/usr/lib/swift/bin/swift[0x250ba75]
/usr/lib/swift/bin/swift[0x24a7d1a]
/usr/lib/swift/bin/swift[0x24a6641]
/usr/lib/swift/bin/swift[0x24878bb]
/usr/lib/swift/bin/swift[0x2527671]
/usr/lib/swift/bin/swift[0x2526333]
/usr/lib/swift/bin/swift[0x256adb9]
/usr/lib/swift/bin/swift[0x2563702]
/usr/lib/swift/bin/swift[0x255a435]
/usr/lib/swift/bin/swift[0x250a780]
/usr/lib/swift/bin/swift[0x250a729]
/usr/lib/swift/bin/swift[0x25f7d7c]
/usr/lib/swift/bin/swift[0x26027c0]
/usr/lib/swift/bin/swift[0x2647328]
/usr/lib/swift/bin/swift[0x26641a3]
/usr/lib/swift/bin/swift[0x265c7f4]
/usr/lib/swift/bin/swift[0x2651a41]
/usr/lib/swift/bin/swift[0x2650979]
/usr/lib/swift/bin/swift[0x2420614]
/usr/lib/swift/bin/swift[0x58d9b8]
/usr/lib/swift/bin/swift[0x579554]
/usr/lib/swift/bin/swift[0x579861]
/usr/lib/swift/bin/swift[0x504c5e]
/usr/lib/swift/bin/swift[0x4fa439]
/usr/lib/swift/bin/swift[0x4f7705]
/usr/lib/swift/bin/swift[0x488066]
/usr/lib/libc.so.6(__libc_start_main+0xf2)[0x7f932f2e8002]
/usr/lib/swift/bin/swift[0x487c9e]
$ uname -r
5.6.16-1-MANJARO
$ swift -version
Swift version 5.2.4 (swift-5.2.4-RELEASE)
Target: x86_64-unknown-linux-gnu
Example code:
import CWaylandClient
import CWaylandEGL
var compositor: UnsafeMutableRawPointer? = nil
func global_registry_handler(data: UnsafeMutableRawPointer?, registry: OpaquePointer?, id: UInt32, interface: UnsafePointer<Int8>?, version: UInt32) {
print("Got a registry event")
let interfaceStr = String(cString: interface!)
if interfaceStr == "wl_compositor" {
var w = wl_compositor_interface
compositor = wl_registry_bind(registry, id, &w, 1)
}
}
func global_registry_remove(data: UnsafeMutableRawPointer?, registry: OpaquePointer?, id: UInt32) {
print("Got a registry remove event")
}
var display = wl_display_connect(nil);
if display == nil {
print("Connect to display failed!")
exit(1)
}
print("Connect to display succeeded!")
var registry = wl_display_get_registry(display)
var registry_listener = wl_registry_listener(global: global_registry_handler, global_remove: global_registry_remove)
wl_registry_add_listener(registry, ®istry_listener, nil)
wl_display_dispatch(display)
wl_display_roundtrip(display)
wl_display_disconnect(display)
print("Hello, world!")
I see two issues in this thread.
We still have an open bug on the compiler crash, although I haven't reproduced it:
Clang crash when calling external C library function with UnsafePointer to C struct as argument (was Can't pass an imported C extern const struct to a C function which expects a struct pointer)
opened 11:22PM - 31 Dec 18 UTC
bug
compiler
| | |
|------------------|-----------------|…
|Previous ID | SR-9589 |
|Radar | rdar://problem/65264230 |
|Original Reporter | ryanw (JIRA User) |
|Type | Bug |
|Status | Reopened |
|Resolution | |
<details>
<summary>Additional Detail from JIRA</summary>
| | |
|------------------|-----------------|
|Votes | 0 |
|Component/s | Compiler |
|Labels | Bug |
|Assignee | None |
|Priority | Medium |
md5: 7b70cae7dc6f369914bd284d4678cb60
</details>
**Issue Description:**
I'm trying to build a Swift app on Linux (Fedora 29) using a Wayland desktop. I need to pass a C struct declared externally as
``` c
extern const struct wl_interface wl_compositor_interface;
```
(from <https://people.freedesktop.org/~whot/wayland-doxygen/wayland/Server/wayland-server-protocol_8h_source.html)>
to a C function declared as:
``` c
static void* wl_registry_bind(
struct wl_registry *wl_registry,
uint32_t name,
const struct wl_interface *interface,
uint32_t version
)
```
However if I try to call the function as:
``` swift
window._compositor = wl_registry_bind(registry, name, &wl_compositor_interface, 1)
```
I get the error:
``` java
error: cannot pass immutable value as inout argument: 'wl_compositor_interface' is a 'let' constant
```
Removing the & gives the (expected) error:
``` java
error: cannot convert value of type 'wl_interface' to expected argument type 'UnsafePointer<wl_interface>?'
```
If I try to create an UnsafePointer to the struct to pass to the function as follows:
``` swift
var compositorInterfacePointer: wl_interface = wl_compositor_interface
window._compositor = wl_registry_bind(registry, name, &compositorInterfacePointer, 1)
```
I get a compiler crash (on both the Fedora-built Swift 4.2.1 and my own build from dev HEAD):
<https://gist.github.com/tokyovigilante/89a1209eb7802b8a035d6f03b63adeae>
Is there any way to pass these externally defined structs to a function which expects a const struct pointer?
rdar://problem/19103795
Reproducer: GitHub - tokyovigilante/WaylandTest
"root cause seems to be that both XDGShell and WaylandWSIWindow (via the CWaylandClient system module) include the wayland-client.h header"
So apparently at least there's a workaround for the compiler crash.
Passing a pointer to an extern C declaration of a global:
This works fine for me though:
C code:
struct MyStruct {
int field;
};
extern const struct MyStruct myGlobal;
void takePointer(const struct MyStruct *);
Swift code
withUnsafePointer(to: myGlobal) {
takePointer($0)
}
So the equivalent workaround posted by @lukasa should work for you:
lukasa:
window._compositor = withUnsafePointer(to: wl_compositor_interface) {
wl_registry_bind(registry, name, $0, 1)
}
1 Like
jrose
(Jordan Rose)
July 8, 2020, 6:38pm
10
Andrew_Trick:
Passing a pointer to an extern C declaration of a global:
Is it guaranteed that withUnsafePointer(to:)
on an immutable C global won't make a temporary copy? (I think that would be a reasonable guarantee to provide, but I don't think there's anything in the compiler or stdlib that actually makes that happen today in -Onone.)
For comparison, we have promised that &global
will not make a temporary copy for a mutable, known-stored global (C or Swift).
@jordan , we're generating a copy :(
I filed SR-13180: withUnsafePointer
should guarantee pointer stability for globals and class properties
https://bugs.swift.org/browse/SR-13180
@andy1247008998 If you still get a runtime crash with the above workaround, then the C implementation must be sensitive to the pointer address of the global wl_compositor_interface
. In that case, my only suggestion is to create a C shim function or dummy global that returns to address of the global for use in Swift as an UnsafePointer.
4 Likes
Thanks a lot.
window._compositor = withUnsafePointer(to: wl_compositor_interface) {
wl_registry_bind(registry, name, $0, 1)
}
This workaround crashes too.
I will use C for my project, then call it from Swift without needing to deal with the global variables as well as many different types of pointers which are really a reasonable mess in Swift. Maybe I should lower my expectation of interoperability between Swift and C.
If you have a C target in your project, you can probably just do something like:
.h
extern const struct wl_interface *wl_compositor_interface_ptr;
.c
const struct wl_interface *wl_compositor_interface_ptr = &wl_compositor_interface;
I pushed my project on GitHub.
$ swift build
swift: /home/build-user/llvm-project/clang/lib/CodeGen/CGRecordLayout.h:186: unsigned int clang::CodeGen::CGRecordLayout::getLLVMFieldNo(const clang::FieldDecl *) const: Assertion `FieldInfo.count(FD) && "Invalid field for record!"' failed.
Stack dump:
0. Program arguments: /usr/lib/swift/bin/swift -frontend -c -primary-file /home/ailion/swift-hello/Sources/swift-hello/main.swift -emit-module-path /home/ailion/swift-hello/.build/x86_64-unknown-linux-gnu/debug/swift_hello.build/main~partial.swiftmodule -emit-module-doc-path /home/ailion/swift-hello/.build/x86_64-unknown-linux-gnu/debug/swift_hello.build/main~partial.swiftdoc -emit-module-source-info-path /home/ailion/swift-hello/.build/x86_64-unknown-linux-gnu/debug/swift_hello.build/main~partial.swiftsourceinfo -emit-dependencies-path /home/ailion/swift-hello/.build/x86_64-unknown-linux-gnu/debug/swift_hello.build/main.d -emit-reference-dependencies-path /home/ailion/swift-hello/.build/x86_64-unknown-linux-gnu/debug/swift_hello.build/main.swiftdeps -target x86_64-unknown-linux-gnu -disable-objc-interop -I /home/ailion/swift-hello/.build/x86_64-unknown-linux-gnu/debug -color-diagnostics -enable-testing -g -module-cache-path /home/ailion/swift-hello/.build/x86_64-unknown-linux-gnu/debug/ModuleCache -swift-version 5 -Onone -D SWIFT_PACKAGE -D DEBUG -enable-anonymous-context-mangled-names -Xcc -fmodule-map-file=/home/ailion/swift-hello/Sources/CWaylandEGL/module.modulemap -Xcc -fmodule-map-file=/home/ailion/swift-hello/Sources/CWaylandClient/module.modulemap -module-name swift_hello -o /home/ailion/swift-hello/.build/x86_64-unknown-linux-gnu/debug/swift_hello.build/main.swift.o -index-store-path /home/ailion/swift-hello/.build/x86_64-unknown-linux-gnu/debug/index/store -index-system-modules
1. Swift version 5.2.4 (swift-5.2.4-RELEASE)
2. /usr/include/wayland-client-protocol.h:1095:1: Generating code for declaration 'wl_registry_bind'
/usr/lib/swift/bin/swift[0x4b9a6d4]
/usr/lib/swift/bin/swift[0x4b9838e]
/usr/lib/swift/bin/swift[0x4b9a996]
/usr/lib/libpthread.so.0(+0x14960)[0x7f1928005960]
/usr/lib/libc.so.6(gsignal+0x145)[0x7f1927a49355]
/usr/lib/libc.so.6(abort+0x127)[0x7f1927a32853]
/usr/lib/libc.so.6(+0x25727)[0x7f1927a32727]
/usr/lib/libc.so.6(+0x34936)[0x7f1927a41936]
/usr/lib/swift/bin/swift[0x2524d4f]
/usr/lib/swift/bin/swift[0x2525393]
/usr/lib/swift/bin/swift[0x250e942]
/usr/lib/swift/bin/swift[0x251a2bc]
/usr/lib/swift/bin/swift[0x250adc1]
/usr/lib/swift/bin/swift[0x2513f15]
/usr/lib/swift/bin/swift[0x2567ac9]
/usr/lib/swift/bin/swift[0x256f0bc]
/usr/lib/swift/bin/swift[0x255a435]
/usr/lib/swift/bin/swift[0x250a780]
/usr/lib/swift/bin/swift[0x250ba75]
/usr/lib/swift/bin/swift[0x24a7d1a]
/usr/lib/swift/bin/swift[0x24a6641]
/usr/lib/swift/bin/swift[0x24878bb]
/usr/lib/swift/bin/swift[0x2527671]
/usr/lib/swift/bin/swift[0x2526333]
/usr/lib/swift/bin/swift[0x256adb9]
/usr/lib/swift/bin/swift[0x2563702]
/usr/lib/swift/bin/swift[0x255a435]
/usr/lib/swift/bin/swift[0x250a780]
/usr/lib/swift/bin/swift[0x250a729]
/usr/lib/swift/bin/swift[0x25f7d7c]
/usr/lib/swift/bin/swift[0x26027c0]
/usr/lib/swift/bin/swift[0x2647328]
/usr/lib/swift/bin/swift[0x26641a3]
/usr/lib/swift/bin/swift[0x265c7f4]
/usr/lib/swift/bin/swift[0x2651a41]
/usr/lib/swift/bin/swift[0x2650979]
/usr/lib/swift/bin/swift[0x2420614]
/usr/lib/swift/bin/swift[0x58d9b8]
/usr/lib/swift/bin/swift[0x579554]
/usr/lib/swift/bin/swift[0x579861]
/usr/lib/swift/bin/swift[0x504c5e]
/usr/lib/swift/bin/swift[0x4fa439]
/usr/lib/swift/bin/swift[0x4f7705]
/usr/lib/swift/bin/swift[0x488066]
/usr/lib/libc.so.6(__libc_start_main+0xf2)[0x7f1927a34002]
/usr/lib/swift/bin/swift[0x487c9e]
Crashes on Manjaro and Ubuntu 18.04 LTS (fresh installed).
You're hitting the known compiler bug. You can add your project link to this bug report
I don't understand the bug, but there's a workaround in the bug report that might work for you. I think that compiler crash was being confused with a feature request. I removed the incorrect radar link. Adding your information should make it clear that it's still an active bug.
CmST0us
(Eric Wu)
September 5, 2024, 3:31am
17
In Swift-5.10.1, it works.
I can get wl_compositor with:
var waylandCompositorInterface = wl_compositor_interface
var waylandCompositor = wl_registry_bind(registry, name, &waylandCompositorInterface, 1)