allocator interface (first pass)
This commit is contained in:
@@ -180,6 +180,21 @@ type_label :: proc(checker: ^Checker, value: types.Type) -> string {
|
||||
return types.name(value)
|
||||
}
|
||||
|
||||
is_ptr_cast_call :: proc(checker: ^Checker, expr: ast.Expr) -> bool {
|
||||
return expr.left == ast.INVALID_EXPR &&
|
||||
!symbol.is_valid(expr.qualifier) &&
|
||||
symbol_text(checker, expr.name) == "ptr_cast"
|
||||
}
|
||||
|
||||
valid_ptr_cast_child :: proc(checker: ^Checker, value: types.Type) -> bool {
|
||||
return types.is_valid(value) &&
|
||||
!types.is_void(value) &&
|
||||
!types.is_anyopaque(value) &&
|
||||
!types.is_function(value, &checker.module.types) &&
|
||||
(types.is_runtime_value(value, &checker.module.types) ||
|
||||
types.is_opaque_struct(value, &checker.module.types))
|
||||
}
|
||||
|
||||
is_type_metatype_syntax :: proc(checker: ^Checker, value: ast.Type_Syntax) -> bool {
|
||||
item, ok := types.node(&checker.module.types, value)
|
||||
return ok && item.name == u32(checker.type_symbol) && item.qualifier == 0
|
||||
@@ -958,7 +973,7 @@ function_pointer_type_for_template :: proc(
|
||||
defer delete(params, checker.allocator)
|
||||
function := checker.ast_module.functions[template]
|
||||
function_type := types.function(&checker.module.types, params, result, function.c_abi, function.variadic)
|
||||
pointer_type := types.pointer(&checker.module.types, function_type, false, true)
|
||||
pointer_type := types.pointer(&checker.module.types, function_type, false, false)
|
||||
spec := INVALID_SPEC
|
||||
if demanded == nil {
|
||||
if demand_spec {
|
||||
@@ -1970,6 +1985,42 @@ infer_expr :: proc(
|
||||
}
|
||||
continue
|
||||
}
|
||||
if is_ptr_cast_call(checker, expr) {
|
||||
if 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_ptr_cast_child(checker, child) {
|
||||
result, _ = types.replace_pointer_child(&checker.module.types, operand, child)
|
||||
}
|
||||
last = result
|
||||
_ = pop(&stack)
|
||||
continue
|
||||
}
|
||||
if callee_type, handled := infer_qualified_value_field_type(checker, expr, locals, pkg, file); handled {
|
||||
_, function_item, function_type, ok := types.function_pointer(callee_type, &checker.module.types)
|
||||
if !ok {
|
||||
last = types.INVALID
|
||||
_ = pop(&stack)
|
||||
continue
|
||||
}
|
||||
stack[frame_index].left = function_type
|
||||
stack[frame_index].args = make([]types.Type, len(expr.args), checker.allocator)
|
||||
stack[frame_index].stage = 6
|
||||
if len(expr.args) > 0 {
|
||||
append(&stack, Infer_Frame{expr=expr.args[0], template=ast.INVALID_FUNCTION})
|
||||
} else if valid_callable_arity(function_item, 0) {
|
||||
last = function_item.child
|
||||
delete(stack[frame_index].args, checker.allocator)
|
||||
stack[frame_index].args = nil
|
||||
_ = pop(&stack)
|
||||
}
|
||||
continue
|
||||
}
|
||||
target_pkg, available := expr_package(checker, expr, pkg, file)
|
||||
template := ast.INVALID_FUNCTION
|
||||
if available {
|
||||
@@ -3477,6 +3528,126 @@ find_struct_field :: proc(checker: ^Checker, struct_type: types.Type, name: symb
|
||||
return 0, {}, false
|
||||
}
|
||||
|
||||
field_type_from_value :: proc(checker: ^Checker, expr: ast.Expr, base_type: types.Type) -> types.Type {
|
||||
store := &checker.module.types
|
||||
field_name := symbol_text(checker, expr.name)
|
||||
item, has_item := types.container(base_type, store)
|
||||
if has_item && (item.kind == .Array || item.kind == .Slice) {
|
||||
if field_name == "len" {
|
||||
return types.USIZE
|
||||
}
|
||||
if field_name == "ptr" &&
|
||||
(item.kind == .Slice || types.is_pointer(base_type, store)) {
|
||||
return container_pointer_type(store, item)
|
||||
}
|
||||
}
|
||||
value_type := base_type
|
||||
if types.is_pointer(value_type, store) {
|
||||
value_type = types.child_type(value_type, store)
|
||||
}
|
||||
_, field, ok := find_struct_field(checker, value_type, expr.name)
|
||||
return field.type if ok else types.INVALID
|
||||
}
|
||||
|
||||
infer_qualified_value_field_type :: proc(
|
||||
checker: ^Checker,
|
||||
expr: ast.Expr,
|
||||
locals: []Infer_Local,
|
||||
pkg: ast.Package_Id,
|
||||
file: ast.File_Id,
|
||||
) -> (types.Type, bool) {
|
||||
if !symbol.is_valid(expr.qualifier) ||
|
||||
find_import(checker, file, expr.qualifier) != ast.INVALID_IMPORT {
|
||||
return types.INVALID, false
|
||||
}
|
||||
base_type := find_infer_local(locals, expr.qualifier)
|
||||
if !types.is_valid(base_type) {
|
||||
if global := find_global(checker, expr.qualifier, pkg); global != ast.INVALID_GLOBAL {
|
||||
base_type = checker.global_types[global]
|
||||
}
|
||||
}
|
||||
if !types.is_valid(base_type) {
|
||||
return types.INVALID, false
|
||||
}
|
||||
return field_type_from_value(checker, expr, base_type), true
|
||||
}
|
||||
|
||||
build_field_from_value :: proc(
|
||||
checker: ^Checker,
|
||||
expr: ast.Expr,
|
||||
base: hir.Expr_Id,
|
||||
base_type: types.Type,
|
||||
) -> (hir.Expr_Id, bool) {
|
||||
store := &checker.module.types
|
||||
field_name := symbol_text(checker, expr.name)
|
||||
item, has_item := types.container(base_type, store)
|
||||
if has_item && (item.kind == .Array || item.kind == .Slice) {
|
||||
if field_name == "len" {
|
||||
return add_hir_expr(checker, hir.Expr{
|
||||
kind=.Length, span=expr.span, type=types.USIZE, left=base,
|
||||
target=hir.INVALID_REF, right=hir.INVALID_EXPR, diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
}), true
|
||||
}
|
||||
if field_name == "ptr" &&
|
||||
(item.kind == .Slice || types.is_pointer(base_type, store)) {
|
||||
return add_hir_expr(checker, hir.Expr{
|
||||
kind=.Slice_Ptr, span=expr.span,
|
||||
type=container_pointer_type(store, item), left=base,
|
||||
target=hir.INVALID_REF, right=hir.INVALID_EXPR, diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
}), true
|
||||
}
|
||||
if field_name == "ptr" && item.kind == .Array {
|
||||
id := source.add(checker.diagnostics, expr.span, "arrays do not expose '.ptr'; take their address first")
|
||||
return invalid_hir_expr(checker, expr.span, id), false
|
||||
}
|
||||
}
|
||||
value_type := base_type
|
||||
if types.is_pointer(value_type, store) {
|
||||
value_type = types.child_type(value_type, store)
|
||||
}
|
||||
index, field, ok := find_struct_field(checker, value_type, expr.name)
|
||||
if !ok {
|
||||
id := source.addf(checker.diagnostics, expr.span, "unknown struct field '%s'", symbol_text(checker, expr.name))
|
||||
return invalid_hir_expr(checker, expr.span, id), false
|
||||
}
|
||||
if types.is_void(field.type) {
|
||||
id := source.addf(checker.diagnostics, expr.span, "variant '%s' has no payload to read", symbol_text(checker, expr.name))
|
||||
return invalid_hir_expr(checker, expr.span, id), false
|
||||
}
|
||||
return add_hir_expr(checker, hir.Expr{
|
||||
kind=.Field, span=expr.span, type=field.type, integer=i64(index), left=base,
|
||||
target=hir.INVALID_REF, right=hir.INVALID_EXPR, diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
}), true
|
||||
}
|
||||
|
||||
build_qualified_value_field :: proc(
|
||||
checker: ^Checker,
|
||||
expr: ast.Expr,
|
||||
locals: []Build_Local,
|
||||
global_reads: ^[dynamic]hir.Global_Id,
|
||||
pkg: ast.Package_Id,
|
||||
file: ast.File_Id,
|
||||
) -> (hir.Expr_Id, bool, bool) {
|
||||
if !symbol.is_valid(expr.qualifier) ||
|
||||
find_import(checker, file, expr.qualifier, true) != ast.INVALID_IMPORT {
|
||||
return hir.INVALID_EXPR, false, false
|
||||
}
|
||||
if local, ok := find_build_local(locals, expr.qualifier); ok {
|
||||
base := add_hir_expr(checker, hir.Expr{
|
||||
kind=.Local, span=expr.span, type=local.type, target=hir.local_ref(local.id),
|
||||
left=hir.INVALID_EXPR, right=hir.INVALID_EXPR, diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
value, ok := build_field_from_value(checker, expr, base, local.type)
|
||||
return value, true, ok
|
||||
}
|
||||
if global := find_global(checker, expr.qualifier, pkg); global != ast.INVALID_GLOBAL {
|
||||
base := build_global_reference(checker, global, expr.span, global_reads)
|
||||
value, ok := build_field_from_value(checker, expr, base, checker.global_types[global])
|
||||
return value, true, ok
|
||||
}
|
||||
return hir.INVALID_EXPR, false, false
|
||||
}
|
||||
|
||||
find_enum_member :: proc(checker: ^Checker, enum_type: types.Type, name: symbol.Id) -> (types.Enum_Member, bool) {
|
||||
for member in types.enum_members_for(&checker.module.types, enum_type) {
|
||||
if member.name == u32(name) {
|
||||
@@ -3581,7 +3752,15 @@ build_function_value :: proc(
|
||||
}
|
||||
defer delete(params, checker.allocator)
|
||||
function_type := types.function(&checker.module.types, params, result, function.c_abi, function.variadic)
|
||||
pointer_type := types.pointer(&checker.module.types, function_type, false, true)
|
||||
pointer_type := types.pointer(&checker.module.types, function_type, false, false)
|
||||
expected_pointer := expected
|
||||
if types.is_optional(expected_pointer, &checker.module.types) {
|
||||
expected_pointer = types.child_type(expected_pointer, &checker.module.types)
|
||||
}
|
||||
if _, _, expected_function, ok := types.function_pointer(expected_pointer, &checker.module.types); ok &&
|
||||
types.equal(expected_function, function_type) {
|
||||
pointer_type = expected_pointer
|
||||
}
|
||||
spec := find_spec(checker, template, params)
|
||||
if spec == INVALID_SPEC {
|
||||
id := source.addf(
|
||||
@@ -4553,6 +4732,41 @@ build_expr :: proc(
|
||||
append(&stack, Build_Expr_Frame{expr=expr.left, expected=types.INVALID, template=ast.INVALID_FUNCTION})
|
||||
continue
|
||||
}
|
||||
if is_ptr_cast_call(checker, expr) {
|
||||
if len(expr.args) != 2 {
|
||||
id := source.addf(checker.diagnostics, expr.span, "ptr_cast expects 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, "ptr_cast target must be a type")
|
||||
last = invalid_hir_expr(checker, expr.span, id)
|
||||
_ = pop(&stack)
|
||||
continue
|
||||
}
|
||||
if !valid_ptr_cast_child(checker, target) {
|
||||
id := source.addf(checker.diagnostics, checker.ast_module.exprs[expr.args[0]].span, "ptr_cast target must be 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})
|
||||
continue
|
||||
}
|
||||
if callee, handled, ok := build_qualified_value_field(checker, expr, locals, global_reads, pkg, file); handled {
|
||||
if !ok {
|
||||
last = callee
|
||||
_ = pop(&stack)
|
||||
continue
|
||||
}
|
||||
stack[frame_index].stage = 6
|
||||
last = callee
|
||||
continue
|
||||
}
|
||||
target_pkg, available := expr_package(checker, expr, pkg, file, true)
|
||||
if !available {
|
||||
id := add_package_resolution_diagnostic(checker, expr, file)
|
||||
@@ -4992,6 +5206,24 @@ 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 {
|
||||
id := source.add(checker.diagnostics, expr.span, "ptr_cast operand must be a pointer or optional pointer")
|
||||
last = invalid_hir_expr(checker, expr.span, id)
|
||||
} else {
|
||||
last = add_hir_expr(checker, hir.Expr{
|
||||
kind=.Pointer_Cast,
|
||||
span=expr.span,
|
||||
type=result,
|
||||
left=last,
|
||||
target=hir.INVALID_REF,
|
||||
right=hir.INVALID_EXPR,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
}
|
||||
_ = pop(&stack)
|
||||
}
|
||||
}
|
||||
return last
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user