feat: support callback signatures and many-pointer addition
This commit is contained in:
+31
-6
@@ -1,5 +1,5 @@
|
||||
#! grammar:
|
||||
#! * type ..... -> identifier {'.' member} | ('@' | '*') ['mut'] type | '[' (expr | '_') ']' ['mut'] type | 'struct' '{' fields '}' | 'enum' ['(' type ')'] '{' members '}' | 'union' ['(' (type | 'enum') ')'] '{' fields '}' | ('distinct' | 'alias') type
|
||||
#! * type ..... -> identifier {'.' member} | ('@' | '*') ['mut'] type | '@proc' '(' parameters ')' type | '[' (expr | '_') ']' ['mut'] type | 'struct' '{' fields '}' | 'enum' ['(' type ')'] '{' members '}' | 'union' ['(' (type | 'enum') ')'] '{' fields '}' | ('distinct' | 'alias') type
|
||||
#! * binding ... -> :: | :=
|
||||
#! * literal .. -> int | float | string | 'true' | 'false'
|
||||
#! * op ....... -> '+' | '-' | '*' | '/' | '==' | '!=' | '<' | '<=' | '>' | '>=' | 'and' | 'or'
|
||||
@@ -45,6 +45,7 @@ ExprKind :: enum {
|
||||
range
|
||||
array_type
|
||||
pointer_type
|
||||
function_type
|
||||
array_literal
|
||||
record_type
|
||||
record_literal
|
||||
@@ -112,6 +113,7 @@ NodeKind :: enum {
|
||||
expr_range
|
||||
expr_array_type
|
||||
expr_pointer_type
|
||||
expr_function_type
|
||||
expr_array_literal
|
||||
expr_record_type
|
||||
expr_record_literal
|
||||
@@ -511,6 +513,19 @@ add_pointer_type proc(
|
||||
}, span)
|
||||
}
|
||||
|
||||
add_function_type proc(
|
||||
builder @mut Builder, span Span, result NodeId, params []NodeId,
|
||||
) NodeId ! mem.AllocError {
|
||||
start :: builder.storage.extra.items.len
|
||||
for (params) |parameter| _ = try add_extra(builder, u32(parameter))
|
||||
return try add_node(builder, Node{
|
||||
kind = .expr_function_type,
|
||||
main = NodeData{ node_id = result },
|
||||
data0 = NodeData{ extra = u32(start) },
|
||||
data1 = NodeData{ extra = u32(builder.storage.extra.items.len) },
|
||||
}, span)
|
||||
}
|
||||
|
||||
add_array_literal proc(builder @mut Builder, span Span, elements []NodeId) NodeId ! mem.AllocError {
|
||||
start :: builder.storage.extra.items.len
|
||||
for (elements) |element| _ = try add_extra(builder, u32(element))
|
||||
@@ -1032,6 +1047,13 @@ function_decl proc(tree @Tree, id NodeId) FunctionDeclView {
|
||||
}
|
||||
}
|
||||
|
||||
callable_result proc(tree @Tree, id NodeId) NodeId {
|
||||
node :: get_node(&tree.storage, id)
|
||||
if (node.kind == .func_decl) return function_decl(tree, id).return_type
|
||||
debug.assert(node.kind == .expr_function_type)
|
||||
return node.main.node_id
|
||||
}
|
||||
|
||||
import_decl proc(tree @Tree, id NodeId) ImportView {
|
||||
node :: get_node(&tree.storage, id)
|
||||
debug.assert(node.kind == .import_decl)
|
||||
@@ -1047,15 +1069,17 @@ import_decl proc(tree @Tree, id NodeId) ImportView {
|
||||
|
||||
function_param_count proc(tree @Tree, id NodeId) usize {
|
||||
node :: get_node(&tree.storage, id)
|
||||
debug.assert(node.kind == .func_decl)
|
||||
return usize(node.data1.extra - node.main.extra - 4)
|
||||
debug.assert(node.kind == .func_decl or node.kind == .expr_function_type)
|
||||
start :: if (node.kind == .func_decl) node.main.extra + 4 else node.data0.extra
|
||||
return usize(node.data1.extra - start)
|
||||
}
|
||||
|
||||
function_param proc(tree @Tree, id NodeId, index usize) NodeId {
|
||||
node :: get_node(&tree.storage, id)
|
||||
debug.assert(node.kind == .func_decl)
|
||||
debug.assert(index < usize(node.data1.extra - node.main.extra - 4))
|
||||
return NodeId(tree.storage.extra.items[usize(node.main.extra) + 4 + index])
|
||||
debug.assert(node.kind == .func_decl or node.kind == .expr_function_type)
|
||||
start :: if (node.kind == .func_decl) node.main.extra + 4 else node.data0.extra
|
||||
debug.assert(index < usize(node.data1.extra - start))
|
||||
return NodeId(tree.storage.extra.items[usize(start) + index])
|
||||
}
|
||||
|
||||
param proc(tree @Tree, id NodeId) ParamView {
|
||||
@@ -1103,6 +1127,7 @@ expression_kind proc(kind NodeKind) ExprKind {
|
||||
.expr_range: .range
|
||||
.expr_array_type: .array_type
|
||||
.expr_pointer_type: .pointer_type
|
||||
.expr_function_type: .function_type
|
||||
.expr_array_literal: .array_literal
|
||||
.expr_record_type: .record_type
|
||||
.expr_record_literal: .record_literal
|
||||
|
||||
@@ -1065,7 +1065,7 @@ lower_expression proc(state @mut State, id ast.NodeId) ir.ValueId ! (mem.AllocEr
|
||||
# ranges have no physical value; only lower_for decomposes an iterable.
|
||||
if (!trap_type(state, id)) unreachable
|
||||
}
|
||||
.array_type, .pointer_type, .record_type, .enum_type, .union_type, .distinct_type: {
|
||||
.array_type, .pointer_type, .function_type, .record_type, .enum_type, .union_type, .distinct_type: {
|
||||
if (!trap_type(state, id)) {
|
||||
trap(state, try diagnostics.add(state.diagnostics, .type_not_value, span), span)
|
||||
}
|
||||
@@ -1240,6 +1240,14 @@ lower_expression proc(state @mut State, id ast.NodeId) ir.ValueId ! (mem.AllocEr
|
||||
if (state.terminated) return ir.no_value
|
||||
right :: try lower_expression(state, expression.right)
|
||||
if (state.terminated or trap_type(state, id)) return ir.no_value
|
||||
if (expression.operator == .add and type == .pointer) {
|
||||
pointer :: typecheck.pointer_type(state.checked, typecheck.type_of(state.checked, id).pointer_type)
|
||||
return try emit(state, ir.Instruction{
|
||||
op = .pointer_offset, type = .pointer, left = left, right = right,
|
||||
pointee = lower_type(state, pointer.element),
|
||||
array_type = try array_shape(state, pointer.element), span = span,
|
||||
})
|
||||
}
|
||||
op ir.Op :: match expression.operator {
|
||||
.add: .add
|
||||
.subtract: .subtract
|
||||
|
||||
+50
-27
@@ -149,33 +149,7 @@ parse_func_decl proc(state @mut State) ast.NodeId ! ParseError {
|
||||
|
||||
params arraylist.ArrayList(ast.NodeId) := arraylist.init(state.allocator)
|
||||
defer arraylist.deinit(¶ms)
|
||||
names arraylist.ArrayList(lexer.Token) := arraylist.init(state.allocator)
|
||||
defer arraylist.deinit(&names)
|
||||
|
||||
while !next_is(state, .close_paren) {
|
||||
arraylist.clear(&names)
|
||||
while true {
|
||||
token :: try consume(state, .ident)
|
||||
try arraylist.append(&names, token)
|
||||
if (starts_type(current(state).kind)) break
|
||||
_ = try consume(state, .comma)
|
||||
}
|
||||
|
||||
type_id :: try parse_type(state)
|
||||
type_span :: ast.builder_span(state.builder, type_id)
|
||||
for (names.items) |parameter_name| {
|
||||
param_id :: try ast.add_param(
|
||||
state.builder,
|
||||
ast.Span{ start = parameter_name.span.start, end = type_span.end, source = parameter_name.span.source },
|
||||
parameter_name.span,
|
||||
parameter_name.data.str_id,
|
||||
type_id,
|
||||
)
|
||||
try arraylist.append(¶ms, param_id)
|
||||
}
|
||||
if next_is(state, .comma) _ = try consume(state, .comma)
|
||||
}
|
||||
|
||||
try parse_parameters(state, ¶ms)
|
||||
_ = try consume(state, .close_paren)
|
||||
return_type :: try parse_type(state)
|
||||
body :: try parse_block(state)
|
||||
@@ -195,6 +169,36 @@ parse_func_decl proc(state @mut State) ast.NodeId ! ParseError {
|
||||
)
|
||||
}
|
||||
|
||||
@hide:file
|
||||
parse_parameters proc(
|
||||
state @mut State, params @mut arraylist.ArrayList(ast.NodeId),
|
||||
) void ! ParseError {
|
||||
names arraylist.ArrayList(lexer.Token) := arraylist.init(state.allocator)
|
||||
defer arraylist.deinit(&names)
|
||||
while !next_is(state, .close_paren) {
|
||||
arraylist.clear(&names)
|
||||
while true {
|
||||
token :: try consume(state, .ident)
|
||||
try arraylist.append(&names, token)
|
||||
if (starts_type(current(state).kind)) break
|
||||
_ = try consume(state, .comma)
|
||||
}
|
||||
type_id :: try parse_type(state)
|
||||
type_span :: ast.builder_span(state.builder, type_id)
|
||||
for (names.items) |parameter_name| {
|
||||
param_id :: try ast.add_param(
|
||||
state.builder,
|
||||
ast.Span{ start = parameter_name.span.start, end = type_span.end, source = parameter_name.span.source },
|
||||
parameter_name.span,
|
||||
parameter_name.data.str_id,
|
||||
type_id,
|
||||
)
|
||||
try arraylist.append(params, param_id)
|
||||
}
|
||||
if next_is(state, .comma) _ = try consume(state, .comma)
|
||||
}
|
||||
}
|
||||
|
||||
@hide:file
|
||||
parse_block proc(state @mut State) ast.NodeId ! ParseError {
|
||||
open :: try consume(state, .open_curly)
|
||||
@@ -610,6 +614,25 @@ parse_type proc(state @mut State) ast.NodeId ! ParseError {
|
||||
state.next_token += 1
|
||||
mutable :: next_is(state, .mut)
|
||||
if (mutable) state.next_token += 1
|
||||
if (next_is(state, .proc)) {
|
||||
state.next_token += 1
|
||||
_ = try consume(state, .open_paren)
|
||||
params := arraylist.init(ast.NodeId, state.allocator)
|
||||
defer arraylist.deinit(¶ms)
|
||||
try parse_parameters(state, ¶ms)
|
||||
_ = try consume(state, .close_paren)
|
||||
result :: try parse_type(state)
|
||||
span :: ast.Span{
|
||||
start = token.span.start,
|
||||
end = ast.builder_span(state.builder, result).end,
|
||||
source = token.span.source,
|
||||
}
|
||||
if (mutable or token.kind == .star) {
|
||||
diagnostic :: try diagnostics.add(state.diagnostics, .invalid_pointer_type, span)
|
||||
return try ast.add_invalid_expr(state.builder, span, diagnostic)
|
||||
}
|
||||
return try ast.add_function_type(state.builder, span, result, params.items)
|
||||
}
|
||||
element :: try parse_type(state)
|
||||
return try ast.add_pointer_type(state.builder, ast.Span{
|
||||
start = token.span.start,
|
||||
|
||||
+71
-10
@@ -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
|
||||
|
||||
@@ -572,6 +572,13 @@ resolve_expr proc(
|
||||
}
|
||||
}
|
||||
.pointer_type: try resolve_expr(state, scope, ast.pointer_type(tree, id).element)
|
||||
.function_type: {
|
||||
try resolve_expr(state, scope, ast.callable_result(tree, id))
|
||||
for 0..ast.function_param_count(tree, id) |i| {
|
||||
parameter :: ast.function_param(tree, id, i)
|
||||
try resolve_expr(state, scope, ast.param(tree, parameter).type)
|
||||
}
|
||||
}
|
||||
.array_type: {
|
||||
type :: ast.array_type(tree, id)
|
||||
if (type.count) |count| try resolve_expr(state, scope, count)
|
||||
|
||||
Reference in New Issue
Block a user