feat: support source pointers and references

This commit is contained in:
2026-10-26 10:37:08 +01:00
parent 9966a1f523
commit 676f3666b4
17 changed files with 981 additions and 78 deletions
+302 -45
View File
@@ -18,6 +18,7 @@ TypeKind :: enum {
function
range
array
pointer
record
enumeration
type_value
@@ -72,11 +73,21 @@ ArrayType :: struct {
mutable bool
}
PointerId :: distinct u32
no_pointer :: maxval!(PointerId)
PointerType :: struct {
element Type
mutable bool
many bool
}
Type :: struct {
kind TypeKind = .none
function ast.NodeId = ast.NodeId(maxval!(u32))
diagnostic diagnostics.DiagnosticId = diagnostics.no_diagnostic
array_type ArrayId = no_array
pointer_type PointerId = no_pointer
range_element TypeKind = .none
record_type RecordId = no_record
enum_type EnumId = no_enum
@@ -88,6 +99,7 @@ Type :: struct {
Storage :: struct {
types []mut Type
arrays arraylist.ArrayList(ArrayType)
pointers arraylist.ArrayList(PointerType)
records arraylist.ArrayList(RecordType)
enums arraylist.ArrayList(EnumType)
backings []mut Type
@@ -151,6 +163,7 @@ Cell :: struct {
producer ast.NodeId = ast.NodeId(maxval!(u32))
capture ast.NodeId = ast.NodeId(maxval!(u32))
record_checking bool = false
record_pointer_depth usize = 0
nominal bool = false
match_pattern bool = false
first_edge usize = no_index
@@ -179,6 +192,7 @@ State :: struct {
groups []mut GroupLink
edges arraylist.ArrayList(Edge)
arrays arraylist.ArrayList(ArrayType)
pointers arraylist.ArrayList(PointerType)
records arraylist.ArrayList(RecordType)
initializers arraylist.ArrayList(RecordInitializer)
enums arraylist.ArrayList(EnumType)
@@ -191,6 +205,7 @@ State :: struct {
slots []mut usize
collecting_assignment bool = false
has_assignments bool = false
pointer_depth usize = 0
}
#! borrows the AST, resolution, and diagnostic store only during checking.
@@ -235,6 +250,7 @@ check proc(
groups = groups,
edges = arraylist.init(allocator),
arrays = arraylist.init(allocator),
pointers = arraylist.init(allocator),
records = arraylist.init(allocator),
initializers = arraylist.init(allocator),
slots = slots,
@@ -247,6 +263,7 @@ check proc(
defer arraylist.deinit(&state.edges)
defer arraylist.deinit(&state.variants)
errdefer arraylist.deinit(&state.arrays)
errdefer arraylist.deinit(&state.pointers)
errdefer {
for (state.enums.items) |descriptor| mem.free(allocator, descriptor.members)
arraylist.deinit(&state.enums)
@@ -278,7 +295,7 @@ check proc(
binding :: sema.binding(resolution, target)
if (binding.kind == .invalid) {
cells[i].type = invalid_type(binding.data.diagnostic)
} else if (binding.kind == .declaration and !mutable_target(&state, target)) {
} else if (binding.kind == .declaration and !mutable_target(&state, target, false)) {
cells[i].type = invalid_type(try diagnostics.add(
diagnostic_store, .invalid_assignment_target, ast.node_span(tree, target),
))
@@ -511,6 +528,7 @@ check proc(
return Result{ storage = Storage{
types = types,
arrays = state.arrays,
pointers = state.pointers,
records = state.records,
initializers = state.initializers,
enums = state.enums,
@@ -544,6 +562,10 @@ array_type proc(result @Result, id ArrayId) ArrayType {
return result.storage.arrays.items[usize(id)]
}
pointer_type proc(result @Result, id PointerId) PointerType {
return result.storage.pointers.items[usize(id)]
}
record_type_count proc(result @Result) usize {
return result.storage.records.items.len
}
@@ -621,6 +643,7 @@ initializer_field proc(result @Result, literal ast.NodeId, index usize) ast.Node
deinit proc(result @mut Result) void {
mem.free(result.storage.allocator, result.storage.types)
arraylist.deinit(&result.storage.arrays)
arraylist.deinit(&result.storage.pointers)
for (result.storage.enums.items) |descriptor| mem.free(result.storage.allocator, descriptor.members)
arraylist.deinit(&result.storage.enums)
mem.free(result.storage.allocator, result.storage.backings)
@@ -741,6 +764,12 @@ collect_invalid proc(state @mut State, id ast.NodeId) void {
check_annotation proc(state @mut State, id ast.NodeId, allow_void bool) Type ! (mem.AllocError | diagnostics.CapacityError) {
if (state.cells[usize(id)].role == .annotation) return state.types[usize(id)]
state.cells[usize(id)].role = .annotation
if (ast.expr_kind(state.tree, id) == .pointer_type) {
type :: try check_pointer_type(state, id)
state.types[usize(id)] = type
state.cells[usize(id)].type = type
return type
}
if (type_syntax(ast.expr_kind(state.tree, id))) {
type :: try check_type_expression(state, id, false)
state.types[usize(id)] = type
@@ -1047,7 +1076,7 @@ collect_expression proc(state @mut State, id ast.NodeId) void ! (mem.AllocError
if (expression.operator == .negate) state.cells[usize(id)].producer = id
if (expression.operator == .logical_not) {
state.cells[usize(id)].type = Type{ kind = .bool }
} else {
} else if (expression.operator == .negate) {
state.cells[usize(id)].numeric = true
}
operand :: expression.operand
@@ -1106,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: {
.record_type, .union_type, .enum_type, .distinct_type, .pointer_type: {
type := try check_type_expression(state, id, state.cells[usize(id)].nominal)
if (type.kind != .invalid) {
type.value_kind = type.kind
@@ -1240,7 +1269,8 @@ bool_equality proc(state @mut State, id ast.NodeId) bool {
return (
(expression.operator == .equal or expression.operator == .not_equal)
and ((left.kind == .bool and right.kind == .bool)
or left.kind == .enumeration or right.kind == .enumeration)
or left.kind == .enumeration or right.kind == .enumeration
or left.kind == .pointer or right.kind == .pointer)
)
}
@@ -1308,6 +1338,14 @@ numeric_constraint proc(state @mut State, expression, operand, operation ast.Nod
demand proc(state @mut State, id ast.NodeId, expected Type, code diagnostics.Code) void ! (mem.AllocError | diagnostics.CapacityError) {
if (state.types[usize(id)].kind == .invalid) return
index :: root(state, usize(id))
if (expected.kind == .pointer and state.cells[usize(id)].role == .expression
and ast.expr_kind(state.tree, id) == .unary
and ast.unary_expr(state.tree, id).operator == .address_of) {
state.cells[index].expected = expected
try demand(state, ast.unary_expr(state.tree, id).operand,
state.pointers.items[usize(expected.pointer_type)].element, code)
return
}
if (state.cells[usize(id)].role == .expression and ast.expr_kind(state.tree, id) == .match_expr) {
state.cells[index].expected = expected
for 0..ast.match_arm_count(state.tree, id) |i| {
@@ -1404,24 +1442,78 @@ require_bool proc(state @mut State, operand ast.NodeId) void ! (mem.AllocError |
}
@hide:file
mutable_target proc(state @State, id ast.NodeId) bool {
if (ast.expr_kind(state.tree, id) == .index) {
view :: ast.index_expr(state.tree, id)
base :: state.types[usize(view.base)]
return base.kind == .array and state.arrays.items[usize(base.array_type)].mutable and mutable_target(state, view.base)
mutable_target proc(state @mut State, id ast.NodeId, allow_global bool) bool {
kind :: ast.expr_kind(state.tree, id)
if (kind == .unary) {
view :: ast.unary_expr(state.tree, id)
base :: group_type(state, view.operand)
return (
view.operator == .dereference and base.kind == .pointer
and state.pointers.items[usize(base.pointer_type)].mutable
)
}
if (ast.expr_kind(state.tree, id) == .selection
and sema.binding(state.resolution, id).kind == .none) {
if (kind == .index) {
view :: ast.index_expr(state.tree, id)
base :: group_type(state, view.base)
if (base.kind == .pointer) {
pointer :: state.pointers.items[usize(base.pointer_type)]
return pointer.mutable and (pointer.many
or (pointer.element.kind == .array and state.arrays.items[usize(pointer.element.array_type)].mutable))
}
return base.kind == .array and state.arrays.items[usize(base.array_type)].mutable and mutable_target(state, view.base, allow_global)
}
if (kind == .selection and sema.binding(state.resolution, id).kind == .none) {
view :: ast.selection_expr(state.tree, id)
return state.types[usize(view.base)].kind == .record and mutable_target(state, view.base)
base :: group_type(state, view.base)
if (base.kind == .pointer) {
pointer :: state.pointers.items[usize(base.pointer_type)]
return pointer.element.kind == .record and pointer.mutable
}
return base.kind == .record and mutable_target(state, view.base, allow_global)
}
binding :: sema.binding(state.resolution, id)
if (binding.kind != .declaration) return false
target :: binding.data.declaration
if (state.cells[usize(target)].role != .declaration or !state.cells[usize(target)].local) return false
if (state.cells[usize(target)].role != .declaration or (!allow_global and !state.cells[usize(target)].local)) return false
return ast.statement_decl(state.tree, target).mutability == .variable
}
@hide:file
indexable_type proc(state @State, type Type) bool {
if (type.nominal != ast.NodeId(maxval!(u32))) return false
if (type.kind == .array) return true
if (type.kind != .pointer) return false
pointer :: state.pointers.items[usize(type.pointer_type)]
return pointer.many or pointer.element.kind == .array
}
@hide:file
addressable_target proc(state @mut State, id ast.NodeId) bool {
kind :: ast.expr_kind(state.tree, id)
if (kind == .unary) {
view :: ast.unary_expr(state.tree, id)
return view.operator == .dereference and group_type(state, view.operand).kind == .pointer
}
if (kind == .index) {
view :: ast.index_expr(state.tree, id)
base :: group_type(state, view.base)
return indexable_type(state, base) and (base.kind == .pointer or addressable_target(state, view.base))
}
if (kind == .selection and sema.binding(state.resolution, id).kind == .none) {
view :: ast.selection_expr(state.tree, id)
base :: group_type(state, view.base)
if (base.kind == .pointer) {
pointer :: state.pointers.items[usize(base.pointer_type)]
return pointer.element.kind == .record
}
return base.kind == .record and addressable_target(state, view.base)
}
binding :: sema.binding(state.resolution, id)
if (binding.kind != .declaration) return false
role :: state.cells[usize(binding.data.declaration)].role
return role == .declaration or role == .parameter or role == .loop_capture
}
@hide:file
same_signature proc(state @State, left, right ast.NodeId) bool {
tree :: state.tree
@@ -1437,11 +1529,22 @@ same_signature proc(state @State, left, right ast.NodeId) bool {
return true
}
@hide:file
assignable_type proc(state @State, actual, expected Type) bool {
if (same_type(state, actual, expected)) return true
if (actual.kind != .pointer or expected.kind != .pointer or actual.nominal != expected.nominal) return false
a :: state.pointers.items[usize(actual.pointer_type)]
b :: state.pointers.items[usize(expected.pointer_type)]
if (a.many != b.many or (!a.mutable and b.mutable)) return false
if (b.mutable) return same_type(state, a.element, b.element)
return assignable_type(state, a.element, b.element)
}
@hide:file
compatible proc(state @mut State, value ast.NodeId, expected Type) void ! (mem.AllocError | diagnostics.CapacityError) {
actual :: state.types[usize(value)]
if (actual.kind == .invalid or expected.kind == .invalid) return
if (!same_type(state, actual, expected)) {
if (!assignable_type(state, actual, expected)) {
try fail(state, value, .type_mismatch, ast.node_span(state.tree, value))
}
}
@@ -1465,11 +1568,18 @@ validate proc(state @mut State, id ast.NodeId) void ! (mem.AllocError | diagnost
assignment :: ast.assignment_stmt(tree, id)
target_type :: state.types[usize(assignment.target)]
if (target_type.kind == .invalid) return
if (mutable_target(state, assignment.target)) {
if (mutable_target(state, assignment.target, false)) {
try compatible(state, assignment.value, target_type)
} else {
code :: if (ast.expr_kind(tree, assignment.target) == .index)
diagnostics.Code.invalid_index_assignment else diagnostics.Code.invalid_assignment_target
kind :: ast.expr_kind(tree, assignment.target)
code := if (kind == .index) diagnostics.Code.invalid_index_assignment else diagnostics.Code.invalid_assignment_target
if (kind == .unary and ast.unary_expr(tree, assignment.target).operator == .dereference) {
code = .invalid_pointer_assignment
} else if (kind == .index and state.types[usize(ast.index_expr(tree, assignment.target).base)].kind == .pointer) {
code = .invalid_pointer_assignment
} else if (kind == .selection and state.types[usize(ast.selection_expr(tree, assignment.target).base)].kind == .pointer) {
code = .invalid_pointer_assignment
}
try fail(state, id, code, ast.node_span(tree, assignment.target))
}
}
@@ -1481,7 +1591,7 @@ validate proc(state @mut State, id ast.NodeId) void ! (mem.AllocError | diagnost
actual :: state.types[usize(value)]
if (actual.kind == .invalid) {
state.types[usize(id)] = actual
} else if (!same_type(state, actual, expected)) {
} else if (!assignable_type(state, actual, expected)) {
try fail(state, value, .return_type_mismatch, ast.node_span(tree, value))
state.types[usize(id)] = state.types[usize(value)]
}
@@ -1497,6 +1607,19 @@ validate proc(state @mut State, id ast.NodeId) void ! (mem.AllocError | diagnost
try require_bool(state, expression.operand)
return
}
if (expression.operator == .address_of) {
if (!addressable_target(state, expression.operand)) {
try fail(state, id, .invalid_address_operand, expression.span)
}
return
}
if (expression.operator == .dereference) {
if (state.types[usize(expression.operand)].kind != .invalid
and state.types[usize(expression.operand)].kind != .pointer) {
try fail(state, id, .invalid_dereference, expression.span)
}
return
}
operand :: state.types[usize(expression.operand)]
if (operand.kind != .invalid and (!numeric_kind(operand.kind) or unsigned_kind(operand.kind))) {
try fail(state, id, .invalid_operand, ast.node_span(tree, id))
@@ -1513,8 +1636,8 @@ validate proc(state @mut State, id ast.NodeId) void ! (mem.AllocError | diagnost
right :: state.types[usize(expression.right)]
if (left.kind == .invalid or right.kind == .invalid) return
if ((expression.operator == .equal or expression.operator == .not_equal)
and (left.kind == .bool or left.kind == .enumeration)
and same_type(state, left, right)) return
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
if (!numeric_kind(left.kind) or !numeric_kind(right.kind) or !same_type(state, left, right)) {
try fail(state, id, .invalid_operand, expression.span)
} else if (expression.operator == .divide and left.kind != .float) {
@@ -1572,8 +1695,7 @@ validate proc(state @mut State, id ast.NodeId) void ! (mem.AllocError | diagnost
if (index.kind != .invalid and (index.kind != .usize or index.nominal != ast.NodeId(maxval!(u32)))) {
try fail(state, view.index, .invalid_index_type, ast.node_span(tree, view.index))
}
if (base.kind != .invalid
and (base.kind != .array or base.nominal != ast.NodeId(maxval!(u32)))) {
if (base.kind != .invalid and !indexable_type(state, base)) {
try fail(state, id, .not_indexable, ast.node_span(tree, view.base))
}
}
@@ -1758,6 +1880,35 @@ evaluate_integer proc(state @mut State, id ast.NodeId, allow_local bool) ?Consta
}
}
@hide:file
intern_pointer proc(state @mut State, descriptor PointerType) Type ! mem.AllocError {
for state.pointers.items |existing, i| {
if (existing.mutable == descriptor.mutable and existing.many == descriptor.many
and same_type(state, existing.element, descriptor.element)) {
return Type{ kind = .pointer, pointer_type = PointerId(i) }
}
}
try arraylist.append(&state.pointers, descriptor)
return Type{ kind = .pointer, pointer_type = PointerId(state.pointers.items.len - 1) }
}
@hide:file
check_pointer_type proc(state @mut State, id ast.NodeId) Type ! (mem.AllocError | diagnostics.CapacityError) {
view :: ast.pointer_type(state.tree, id)
state.pointer_depth += 1
defer state.pointer_depth -= 1
element :: try check_annotation(state, view.element, true)
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 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)))
}
return try intern_pointer(state, PointerType{ element = element, mutable = view.mutable, many = view.many })
}
@hide:file
intern_array proc(state @mut State, descriptor ArrayType) Type ! mem.AllocError {
for state.arrays.items |existing, i| {
@@ -1775,6 +1926,11 @@ same_type proc(state @State, left, right Type) bool {
if (left.kind != right.kind) return false
if (left.nominal != right.nominal) return false
if (left.kind == .enumeration) return left.enum_type == right.enum_type
if (left.kind == .pointer) {
a :: state.pointers.items[usize(left.pointer_type)]
b :: state.pointers.items[usize(right.pointer_type)]
return a.mutable == b.mutable and a.many == b.many and same_type(state, a.element, b.element)
}
if (left.kind == .type_value) {
a := left
b := right
@@ -1881,6 +2037,33 @@ 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) == .unary) {
view :: ast.unary_expr(state.tree, id)
if (view.operator == .address_of or view.operator == .dereference) {
operand :: try infer_shape(state, view.operand, defaults)
if (operand.kind == .none) return Type{}
if (operand.kind == .invalid) {
seed(state, id, operand)
return operand
}
if (view.operator == .dereference) {
if (operand.kind != .pointer) {
try fail(state, id, .invalid_dereference, view.span)
seed(state, id, state.types[usize(id)])
return group_type(state, id)
}
element :: state.pointers.items[usize(operand.pointer_type)].element
seed(state, id, element)
return element
}
mutable :: mutable_target(state, view.operand, true)
expected :: state.cells[index].expected
many :: expected.kind == .pointer and state.pointers.items[usize(expected.pointer_type)].many
type :: try intern_pointer(state, PointerType{ element = operand, mutable = mutable, many = many })
seed(state, id, type)
return type
}
}
if (state.cells[usize(id)].role == .expression and ast.expr_kind(state.tree, id) == .match_expr) {
return try infer_match(state, id, defaults)
}
@@ -1899,7 +2082,28 @@ infer_shape proc(state @mut State, id ast.NodeId, defaults bool) Type ! (mem.All
enum_shape :: try check_annotation(state, view.base, false)
if (enum_shape.kind == .enumeration) return try select_enum_member(state, id, view.member, enum_shape)
}
base :: try infer_shape(state, view.base, defaults)
base := try infer_shape(state, view.base, defaults)
if (base.kind == .pointer and base.nominal == ast.NodeId(maxval!(u32))) {
pointer :: state.pointers.items[usize(base.pointer_type)]
if (!pointer.many and pointer.element.kind == .array
and ast.expr_kind(state.tree, view.member) == .identifier) {
member :: ast.source_text(state.tree, ast.node_span(state.tree, view.member))
if (mem.eql(member, "len")) {
type :: Type{ kind = .usize }
seed(state, id, type)
return type
}
if (mem.eql(member, "ptr")) {
array :: state.arrays.items[usize(pointer.element.array_type)]
type :: try intern_pointer(state, PointerType{
element = array.element, mutable = pointer.mutable and array.mutable, many = true,
})
seed(state, id, type)
return type
}
}
if (!pointer.many or pointer.element.kind == .record) base = pointer.element
}
if (base.kind == .record and base.nominal == ast.NodeId(maxval!(u32))) {
fields :: state.records.items[usize(base.record_type)].fields
slot :: selected_field(state, fields, view.member)
@@ -1987,7 +2191,15 @@ infer_shape proc(state @mut State, id ast.NodeId, defaults bool) Type ! (mem.All
}
if (state.cells[usize(id)].role == .expression and ast.expr_kind(state.tree, id) == .index) {
view :: ast.index_expr(state.tree, id)
base :: try infer_shape(state, view.base, defaults)
base := try infer_shape(state, view.base, defaults)
if (base.kind == .pointer and base.nominal == ast.NodeId(maxval!(u32))) {
pointer :: state.pointers.items[usize(base.pointer_type)]
if (pointer.many) {
seed(state, id, pointer.element)
return pointer.element
}
base = pointer.element
}
if (base.kind == .array and base.nominal == ast.NodeId(maxval!(u32))) {
element :: state.arrays.items[usize(base.array_type)].element
seed(state, id, element)
@@ -2057,7 +2269,7 @@ apply_contexts proc(state @mut State, assignment_values, early bool) void ! (mem
}
if (assignment_values and state.cells[i].role == .assignment_stmt) {
view :: ast.assignment_stmt(tree, id)
if (mutable_target(state, view.target)) try context_demand(state, view.value, state.types[usize(view.target)], .type_mismatch, early)
if (mutable_target(state, view.target, false)) try context_demand(state, view.value, state.types[usize(view.target)], .type_mismatch, early)
}
if (state.cells[i].role != .expression or state.cells[i].assignment_value != assignment_values or ast.expr_kind(tree, id) != .call) continue
if (state.backings[i].kind != .none) {
@@ -2093,7 +2305,7 @@ apply_contexts proc(state @mut State, assignment_values, early bool) void ! (mem
@hide:file
context_demand proc(state @mut State, id ast.NodeId, expected Type, code diagnostics.Code, early bool) void ! (mem.AllocError | diagnostics.CapacityError) {
if (early and expected.kind != .array and expected.kind != .record
and expected.kind != .enumeration and !integer_kind(expected.kind)) return
and expected.kind != .pointer and expected.kind != .enumeration and !integer_kind(expected.kind)) return
try demand(state, id, expected, code)
}
@@ -2281,18 +2493,35 @@ intern_record proc(state @mut State, declaration ast.NodeId, fields []mut Record
@hide:file
check_record proc(state @mut State, id ast.NodeId, nominal bool) Type ! (mem.AllocError | diagnostics.CapacityError) {
cached := state.cells[usize(id)].type
if (cached.kind == .record or cached.kind == .type_value) {
if (cached.kind == .invalid) return cached
if (!state.cells[usize(id)].record_checking and (cached.kind == .record or cached.kind == .type_value)) {
cached.kind = .record
return cached
}
if (cached.kind == .invalid) return cached
if (state.cells[usize(id)].record_checking) {
if (cached.kind == .record and state.pointer_depth > state.cells[usize(id)].record_pointer_depth) {
cached.kind = .record
return cached
}
type :: invalid_type(try diagnostics.add(state.diagnostics, .recursive_record, ast.node_span(state.tree, id)))
state.cells[usize(id)].type = type
return type
}
state.cells[usize(id)].record_checking = true
defer state.cells[usize(id)].record_checking = false
state.cells[usize(id)].record_pointer_depth = state.pointer_depth
record_id := no_record
result_type := Type{}
if (nominal) {
record_id = RecordId(state.records.items.len)
empty :: try mem.alloc(RecordField, state.records.allocator, 0)
arraylist.append(&state.records, RecordType{ declaration = id, fields = empty }) catch |err| {
mem.free(state.records.allocator, empty)
return err
}
result_type = Type{ kind = .record, record_type = record_id }
state.cells[usize(id)].type = result_type
}
count :: ast.record_field_count(state.tree, id)
fields :: try mem.alloc(RecordField, state.records.allocator, count)
owned := true
@@ -2319,10 +2548,12 @@ check_record proc(state @mut State, id ast.NodeId, nominal bool) Type ! (mem.All
state.cells[usize(id)].type = failure
return failure
}
declaration :: if (nominal) id else ast.NodeId(maxval!(u32))
type :: try intern_record(state, declaration, fields)
if (nominal) {
mem.free(state.records.allocator, state.records.items[usize(record_id)].fields)
state.records.items[usize(record_id)].fields = fields
} else result_type = try intern_record(state, ast.NodeId(maxval!(u32)), fields)
owned = false
state.cells[usize(id)].type = type
state.cells[usize(id)].type = result_type
state.cells[usize(id)].record_checking = false
saved_assignment :: state.collecting_assignment
state.collecting_assignment = false
@@ -2332,7 +2563,7 @@ check_record proc(state @mut State, id ast.NodeId, nominal bool) Type ! (mem.All
try collect_expression(state, value)
}
}
return type
return result_type
}
@hide:file
@@ -2457,6 +2688,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
)
}
@@ -2483,16 +2715,22 @@ immediate_backing proc(state @State, type Type) Type {
@hide:file
check_type_expression proc(state @mut State, id ast.NodeId, nominal bool) Type ! (mem.AllocError | diagnostics.CapacityError) {
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 == .union_type) {
type :: try check_union(state, id, nominal)
state.cells[usize(id)].type = type
return type
}
cached := state.cells[usize(id)].type
if (cached.kind == .type_value) {
cached.kind = cached.value_kind
return cached
}
if (cached.kind != .none) return cached
kind :: ast.expr_kind(state.tree, id)
if (kind == .record_type) return try check_record(state, id, nominal)
if (kind == .union_type or kind == .enum_type) {
type :: if (kind == .union_type) try check_union(state, id, nominal) else try check_enum(state, id)
if (kind == .enum_type) {
type :: try check_enum(state, id)
state.cells[usize(id)].type = type
return type
}
@@ -2630,11 +2868,33 @@ check_enum proc(state @mut State, id ast.NodeId) Type ! (mem.AllocError | diagno
@hide:file
check_union proc(state @mut State, id ast.NodeId, nominal bool) Type ! (mem.AllocError | diagnostics.CapacityError) {
cached := state.cells[usize(id)].type
if (cached.kind == .invalid) return cached
if (!state.cells[usize(id)].record_checking and (cached.kind == .record or cached.kind == .type_value)) {
cached.kind = .record
return cached
}
if (state.cells[usize(id)].record_checking) {
if (state.pointer_depth > state.cells[usize(id)].record_pointer_depth) {
cached.kind = .record
return cached
}
return invalid_type(try diagnostics.add(state.diagnostics, .recursive_record, ast.node_span(state.tree, id)))
}
state.cells[usize(id)].record_checking = true
defer state.cells[usize(id)].record_checking = false
state.cells[usize(id)].record_pointer_depth = state.pointer_depth
record_id :: RecordId(state.records.items.len)
empty :: try mem.alloc(RecordField, state.records.allocator, 0)
declaration :: if (nominal) id else ast.NodeId(maxval!(u32))
arraylist.append(&state.records, RecordType{
declaration = declaration, fields = empty, is_union = true,
}) catch |err| {
mem.free(state.records.allocator, empty)
return err
}
result_type :: Type{ kind = .record, record_type = record_id }
state.cells[usize(id)].type = result_type
count :: ast.record_field_count(state.tree, id)
fields :: try mem.alloc(RecordField, state.records.allocator, count)
owned := true
@@ -2688,16 +2948,13 @@ check_union proc(state @mut State, id ast.NodeId, nominal bool) Type ! (mem.Allo
members_owned = false
tag = Type{ kind = .enumeration, enum_type = EnumId(state.enums.items.len - 1) }
}
declaration :: if (nominal) id else ast.NodeId(maxval!(u32))
try arraylist.append(&state.records, RecordType{
declaration = declaration,
fields = fields, is_union = true, tag_type = tag,
declared_tag_type = declared_tag,
})
mem.free(state.records.allocator, state.records.items[usize(record_id)].fields)
state.records.items[usize(record_id)].fields = fields
state.records.items[usize(record_id)].tag_type = tag
state.records.items[usize(record_id)].declared_tag_type = declared_tag
owned = false
type :: Type{ kind = .record, record_type = RecordId(state.records.items.len - 1) }
state.cells[usize(id)].type = type
return type
state.cells[usize(id)].type = result_type
return result_type
}
@hide:file
+1
View File
@@ -571,6 +571,7 @@ resolve_expr proc(
try resolve_expr(state, scope, ast.call_arg(tree, id, i))
}
}
.pointer_type: try resolve_expr(state, scope, ast.pointer_type(tree, id).element)
.array_type: {
type :: ast.array_type(tree, id)
if (type.count) |count| try resolve_expr(state, scope, count)