Odin icon indicating copy to clipboard operation
Odin copied to clipboard

Missing parapoly variable in argument of `size_of` results in bad codegen

Open zen3ger opened this issue 8 months ago • 0 comments

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)
}

zen3ger avatar Feb 02 '25 21:02 zen3ger