[WIP] Swift export: support Optional<Range> via MixedAST #KT-82564 Fixed
diff --git a/native/swift/sir-providers/src/org/jetbrains/kotlin/sir/providers/impl/BridgeProvider/TypeBridging.kt b/native/swift/sir-providers/src/org/jetbrains/kotlin/sir/providers/impl/BridgeProvider/TypeBridging.kt index 037b386..1e4e2e2 100644 --- a/native/swift/sir-providers/src/org/jetbrains/kotlin/sir/providers/impl/BridgeProvider/TypeBridging.kt +++ b/native/swift/sir-providers/src/org/jetbrains/kotlin/sir/providers/impl/BridgeProvider/TypeBridging.kt
@@ -654,18 +654,37 @@ class AsOptionalWrapper( val wrappedObject: AnyBridge, ) : Bridge( - wrappedObject.swiftType.optional(), - wrappedObject.typeList.map { AnyBridge.TypePair(it.kotlinType, it.cType.nullable) }, + swiftType = wrappedObject.swiftType.optional(), + typeList = wrappedObject.typeList.map { + // We can't use _Nullable on primitive numbers + val cType = if (it.kotlinType.isPrimitiveNumber) CType.NSNumber.nullable else it.cType.nullable + // If we use NSNumber* in ObjC we have to use NativePtr in Kotlin + val kotlinType = if (it.kotlinType.isPrimitiveNumber) KotlinType.KotlinObject else it.kotlinType + AnyBridge.TypePair(kotlinType, cType) + }, ) { + private val nsNumberBridge by lazy { AsNSNumber((wrappedObject.swiftType as SirNominalType).typeArguments.single()) } override val inKotlinSources: ValueConversion get() = object : ValueConversion { override fun swiftToKotlin(typeNamer: SirTypeNamer, valueExpression: MixedAST): MixedAST { require(wrappedObject is SwiftToKotlinBridge) - return valueExpression.eq(MixedAST.NativeNull).cond( - MixedAST.Null, - wrappedObject.inKotlinSources.swiftToKotlin(typeNamer, valueExpression) - ) + + val wrappedResult = wrappedObject.inKotlinSources.swiftToKotlin(typeNamer, valueExpression) + val condition = if (typeList.size > 1 && wrappedResult is MixedAST.BinaryOp) { + wrappedResult.replace(operator = MixedAST.Operator.OR) { + it.op(MixedAST.Operator.EQUALS, MixedAST.NativeNull) + } + } else { + valueExpression.op(MixedAST.Operator.EQUALS, MixedAST.NativeNull) + } + val result = if (wrappedResult is MixedAST.BinaryOp) { + wrappedResult.replace { nsNumberBridge.inKotlinSources.swiftToKotlin(typeNamer, it) } + } else { + wrappedResult + } + + return condition.cond(MixedAST.Null, result) } override fun kotlinToSwift(typeNamer: SirTypeNamer, valueExpression: MixedAST): MixedAST { @@ -684,6 +703,18 @@ wrappedObject is AsExistential || wrappedObject is AsAnyBridgeable || wrappedObject is AsBlock || wrappedObject is SirCustomTypeTranslatorImpl.RangeBridge ) + val wrappedResult = wrappedObject.inSwiftSources.swiftToKotlin(typeNamer, valueExpression) + return wrappedResult.replace(operator = MixedAST.Operator.COMMA) { + valueExpression.mapSwift { + if (it is MixedAST.BinaryOp) { + it + } else { + nsNumberBridge.inSwiftSources.swiftToKotlin(typeNamer, it) + } + }.op(MixedAST.Operator.SWIFT_ELVIS, wrappedObject.nil()) + } + + return valueExpression.mapSwift { wrappedObject.inSwiftSources.swiftToKotlin(typeNamer, it) }.op(MixedAST.Operator.SWIFT_ELVIS, wrappedObject.nil())
diff --git a/native/swift/sir-providers/src/org/jetbrains/kotlin/sir/providers/impl/BridgeProvider/Types.kt b/native/swift/sir-providers/src/org/jetbrains/kotlin/sir/providers/impl/BridgeProvider/Types.kt index b1c1fe3..3797555 100644 --- a/native/swift/sir-providers/src/org/jetbrains/kotlin/sir/providers/impl/BridgeProvider/Types.kt +++ b/native/swift/sir-providers/src/org/jetbrains/kotlin/sir/providers/impl/BridgeProvider/Types.kt
@@ -8,8 +8,12 @@ internal sealed class CType { abstract fun render(name: String): String - val nullable: CType get() = ((this as? NullabilityAnnotated)?.wrapped ?: this).let { NullabilityAnnotated(it, Nullability.NULLABLE) } - val nonnulll: CType get() = ((this as? NullabilityAnnotated)?.wrapped ?: this).let { NullabilityAnnotated(it, Nullability.NONNULL) } + val nullable: CType + get() = NullabilityAnnotated(unwrapAnnotated(), Nullability.NULLABLE) + val nonnulll: CType + get() = NullabilityAnnotated(unwrapAnnotated(), Nullability.NONNULL) + + fun unwrapAnnotated(): CType = (this as? NullabilityAnnotated)?.wrapped ?: this sealed class Predefined(private val repr: String) : CType() { override fun render(name: String): String = if (name.isBlank()) repr else "$repr $name"
diff --git a/native/swift/sir-providers/src/org/jetbrains/kotlin/sir/providers/impl/tree/MixedAST.kt b/native/swift/sir-providers/src/org/jetbrains/kotlin/sir/providers/impl/tree/MixedAST.kt index 97e2929..66f6b9f 100644 --- a/native/swift/sir-providers/src/org/jetbrains/kotlin/sir/providers/impl/tree/MixedAST.kt +++ b/native/swift/sir-providers/src/org/jetbrains/kotlin/sir/providers/impl/tree/MixedAST.kt
@@ -137,6 +137,7 @@ AS_EXCL(" as! "), ASSIGN(" = "), EQUALS(" == "), + OR(representation = " || "), IN(" in "), SWIFT_ELVIS(" ?? "), RANGE_KOTLIN(" .. "),
diff --git a/native/swift/swift-export-standalone-integration-tests/simple/testData/execution/ranges/ranges.kt b/native/swift/swift-export-standalone-integration-tests/simple/testData/execution/ranges/ranges.kt index 0d04ee6..fd5a463 100644 --- a/native/swift/swift-export-standalone-integration-tests/simple/testData/execution/ranges/ranges.kt +++ b/native/swift/swift-export-standalone-integration-tests/simple/testData/execution/ranges/ranges.kt
@@ -9,6 +9,6 @@ fun simple(some: IntRange): IntRange = some.start - 1 .. some.endInclusive + 1 -//fun nullable(some: IntRange?): IntRange? = some?.let { simple(it) } +fun nullable(some: IntRange?): IntRange? = some?.let { simple(it) } //fun total(list: List<IntRange>): IntRange = list.map { it.start }.min() .. list.map { it.endInclusive }.max()
diff --git a/native/swift/swift-export-standalone-integration-tests/simple/testData/execution/ranges/ranges.swift b/native/swift/swift-export-standalone-integration-tests/simple/testData/execution/ranges/ranges.swift index fb8a406..30cd54e 100644 --- a/native/swift/swift-export-standalone-integration-tests/simple/testData/execution/ranges/ranges.swift +++ b/native/swift/swift-export-standalone-integration-tests/simple/testData/execution/ranges/ranges.swift
@@ -16,11 +16,10 @@ @Test func nullable() throws { - // Currently crashes with SIGSEGV - //let n1 = nullable(some: -10 ... -5) - //let n2 = nullable(some: nil) - //#expect(n1 == -11 ... -4) - //#expect(n2 == nil) + let n1 = nullable(some: -10 ... -5) + let n2 = nullable(some: nil) + #expect(n1 == -11 ... -4) + #expect(n2 == nil) } @Test
diff --git a/native/swift/swift-export-standalone-integration-tests/simple/testData/generation/ranges/golden_result/main/main.h b/native/swift/swift-export-standalone-integration-tests/simple/testData/generation/ranges/golden_result/main/main.h index 66b3e33..1b55c0e 100644 --- a/native/swift/swift-export-standalone-integration-tests/simple/testData/generation/ranges/golden_result/main/main.h +++ b/native/swift/swift-export-standalone-integration-tests/simple/testData/generation/ranges/golden_result/main/main.h
@@ -7,12 +7,18 @@ void * generation_ranges_ranges_acceptClosed__TypesOfArguments__Swift_ClosedRange_Swift_Int32___(int32_t range_1, int32_t range_2); +void * generation_ranges_ranges_acceptNullable__TypesOfArguments__Swift_Optional_Swift_ClosedRange_Swift_Int32____(NSNumber * _Nullable range_1, NSNumber * _Nullable range_2); + void * generation_ranges_ranges_bar(); void * generation_ranges_ranges_baz(); void * generation_ranges_ranges_foo(); +NSArray<id> * generation_ranges_ranges_mapper__TypesOfArguments__Swift_Array_Swift_ClosedRange_Swift_Int64____(NSArray<id> * arg); + +void * generation_ranges_ranges_total__TypesOfArguments__Swift_Array_Swift_ClosedRange_Swift_Int32____(NSArray<id> * list); + void * generation_ranges_ranges_unsupported(); int32_t kotlin_ranges_closedRange_getEndInclusive_int(void * nativePtr);
diff --git a/native/swift/swift-export-standalone-integration-tests/simple/testData/generation/ranges/golden_result/main/main.kt b/native/swift/swift-export-standalone-integration-tests/simple/testData/generation/ranges/golden_result/main/main.kt index adfae94..c50552c 100644 --- a/native/swift/swift-export-standalone-integration-tests/simple/testData/generation/ranges/golden_result/main/main.kt +++ b/native/swift/swift-export-standalone-integration-tests/simple/testData/generation/ranges/golden_result/main/main.kt
@@ -18,6 +18,13 @@ return kotlin.native.internal.ref.createRetainedExternalRCRef(_result) } +@ExportedBridge("generation_ranges_ranges_acceptNullable__TypesOfArguments__Swift_Optional_Swift_ClosedRange_Swift_Int32____") +public fun generation_ranges_ranges_acceptNullable__TypesOfArguments__Swift_Optional_Swift_ClosedRange_Swift_Int32____(range_1: kotlin.native.internal.NativePtr, range_2: kotlin.native.internal.NativePtr): kotlin.native.internal.NativePtr { + val __range = if (range_1 == kotlin.native.internal.NativePtr.NULL || range_2 == kotlin.native.internal.NativePtr.NULL) null else interpretObjCPointer<Int>(range_1) .. interpretObjCPointer<Int>(range_2) + val _result = generation.ranges.ranges.acceptNullable(__range) + return if (_result == null) kotlin.native.internal.NativePtr.NULL else kotlin.native.internal.ref.createRetainedExternalRCRef(_result) +} + @ExportedBridge("generation_ranges_ranges_bar") public fun generation_ranges_ranges_bar(): kotlin.native.internal.NativePtr { val _result = generation.ranges.ranges.bar() @@ -36,6 +43,20 @@ return kotlin.native.internal.ref.createRetainedExternalRCRef(_result) } +@ExportedBridge("generation_ranges_ranges_mapper__TypesOfArguments__Swift_Array_Swift_ClosedRange_Swift_Int64____") +public fun generation_ranges_ranges_mapper__TypesOfArguments__Swift_Array_Swift_ClosedRange_Swift_Int64____(arg: kotlin.native.internal.NativePtr): kotlin.native.internal.NativePtr { + val __arg = interpretObjCPointer<kotlin.collections.List<kotlin.ranges.LongRange>>(arg) + val _result = generation.ranges.ranges.mapper(__arg) + return _result.objcPtr() +} + +@ExportedBridge("generation_ranges_ranges_total__TypesOfArguments__Swift_Array_Swift_ClosedRange_Swift_Int32____") +public fun generation_ranges_ranges_total__TypesOfArguments__Swift_Array_Swift_ClosedRange_Swift_Int32____(list: kotlin.native.internal.NativePtr): kotlin.native.internal.NativePtr { + val __list = interpretObjCPointer<kotlin.collections.List<kotlin.ranges.IntRange>>(list) + val _result = generation.ranges.ranges.total(__list) + return kotlin.native.internal.ref.createRetainedExternalRCRef(_result) +} + @ExportedBridge("generation_ranges_ranges_unsupported") public fun generation_ranges_ranges_unsupported(): kotlin.native.internal.NativePtr { val _result = generation.ranges.ranges.unsupported()
diff --git a/native/swift/swift-export-standalone-integration-tests/simple/testData/generation/ranges/golden_result/main/main.swift b/native/swift/swift-export-standalone-integration-tests/simple/testData/generation/ranges/golden_result/main/main.swift index 00942ab..87a1102 100644 --- a/native/swift/swift-export-standalone-integration-tests/simple/testData/generation/ranges/golden_result/main/main.swift +++ b/native/swift/swift-export-standalone-integration-tests/simple/testData/generation/ranges/golden_result/main/main.swift
@@ -17,6 +17,12 @@ let _result = generation_ranges_ranges_acceptClosed__TypesOfArguments__Swift_ClosedRange_Swift_Int32___(range.lowerBound, range.upperBound) return kotlin_ranges_openEndRange_getStart_int(_result) ..< kotlin_ranges_openEndRange_getEndExclusive_int(_result) } + public static func acceptNullable( + range: Swift.ClosedRange<Swift.Int32>? + ) -> Swift.ClosedRange<Swift.Int32>? { + let _result = generation_ranges_ranges_acceptNullable__TypesOfArguments__Swift_Optional_Swift_ClosedRange_Swift_Int32____(range.map { it in NSNumber(value: it.lowerBound) } ?? nil, range.map { it in NSNumber(value: it.upperBound) } ?? nil) + return { switch _result { case nil: .none; case let res: kotlin_ranges_intRange_getStart_int(res) ... kotlin_ranges_intRange_getEndInclusive_int(res); } }() + } public static func bar() -> Swift.ClosedRange<Swift.Int32> { let _result = generation_ranges_ranges_bar() return kotlin_ranges_closedRange_getStart_int(_result) ... kotlin_ranges_closedRange_getEndInclusive_int(_result) @@ -29,6 +35,17 @@ let _result = generation_ranges_ranges_foo() return kotlin_ranges_intRange_getStart_int(_result) ... kotlin_ranges_intRange_getEndInclusive_int(_result) } + public static func mapper( + arg: [Swift.ClosedRange<Swift.Int64>] + ) -> [Swift.ClosedRange<Swift.Int32>] { + return generation_ranges_ranges_mapper__TypesOfArguments__Swift_Array_Swift_ClosedRange_Swift_Int64____(arg) as! Swift.Array<Swift.ClosedRange<Swift.Int32>> + } + public static func total( + list: [Swift.ClosedRange<Swift.Int32>] + ) -> Swift.ClosedRange<Swift.Int32> { + let _result = generation_ranges_ranges_total__TypesOfArguments__Swift_Array_Swift_ClosedRange_Swift_Int32____(list) + return kotlin_ranges_intRange_getStart_int(_result) ... kotlin_ranges_intRange_getEndInclusive_int(_result) + } public static func unsupported() -> any ExportedKotlinPackages.kotlin.ranges.ClosedRange { return KotlinRuntime.KotlinBase.__createProtocolWrapper(externalRCRef: generation_ranges_ranges_unsupported()) as! any ExportedKotlinPackages.kotlin.ranges.ClosedRange }
diff --git a/native/swift/swift-export-standalone-integration-tests/simple/testData/generation/ranges/ranges.kt b/native/swift/swift-export-standalone-integration-tests/simple/testData/generation/ranges/ranges.kt index cba0978..c32cfc2 100644 --- a/native/swift/swift-export-standalone-integration-tests/simple/testData/generation/ranges/ranges.kt +++ b/native/swift/swift-export-standalone-integration-tests/simple/testData/generation/ranges/ranges.kt
@@ -8,8 +8,16 @@ return 5L ..< 10L } +fun mapper(arg: List<LongRange>): List<IntRange> = + arg.map { it.start.toInt() .. it.endInclusive.toInt() } + +fun total(list: List<IntRange>): IntRange = list.map { it.start }.min() .. list.map { it.endInclusive }.max() + fun accept(range: IntRange): LongRange = range.start.toLong() .. range.endInclusive.toLong() +fun acceptNullable(range: IntRange?): IntRange? = + if (range == null || range.isEmpty()) null else range + fun acceptClosed(range: ClosedRange<Int>): OpenEndRange<Int> = range.start ..< range.endInclusive fun unsupported(): ClosedRange<String> = "alpha" .. "omega"