feat: support callback signatures and many-pointer addition

This commit is contained in:
2026-10-26 11:32:29 +01:00
parent 676f3666b4
commit 7856f4f963
8 changed files with 417 additions and 50 deletions
+71 -10
View File
@@ -351,7 +351,7 @@ check proc(
callee :: ast.call_expr(tree, id).callee
signature :: try infer_shape(&state, callee, false)
if (signature.function != ast.NodeId(maxval!(u32))) {
result :: ast.function_decl(tree, signature.function).return_type
result :: ast.callable_result(tree, signature.function)
seed(&state, id, types[usize(result)])
} else if (signature.kind == .invalid) {
seed(&state, id, signature)
@@ -391,7 +391,7 @@ check proc(
}
.binary: {
expression :: ast.binary_expr(tree, id)
if (logical(expression.operator) or bool_equality(&state, id)) continue
if (logical(expression.operator) or bool_equality(&state, id) or pointer_addition(&state, id)) continue
try require_numeric(&state, id, expression.left)
try require_numeric(&state, id, expression.right)
}
@@ -411,7 +411,7 @@ check proc(
}
.binary: {
expression :: ast.binary_expr(tree, id)
if (logical(expression.operator)) continue
if (logical(expression.operator) or pointer_addition(&state, id)) continue
if (comparison(expression.operator)) {
if (!bool_equality(&state, id)) try comparison_operands(&state, id)
} else {
@@ -1135,7 +1135,7 @@ collect_expression proc(state @mut State, id ast.NodeId) void ! (mem.AllocError
.array_type: {
state.cells[usize(id)].type = invalid_type(try diagnostics.add(state.diagnostics, .type_not_value, ast.node_span(tree, id)))
}
.record_type, .union_type, .enum_type, .distinct_type, .pointer_type: {
.record_type, .union_type, .enum_type, .distinct_type, .pointer_type, .function_type: {
type := try check_type_expression(state, id, state.cells[usize(id)].nominal)
if (type.kind != .invalid) {
type.value_kind = type.kind
@@ -1261,6 +1261,17 @@ logical proc(operator ast.BinaryOperator) bool {
return operator == .logical_and or operator == .logical_or
}
@hide:file
pointer_addition proc(state @mut State, id ast.NodeId) bool {
expression :: ast.binary_expr(state.tree, id)
left :: group_type(state, expression.left)
return (
expression.operator == .add and left.kind == .pointer
and left.nominal == ast.NodeId(maxval!(u32))
and state.pointers.items[usize(left.pointer_type)].many
)
}
@hide:file
bool_equality proc(state @mut State, id ast.NodeId) bool {
expression :: ast.binary_expr(state.tree, id)
@@ -1516,14 +1527,15 @@ addressable_target proc(state @mut State, id ast.NodeId) bool {
@hide:file
same_signature proc(state @State, left, right ast.NodeId) bool {
if (left == right) return true
tree :: state.tree
if (ast.function_param_count(tree, left) != ast.function_param_count(tree, right)) return false
left_result :: ast.function_decl(tree, left).return_type
right_result :: ast.function_decl(tree, right).return_type
left_result :: ast.callable_result(tree, left)
right_result :: ast.callable_result(tree, right)
if (!same_type(state, state.types[usize(left_result)], state.types[usize(right_result)])) return false
for (0..ast.function_param_count(tree, left)) |i| {
left_param :: ast.function_param(tree, left, i)
right_param :: ast.function_param(tree, right, i)
left_param :: ast.param(tree, ast.function_param(tree, left, i)).type
right_param :: ast.param(tree, ast.function_param(tree, right, i)).type
if (!same_type(state, state.types[usize(left_param)], state.types[usize(right_param)])) return false
}
return true
@@ -1635,6 +1647,12 @@ validate proc(state @mut State, id ast.NodeId) void ! (mem.AllocError | diagnost
left :: state.types[usize(expression.left)]
right :: state.types[usize(expression.right)]
if (left.kind == .invalid or right.kind == .invalid) return
if (pointer_addition(state, id)) {
if (right.kind != .usize or right.nominal != ast.NodeId(maxval!(u32))) {
try fail(state, id, .invalid_operand, expression.span)
}
return
}
if ((expression.operator == .equal or expression.operator == .not_equal)
and (left.kind == .bool or left.kind == .enumeration or left.kind == .pointer)
and (assignable_type(state, left, right) or assignable_type(state, right, left))) return
@@ -1901,7 +1919,7 @@ check_pointer_type proc(state @mut State, id ast.NodeId) Type ! (mem.AllocError
if (element.kind == .invalid) return element
binding :: sema.binding(state.resolution, view.element)
if (element.kind == .none or element.kind == .void or element.kind == .range
or element.kind == .type_value or element.kind == .function
or element.kind == .type_value
or has_inferred_count(state, element)
or (binding.kind == .builtin and (binding.data.builtin == .int or binding.data.builtin == .float))) {
return invalid_type(try diagnostics.add(state.diagnostics, .invalid_pointer_type, ast.node_span(state.tree, id)))
@@ -1909,6 +1927,35 @@ check_pointer_type proc(state @mut State, id ast.NodeId) Type ! (mem.AllocError
return try intern_pointer(state, PointerType{ element = element, mutable = view.mutable, many = view.many })
}
@hide:file
check_function_type proc(state @mut State, id ast.NodeId) Type ! (mem.AllocError | diagnostics.CapacityError) {
cached := state.cells[usize(id)].type
if (cached.kind == .type_value) cached.kind = cached.value_kind
if (cached.kind != .none) return cached
type := Type{ kind = .function, function = id }
state.cells[usize(id)].type = type
state.pointer_depth += 1
defer state.pointer_depth -= 1
result :: ast.callable_result(state.tree, id)
result_type :: try check_annotation(state, result, true)
if (result_type.kind == .invalid) type = result_type
try depend(state, result, id)
for 0..ast.function_param_count(state.tree, id) |i| {
parameter :: ast.function_param(state.tree, id, i)
annotation :: ast.param(state.tree, parameter).type
parameter_type :: try check_annotation(state, annotation, false)
state.cells[usize(parameter)].role = .annotation
state.cells[usize(parameter)].type = parameter_type
state.types[usize(parameter)] = parameter_type
if (parameter_type.kind == .invalid and type.kind != .invalid) type = parameter_type
try depend(state, annotation, parameter)
try depend(state, parameter, id)
}
type.function = id
state.cells[usize(id)].type = type
return type
}
@hide:file
intern_array proc(state @mut State, descriptor ArrayType) Type ! mem.AllocError {
for state.arrays.items |existing, i| {
@@ -2037,6 +2084,19 @@ infer_shape proc(state @mut State, id ast.NodeId, defaults bool) Type ! (mem.All
if (state.cells[index].type.kind == .invalid) return state.cells[index].type
state.cells[index].visiting = true
defer state.cells[index].visiting = false
if (state.cells[usize(id)].role == .expression and ast.expr_kind(state.tree, id) == .binary) {
view :: ast.binary_expr(state.tree, id)
if (view.operator == .add) {
_ = try infer_shape(state, view.left, defaults)
if (pointer_addition(state, id)) {
type :: group_type(state, view.left)
state.cells[index].numeric = false
seed(state, id, type)
try demand(state, view.right, Type{ kind = .usize }, .invalid_operand)
return group_type(state, id)
}
}
}
if (state.cells[usize(id)].role == .expression and ast.expr_kind(state.tree, id) == .unary) {
view :: ast.unary_expr(state.tree, id)
if (view.operator == .address_of or view.operator == .dereference) {
@@ -2688,7 +2748,7 @@ type_syntax proc(kind ast.ExprKind) bool {
return (
kind == .record_type or kind == .union_type or kind == .enum_type
or kind == .distinct_type or kind == .alias_expr
or kind == .pointer_type
or kind == .pointer_type or kind == .function_type
)
}
@@ -2718,6 +2778,7 @@ check_type_expression proc(state @mut State, id ast.NodeId, nominal bool) Type !
kind :: ast.expr_kind(state.tree, id)
if (kind == .record_type) return try check_record(state, id, nominal)
if (kind == .pointer_type) return try check_pointer_type(state, id)
if (kind == .function_type) return try check_function_type(state, id)
if (kind == .union_type) {
type :: try check_union(state, id, nominal)
state.cells[usize(id)].type = type