memcopy! and memset! intrinsics

This commit is contained in:
2026-07-20 22:53:04 +02:00
parent 297f2e3078
commit dd00af7731
11 changed files with 729 additions and 11 deletions
+137
View File
@@ -811,6 +811,68 @@ Division_Builtin :: enum u8 {
Mod,
}
Memory_Builtin :: enum u8 {
None,
Copy,
Set,
}
memory_builtin_call :: proc(checker: ^Checker, expr: ast.Expr) -> Memory_Builtin {
if !expr.intrinsic || expr.kind != .Call || expr.left != ast.INVALID_EXPR || symbol.is_valid(expr.qualifier) {
return .None
}
switch symbol_text(checker, expr.name) {
case "memcopy": return .Copy
case "memset": return .Set
}
return .None
}
memory_region_type :: proc(checker: ^Checker, value: types.Type) -> (child: types.Type, mutable: bool, ok: bool) {
store := &checker.module.types
resolved := types.resolve_alias(value, store)
if item, item_ok := types.node(store, resolved); item_ok && item.kind == .Slice {
return item.child, item.mutable, true
}
pointer, pointer_ok := types.node(store, resolved)
if pointer_ok && pointer.kind == .Pointer && !pointer.many {
array, array_ok := types.node(store, types.resolve_alias(pointer.child, store))
if array_ok && array.kind == .Array {
return array.child, pointer.mutable && array.mutable, true
}
}
return types.INVALID, false, false
}
infer_memory_builtin :: proc(
checker: ^Checker,
expr: ast.Expr,
kind: Memory_Builtin,
locals: []Infer_Local,
pkg: ast.Package_Id,
file: ast.File_Id,
demanded: ^[dynamic]Spec_Id,
local_types: []types.Type,
) -> types.Type {
if len(expr.args) != 2 {
return types.INVALID
}
destination := infer_nested_expr(checker, expr.args[0], locals, pkg, file, demanded, local_types)
child, _, ok := memory_region_type(checker, destination)
if !ok {
_ = infer_nested_expr(checker, expr.args[1], locals, pkg, file, demanded, local_types)
return types.INVALID
}
if kind == .Set {
if !is_undefined_expr(checker, expr.args[1]) {
_ = infer_nested_expr(checker, expr.args[1], locals, pkg, file, demanded, local_types, child)
}
} else {
_ = infer_nested_expr(checker, expr.args[1], locals, pkg, file, demanded, local_types)
}
return types.VOID
}
division_builtin_call :: proc(checker: ^Checker, expr: ast.Expr) -> Division_Builtin {
if !expr.intrinsic || expr.kind != .Call || expr.left != ast.INVALID_EXPR || symbol.is_valid(expr.qualifier) {
return .None
@@ -5036,6 +5098,11 @@ infer_expr :: proc(
_ = pop(&stack)
continue
}
if builtin := memory_builtin_call(checker, expr); builtin != .None {
last = infer_memory_builtin(checker, expr, builtin, locals, pkg, file, demanded, local_types)
_ = pop(&stack)
continue
}
if is_intrinsic_call(checker, expr, "some") {
if len(expr.args) == 1 && types.is_optional(frame.expected, &checker.module.types) {
child := types.child_type(frame.expected, &checker.module.types)
@@ -7548,6 +7615,71 @@ build_division_builtin :: proc(
})
}
build_memory_builtin :: proc(
checker: ^Checker,
expr: ast.Expr,
kind: Memory_Builtin,
locals: []Build_Local,
global_reads: ^[dynamic]hir.Global_Id,
calls: ^[dynamic]hir.Function_Id,
pkg: ast.Package_Id,
file: ast.File_Id,
) -> hir.Expr_Id {
name := symbol_text(checker, expr.name)
if len(expr.args) != 2 {
id := source.addf(checker.diagnostics, expr.span, "%s! expects 2 arguments, got %d", name, len(expr.args))
return invalid_hir_expr(checker, expr.span, id, types.VOID)
}
destination := build_nested_expr(checker, expr.args[0], locals, global_reads, calls, types.INVALID, pkg, file)
destination_type := checker.module.exprs[destination].type
destination_child, destination_mutable, destination_ok := memory_region_type(checker, destination_type)
if !destination_ok || !destination_mutable {
id := source.addf(
checker.diagnostics, checker.ast_module.exprs[expr.args[0]].span,
"%s! destination must be a mutable slice or mutable pointer-to-array", name,
)
return invalid_hir_expr(checker, expr.span, id, types.VOID)
}
right := hir.INVALID_EXPR
result_kind := hir.Expr_Kind.Mem_Copy
if kind == .Set {
if is_undefined_expr(checker, expr.args[1]) {
right = add_hir_expr(checker, hir.Expr{
kind=.Undefined, span=checker.ast_module.exprs[expr.args[1]].span, type=destination_child,
target=hir.INVALID_REF, left=hir.INVALID_EXPR, right=hir.INVALID_EXPR,
diagnostic=source.INVALID_DIAGNOSTIC,
})
} else {
right = build_nested_expr(checker, expr.args[1], locals, global_reads, calls, destination_child, pkg, file)
right = coerce_expr(checker, right, destination_child, checker.ast_module.exprs[expr.args[1]].span)
}
result_kind = .Mem_Set
} else {
source_expr := build_nested_expr(checker, expr.args[1], locals, global_reads, calls, types.INVALID, pkg, file)
source_type := checker.module.exprs[source_expr].type
source_child, _, source_ok := memory_region_type(checker, source_type)
if !source_ok {
id := source.add(
checker.diagnostics, checker.ast_module.exprs[expr.args[1]].span,
"memcopy! source must be a slice or pointer-to-array",
)
return invalid_hir_expr(checker, expr.span, id, types.VOID)
}
if !types.equal(
types.resolve_alias(destination_child, &checker.module.types),
types.resolve_alias(source_child, &checker.module.types),
) {
id := source.add(checker.diagnostics, expr.span, "memcopy! source and destination element types must match")
return invalid_hir_expr(checker, expr.span, id, types.VOID)
}
right = source_expr
}
return add_hir_expr(checker, hir.Expr{
kind=result_kind, span=expr.span, type=types.VOID, left=destination, right=right,
target=hir.INVALID_REF, diagnostic=source.INVALID_DIAGNOSTIC,
})
}
fallible_aggregate :: proc(
checker: ^Checker,
span: source.Span,
@@ -8793,6 +8925,11 @@ build_expr :: proc(
_ = pop(&stack)
continue
}
if builtin := memory_builtin_call(checker, expr); builtin != .None {
last = build_memory_builtin(checker, expr, builtin, locals, global_reads, calls, pkg, file)
_ = pop(&stack)
continue
}
if is_intrinsic_call(checker, expr, "some") {
if len(expr.args) != 1 {
id := source.addf(checker.diagnostics, expr.span, "some! expects 1 argument, got %d", len(expr.args))