Odin
Odin copied to clipboard
Missing parapoly variable in argument of `size_of` results in bad codegen
Context
Please provide any relevant information about your setup. This is important in case the issue is not reproducible except for under certain conditions.
Odin: dev-2025-01:20fa9fbd6
OS: openSUSE Tumbleweed, Linux 6.12.9-1-default
CPU: AMD Ryzen 9 5900X 12-Core Processor
RAM: 31999 MiB
Backend: LLVM 19.1.6
Expected Behavior
Compiler error on missing parapoly var.
Current Behavior
No compiler error, generated code results in memory corruption.
Failure Information (for bugs)
Please help provide information about the failure if this is a bug. If it is not a bug, please remove the rest of this template.
Steps to Reproduce
Run the following example with odin test:
package foo
import "base:runtime"
import "core:testing"
Block :: struct ($T: typeid) {
len: int,
cap: int,
data: [0]T,
}
new_block_bad :: proc($T: typeid, cap: int, allocator := context.allocator) -> ^Block(T) {
// missed parapoly type in `size_of(Block)`, compiles and generated code causes consecutive allocs
// to corrupt memory of another block on free
bytes, err := runtime.mem_alloc(size_of(Block) + size_of(T) * cap, align_of(Block(T)), allocator)
// ^^^^^^^^^^^^^^
assert(err == nil)
block := cast(^Block(T))raw_data(bytes)
block.cap = cap
return block
}
new_block_ok :: proc($T: typeid, cap: int, allocator := context.allocator) -> ^Block(T) {
bytes, err := runtime.mem_alloc(size_of(Block(T)) + size_of(T) * cap, align_of(Block(T)), allocator)
assert(err == nil)
block := cast(^Block(T))raw_data(bytes)
block.cap = cap
return block
}
free_block :: proc(block: ^Block($T), allocator := context.allocator) {
runtime.mem_free(block, allocator)
}
block_to_slice :: proc(block: ^Block($T)) -> []T {
return (cast([^]T)&block.data)[:block.cap]
}
CANARY :: 0xAAAA_AAAA
@(test)
test_bad_alloc :: proc(t: ^testing.T) {
b1 := new_block_bad(uint, 7)
b2 := new_block_bad(uint, 8)
s1 := block_to_slice(b1)
for &u in s1 {
u = CANARY
}
s2 := block_to_slice(b2)
for &u in s2 {
u = CANARY
}
free_block(b2)
for u in s1 {
testing.expectf(t, u == CANARY, "corrupt %v -> %v", CANARY, u)
}
free_block(b1)
}
@(test)
test_ok_alloc :: proc(t: ^testing.T) {
b1 := new_block_ok(uint, 7)
b2 := new_block_ok(uint, 8)
s1 := block_to_slice(b1)
for &u in s1 {
u = CANARY
}
s2 := block_to_slice(b2)
for &u in s2 {
u = CANARY
}
free_block(b2)
for u in s1 {
testing.expectf(t, u == CANARY, "corrupt %v -> %v", CANARY, u)
}
free_block(b1)
}