Skip to content

Commit 68d48d4

Browse files
committed
Implement lowering of Swift cdecl functions to C functions
Leverage the new C type system so that we can lower Swift cdecl functions down to their C representation. This could be used to generate C headers (albeit ugly ones) in the future, but for now is part of the validation of lowering Swift functions to cdecl thunks.
1 parent 4fdef82 commit 68d48d4

File tree

6 files changed

+267
-143
lines changed

6 files changed

+267
-143
lines changed

Sources/JExtractSwift/CTypes/CType.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,3 +195,16 @@ extension CType: CustomStringConvertible {
195195
print(placeholder: nil)
196196
}
197197
}
198+
199+
extension CType {
200+
/// Apply the rules for function parameter decay to produce the resulting
201+
/// decayed type. For example, this will adjust a function type to a
202+
/// pointer-to-function type.
203+
var parameterDecay: CType {
204+
switch self {
205+
case .floating, .integral, .pointer, .qualified, .tag, .void: self
206+
207+
case .function: .pointer(self)
208+
}
209+
}
210+
}

Sources/JExtractSwift/Swift2JavaTranslator+FunctionLowering.swift

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ extension Swift2JavaTranslator {
145145
parameterName: parameterName,
146146
type: .nominal(
147147
SwiftNominalType(
148-
nominalTypeDecl: swiftStdlibTypes.unsafeRawPointerDecl
148+
nominalTypeDecl: swiftStdlibTypes[.unsafeRawPointer]
149149
)
150150
)
151151
)
@@ -187,8 +187,8 @@ extension Swift2JavaTranslator {
187187
type: .nominal(
188188
SwiftNominalType(
189189
nominalTypeDecl: mutable
190-
? swiftStdlibTypes.unsafeMutableRawPointerDecl
191-
: swiftStdlibTypes.unsafeRawPointerDecl
190+
? swiftStdlibTypes[.unsafeMutableRawPointer]
191+
: swiftStdlibTypes[.unsafeRawPointer]
192192
)
193193
)
194194
)
@@ -276,8 +276,8 @@ extension Swift2JavaTranslator {
276276

277277
// At the @_cdecl level, make everything a raw pointer.
278278
let cdeclPointerType = mutable
279-
? swiftStdlibTypes.unsafeMutableRawPointerDecl
280-
: swiftStdlibTypes.unsafeRawPointerDecl
279+
? swiftStdlibTypes[.unsafeMutableRawPointer]
280+
: swiftStdlibTypes[.unsafeRawPointer]
281281
var cdeclToOriginal: LoweringStep
282282
switch (requiresArgument, hasCount) {
283283
case (false, false):
@@ -327,7 +327,7 @@ extension Swift2JavaTranslator {
327327
convention: convention,
328328
parameterName: parameterName + "_count",
329329
type: SwiftType.nominal(
330-
SwiftNominalType(nominalTypeDecl: swiftStdlibTypes.intDecl)
330+
SwiftNominalType(nominalTypeDecl: swiftStdlibTypes[.int])
331331
)
332332
),
333333
.SwiftInt
@@ -353,6 +353,33 @@ extension Swift2JavaTranslator {
353353
cdeclParameters: lowered.map(\.0)
354354
)
355355
}
356+
357+
/// Given a Swift function signature that represents a @_cdecl function,
358+
/// produce the equivalent C function with the given name.
359+
///
360+
/// Lowering to a @_cdecl function should never produce a
361+
@_spi(Testing)
362+
public func cdeclToCFunctionLowering(
363+
_ cdeclSignature: SwiftFunctionSignature,
364+
cName: String
365+
) -> CFunction {
366+
assert(cdeclSignature.selfParameter == nil)
367+
368+
let cResultType = try! swiftStdlibTypes.cdeclToCLowering(cdeclSignature.result.type)
369+
let cParameters = cdeclSignature.parameters.map { parameter in
370+
CParameter(
371+
name: parameter.parameterName,
372+
type: try! swiftStdlibTypes.cdeclToCLowering(parameter.type).parameterDecay
373+
)
374+
}
375+
376+
return CFunction(
377+
resultType: cResultType,
378+
name: cName,
379+
parameters: cParameters,
380+
isVariadic: false
381+
)
382+
}
356383
}
357384

358385
struct LabeledArgument<Element> {
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2025 Apple Inc. and the Swift.org project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of Swift.org project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
extension SwiftStandardLibraryTypes {
16+
/// Lower the given Swift type down to a its corresponding C type.
17+
///
18+
/// This operation only supports the subset of Swift types that are
19+
/// representable in a Swift `@_cdecl` function. If lowering an arbitrary
20+
/// Swift function, first go through Swift -> cdecl lowering.
21+
func cdeclToCLowering(_ swiftType: SwiftType) throws -> CType {
22+
switch swiftType {
23+
case .nominal(let nominalType):
24+
if let knownType = self[nominalType.nominalTypeDecl] {
25+
return try knownType.loweredCType()
26+
}
27+
28+
throw CDeclToCLoweringError.invalidNominalType(nominalType.nominalTypeDecl)
29+
30+
case .function(let functionType):
31+
switch functionType.convention {
32+
case .swift:
33+
throw CDeclToCLoweringError.invalidFunctionConvention(functionType)
34+
35+
case .c:
36+
let resultType = try cdeclToCLowering(functionType.resultType)
37+
let parameterTypes = try functionType.parameters.map { param in
38+
try cdeclToCLowering(param.type)
39+
}
40+
41+
return .function(
42+
resultType: resultType,
43+
parameters: parameterTypes,
44+
variadic: false
45+
)
46+
}
47+
48+
case .tuple([]):
49+
return .void
50+
51+
case .metatype, .optional, .tuple:
52+
throw CDeclToCLoweringError.invalidCDeclType(swiftType)
53+
}
54+
}
55+
}
56+
57+
extension KnownStandardLibraryType {
58+
func loweredCType() throws -> CType {
59+
switch self {
60+
case .bool: .integral(.bool)
61+
case .int: .integral(.ptrdiff_t)
62+
case .uint: .integral(.size_t)
63+
case .int8: .integral(.signed(bits: 8))
64+
case .uint8: .integral(.unsigned(bits: 8))
65+
case .int16: .integral(.signed(bits: 16))
66+
case .uint16: .integral(.unsigned(bits: 16))
67+
case .int32: .integral(.signed(bits: 32))
68+
case .uint32: .integral(.unsigned(bits: 32))
69+
case .int64: .integral(.signed(bits: 64))
70+
case .uint64: .integral(.unsigned(bits: 64))
71+
case .float: .floating(.float)
72+
case .double: .floating(.double)
73+
case .unsafeMutableRawPointer: .pointer(.void)
74+
case .unsafeRawPointer: .pointer(
75+
.qualified(const: true, volatile: false, type: .void)
76+
)
77+
}
78+
}
79+
}
80+
enum CDeclToCLoweringError: Error {
81+
case invalidCDeclType(SwiftType)
82+
case invalidNominalType(SwiftNominalTypeDeclaration)
83+
case invalidFunctionConvention(SwiftFunctionType)
84+
}
85+

0 commit comments

Comments
 (0)