Skip to content

Introduce @extern(c) to declare C function without Clang module #69207

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 13 commits into from
Oct 23, 2023

Conversation

kateinoigakukun
Copy link
Member

@kateinoigakukun kateinoigakukun commented Oct 16, 2023

Motivation

Currently the only proper way to reference a C function from Swift is by importing it through Clang module.
However there are a number of places using the incorrect @_silgen_name way to reference C function. (The attribute expects Swift calling convention and it causes calling convention mismatch.)
These incorrect references happened because defining a new Clang module requires non-trivial work mostly around the build system. (Also there is a very complex issue, which is not easy to solve with Clang module approach #67853 (comment))

So this PR adds a new variant of @extern for C function declaration. Also this unlocks a way to declare a function with C calling convention imported through Wasm’s import mechanism by mixing with @extern(wasm)

The attribute is now gated by -enable-experimental-feature Extern

Description

The new attribute unlocks declaring a C function in Swift source code. The declared function interfaces are limited to be C-compatible, so no async, no throws, and no ref counted types are allowed. (We might be able to unlock the use of ref counted type in theory but it introduces a lot of complexities)

Examples:

@extern(c)
func malloc(size: Int) -> UnsafeMutableRawPointer!

@extern(c, "swift_demangle")
public func _stdlib_demangleImpl(
  mangledName: UnsafePointer<CChar>?,
  mangledNameLength: UInt,
  outputBuffer: UnsafeMutablePointer<CChar>?,
  outputBufferSize: UnsafeMutablePointer<UInt>?,
  flags: UInt32
) -> UnsafeMutablePointer<CChar>?

@extern(wasm, module: "wasi_snapshot_preview1", name: "random_get")
@extern(c) // NOTE: Just for calling it with C calling convention
func wasi_random_get(
  buffer: UnsafeMutablePointer<UInt8>,
  count: UInt32
) -> Int32

Resolves rdar://115802180 and unblock #67853

Alternative consideration: Allow @_cdecl without body

We initially planned this approach, but didn’t take it for the following reason

  • The current semantics of @_cdecl is “expose the Swift declaration to C/Objective-C world” and it allows having Objective-C types in its interface.
    • Thus it requires a native-to-foreign thunk to be emitted within the module, which defines the @_cdecl function.
  • The new mode (without body) would add a semantics: “if no function body is defined, reference a C function corresponding to the Swift function declaration”.
    • This requires a foreign-to-native thunk to be emitted within the module, which declare the @_cdecl function.
  • The problem here is we have to determine if a function with @_cdecl requires foreign-to-native thunk based on where function declarations come from because a @_cdecl function without body declared within a compiling module and imported @_cdecl function both do not have body.

@kateinoigakukun
Copy link
Member Author

@swift-ci Please smoke test

@kateinoigakukun
Copy link
Member Author

@swift-ci Please smoke test

@kateinoigakukun kateinoigakukun added attributes Feature: Declaration and type attributes c interop Feature: Interoperability with C labels Oct 16, 2023
@kateinoigakukun
Copy link
Member Author

@swift-ci Please test Apple Silicon

@kateinoigakukun
Copy link
Member Author

@swift-ci Please build toolchain

Copy link
Contributor

@al45tair al45tair left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is great :-)

It'd be nice to have someone else sign off on it as well though. Don't know who's the best person to look at it; @lorentey seems like a good choice because he's likely to actually use it. @mikeash might also appreciate it and I don't think is on the reviewers list.

The Attr.h is shared with SwiftCompilerSources through C++ interop and
C++ interop somehow crashes with libc++'s std::optional. So use legacy
llvm::Optional for now.
…ible way

To fix windows-x86_64 case, which doesn't have byval.
`@_extern(wasm)` has no limitation on the function interface, so the
check should be applied only for `@_extern(c)`.
To avoid ambiguity at SIL level name since both tries to change the SIL
level name.
And allow expert users to use potentially invalid symbols by specifying
it explicitly.
@kateinoigakukun kateinoigakukun changed the title Introduce @_extern(c) to declare C function without Clang module Introduce @extern(c) to declare C function without Clang module Oct 20, 2023
@kateinoigakukun
Copy link
Member Author

@swift-ci Please smoke test

Now the feature is gated by experimental feature flag.
It's not shipped in any language release, so this rename should be fine.
@kateinoigakukun
Copy link
Member Author

@swift-ci Please smoke test

@MaxDesiatov
Copy link
Contributor

@swift-ci build toolchain

@kateinoigakukun
Copy link
Member Author

@swift-ci Please smoke test

@jckarter
Copy link
Contributor

The new mode (without body) would add a semantics: “if no function body is defined, reference a C function corresponding to the Swift function declaration”.
This requires a foreign-to-native thunk to be emitted within the module, which declare the @_cdecl function

Note that the foreign-to-native thunk is still necessary in general. Currently this crashes the compiler:

@_extern(c)
func foo(x: Int)

func bar(_: (Int) -> Void) {}

func bas() {
    bar(foo)
}

It would be cool if we treated these like we do functions imported from Clang and generate the foreign-to-native thunk as a shared thunk on demand.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
attributes Feature: Declaration and type attributes c interop Feature: Interoperability with C
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants