diff --git a/LANGUAGE.md b/LANGUAGE.md index b1697c0..8201b30 100644 --- a/LANGUAGE.md +++ b/LANGUAGE.md @@ -35,7 +35,7 @@ roadmap and milestone history. - arrays `[N]T`, inferred-count arrays `[_]T`, sentinel arrays `[N;S]T`, compile-time expression array counts, slices `[]T` / `[;S]T`, single-item pointers `@T`, many-item pointers `*T`, and sentinel many-item pointers `[*;S]T` - pointer mutability via `mut`, optional pointers as nullable pointers, pointer arithmetic for many-item pointers, postfix dereference `^`, and trapping optional unwrap `?` - pointer-to-array `.len`, indexing, slicing, `.ptr` on slices and pointers-to-arrays, implicit address-taking for array-variable slices, and pointer/slice sentinel weakening -- `ptrcast!(T, ptr)` as a first-pass pointer-child retype that preserves optionality, pointer kind, mutability, and sentinel shape +- `ptrcast!(ptr)` infers its complete pointer result from context; `ptrcast!(P, ptr)` supplies `P` explicitly; both change the child type, then permit only ordinary implicit pointer conversions to the requested target - unsafe `constcast!(value)` for restoring mutability to pointers, optional pointers, and slices without changing their child type or shape - UTF-8 string literals as immutable pointers to static zero-terminated byte arrays, supporting `\\`, `\"`, `\n`, `\r`, `\t`, `\0`, and `\xNN` escapes, plus raw backtick multiline strings - narrow immutable zero-terminated byte pointer/slice conversion to `*c_char` / `?*c_char` without general `u8`/`c_char` interchange @@ -289,7 +289,7 @@ exactly once. Bare functions named `memcopy` or `memset` remain ordinary user fu - non-plain C record layouts such as bitfields, packed records, flexible arrays, qualified fields, and C variadic record arguments - arenas, pools, build-mode heap policy, and escaping-allocation diagnostics - recursive type factories, reflection payloads beyond records/enums, and type-producing unions/enums -- broader Zig-style pointer/result casts beyond V1 `ptrcast!(T, ptr)` +- pointer-shape-changing casts beyond the safe implicit conversions accepted by `ptrcast!` - sum-type ABI/layout polish, including dynamic tag-width shrinking, all-void channel collapse, and cross-module global-id determinism - backed/C enum composition and must-consume fallible linting - result-to-argument type-demand propagation through function call boundaries diff --git a/README.md b/README.md index 9620c07..e4ca10e 100644 --- a/README.md +++ b/README.md @@ -228,7 +228,7 @@ Current prototype features: - `#` comments - Immutable inferred/typed `::` bindings, mutable inferred/typed `:=` locals/globals, and `_` sinks - Exact-width signed/unsigned integers, `f32`, `f64`, `isize`, `usize`, and loose integer-constrained `int` -- Target-dependent atomic `c_*` primitive types, `c_func`, complete `c_struct`, `opaque`, `anyopaque`, and V1 `ptrcast!(T, ptr)` +- Target-dependent atomic `c_*` primitive types, `c_func`, complete `c_struct`, `opaque`, `anyopaque`, and contextual or explicit full-result `ptrcast!` - Arrays, sentinel arrays, single-item pointers, many-item pointers, sentinel many-item pointers, slices, sentinel slices, strings, character literals, optionals, and native structs - String literals as immutable pointers to static zero-terminated byte arrays - Pointer-preserving `.ptr`/`.len`, pointer-to-array indexing and slicing, postfix pointer dereference and optional unwrap, and keyed struct literals diff --git a/TODO.md b/TODO.md index 0148122..412ed34 100644 --- a/TODO.md +++ b/TODO.md @@ -176,8 +176,8 @@ - `Name :: opaque` is the incomplete nominal record spelling; bodyless `c_struct` is invalid - `anyopaque` is the erased object type used behind pointers for C `void*` and allocator contexts - C `void` function results remain `void`; C `void*` / `const void*` import and render as `?*mut anyopaque` / `?*anyopaque` - - `ptrcast!(T, ptr)` preserves pointer shape and only changes the child type in v1 - - future direction: generalize toward Zig-style arbitrary pointer-result casts once casts have a broader result-type story + - `ptrcast!(ptr)` infers its complete pointer result from context; `ptrcast!(P, ptr)` accepts an explicit complete pointer type + - both forms change the child type, then permit only ordinary implicit pointer conversions to the requested target; broader pointer-shape changes remain deferred 12. `undefined` as inspired by zig (implemented): - allow mutable local and global declarations with `undefined` @@ -973,7 +973,7 @@ For casts that bypass safety checks, Honey provides builtin functions: | -- | -- | -- | | `truncate(x, T)` | Keep low bits, discard rest | Never | | `bitcast(x, T)` | Reinterpret bits, no cast | Sizes don't match (compile error) | -| `ptrcast!(T, p)` | Change a pointer's child type while preserving its shape | Invalid child or non-pointer operand (compile error) | +| `ptrcast!(p)` / `ptrcast!(P, p)` | Infer or explicitly supply a complete pointer target, changing the child before ordinary pointer coercion | Missing target context, invalid target, incompatible shape, or non-pointer operand (compile error) | | `constcast!(p)` | Restore pointer or slice mutability | Non-pointer/slice operand (compile error) | ```honey @@ -989,8 +989,10 @@ bits := bitcast(f, u32) # IEEE 754 representation # pointer casts buf *u8 := get_buffer() -ints *u32 := ptrcast!(u32, buf) # element type change, same pointer shape -writable *mut u8 := constcast!(buf) # explicit unsafe mutability restoration +ints *u32 := ptrcast!(buf) # target inferred from the declaration +raw ?*mut anyopaque := get_raw_buffer() +bytes := ptrcast!(?*mut u8, raw) # complete target supplied explicitly +writable *mut u8 := constcast!(buf) # explicit unsafe mutability restoration ``` ## A word on multi-unwrap diff --git a/compiler/checker/checker.odin b/compiler/checker/checker.odin index f1d35fa..7bb56ca 100644 --- a/compiler/checker/checker.odin +++ b/compiler/checker/checker.odin @@ -934,6 +934,30 @@ valid_ptrcast_child :: proc(checker: ^Checker, value: types.Type) -> bool { types.is_opaque_struct(value, &checker.module.types)) } +ptrcast_target_child :: proc(checker: ^Checker, value: types.Type) -> (types.Type, bool) { + item, ok := types.node(&checker.module.types, types.resolve_alias(value, &checker.module.types)) + if !ok { + return types.INVALID, false + } + if item.kind == .Optional { + item, ok = types.node(&checker.module.types, types.resolve_alias(item.child, &checker.module.types)) + } + if !ok || item.kind != .Pointer || !valid_ptrcast_child(checker, item.child) { + return types.INVALID, false + } + return item.child, true +} + +ptrcast_retyped_source :: proc(checker: ^Checker, source, target: types.Type) -> (types.Type, bool) { + child, child_ok := ptrcast_target_child(checker, target) + if !child_ok { + return types.INVALID, false + } + retyped, retyped_ok := types.replace_pointer_child(&checker.module.types, source, child) + return retyped, retyped_ok && + can_implicitly_convert_type(checker, retyped, target) +} + valid_layout_type :: proc(checker: ^Checker, value: types.Type) -> bool { return types.is_runtime_value(value, &checker.module.types) } @@ -5655,18 +5679,20 @@ infer_expr :: proc( continue } if is_ptrcast_call(checker, expr) { - if len(expr.args) != 2 { + if len(expr.args) != 1 && len(expr.args) != 2 { last = types.INVALID _ = pop(&stack) continue } - child, child_ok := resolve_type_argument(checker, expr.args[0], pkg, file) - operand := infer_nested_expr(checker, expr.args[1], locals, pkg, file, demanded, local_types) - result := types.INVALID - if child_ok && valid_ptrcast_child(checker, child) { - result, _ = types.replace_pointer_child(&checker.module.types, operand, child) + target := frame.expected + operand_index := 0 + if len(expr.args) == 2 { + target, _ = resolve_type_argument(checker, expr.args[0], pkg, file) + operand_index = 1 } - last = result + operand := infer_nested_expr(checker, expr.args[operand_index], locals, pkg, file, demanded, local_types) + _, target_ok := ptrcast_retyped_source(checker, operand, target) + last = target if target_ok else types.INVALID _ = pop(&stack) continue } @@ -9802,28 +9828,39 @@ build_expr :: proc( continue } if is_ptrcast_call(checker, expr) { - if len(expr.args) != 2 { - id := source.addf(checker.diagnostics, expr.span, "ptrcast! expects 2 arguments, got %d", len(expr.args)) + if len(expr.args) != 1 && len(expr.args) != 2 { + id := source.addf(checker.diagnostics, expr.span, "ptrcast! expects 1 or 2 arguments, got %d", len(expr.args)) last = invalid_hir_expr(checker, expr.span, id) _ = pop(&stack) continue } - target, target_ok := resolve_type_argument(checker, expr.args[0], pkg, file) - if !target_ok { - id := source.add(checker.diagnostics, checker.ast_module.exprs[expr.args[0]].span, "ptrcast! target must be a type") + target := frame.expected + operand_index := 0 + if len(expr.args) == 2 { + target_ok: bool + target, target_ok = resolve_type_argument(checker, expr.args[0], pkg, file) + if !target_ok { + id := source.add(checker.diagnostics, checker.ast_module.exprs[expr.args[0]].span, "ptrcast! target must be a type") + last = invalid_hir_expr(checker, expr.span, id) + _ = pop(&stack) + continue + } + operand_index = 1 + } else if !types.is_valid(target) { + id := source.add(checker.diagnostics, expr.span, "cannot infer ptrcast! target type; pass a pointer type explicitly") last = invalid_hir_expr(checker, expr.span, id) _ = pop(&stack) continue } - if !valid_ptrcast_child(checker, target) { - id := source.addf(checker.diagnostics, checker.ast_module.exprs[expr.args[0]].span, "ptrcast! target must be a sized runtime object type, got %s", type_label(checker, target)) + if _, target_ok := ptrcast_target_child(checker, target); !target_ok { + id := source.addf(checker.diagnostics, expr.span, "ptrcast! target must be a pointer or optional pointer to a sized runtime object type, got %s", type_label(checker, target)) last = invalid_hir_expr(checker, expr.span, id) _ = pop(&stack) continue } stack[frame_index].target_type = target stack[frame_index].stage = 9 - append(&stack, Build_Expr_Frame{expr=expr.args[1], expected=types.INVALID, template=ast.INVALID_FUNCTION}) + append(&stack, Build_Expr_Frame{expr=expr.args[operand_index], expected=types.INVALID, template=ast.INVALID_FUNCTION}) continue } if is_intrinsic_call(checker, expr, "constcast") { @@ -10556,20 +10593,30 @@ build_expr :: proc( _ = pop(&stack) } if frame.stage == 9 { - result, ok := types.replace_pointer_child(&checker.module.types, checker.module.exprs[last].type, frame.target_type) - if !ok { + actual := checker.module.exprs[last].type + retyped, ok := ptrcast_retyped_source(checker, actual, frame.target_type) + if !types.is_pointer(actual, &checker.module.types) && + !types.is_optional_pointer(actual, &checker.module.types) { id := source.add(checker.diagnostics, expr.span, "ptrcast! operand must be a pointer or optional pointer") last = invalid_hir_expr(checker, expr.span, id) + } else if !ok { + id := source.addf( + checker.diagnostics, expr.span, + "ptrcast! cannot convert pointer shape from %s to %s", + type_label(checker, actual), type_label(checker, frame.target_type), + ) + last = invalid_hir_expr(checker, expr.span, id) } else { - last = add_hir_expr(checker, hir.Expr{ + cast_expr := add_hir_expr(checker, hir.Expr{ kind=.Pointer_Cast, span=expr.span, - type=result, + type=retyped, left=last, target=hir.INVALID_REF, right=hir.INVALID_EXPR, diagnostic=source.INVALID_DIAGNOSTIC, }) + last = coerce_expr(checker, cast_expr, frame.target_type, expr.span) } _ = pop(&stack) } diff --git a/compiler_tests.odin b/compiler_tests.odin index 5a357b3..a233489 100644 --- a/compiler_tests.odin +++ b/compiler_tests.odin @@ -2498,19 +2498,21 @@ main func() void { opaque_anyopaque_and_ptrcast_compile_and_lower :: proc(t: ^testing.T) { text := `Handle :: opaque take func(_ ?*mut anyopaque) void {} +take_bytes func(_ ?*mut u8) void {} use_handle func(_ ?@mut Handle) void {} main func() void { values [2]mut u8 := [1, 2] raw ?*mut anyopaque := (&values).ptr - bytes ?*mut u8 := ptrcast!(u8, raw) + bytes ?*mut u8 := ptrcast!(raw) take(bytes) + take_bytes(ptrcast!(raw)) if bytes |p| { p[1] = 5 } one u8 := 1 single ?@mut anyopaque := &one - typed ?@mut u8 := ptrcast!(u8, single) + typed ?@mut u8 := ptrcast!(?@mut u8, single) if typed |p| { p^ = 2 } @@ -2545,8 +2547,8 @@ main func() void { } testing.expect_value(t, len(diagnostics.items), 0) - testing.expect_value(t, hir_casts, 2) - testing.expect_value(t, ir_casts, 2) + testing.expect_value(t, hir_casts, 3) + testing.expect_value(t, ir_casts, 3) } @(test) @@ -2555,10 +2557,11 @@ anyopaque_by_value_and_invalid_ptrcasts_are_rejected :: proc(t: ^testing.T) { main func() void { raw ?*mut anyopaque := null value anyopaque := undefined - _ = ptrcast!(void, raw) - _ = ptrcast!(anyopaque, raw) - _ = ptrcast!(Callback, raw) - _ = ptrcast!(u8, 1) + _ = ptrcast!(raw) + _ = ptrcast!(u8, raw) + _ = ptrcast!(?*mut void, raw) + _ = ptrcast!(?@mut u8, raw) + _ = ptrcast!(?*mut u8, 1) _ = ptrcast!(1, raw) } ` @@ -2575,19 +2578,25 @@ main func() void { defer hir.destroy_module(&hir_module) found_by_value := false + found_inference := false found_bad_target := false found_bad_operand := false found_target_type := false + found_shape := false for diagnostic in diagnostics.items { found_by_value = found_by_value || strings.contains(diagnostic.message, "could not infer a concrete type") - found_bad_target = found_bad_target || strings.contains(diagnostic.message, "ptrcast! target must be a sized runtime object type") + found_inference = found_inference || strings.contains(diagnostic.message, "cannot infer ptrcast! target type") + found_bad_target = found_bad_target || strings.contains(diagnostic.message, "ptrcast! target must be a pointer or optional pointer") found_bad_operand = found_bad_operand || strings.contains(diagnostic.message, "ptrcast! operand must be a pointer") found_target_type = found_target_type || strings.contains(diagnostic.message, "ptrcast! target must be a type") + found_shape = found_shape || strings.contains(diagnostic.message, "ptrcast! cannot convert pointer shape") } testing.expect(t, found_by_value) + testing.expect(t, found_inference) testing.expect(t, found_bad_target) testing.expect(t, found_bad_operand) testing.expect(t, found_target_type) + testing.expect(t, found_shape) } @(test) diff --git a/examples/programs/mem_allocator/typed_alloc.bro b/examples/programs/mem_allocator/typed_alloc.bro index d1cfd52..9d62645 100644 --- a/examples/programs/mem_allocator/typed_alloc.bro +++ b/examples/programs/mem_allocator/typed_alloc.bro @@ -2,7 +2,7 @@ mem :: import "@std/mem" @hide probe_count func(context ?@mut anyopaque) void { if context |raw| { - count @mut usize :: ptrcast!(usize, raw) + count @mut usize :: ptrcast!(raw) count^ += 1 } } diff --git a/std/io/file.hon b/std/io/file.hon index 353229f..2562008 100644 --- a/std/io/file.hon +++ b/std/io/file.hon @@ -98,7 +98,7 @@ writer func(file File) Writer { .read_write: flags = c.O_RDWR } while true { - fd c_int :: c.open(ptrcast!(c_char, path.ptr), flags) + fd c_int :: c.open(ptrcast!(path.ptr), flags) if fd >= 0 { return Handle {file_desc = fd} } diff --git a/std/io/io.hon b/std/io/io.hon index dac7997..6068d58 100644 --- a/std/io/io.hon +++ b/std/io/io.hon @@ -317,7 +317,7 @@ print func(output Writer, $format []u8, $Args type, args Args) void ! WriteError } } @hide distinct_value func($Backing, $Distinct type, value Distinct) Backing { - return ptrcast!(Backing, &value)^ + return ptrcast!(@Backing, &value)^ } @hide scalar_or_distinct_type func($T type) bool { @@ -357,14 +357,14 @@ print func(output Writer, $format []u8, $Args type, args Args) void ! WriteError count c_int := 0 if sizeof!(T) == 4 { if scientific { - count = c.snprintf(ptrcast!(c_char, (&buffer).ptr), c_ulong(buffer.len), "%.8e", value) + count = c.snprintf(ptrcast!((&buffer).ptr), c_ulong(buffer.len), "%.8e", value) } else { - count = c.snprintf(ptrcast!(c_char, (&buffer).ptr), c_ulong(buffer.len), "%.9g", value) + count = c.snprintf(ptrcast!((&buffer).ptr), c_ulong(buffer.len), "%.9g", value) } } else if scientific { - count = c.snprintf(ptrcast!(c_char, (&buffer).ptr), c_ulong(buffer.len), "%.16e", value) + count = c.snprintf(ptrcast!((&buffer).ptr), c_ulong(buffer.len), "%.16e", value) } else { - count = c.snprintf(ptrcast!(c_char, (&buffer).ptr), c_ulong(buffer.len), "%.17g", value) + count = c.snprintf(ptrcast!((&buffer).ptr), c_ulong(buffer.len), "%.17g", value) } if count < 0 or usize(count) >= buffer.len { return .write_failed diff --git a/std/mem/mem.hon b/std/mem/mem.hon index 5879289..1bb665a 100644 --- a/std/mem/mem.hon +++ b/std/mem/mem.hon @@ -48,7 +48,7 @@ alloc func($T type, allocator Allocator, count usize) []mut T ! AllocError { memory ?*mut u8 := raw_alloc(allocator, count * element_size, alignof!(T)) if memory |bytes| { - pointer *mut T :: ptrcast!(T, bytes) + pointer *mut T :: ptrcast!(bytes) return pointer[..count] } return .out_of_memory @@ -78,7 +78,7 @@ realloc func($T type, allocator Allocator, memory []mut T, new_count usize) []mu old_memory ?*mut u8 := null old_size usize := 0 if memory.len != 0 { - old_memory = ptrcast!(u8, memory.ptr) + old_memory = ptrcast!(memory.ptr) old_size = memory.len * element_size } resized ?*mut u8 := raw_realloc( @@ -89,7 +89,7 @@ realloc func($T type, allocator Allocator, memory []mut T, new_count usize) []mu alignof!(T), ) if resized |bytes| { - pointer *mut T :: ptrcast!(T, bytes) + pointer *mut T :: ptrcast!(bytes) return pointer[..new_count] } @@ -101,7 +101,6 @@ realloc func($T type, allocator Allocator, memory []mut T, new_count usize) []mu free func($T type, allocator Allocator, memory []T) void { if (memory.len == 0 or sizeof!(T) == 0) return raw_free(allocator, ptrcast!( - u8, constcast!(memory).ptr), memory.len * sizeof!(T), alignof!(T), @@ -110,7 +109,7 @@ free func($T type, allocator Allocator, memory []T) void { #! get an empty slice of type `T` with `count` elements. empty_slice func($T type, count usize) []mut T { - pointer *mut T :: ptrcast!(T, (&empty_storage).ptr) + pointer *mut T :: ptrcast!((&empty_storage).ptr) return pointer[..count] } @@ -138,14 +137,14 @@ empty func($T type) []mut T { if (power_of_two(alignment) == false) return null if alignment <= malloc_alignment { - return ptrcast!(u8, c.malloc(c_ulong(size))) + return ptrcast!(c.malloc(c_ulong(size))) } memory [1]mut ?*mut anyopaque := [null] status c_int := c.posix_memalign((&memory).ptr, c_ulong(alignment), c_ulong(size)) if (status != 0) return null - return ptrcast!(u8, memory[0]) + return ptrcast!(memory[0]) } @hide c_realloc func(_ ?@mut anyopaque, memory ?*mut u8, old_size usize, new_size usize, alignment usize) ?*mut u8 { @@ -158,7 +157,7 @@ empty func($T type) []mut T { if memory |old_memory| { if alignment <= malloc_alignment { - return ptrcast!(u8, c.realloc(old_memory, c_ulong(new_size))) + return ptrcast!(c.realloc(old_memory, c_ulong(new_size))) } new_memory ?*mut u8 := c_alloc(null, new_size, alignment) diff --git a/testbed/lexer/std/mem/mem.bro b/testbed/lexer/std/mem/mem.bro index 096bb98..45c457b 100644 --- a/testbed/lexer/std/mem/mem.bro +++ b/testbed/lexer/std/mem/mem.bro @@ -44,7 +44,7 @@ eql func($T type, left, right []T) bool { @hide empty_storage [1]mut u64 := [0] @hide empty_slice func($T type, count usize) []mut T { - pointer *mut T :: ptrcast!(T, (&empty_storage).ptr) + pointer *mut T :: ptrcast!((&empty_storage).ptr) return pointer[..count] } @@ -67,7 +67,7 @@ alloc func($T type, allocator Allocator, count usize) []mut T ! AllocError { memory ?*mut u8 := raw_alloc(allocator, count * element_size, alignof!(T)) if memory |bytes| { - pointer *mut T :: ptrcast!(T, bytes) + pointer *mut T :: ptrcast!(bytes) return pointer[..count] } return .out_of_memory @@ -93,7 +93,7 @@ realloc func($T type, allocator Allocator, memory []mut T, new_count usize) []mu old_memory ?*mut u8 := null old_size usize := 0 if memory.len != 0 { - old_memory = ptrcast!(u8, memory.ptr) + old_memory = ptrcast!(memory.ptr) old_size = memory.len * element_size } resized ?*mut u8 := raw_realloc( @@ -104,7 +104,7 @@ realloc func($T type, allocator Allocator, memory []mut T, new_count usize) []mu alignof!(T), ) if resized |bytes| { - pointer *mut T :: ptrcast!(T, bytes) + pointer *mut T :: ptrcast!(bytes) return pointer[..new_count] } return .out_of_memory @@ -112,7 +112,7 @@ realloc func($T type, allocator Allocator, memory []mut T, new_count usize) []mu free func($T type, allocator Allocator, memory []mut T) void { if memory.len != 0 and sizeof!(T) != 0 { - raw_free(allocator, ptrcast!(u8, memory.ptr), memory.len * sizeof!(T), alignof!(T)) + raw_free(allocator, ptrcast!(memory.ptr), memory.len * sizeof!(T), alignof!(T)) } } @@ -141,7 +141,7 @@ free func($T type, allocator Allocator, memory []mut T) void { } if alignment <= malloc_alignment { - return ptrcast!(u8, c.malloc(c_ulong(size))) + return ptrcast!(c.malloc(c_ulong(size))) } memory [1]mut ?*mut anyopaque := [null] @@ -150,7 +150,7 @@ free func($T type, allocator Allocator, memory []mut T) void { return null } - return ptrcast!(u8, memory[0]) + return ptrcast!(memory[0]) } @hide c_realloc func(_ ?@mut anyopaque, memory ?*mut u8, old_size usize, new_size usize, alignment usize) ?*mut u8 { @@ -165,7 +165,7 @@ free func($T type, allocator Allocator, memory []mut T) void { if memory |old_memory| { if alignment <= malloc_alignment { - return ptrcast!(u8, c.realloc(old_memory, c_ulong(new_size))) + return ptrcast!(c.realloc(old_memory, c_ulong(new_size))) } new_memory ?*mut u8 := c_alloc(null, new_size, alignment) diff --git a/testbed/mem_alloc/std/mem/mem.bro b/testbed/mem_alloc/std/mem/mem.bro index 4e2f8e6..795b1a0 100644 --- a/testbed/mem_alloc/std/mem/mem.bro +++ b/testbed/mem_alloc/std/mem/mem.bro @@ -30,7 +30,7 @@ raw_free func(allocator Allocator, memory ?*mut u8, size usize, alignment usize) @hide empty_storage [1]mut u64 := [0] @hide empty_slice func($T type, count usize) []mut T { - pointer *mut T :: ptrcast!(T, (&empty_storage).ptr) + pointer *mut T :: ptrcast!((&empty_storage).ptr) return pointer[..count] } @@ -49,7 +49,7 @@ alloc func($T type, allocator Allocator, count usize) []mut T ! AllocError { memory ?*mut u8 := raw_alloc(allocator, count * element_size, alignof!(T)) if memory |bytes| { - pointer *mut T :: ptrcast!(T, bytes) + pointer *mut T :: ptrcast!(bytes) return pointer[..count] } return .out_of_memory @@ -57,7 +57,7 @@ alloc func($T type, allocator Allocator, count usize) []mut T ! AllocError { free func($T type, allocator Allocator, memory []mut T) void { if memory.len != 0 and sizeof!(T) != 0 { - raw_free(allocator, ptrcast!(u8, memory.ptr), memory.len * sizeof!(T), alignof!(T)) + raw_free(allocator, ptrcast!(memory.ptr), memory.len * sizeof!(T), alignof!(T)) } } @@ -86,7 +86,7 @@ free func($T type, allocator Allocator, memory []mut T) void { } if alignment <= malloc_alignment { - return ptrcast!(u8, c.malloc(c_ulong(size))) + return ptrcast!(c.malloc(c_ulong(size))) } memory [1]mut ?*mut anyopaque := [null] @@ -95,7 +95,7 @@ free func($T type, allocator Allocator, memory []mut T) void { return null } - return ptrcast!(u8, memory[0]) + return ptrcast!(memory[0]) } @hide c_realloc func(_ ?@mut anyopaque, memory ?*mut u8, old_size usize, new_size usize, alignment usize) ?*mut u8 { @@ -110,7 +110,7 @@ free func($T type, allocator Allocator, memory []mut T) void { if memory |old_memory| { if alignment <= malloc_alignment { - return ptrcast!(u8, c.realloc(old_memory, c_ulong(new_size))) + return ptrcast!(c.realloc(old_memory, c_ulong(new_size))) } new_memory ?*mut u8 := c_alloc(null, new_size, alignment)