1
0
Fork 0

Fix gint64/guint64 marshalling for macOS glib builds

This commit is contained in:
Brendan Szymanski 2026-08-12 02:17:23 -04:00
parent 298e5a4b43
commit 5866ed57e9
3 changed files with 40 additions and 4 deletions

View file

@ -1177,6 +1177,17 @@ private func renderWrapperExpr(for p: ParameterPlan, rawName: String, ownerIsInt
return "String(cString: \(rawName))"
case .boolToGboolean:
return "\(rawName) != 0"
case .numericCast:
// The raw C parameter is `cSwiftType` (e.g. the literal `gint64`
// typedef), which is not always the same nominal type as the
// public `swiftType` (`Int`) exposed on the closure. `numericCast`
// alone can't infer its target here (this becomes a standalone
// `let` binding, with no call-site context to infer from), so
// construct the public type directly instead equivalent to
// `numericCast` for the always-non-optional scalar types this
// marshalling case covers.
let baseType = p.mapping.swiftType.hasSuffix("?") ? String(p.mapping.swiftType.dropLast()) : p.mapping.swiftType
return "\(baseType)(\(rawName))"
default:
return rawName
}
@ -2088,6 +2099,13 @@ private func gvalueGetterBody(swiftType: String, girName: String,
resultExpr = hasCopyFunction
? "\(swiftType)(retaining: g_value_get_boxed(&gvalue))"
: "\(swiftType)(takingOwnership: g_value_get_boxed(&gvalue))"
} else if suffix == "int64" || suffix == "uint64" {
// `g_value_get_int64`/`_uint64` return the literal `gint64`/`guint64`
// typedef, which glib defines as `long long` on some platforms and
// plain `long` on others (see `TypeMapper.int64Mapping`) bridge
// through `numericCast` so this compiles regardless of which one
// the Clang importer resolved it to.
resultExpr = "numericCast(g_value_get_\(suffix)(&gvalue))"
} else {
resultExpr = "g_value_get_\(suffix)(&gvalue)"
}
@ -2127,6 +2145,11 @@ private func gvalueSetterBody(swiftType: String, girName: String,
: "g_value_set_object(&gvalue, newValue.pointer)"
} else if suffix == "boxed" {
setCall = "g_value_set_boxed(&gvalue, newValue.pointer)"
} else if suffix == "int64" || suffix == "uint64" {
// See the getter's matching branch: `g_value_set_int64`/`_uint64`
// take the literal `gint64`/`guint64` typedef, which is not always
// the same nominal Swift type as the public `Int`/`UInt` property.
setCall = "g_value_set_\(suffix)(&gvalue, numericCast(newValue))"
} else {
setCall = "g_value_set_\(suffix)(&gvalue, newValue)"
}

View file

@ -594,7 +594,16 @@ extension Mapping {
gvalue: GValueOps(typeMacro: "G_TYPE_INT",
getterSuffix: "int", setterSuffix: "int"))
static let int64Mapping = Mapping(
swiftType: "Int", cSwiftType: "Int", // gint64 Int on LP64, Int64 on LLP64
// `cSwiftType` is the literal glib typedef name, not a guessed Swift
// equivalent: glib defines `gint64` unconditionally as `long long`
// on some builds (e.g. Homebrew's glib on macOS) and as plain `long`
// on others (e.g. glibc-targeting Linux builds), so it imports into
// Swift as `Int64` on one and `Int` on the other. Using the raw
// typedef name lets the Clang importer resolve the exact pointee
// type for out-param locals (`var out0: gint64 = 0; &out0`) on
// every platform; `numericCast` still bridges it to the public
// `Int` at the Swift boundary.
swiftType: "Int", cSwiftType: "gint64",
marshalIn: .numericCast(targetType: "Int"), marshalOut: .numericCast(fromType: "Int"),
gvalue: GValueOps(typeMacro: "G_TYPE_INT64",
getterSuffix: "int64", setterSuffix: "int64"))
@ -610,7 +619,11 @@ extension Mapping {
gvalue: GValueOps(typeMacro: "G_TYPE_UINT",
getterSuffix: "uint", setterSuffix: "uint"))
static let uint64Mapping = Mapping(
swiftType: "UInt", cSwiftType: "UInt", // guint64 UInt on LP64, UInt64 on LLP64
// See `int64Mapping`: `cSwiftType` is the literal `guint64` typedef
// name so out-param locals get the exact platform pointee type
// regardless of whether glib defines it as `unsigned long long` or
// `unsigned long`.
swiftType: "UInt", cSwiftType: "guint64",
marshalIn: .numericCast(targetType: "UInt"), marshalOut: .numericCast(fromType: "UInt"),
gvalue: GValueOps(typeMacro: "G_TYPE_UINT64",
getterSuffix: "uint64", setterSuffix: "uint64"))

View file

@ -146,7 +146,7 @@ struct TypeMapperTests {
PrimitiveCase(type: .int8, swiftType: "Int8", cSwiftType: "Int8", gvalueTypeMacro: nil, gvalueGetter: nil),
PrimitiveCase(type: .int16, swiftType: "Int16", cSwiftType: "Int16", gvalueTypeMacro: nil, gvalueGetter: nil),
PrimitiveCase(type: .int32, swiftType: "Int32", cSwiftType: "Int32", gvalueTypeMacro: "G_TYPE_INT", gvalueGetter: "int"),
PrimitiveCase(type: .int64, swiftType: "Int", cSwiftType: "Int", gvalueTypeMacro: "G_TYPE_INT64", gvalueGetter: "int64",
PrimitiveCase(type: .int64, swiftType: "Int", cSwiftType: "gint64", gvalueTypeMacro: "G_TYPE_INT64", gvalueGetter: "int64",
marshalIn: .numericCast(targetType: "Int"), marshalOut: .numericCast(fromType: "Int")),
])
func signedIntegers(_ tc: PrimitiveCase) throws {
@ -167,7 +167,7 @@ struct TypeMapperTests {
PrimitiveCase(type: .uint8, swiftType: "UInt8", cSwiftType: "UInt8", gvalueTypeMacro: nil, gvalueGetter: nil),
PrimitiveCase(type: .uint16, swiftType: "UInt16", cSwiftType: "UInt16", gvalueTypeMacro: nil, gvalueGetter: nil),
PrimitiveCase(type: .uint32, swiftType: "UInt32", cSwiftType: "UInt32", gvalueTypeMacro: "G_TYPE_UINT", gvalueGetter: "uint"),
PrimitiveCase(type: .uint64, swiftType: "UInt", cSwiftType: "UInt", gvalueTypeMacro: "G_TYPE_UINT64", gvalueGetter: "uint64",
PrimitiveCase(type: .uint64, swiftType: "UInt", cSwiftType: "guint64", gvalueTypeMacro: "G_TYPE_UINT64", gvalueGetter: "uint64",
marshalIn: .numericCast(targetType: "UInt"), marshalOut: .numericCast(fromType: "UInt")),
])
func unsignedIntegers(_ tc: PrimitiveCase) throws {