Skip to content

Bug in Embedded/ARC/WebAssembly combination? #73768

@turbolent

Description

@turbolent

Description

I'm seeing some strange behaviour when using classes/ARC in WebAssembly/Embedded (might be unrelated):
In some cases calls to deinit seem to be missing, and in some cases, deinit seems to be called before a last use of the instance.

Reproduction

Missing deinit

@_extern(wasm, module: "foo", name: "a")
@_extern(c)
func foo_a()

@_extern(wasm, module: "foo", name: "b")
@_extern(c)
func foo_b()

@_extern(wasm, module: "foo", name: "c")
@_extern(c)
func foo_c()

class Foo {

    init() {
        foo_a()
    }

    func foo() {
        foo_b()
    }

    deinit {
        foo_c()
    }
}


@_expose(wasm, "test1")
@_cdecl("test1")
func test1() {
    let foo = Foo()
    foo.foo()
}

gets compiled to (pseudo-code produced by wasm-decompile):

export function test1() {
  foo_a();
  foo_b();
}

Why is the call to foo_c in deinit missing?

Deinit before last use

@_extern(wasm, module: "bar", name: "new")
@_extern(c)
func bar_new() -> UnsafeMutablePointer<Int>

@_extern(wasm, module: "bar", name: "use")
@_extern(c)
func bar_use(_ ptr: UnsafeMutablePointer<Int>)

@_extern(wasm, module: "bar", name: "free")
@_extern(c)
func bar_free(_ ptr: UnsafeMutablePointer<Int>)

class Bar {

    let ptr: UnsafeMutablePointer<Int>

    init() {
        ptr = bar_new()
    }

    func use() {
        bar_use(ptr)
    }

    deinit {
        bar_free(ptr)
    }
}


@_expose(wasm, "test2")
@_cdecl("test2")
func test2() {
    let bar = Bar()
    bar.use()
}

gets compiled to:

export function test2() {
  var a:int = bar_new();
  bar_free(a);
  bar_use(a);
}

Note the order: Why is bar_free called before bar_use?

Is this expected? If so, how so? If not, are these bugs? They might not be related to WebAssembly or Embedded at all, it is just the combination I'm trying out and can easily reproduce locally.

Expected behavior

I had assumed that the results are:

export function test1() {
  foo_a();
  foo_b();
  foo_c();
}

and

export function test2() {
  var a:int = bar_new();
  bar_use(a);
  bar_free(a);
}

Environment

Swift version 6.0-dev (LLVM cef183591317ec7, Swift 66e311074bff491) / swift-DEVELOPMENT-SNAPSHOT-2024-05-15-a-ubuntu22.04, and the following flags in the Package.swift file:

            cSettings: [
                .unsafeFlags(["-fdeclspec"])
            ],
            swiftSettings: [
                .enableExperimentalFeature("Embedded"),
                .interoperabilityMode(.C),
                .unsafeFlags([
                    "-wmo",
                    "-disable-cmo",
                    "-Xfrontend", "-disable-stack-protector"
                ])
            ],
            linkerSettings: [
                .unsafeFlags([
                    "-Xclang-linker", "-nostdlib",
                    "-Xlinker", "--no-entry"
                ])
            ]

Additional information

Asked about this in the forum https://forums.swift.org/t/bug-in-embedded-arc-webassembly-combination/71967.
@kubamracek asked me to open a bug report

Metadata

Metadata

Assignees

Labels

bugA deviation from expected or documented behavior. Also: expected but undesirable behavior.embeddedEmbedded Swifttriage neededThis issue needs more specific labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions