skiptools / skip

Skip transpiler for creating SwiftUI apps for iOS and Android

Home Page:https://skip.tools

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Mutating enum self

pnewell opened this issue · comments

This may be impossible to get around, but technically structs and enums in Swift work the same way, but it looks like trying to mutate an enum instance doesn't work. The following gives me the error "Variable expected" for "this =". I believe it is because the enum is not adopting MutatingStruct.

enum Count {
    case one
    case two
    case three
    
    mutating func reset() {
        self = .one
    }
}

I don't get a compilation error, but I do see a test case based on it passing on the Swift side and failing on the Kotlin side:

class MutatingEnumTests: XCTestCase {
    enum Count {
        case one
        case two
        case three

        mutating func reset() {
            self = .one
        }
    }

    func testMutatingEnum() throws {
        var b = Count.three
        XCTAssertEqual(Count.three, b)
        b.reset()
        XCTAssertEqual(Count.one, b)
    }
}

This transpiles into MutatingEnumTests.kt:

internal class MutatingEnumTests: XCTestCase {
    internal enum class Count {
        one,
        two,
        three;

        internal fun reset(): Unit = assignfrom(MutatingEnumTests.Count.one)

        private fun assignfrom(target: MutatingEnumTests.Count) = Unit
    }

    @Test
    internal fun testMutatingEnum() {
        var b = Count.three
        XCTAssertEqual(Count.three, b)
        b.reset()
        XCTAssertEqual(Count.one, b)
    }
}

This is a transpiler bug, so I'll move this issue over to the skip repo. Thanks for reporting it!

For completeness, I'll point out that the equivalent test does pass with structs:

@available(macOS 13, *)
final class MutatingStructTests: XCTestCase {
    struct Count : Equatable {
        private let count: Int

        static let one = Count(count: 1)
        static let two = Count(count: 2)
        static let three = Count(count: 3)

        mutating func reset() {
            self = .one
        }
    }

    func testMutatingStruct() throws {
        var b = Count.three
        XCTAssertEqual(Count.three, b)
        b.reset()
        XCTAssertEqual(Count.one, b)
    }
}

which transpiles to:

internal class MutatingStructTests: XCTestCase {
    internal class Count: MutableStruct {
        private var count: Int

        internal fun reset() {
            willmutate()
            try {
                assignfrom(MutatingStructTests.Count.one)
            } finally {
                didmutate()
            }
        }

        private constructor(count: Int) {
            this.count = count
        }

        override var supdate: ((Any) -> Unit)? = null
        override var smutatingcount = 0
        override fun scopy(): MutableStruct = MutatingStructTests.Count(count)

        private fun assignfrom(target: MutatingStructTests.Count) {
            this.count = target.count
        }

        override fun equals(other: Any?): Boolean {
            if (other !is MutatingStructTests.Count) return false
            return count == other.count
        }

        companion object {

            internal val one = Count(count = 1)
            internal val two = Count(count = 2)
            internal val three = Count(count = 3)
        }
    }

    @Test
    internal fun testMutatingStruct() {
        var b = Count.three.sref()
        XCTAssertEqual(Count.three, b)
        b.reset()
        XCTAssertEqual(Count.one, b)
    }
}

So the issue is specific to enums, not to all value types.

We're not going to be able to support this and still use Kotlin enums. I added detection and an error message to that effect to the transpiler.