I was testing my code the same way I have for almost a year, I am compiling for wasm32-none-wasm and after a while my basically empty test program crashed as it ran out of memory.
I am using a minimal wasm malloc implementation which is unlikely to be the problem as it never had such issues, and the memory is otherwise completely managed by Swift's classes.
This wouldn't be the first time the compiler caused memory leaks, but before it was easy to narrow it down to variadic generics — this time I have no idea where to look. How do I find what's causing this and if it's the compiler?
The only thing that comes to mind would be adding lazy
properties in a few places, are there issues with those in the embedded mode?
A lot of malloc calls come from result builders:
Due to embedded swift lacking variadic generics, types like VStack have to allocate — so this would be the only place repeatedly calling malloc as I didn't bother with caching it (since when variadic generics are fixed it should be implementable lazily). While this code is not efficient it should not leak memory.
I tested the allocator with this:
UnsafeMutableRawPointer
.allocate(byteCount: 100000000, alignment: 1)
.deallocate()
And the memory usage was consistent, so the allocator is definitely fine. My does not allocate manually so this is almost certainly something wrong with the compiler.
I then commented out the result builder use and the memory leak went away.
this is the code:
@resultBuilder
public struct FlatteningDrawableBuilder {
public static func buildPartialBlock(first: some Drawable) -> [Image] { [first.flatten()] }
public static func buildPartialBlock(first: [Image]) -> [Image] { first }
public static func buildPartialBlock(accumulated: [Image], next: some Drawable) -> [Image] {
var acc = accumulated
acc.append(next.flatten())
return acc
}
public static func buildArray(_ components: [[Image]]) -> [Image] { components.joined() }
}
I think the memory leak happens here