feat: infer complete ptrcast target types

This commit is contained in:
2026-09-20 00:01:13 +02:00
parent 5dac5baa05
commit 7f7de7a8d2
11 changed files with 122 additions and 65 deletions
+66 -19
View File
@@ -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)
}