memcopy! and memset! intrinsics
This commit is contained in:
@@ -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))
|
||||
|
||||
Reference in New Issue
Block a user