Skip to content

Commit 4fdef82

Browse files
committed
Introduce a C type system for lowering purposes
Swift functions are lowered into C-compatible thunks. Those thunks currently have their types represented in Swift, which is needed for the `@_cdecl` declaration. Introduce a representation of the C type system so that we can describe the specific C types that each parameter has. This intentionally represents C types in an abstract form that fits will with the mapping from Swift, for example representing integral types by the number of bits and their signedness, rather than the actual C primitive types like `int` or `unsigned long`. Implement printing of C types and functions, in case we decide that we want to render C declarations for consumption by humans or tools that want to bind to them.
1 parent 2ca6d55 commit 4fdef82

File tree

8 files changed

+468
-0
lines changed

8 files changed

+468
-0
lines changed
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
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+
/// Describes a C enum type.
16+
public class CEnum {
17+
public var name: String
18+
public init(name: String) {
19+
self.name = name
20+
}
21+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
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+
/// Describes a C function.
16+
public struct CFunction {
17+
/// The result type of the function.
18+
public var resultType: CType
19+
20+
/// The name of the function.
21+
public var name: String
22+
23+
/// The parameters of the function.
24+
public var parameters: [CParameter]
25+
26+
/// Whether the function is variadic.
27+
public var isVariadic: Bool
28+
29+
public init(resultType: CType, name: String, parameters: [CParameter], isVariadic: Bool) {
30+
self.resultType = resultType
31+
self.name = name
32+
self.parameters = parameters
33+
self.isVariadic = isVariadic
34+
}
35+
36+
/// Produces the type of the function.
37+
public var functionType: CType {
38+
.function(
39+
resultType: resultType,
40+
parameters: parameters.map { $0.type },
41+
variadic: isVariadic
42+
)
43+
}
44+
}
45+
46+
extension CFunction: CustomStringConvertible {
47+
/// Print the declaration of this C function
48+
public var description: String {
49+
var result = ""
50+
51+
resultType.printBefore(result: &result)
52+
53+
// FIXME: parentheses when needed.
54+
result += " "
55+
result += name
56+
57+
// Function parameters.
58+
result += "("
59+
result += parameters.map { $0.description }.joined(separator: ", ")
60+
CType.printFunctionParametersSuffix(
61+
isVariadic: isVariadic,
62+
hasZeroParameters: parameters.isEmpty,
63+
to: &result
64+
)
65+
result += ")"
66+
67+
resultType.printAfter(result: &result)
68+
69+
result += ""
70+
return result
71+
}
72+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
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+
/// Describes a parameter to a C function.
16+
public struct CParameter {
17+
/// The name of the parameter, if provided.
18+
public var name: String?
19+
20+
/// The type of the parameter.
21+
public var type: CType
22+
23+
public init(name: String? = nil, type: CType) {
24+
self.name = name
25+
self.type = type
26+
}
27+
}
28+
29+
extension CParameter: CustomStringConvertible {
30+
public var description: String {
31+
type.print(placeholder: name ?? "")
32+
}
33+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
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+
/// Describes a C struct type.
16+
public class CStruct {
17+
public var name: String
18+
19+
public init(name: String) {
20+
self.name = name
21+
}
22+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
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+
/// Describes a tag type in C, which is either a struct or an enum.
16+
public enum CTag {
17+
case `struct`(CStruct)
18+
case `enum`(CEnum)
19+
case `union`(CUnion)
20+
21+
public var name: String {
22+
switch self {
23+
case .struct(let cStruct): return cStruct.name
24+
case .enum(let cEnum): return cEnum.name
25+
case .union(let cUnion): return cUnion.name
26+
}
27+
}
28+
}
29+
Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
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+
/// Describes a type in the C type system as it is used for lowering of Swift
16+
/// declarations to C.
17+
///
18+
/// This description of the C type system only has to account for the types
19+
/// that are used when providing C-compatible thunks from Swift code. It is
20+
/// not a complete representation of the C type system, and leaves some
21+
/// target-specific types (like the actual type that ptrdiff_t and size_t
22+
/// map to) unresolved.
23+
public enum CType {
24+
/// A tag type, such as a struct or enum.
25+
case tag(CTag)
26+
27+
/// An integral type.
28+
case integral(IntegralType)
29+
30+
/// A floating-point type.
31+
case floating(FloatingType)
32+
33+
case void
34+
35+
/// A qualiied type, such as 'const T'.
36+
indirect case qualified(const: Bool, volatile: Bool, type: CType)
37+
38+
/// A pointer to the given type.
39+
indirect case pointer(CType)
40+
41+
/// A function type.
42+
indirect case function(resultType: CType, parameters: [CType], variadic: Bool)
43+
44+
/// An integral type in C, described mostly in terms of the bit-sized
45+
/// typedefs rather than actual C types (like int or long), because Swift
46+
/// deals in bit-widths.
47+
public enum IntegralType {
48+
case bool
49+
50+
/// A signed integer type stored with the given number of bits. This
51+
/// corresponds to the intNNN_t types from <stdint.h>.
52+
case signed(bits: Int)
53+
54+
/// An unsigned integer type stored with the given number of bits. This
55+
/// corresponds to the uintNNN_t types from <stdint.h>.
56+
case unsigned(bits: Int)
57+
58+
/// The ptrdiff_t type, which in C is a typedef for the signed integer
59+
/// type that is the same size as a pointer.
60+
case ptrdiff_t
61+
62+
/// The size_t type, which in C is a typedef for the unsigned integer
63+
/// type that is the same size as a pointer.
64+
case size_t
65+
}
66+
67+
/// A floating point type in C.
68+
public enum FloatingType {
69+
case float
70+
case double
71+
}
72+
}
73+
74+
extension CType: CustomStringConvertible {
75+
/// Print the part of this type that comes before the declarator, appending
76+
/// it to the provided `result` string.
77+
func printBefore(result: inout String) {
78+
switch self {
79+
case .floating(let floating):
80+
switch floating {
81+
case .float: result += "float"
82+
case .double: result += "double"
83+
}
84+
85+
case .function(resultType: let resultType, parameters: _, variadic: _):
86+
resultType.printBefore(result: &result)
87+
88+
// FIXME: Clang inserts a parentheses in here if there's a non-empty
89+
// placeholder, which is Very Stateful. How should I model that?
90+
91+
case .integral(let integral):
92+
switch integral {
93+
case .bool: result += "_Bool"
94+
case .signed(let bits): result += "int\(bits)_t"
95+
case .unsigned(let bits): result += "uint\(bits)_t"
96+
case .ptrdiff_t: result += "ptrdiff_t"
97+
case .size_t: result += "size_t"
98+
}
99+
100+
case .pointer(let pointee):
101+
pointee.printBefore(result: &result)
102+
result += "*"
103+
104+
case .qualified(const: let isConst, volatile: let isVolatile, type: let underlying):
105+
underlying.printBefore(result: &result)
106+
107+
// FIXME: "east const" is easier to print correctly, so do that. We could
108+
// follow Clang and decide when it's correct to print "west const" by
109+
// splitting the qualifiers before we get here.
110+
if isConst {
111+
result += " const"
112+
}
113+
if isVolatile {
114+
result += " volatile"
115+
}
116+
117+
case .tag(let tag):
118+
switch tag {
119+
case .enum(let cEnum): result += "enum \(cEnum.name)"
120+
case .struct(let cStruct): result += "struct \(cStruct.name)"
121+
case .union(let cUnion): result += "union \(cUnion.name)"
122+
}
123+
124+
case .void: result += "void"
125+
}
126+
}
127+
128+
/// Render an appropriate "suffix" to the parameter list of a function,
129+
/// which goes just before the closing ")" of that function, appending
130+
/// it to the string. This includes whether the function is variadic and
131+
/// whether is had zero parameters.
132+
static func printFunctionParametersSuffix(
133+
isVariadic: Bool,
134+
hasZeroParameters: Bool,
135+
to result: inout String
136+
) {
137+
// Take care of variadic parameters and empty parameter lists together,
138+
// because the formatter of the former depends on the latter.
139+
switch (isVariadic, hasZeroParameters) {
140+
case (true, false): result += ", ..."
141+
case (true, true): result += "..."
142+
case (false, true): result += "void"
143+
case (false, false): break
144+
}
145+
}
146+
147+
/// Print the part of the type that comes after the declarator, appending
148+
/// it to the provided `result` string.
149+
func printAfter(result: inout String) {
150+
switch self {
151+
case .floating, .integral, .tag, .void: break
152+
153+
case .function(resultType: let resultType, parameters: let parameters, variadic: let variadic):
154+
// FIXME: Clang inserts a parentheses in here if there's a non-empty
155+
// placeholder, which is Very Stateful. How should I model that?
156+
157+
result += "("
158+
159+
// Render the parameter types.
160+
result += parameters.map { $0.description }.joined(separator: ", ")
161+
162+
CType.printFunctionParametersSuffix(
163+
isVariadic: variadic,
164+
hasZeroParameters: parameters.isEmpty,
165+
to: &result
166+
)
167+
168+
result += ")"
169+
170+
resultType.printAfter(result: &result)
171+
172+
case .pointer(let pointee):
173+
pointee.printAfter(result: &result)
174+
175+
case .qualified(const: _, volatile: _, type: let underlying):
176+
underlying.printAfter(result: &result)
177+
}
178+
}
179+
180+
/// Print this type into a string, with the given placeholder as the name
181+
/// of the entity being declared.
182+
public func print(placeholder: String?) -> String {
183+
var result = ""
184+
printBefore(result: &result)
185+
if let placeholder {
186+
result += " "
187+
result += placeholder
188+
}
189+
printAfter(result: &result)
190+
return result
191+
}
192+
193+
/// Render the C type into a string that represents the type in C.
194+
public var description: String {
195+
print(placeholder: nil)
196+
}
197+
}

0 commit comments

Comments
 (0)