style: align layout and ownership documentation
This commit is contained in:
+257
-121
@@ -227,7 +227,7 @@ State :: struct {
|
||||
pointer_depth usize = 0
|
||||
}
|
||||
|
||||
#! borrows resolution and diagnostics during checking; Result retains the AST borrow for signature comparisons.
|
||||
#! borrows `resolution` and `diagnostic_store` during checking; Result retains the AST borrow for signature comparisons.
|
||||
#! temporary constraints are freed on every exit; Result owns types, aggregate descriptors, initializer mappings, and fallthrough obligations.
|
||||
check proc(
|
||||
allocator mem.Allocator,
|
||||
@@ -322,7 +322,7 @@ check_elaborated proc(
|
||||
try collect_declaration(&state, constraint)
|
||||
} else try collect_expression(&state, constraint)
|
||||
}
|
||||
# Preserve binding errors before RHS errors propagate. Indexed writability
|
||||
# preserve binding errors before RHS errors propagate. indexed writability
|
||||
# waits for shapes; neither check poisons the target's type.
|
||||
for 0..count |i| {
|
||||
if (cells[i].role != .assignment_stmt) continue
|
||||
@@ -357,8 +357,10 @@ check_elaborated proc(
|
||||
if (cells[i].role != .expression or !declaration_reference(tree, id)) continue
|
||||
if (cells[i].type.kind == .invalid) continue
|
||||
binding :: sema.binding(resolution, id)
|
||||
if (binding.kind == .declaration and !cells[i].assignment_value
|
||||
and !cells[usize(binding.data.declaration)].fixed and !has_narrowing(&state, id)) {
|
||||
if (
|
||||
binding.kind == .declaration and !cells[i].assignment_value
|
||||
and !cells[usize(binding.data.declaration)].fixed and !has_narrowing(&state, id)
|
||||
) {
|
||||
join(&state, id, binding.data.declaration)
|
||||
}
|
||||
}
|
||||
@@ -371,7 +373,8 @@ check_elaborated proc(
|
||||
if (assignment_values) {
|
||||
for (0..count) |i| {
|
||||
id :: ast.NodeId(i)
|
||||
if (!cells[i].assignment_value
|
||||
if (
|
||||
!cells[i].assignment_value
|
||||
or cells[i].role != .expression
|
||||
or !declaration_reference(tree, id)
|
||||
) continue
|
||||
@@ -380,7 +383,7 @@ check_elaborated proc(
|
||||
if (binding.kind == .declaration) cells[i].type = types[usize(binding.data.declaration)]
|
||||
}
|
||||
}
|
||||
# Calls use the signature result before contextual literal inference.
|
||||
# calls use the signature result before contextual literal inference.
|
||||
try prepare_optional_captures(&state, false)
|
||||
for (0..count) |i| {
|
||||
id :: ast.NodeId(i)
|
||||
@@ -840,9 +843,13 @@ check_annotation proc(state @mut State, id ast.NodeId, allow_void bool) Type ! (
|
||||
view :: ast.array_type(state.tree, id)
|
||||
element := try check_annotation(state, view.element, true)
|
||||
element_binding :: sema.binding(state.resolution, view.element)
|
||||
if ((element_binding.kind == .builtin
|
||||
and (element_binding.data.builtin == .int or element_binding.data.builtin == .float))
|
||||
or element.kind == .void or element.kind == .range) {
|
||||
if (
|
||||
(
|
||||
element_binding.kind == .builtin
|
||||
and (element_binding.data.builtin == .int or element_binding.data.builtin == .float)
|
||||
)
|
||||
or element.kind == .void or element.kind == .range
|
||||
) {
|
||||
element = invalid_type(try diagnostics.add(state.diagnostics, .invalid_array_element_type, ast.node_span(state.tree, view.element)))
|
||||
state.types[usize(view.element)] = element
|
||||
state.cells[usize(view.element)].type = element
|
||||
@@ -920,9 +927,11 @@ collect_declaration proc(state @mut State, id ast.NodeId) void ! (mem.AllocError
|
||||
state.cells[usize(id)].type = invalid_type(binding.data.diagnostic)
|
||||
}
|
||||
try collect_expression(state, declaration.initializer)
|
||||
if (type_syntax(ast.expr_kind(state.tree, declaration.initializer))
|
||||
if (
|
||||
type_syntax(ast.expr_kind(state.tree, declaration.initializer))
|
||||
and state.cells[usize(id)].type.kind != .invalid
|
||||
and state.cells[usize(declaration.initializer)].type.kind != .none) {
|
||||
and state.cells[usize(declaration.initializer)].type.kind != .none
|
||||
) {
|
||||
state.cells[usize(id)].fixed = true
|
||||
state.cells[usize(id)].type = state.cells[usize(declaration.initializer)].type
|
||||
}
|
||||
@@ -1208,8 +1217,10 @@ collect_expression proc(state @mut State, id ast.NodeId) void ! (mem.AllocError
|
||||
base :: ast.selection_expr(tree, id).base
|
||||
if (type_reference(state, base)) {
|
||||
checked :: try check_annotation(state, base, true)
|
||||
if (checked.kind != .invalid
|
||||
and (checked.kind != .enumeration or checked.nominal != ast.NodeId(maxval!(u32)))) {
|
||||
if (
|
||||
checked.kind != .invalid
|
||||
and (checked.kind != .enumeration or checked.nominal != ast.NodeId(maxval!(u32)))
|
||||
) {
|
||||
try fail(state, base, .type_not_value, ast.node_span(tree, base))
|
||||
state.cells[usize(base)].type = state.types[usize(base)]
|
||||
}
|
||||
@@ -1219,8 +1230,10 @@ collect_expression proc(state @mut State, id ast.NodeId) void ! (mem.AllocError
|
||||
if (binding.kind == .declaration) {
|
||||
target :: binding.data.declaration
|
||||
role :: state.cells[usize(target)].role
|
||||
if (role != .parameter and role != .loop_capture and ast.decl_kind(tree, target) == .statement
|
||||
and type_syntax(ast.expr_kind(tree, ast.statement_decl(tree, target).initializer))) {
|
||||
if (
|
||||
role != .parameter and role != .loop_capture and ast.decl_kind(tree, target) == .statement
|
||||
and type_syntax(ast.expr_kind(tree, ast.statement_decl(tree, target).initializer))
|
||||
) {
|
||||
state.cells[usize(id)].type = invalid_type(try diagnostics.add(state.diagnostics, .type_not_value, ast.node_span(tree, id)))
|
||||
}
|
||||
}
|
||||
@@ -1451,11 +1464,13 @@ bool_equality proc(state @mut State, id ast.NodeId) bool {
|
||||
right :: group_type(state, expression.right)
|
||||
return (
|
||||
(expression.operator == .equal or expression.operator == .not_equal)
|
||||
and ((left.kind == .bool and right.kind == .bool)
|
||||
and (
|
||||
(left.kind == .bool and right.kind == .bool)
|
||||
or left.kind == .enumeration or right.kind == .enumeration
|
||||
or left.kind == .pointer or right.kind == .pointer
|
||||
or left.kind == .optional or right.kind == .optional
|
||||
or left.kind == .null or right.kind == .null)
|
||||
or left.kind == .null or right.kind == .null
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1534,8 +1549,10 @@ demand proc(state @mut State, id ast.NodeId, expected Type, code diagnostics.Cod
|
||||
if (expected.kind == .fallible) {
|
||||
descriptor :: state.arrays.items[usize(expected.array_type)]
|
||||
actual := group_type(state, id)
|
||||
if (state.cells[usize(id)].role == .expression and declaration_reference(state.tree, id)
|
||||
and has_narrowing(state, id)) actual = try infer_shape(state, id, false)
|
||||
if (
|
||||
state.cells[usize(id)].role == .expression and declaration_reference(state.tree, id)
|
||||
and has_narrowing(state, id)
|
||||
) actual = try infer_shape(state, id, false)
|
||||
if (actual.kind == .fallible) return
|
||||
target := descriptor.element
|
||||
literal :: state.cells[index].literal
|
||||
@@ -1547,16 +1564,24 @@ demand proc(state @mut State, id ast.NodeId, expected Type, code diagnostics.Cod
|
||||
if (ast.record_literal(state.tree, literal).type) |annotation| {
|
||||
if (ast.expr_kind(state.tree, annotation) == .enum_literal) name = ast.enum_literal(state.tree, annotation)
|
||||
}
|
||||
if (name == ast.NodeId(maxval!(u32)) and actual.kind == .none
|
||||
if (
|
||||
name == ast.NodeId(maxval!(u32)) and actual.kind == .none
|
||||
and descriptor.element.kind != .record and descriptor.error_type.kind == .record
|
||||
and !state.records.items[usize(descriptor.error_type.record_type)].is_union) target = descriptor.error_type
|
||||
and !state.records.items[usize(descriptor.error_type.record_type)].is_union
|
||||
) target = descriptor.error_type
|
||||
}
|
||||
if (name != ast.NodeId(maxval!(u32)) and type_has_variant(state, descriptor.error_type, name)
|
||||
and !type_has_variant(state, descriptor.element, name)) target = descriptor.error_type
|
||||
else if (actual.kind != .none and !assignable_type(state, actual, target)
|
||||
and assignable_type(state, actual, descriptor.error_type)) target = descriptor.error_type
|
||||
} else if (actual.kind != .none and !assignable_type(state, actual, target)
|
||||
and assignable_type(state, actual, descriptor.error_type)) target = descriptor.error_type
|
||||
if (
|
||||
name != ast.NodeId(maxval!(u32)) and type_has_variant(state, descriptor.error_type, name)
|
||||
and !type_has_variant(state, descriptor.element, name)
|
||||
) target = descriptor.error_type
|
||||
else if (
|
||||
actual.kind != .none and !assignable_type(state, actual, target)
|
||||
and assignable_type(state, actual, descriptor.error_type)
|
||||
) target = descriptor.error_type
|
||||
} else if (
|
||||
actual.kind != .none and !assignable_type(state, actual, target)
|
||||
and assignable_type(state, actual, descriptor.error_type)
|
||||
) target = descriptor.error_type
|
||||
try demand(state, id, target, code)
|
||||
return
|
||||
}
|
||||
@@ -1587,9 +1612,11 @@ demand proc(state @mut State, id ast.NodeId, expected Type, code diagnostics.Cod
|
||||
}
|
||||
return
|
||||
}
|
||||
if (expected.kind == .pointer and state.cells[usize(id)].role == .expression
|
||||
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) {
|
||||
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)
|
||||
@@ -1604,8 +1631,10 @@ demand proc(state @mut State, id ast.NodeId, expected Type, code diagnostics.Cod
|
||||
return
|
||||
}
|
||||
literal_node :: state.cells[index].literal
|
||||
if (expected.nominal != ast.NodeId(maxval!(u32))
|
||||
and (expected.kind == .record or expected.kind == .array)) return
|
||||
if (
|
||||
expected.nominal != ast.NodeId(maxval!(u32))
|
||||
and (expected.kind == .record or expected.kind == .array)
|
||||
) return
|
||||
if (literal_node != ast.NodeId(maxval!(u32)) and ast.expr_kind(state.tree, literal_node) == .enum_literal) {
|
||||
previous :: state.cells[index].expected
|
||||
if (previous.kind != .none and !same_type(state, previous, expected)) {
|
||||
@@ -1655,8 +1684,10 @@ demand proc(state @mut State, id ast.NodeId, expected Type, code diagnostics.Cod
|
||||
if (!numeric_kind(expected.kind)) return
|
||||
if (!state.cells[index].numeric and state.cells[index].type.kind != .none) return
|
||||
state.cells[index].numeric = true
|
||||
if (state.cells[index].expected.kind != .none
|
||||
and !same_type(state, state.cells[index].expected, expected)) {
|
||||
if (
|
||||
state.cells[index].expected.kind != .none
|
||||
and !same_type(state, state.cells[index].expected, expected)
|
||||
) {
|
||||
state.cells[index].type = invalid_type(try diagnostics.add(state.diagnostics, code, ast.node_span(state.tree, id)))
|
||||
return
|
||||
}
|
||||
@@ -1706,8 +1737,13 @@ mutable_target proc(state @mut State, id ast.NodeId, allow_global bool) bool {
|
||||
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 (
|
||||
pointer.mutable
|
||||
and (
|
||||
pointer.many
|
||||
or (pointer.element.kind == .array and state.arrays.items[usize(pointer.element.array_type)].mutable)
|
||||
)
|
||||
)
|
||||
}
|
||||
if (base.kind == .slice) return state.arrays.items[usize(base.array_type)].mutable
|
||||
return base.kind == .array and state.arrays.items[usize(base.array_type)].mutable and mutable_target(state, view.base, allow_global)
|
||||
@@ -1798,8 +1834,10 @@ assignable_type proc(state @State, actual, expected Type) bool {
|
||||
or error_subset(state.records.items, state.enums.items, actual, target.error_type)
|
||||
)
|
||||
}
|
||||
if ((composed_type(state, actual) or composed_type(state, expected))
|
||||
and error_subset(state.records.items, state.enums.items, actual, expected)) return true
|
||||
if (
|
||||
(composed_type(state, actual) or composed_type(state, expected))
|
||||
and error_subset(state.records.items, state.enums.items, actual, expected)
|
||||
) return true
|
||||
if (expected.kind == .optional) {
|
||||
child :: state.arrays.items[usize(expected.array_type)].element
|
||||
if (actual.kind == .null) return true
|
||||
@@ -1851,9 +1889,11 @@ assignable_value proc(state @mut State, value ast.NodeId, actual, expected Type)
|
||||
if (!assignable_type(state, actual, expected)) return false
|
||||
target := expected
|
||||
while (target.kind == .optional) target = state.arrays.items[usize(target.array_type)].element
|
||||
if (actual.kind == .array and target.kind == .slice
|
||||
if (
|
||||
actual.kind == .array and target.kind == .slice
|
||||
and state.arrays.items[usize(target.array_type)].mutable
|
||||
and addressable_target(state, value)) return mutable_target(state, value, true)
|
||||
and addressable_target(state, value)
|
||||
) return mutable_target(state, value, true)
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -1907,8 +1947,10 @@ validate proc(state @mut State, id ast.NodeId) void ! (mem.AllocError | diagnost
|
||||
try fail(state, value, .return_type_mismatch, ast.node_span(tree, value))
|
||||
state.types[usize(id)] = state.types[usize(value)]
|
||||
}
|
||||
} else if (expected.kind != .void
|
||||
and !(expected.kind == .fallible and state.arrays.items[usize(expected.array_type)].element.kind == .void)) {
|
||||
} else if (
|
||||
expected.kind != .void
|
||||
and !(expected.kind == .fallible and state.arrays.items[usize(expected.array_type)].element.kind == .void)
|
||||
) {
|
||||
try fail(state, id, .return_type_mismatch, statement.span)
|
||||
}
|
||||
}
|
||||
@@ -1939,8 +1981,10 @@ validate proc(state @mut State, id ast.NodeId) void ! (mem.AllocError | diagnost
|
||||
return
|
||||
}
|
||||
if (expression.operator == .dereference) {
|
||||
if (state.types[usize(expression.operand)].kind != .invalid
|
||||
and state.types[usize(expression.operand)].kind != .pointer) {
|
||||
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
|
||||
@@ -1966,11 +2010,15 @@ validate proc(state @mut State, id ast.NodeId) void ! (mem.AllocError | diagnost
|
||||
}
|
||||
return
|
||||
}
|
||||
if ((expression.operator == .equal or expression.operator == .not_equal)
|
||||
and ((left.kind == .optional and right.kind == .null) or (left.kind == .null and right.kind == .optional))) return
|
||||
if ((expression.operator == .equal or expression.operator == .not_equal)
|
||||
if (
|
||||
(expression.operator == .equal or expression.operator == .not_equal)
|
||||
and ((left.kind == .optional and right.kind == .null) or (left.kind == .null and right.kind == .optional))
|
||||
) 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
|
||||
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) {
|
||||
@@ -2066,9 +2114,13 @@ validate_call proc(state @mut State, id ast.NodeId) void ! (mem.AllocError | dia
|
||||
and state.enums.items[usize(actual.enum_type)].explicit_backing
|
||||
)
|
||||
backing :: immediate_backing(state, actual)
|
||||
if (actual.kind != .invalid
|
||||
and (!numeric_kind(backing.kind) and !allowed_enum
|
||||
or backing.nominal != ast.NodeId(maxval!(u32)))) {
|
||||
if (
|
||||
actual.kind != .invalid
|
||||
and (
|
||||
!numeric_kind(backing.kind) and !allowed_enum
|
||||
or backing.nominal != ast.NodeId(maxval!(u32))
|
||||
)
|
||||
) {
|
||||
try fail(state, argument, .invalid_operand, ast.node_span(tree, argument))
|
||||
}
|
||||
}
|
||||
@@ -2167,14 +2219,18 @@ evaluate_integer proc(state @mut State, id ast.NodeId, allow_local bool) ?Consta
|
||||
binding :: sema.binding(state.resolution, id)
|
||||
if (binding.kind != .declaration) return null
|
||||
target :: binding.data.declaration
|
||||
if (!state.cells[usize(target)].count_declaration
|
||||
and (!allow_local or !state.cells[usize(target)].local)) return null
|
||||
if (
|
||||
!state.cells[usize(target)].count_declaration
|
||||
and (!allow_local or !state.cells[usize(target)].local)
|
||||
) return null
|
||||
declaration :: ast.statement_decl(state.tree, target)
|
||||
if (declaration.mutability != .constant) return null
|
||||
if (declaration.type) |annotation| {
|
||||
type_binding :: sema.binding(state.resolution, annotation)
|
||||
if (!integer_kind(state.types[usize(annotation)].kind)
|
||||
and (type_binding.kind != .builtin or !integer_kind(builtin_kind(type_binding.data.builtin)))) return null
|
||||
if (
|
||||
!integer_kind(state.types[usize(annotation)].kind)
|
||||
and (type_binding.kind != .builtin or !integer_kind(builtin_kind(type_binding.data.builtin)))
|
||||
) return null
|
||||
}
|
||||
return evaluate_integer(state, declaration.initializer, allow_local)
|
||||
}
|
||||
@@ -2224,8 +2280,10 @@ 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)) {
|
||||
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) }
|
||||
}
|
||||
}
|
||||
@@ -2241,10 +2299,12 @@ check_pointer_type proc(state @mut State, id ast.NodeId) Type ! (mem.AllocError
|
||||
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
|
||||
if (
|
||||
element.kind == .none or element.kind == .void or element.kind == .range
|
||||
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))) {
|
||||
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 })
|
||||
@@ -2282,9 +2342,11 @@ check_function_type proc(state @mut State, id ast.NodeId) Type ! (mem.AllocError
|
||||
@hide:file
|
||||
intern_array proc(state @mut State, descriptor ArrayType) Type ! mem.AllocError {
|
||||
for state.arrays.items |existing, i| {
|
||||
if (existing.kind == descriptor.kind and existing.len == descriptor.len and existing.mutable == descriptor.mutable
|
||||
if (
|
||||
existing.kind == descriptor.kind and existing.len == descriptor.len and existing.mutable == descriptor.mutable
|
||||
and same_sentinel(existing.sentinel, descriptor.sentinel) and same_type(state, existing.element, descriptor.element)
|
||||
and same_type(state, existing.error_type, descriptor.error_type)) {
|
||||
and same_type(state, existing.error_type, descriptor.error_type)
|
||||
) {
|
||||
return Type{ kind = descriptor.kind, array_type = ArrayId(i) }
|
||||
}
|
||||
}
|
||||
@@ -2527,8 +2589,10 @@ same_type proc(state @State, left, right Type) bool {
|
||||
and error_subset(state.records.items, state.enums.items, right, left)
|
||||
)
|
||||
if (a.is_union or b.is_union) return false
|
||||
if (a.declaration != ast.NodeId(maxval!(u32)) or b.declaration != ast.NodeId(maxval!(u32))
|
||||
or a.fields.len != b.fields.len) return false
|
||||
if (
|
||||
a.declaration != ast.NodeId(maxval!(u32)) or b.declaration != ast.NodeId(maxval!(u32))
|
||||
or a.fields.len != b.fields.len
|
||||
) return false
|
||||
for a.fields |field, i| {
|
||||
other :: b.fields[i]
|
||||
if ((field.name == null) != (other.name == null)) return false
|
||||
@@ -2749,9 +2813,11 @@ infer_shape proc(state @mut State, id ast.NodeId, defaults bool) Type ! (mem.All
|
||||
}
|
||||
}
|
||||
}
|
||||
if (state.cells[usize(id)].role == .expression
|
||||
if (
|
||||
state.cells[usize(id)].role == .expression
|
||||
and ast.expr_kind(state.tree, id) == .selection
|
||||
and sema.binding(state.resolution, id).kind == .none) {
|
||||
and sema.binding(state.resolution, id).kind == .none
|
||||
) {
|
||||
view :: ast.selection_expr(state.tree, id)
|
||||
if (type_reference(state, view.base)) {
|
||||
enum_shape :: try check_annotation(state, view.base, false)
|
||||
@@ -2777,8 +2843,10 @@ infer_shape proc(state @mut State, id ast.NodeId, defaults bool) Type ! (mem.All
|
||||
}
|
||||
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) {
|
||||
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 }
|
||||
@@ -2837,8 +2905,10 @@ infer_shape proc(state @mut State, id ast.NodeId, defaults bool) Type ! (mem.All
|
||||
binding :: sema.binding(state.resolution, id)
|
||||
if (binding.kind == .declaration) {
|
||||
target :: binding.data.declaration
|
||||
if (state.cells[usize(target)].role == .declaration and state.cells[usize(target)].fixed
|
||||
and has_inferred_count(state, state.cells[usize(target)].type)) {
|
||||
if (
|
||||
state.cells[usize(target)].role == .declaration and state.cells[usize(target)].fixed
|
||||
and has_inferred_count(state, state.cells[usize(target)].type)
|
||||
) {
|
||||
declaration :: ast.statement_decl(state.tree, target)
|
||||
actual :: try infer_shape(state, declaration.initializer, defaults)
|
||||
if (actual.kind == .array and !has_inferred_count(state, actual)) {
|
||||
@@ -2931,8 +3001,10 @@ prepare_optional_captures proc(state @mut State, defaults bool) void ! (mem.Allo
|
||||
for (0..(state.cells.len)) |j| {
|
||||
if (state.cells[j].role != .expression or !declaration_reference(state.tree, ast.NodeId(j))) continue
|
||||
binding :: sema.binding(state.resolution, ast.NodeId(j))
|
||||
if (binding.kind == .declaration and binding.data.declaration == capture
|
||||
and !has_narrowing(state, ast.NodeId(j))) seed(state, ast.NodeId(j), child)
|
||||
if (
|
||||
binding.kind == .declaration and binding.data.declaration == capture
|
||||
and !has_narrowing(state, ast.NodeId(j))
|
||||
) seed(state, ast.NodeId(j), child)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2999,8 +3071,10 @@ apply_contexts proc(state @mut State, assignment_values, early bool) void ! (mem
|
||||
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) {
|
||||
backing :: state.backings[i]
|
||||
if (ast.call_arg_count(tree, id) == 1
|
||||
and (backing.nominal != ast.NodeId(maxval!(u32)) or !numeric_kind(backing.kind))) {
|
||||
if (
|
||||
ast.call_arg_count(tree, id) == 1
|
||||
and (backing.nominal != ast.NodeId(maxval!(u32)) or !numeric_kind(backing.kind))
|
||||
) {
|
||||
try context_demand(state, ast.call_arg(tree, id, 0), backing, .type_mismatch, early)
|
||||
}
|
||||
continue
|
||||
@@ -3029,9 +3103,11 @@ 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
|
||||
if (
|
||||
early and expected.kind != .array and expected.kind != .record
|
||||
and expected.kind != .pointer and expected.kind != .slice and expected.kind != .optional and expected.kind != .fallible
|
||||
and expected.kind != .enumeration and !integer_kind(expected.kind)) return
|
||||
and expected.kind != .enumeration and !integer_kind(expected.kind)
|
||||
) return
|
||||
try demand(state, id, expected, code)
|
||||
}
|
||||
|
||||
@@ -3080,13 +3156,16 @@ prepare_ranges proc(state @mut State, assignment_values, constrain bool) void !
|
||||
}
|
||||
for 0..(state.cells.len) |i| {
|
||||
id :: ast.NodeId(i)
|
||||
if (state.cells[i].role != .expression
|
||||
if (
|
||||
state.cells[i].role != .expression
|
||||
or state.cells[i].assignment_value
|
||||
or !declaration_reference(tree, id)
|
||||
) continue
|
||||
binding :: sema.binding(state.resolution, id)
|
||||
if (binding.kind == .declaration and state.cells[usize(binding.data.declaration)].role == .loop_capture
|
||||
and !has_narrowing(state, id)) {
|
||||
if (
|
||||
binding.kind == .declaration and state.cells[usize(binding.data.declaration)].role == .loop_capture
|
||||
and !has_narrowing(state, id)
|
||||
) {
|
||||
seed(state, id, group_type(state, binding.data.declaration))
|
||||
}
|
||||
}
|
||||
@@ -3142,13 +3221,16 @@ specialize_annotations proc(state @mut State) void ! (mem.AllocError | diagnosti
|
||||
}
|
||||
for 0..(state.cells.len) |i| {
|
||||
id :: ast.NodeId(i)
|
||||
if (state.cells[i].role != .expression
|
||||
if (
|
||||
state.cells[i].role != .expression
|
||||
or state.cells[i].assignment_value
|
||||
or !declaration_reference(state.tree, id)
|
||||
) continue
|
||||
binding :: sema.binding(state.resolution, id)
|
||||
if (binding.kind == .declaration and state.cells[usize(binding.data.declaration)].fixed
|
||||
and !has_narrowing(state, id)) {
|
||||
if (
|
||||
binding.kind == .declaration and state.cells[usize(binding.data.declaration)].fixed
|
||||
and !has_narrowing(state, id)
|
||||
) {
|
||||
seed(state, id, state.cells[usize(binding.data.declaration)].type)
|
||||
}
|
||||
}
|
||||
@@ -3187,8 +3269,10 @@ intern_record proc(state @mut State, declaration ast.NodeId, fields []mut Record
|
||||
for fields |field, j| {
|
||||
slot := j
|
||||
if (field.name) |name| slot = named_field(state.tree, existing.fields, name)
|
||||
if (slot == no_index or (field.name == null and existing.fields[slot].name != null)
|
||||
or !same_type(state, field.type, existing.fields[slot].type)) {
|
||||
if (
|
||||
slot == no_index or (field.name == null and existing.fields[slot].name != null)
|
||||
or !same_type(state, field.type, existing.fields[slot].type)
|
||||
) {
|
||||
equal = false
|
||||
break
|
||||
}
|
||||
@@ -3480,8 +3564,10 @@ check_type_expression proc(state @mut State, id ast.NodeId, nominal bool) Type !
|
||||
and (operand_binding.data.builtin == .int or operand_binding.data.builtin == .float)
|
||||
)
|
||||
if (kind == .distinct_type and type.kind != .invalid) {
|
||||
if (type.kind == .none or type.kind == .void or type.kind == .range or type.kind == .type_value
|
||||
or type.kind == .function or has_inferred_count(state, type) or constraint) {
|
||||
if (
|
||||
type.kind == .none or type.kind == .void or type.kind == .range or type.kind == .type_value
|
||||
or type.kind == .function or has_inferred_count(state, type) or constraint
|
||||
) {
|
||||
type = invalid_type(try diagnostics.add(state.diagnostics, .invalid_distinct_backing, ast.node_span(state.tree, id)))
|
||||
} else {
|
||||
state.backings[usize(id)] = type
|
||||
@@ -3570,13 +3656,18 @@ check_enum proc(state @mut State, id ast.NodeId) Type ! (mem.AllocError | diagno
|
||||
} else valid = false
|
||||
}
|
||||
if (!integer_fits(value, backing.kind)) valid = false
|
||||
if (explicit and i != 0 and constant_order(
|
||||
ConstantInteger{ magnitude = members[i - 1].magnitude, negative = members[i - 1].negative },
|
||||
value,
|
||||
) >= 0) valid = false
|
||||
if (
|
||||
explicit and i != 0
|
||||
and constant_order(
|
||||
ConstantInteger{ magnitude = members[i - 1].magnitude, negative = members[i - 1].negative },
|
||||
value,
|
||||
) >= 0
|
||||
) valid = false
|
||||
for members[0..i] |member| {
|
||||
if (ast.name_id(state.tree, member.name) == ast.name_id(state.tree, field.name?)
|
||||
or (member.magnitude == value.magnitude and member.negative == value.negative)) valid = false
|
||||
if (
|
||||
ast.name_id(state.tree, member.name) == ast.name_id(state.tree, field.name?)
|
||||
or (member.magnitude == value.magnitude and member.negative == value.negative)
|
||||
) valid = false
|
||||
}
|
||||
if (!valid) {
|
||||
failure :: invalid_type(try diagnostics.add(state.diagnostics, .invalid_enum_value, field.span))
|
||||
@@ -3648,8 +3739,10 @@ check_union proc(state @mut State, id ast.NodeId, nominal bool) Type ! (mem.Allo
|
||||
field :: ast.field(state.tree, ast.record_field(state.tree, id, i))
|
||||
type :: try check_annotation(state, field.type?, true)
|
||||
if (type.kind == .invalid) return type
|
||||
if (type.kind == .none or type.kind == .range or type.kind == .type_value or field.value != null
|
||||
or (type.kind == .void and tag.kind == .none and !view.auto_tag)) {
|
||||
if (
|
||||
type.kind == .none or type.kind == .range or type.kind == .type_value or field.value != null
|
||||
or (type.kind == .void and tag.kind == .none and !view.auto_tag)
|
||||
) {
|
||||
return invalid_type(try diagnostics.add(state.diagnostics, .invalid_record_field_type, field.span))
|
||||
}
|
||||
if (field.name == null or named_field(state.tree, fields[0..i], field.name?) != no_index) {
|
||||
@@ -3896,8 +3989,10 @@ infer_match proc(state @mut State, id ast.NodeId, defaults bool) Type ! (mem.All
|
||||
)
|
||||
if (tagged) key = descriptor.tag_type
|
||||
}
|
||||
if (subject.kind != .none and !numeric_kind(subject.kind) and subject.kind != .bool
|
||||
and !(subject.kind == .enumeration and subject.nominal == ast.NodeId(maxval!(u32))) and !tagged) {
|
||||
if (
|
||||
subject.kind != .none and !numeric_kind(subject.kind) and subject.kind != .bool
|
||||
and !(subject.kind == .enumeration and subject.nominal == ast.NodeId(maxval!(u32))) and !tagged
|
||||
) {
|
||||
try fail(state, id, .invalid_match_subject, ast.node_span(state.tree, id))
|
||||
seed(state, id, state.types[usize(id)])
|
||||
return state.types[usize(id)]
|
||||
@@ -3942,8 +4037,10 @@ infer_match proc(state @mut State, id ast.NodeId, defaults bool) Type ! (mem.All
|
||||
for 0..(state.cells.len) |j| {
|
||||
if (state.cells[j].role != .expression or !declaration_reference(state.tree, ast.NodeId(j))) continue
|
||||
binding :: sema.binding(state.resolution, ast.NodeId(j))
|
||||
if (binding.kind == .declaration and binding.data.declaration == capture
|
||||
and !has_narrowing(state, ast.NodeId(j))) seed(state, ast.NodeId(j), payload)
|
||||
if (
|
||||
binding.kind == .declaration and binding.data.declaration == capture
|
||||
and !has_narrowing(state, ast.NodeId(j))
|
||||
) seed(state, ast.NodeId(j), payload)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4103,10 +4200,20 @@ validate_match proc(state @mut State, id ast.NodeId) void ! (mem.AllocError | di
|
||||
} else try compatible(state, pattern, key)
|
||||
current :: try pattern_interval(state, pattern, key)
|
||||
if (current) |interval| {
|
||||
if ((interval.real and (interval.real_low > interval.real_high
|
||||
or (!interval.inclusive and interval.real_low == interval.real_high)))
|
||||
or (!interval.real and (constant_order(interval.low, interval.high) > 0
|
||||
or (!interval.inclusive and constant_order(interval.low, interval.high) == 0)))) {
|
||||
if (
|
||||
(
|
||||
interval.real and (
|
||||
interval.real_low > interval.real_high
|
||||
or (!interval.inclusive and interval.real_low == interval.real_high)
|
||||
)
|
||||
)
|
||||
or (
|
||||
!interval.real and (
|
||||
constant_order(interval.low, interval.high) > 0
|
||||
or (!interval.inclusive and constant_order(interval.low, interval.high) == 0)
|
||||
)
|
||||
)
|
||||
) {
|
||||
try fail(state, id, .invalid_match_pattern, ast.node_span(state.tree, pattern))
|
||||
}
|
||||
if (key.kind == .bool) {
|
||||
@@ -4145,8 +4252,10 @@ validate_match proc(state @mut State, id ast.NodeId) void ! (mem.AllocError | di
|
||||
@hide:file
|
||||
global_variant proc(state @mut State, name ast.NodeId, payload Type) u64 ! mem.AllocError {
|
||||
for state.variants.items |variant, i| {
|
||||
if (ast.name_id(state.tree, variant.name) == ast.name_id(state.tree, name)
|
||||
and same_type(state, variant.payload, payload)) return u64(i + 1)
|
||||
if (
|
||||
ast.name_id(state.tree, variant.name) == ast.name_id(state.tree, name)
|
||||
and same_type(state, variant.payload, payload)
|
||||
) return u64(i + 1)
|
||||
}
|
||||
try arraylist.append(&state.variants, Variant{ name = name, payload = payload })
|
||||
return u64(state.variants.items.len)
|
||||
@@ -4181,8 +4290,10 @@ error_subset proc(records []RecordType, enums []EnumType, actual, expected Type)
|
||||
if (!descriptor.is_union or descriptor.tag_type.kind != .enumeration) return false
|
||||
if (descriptor.composed and actual.kind == .record and !records[usize(actual.record_type)].is_union) {
|
||||
for descriptor.fields |field| {
|
||||
if (field.type.kind == .record and field.type.record_type == actual.record_type
|
||||
and field.type.nominal == actual.nominal) return true
|
||||
if (
|
||||
field.type.kind == .record and field.type.record_type == actual.record_type
|
||||
and field.type.nominal == actual.nominal
|
||||
) return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -4199,8 +4310,10 @@ error_subset proc(records []RecordType, enums []EnumType, actual, expected Type)
|
||||
if (source.kind != .enumeration) return false
|
||||
origin :: enums[usize(source.enum_type)]
|
||||
if (origin.explicit_backing or destination.explicit_backing) {
|
||||
if (!origin.explicit_backing or !destination.explicit_backing
|
||||
or origin.declaration != destination.declaration or origin.backing != destination.backing) return false
|
||||
if (
|
||||
!origin.explicit_backing or !destination.explicit_backing
|
||||
or origin.declaration != destination.declaration or origin.backing != destination.backing
|
||||
) return false
|
||||
}
|
||||
for origin.members |member| {
|
||||
found := false
|
||||
@@ -4233,11 +4346,20 @@ check_fallible_type proc(state @mut State, id ast.NodeId) Type ! (mem.AllocError
|
||||
failure :: try check_annotation(state, expression.right, false)
|
||||
if (success.kind == .invalid) return success
|
||||
if (failure.kind == .invalid) return failure
|
||||
valid_error :: failure.kind == .enumeration or (failure.kind == .record
|
||||
and (!state.records.items[usize(failure.record_type)].is_union
|
||||
or state.records.items[usize(failure.record_type)].tag_type.kind == .enumeration))
|
||||
if (!valid_error or success.kind == .none or success.kind == .range or success.kind == .type_value
|
||||
or success.kind == .anyopaque or has_inferred_count(state, success)) {
|
||||
valid_error :: (
|
||||
failure.kind == .enumeration
|
||||
or (
|
||||
failure.kind == .record
|
||||
and (
|
||||
!state.records.items[usize(failure.record_type)].is_union
|
||||
or state.records.items[usize(failure.record_type)].tag_type.kind == .enumeration
|
||||
)
|
||||
)
|
||||
)
|
||||
if (
|
||||
!valid_error or success.kind == .none or success.kind == .range or success.kind == .type_value
|
||||
or success.kind == .anyopaque or has_inferred_count(state, success)
|
||||
) {
|
||||
return invalid_type(try diagnostics.add(state.diagnostics, .invalid_fallible_type, ast.node_span(state.tree, id)))
|
||||
}
|
||||
type :: try intern_array(state, ArrayType{ kind = .fallible, element = success, error_type = failure })
|
||||
@@ -4275,8 +4397,10 @@ append_sum_variants proc(state @mut State, variants @mut arraylist.ArrayList(Sum
|
||||
name := ast.NodeId(maxval!(u32))
|
||||
for 0..ast.package_decl_count(state.tree) |i| {
|
||||
declaration :: ast.package_decl(state.tree, i)
|
||||
if (ast.decl_kind(state.tree, declaration) == .statement
|
||||
and ast.statement_decl(state.tree, declaration).initializer == descriptor.declaration) name = declaration
|
||||
if (
|
||||
ast.decl_kind(state.tree, declaration) == .statement
|
||||
and ast.statement_decl(state.tree, declaration).initializer == descriptor.declaration
|
||||
) name = declaration
|
||||
}
|
||||
if (name == ast.NodeId(maxval!(u32))) {
|
||||
for 0..(state.cells.len) |i| {
|
||||
@@ -4362,8 +4486,10 @@ seed_capture proc(state @mut State, capture ast.NodeId, type Type) void {
|
||||
for 0..(state.cells.len) |i| {
|
||||
if (state.cells[i].role != .expression or !declaration_reference(state.tree, ast.NodeId(i))) continue
|
||||
binding :: sema.binding(state.resolution, ast.NodeId(i))
|
||||
if (binding.kind == .declaration and binding.data.declaration == capture
|
||||
and !has_narrowing(state, ast.NodeId(i))) seed(state, ast.NodeId(i), type)
|
||||
if (
|
||||
binding.kind == .declaration and binding.data.declaration == capture
|
||||
and !has_narrowing(state, ast.NodeId(i))
|
||||
) seed(state, ast.NodeId(i), type)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4434,8 +4560,14 @@ validate_fallible_expression proc(state @mut State, id ast.NodeId) void ! (mem.A
|
||||
if (result.kind == .invalid) return
|
||||
if (result.kind != .fallible) {
|
||||
try fail(state, id, .try_outside_fallible, ast.node_span(state.tree, id))
|
||||
} else if (!error_subset(state.records.items, state.enums.items, descriptor.error_type,
|
||||
state.arrays.items[usize(result.array_type)].error_type)) {
|
||||
} else if (
|
||||
!error_subset(
|
||||
state.records.items,
|
||||
state.enums.items,
|
||||
descriptor.error_type,
|
||||
state.arrays.items[usize(result.array_type)].error_type,
|
||||
)
|
||||
) {
|
||||
try fail(state, id, .incompatible_propagated_error, ast.node_span(state.tree, id))
|
||||
}
|
||||
}
|
||||
@@ -4445,8 +4577,10 @@ matching_narrowing_arm proc(state @State, id ast.NodeId) ast.NodeId {
|
||||
binding :: sema.binding(state.resolution, id)
|
||||
if (binding.kind != .declaration) return ast.NodeId(maxval!(u32))
|
||||
target :: binding.data.declaration
|
||||
if (state.cells[usize(target)].role == .declaration
|
||||
and ast.statement_decl(state.tree, target).mutability == .variable) return ast.NodeId(maxval!(u32))
|
||||
if (
|
||||
state.cells[usize(target)].role == .declaration
|
||||
and ast.statement_decl(state.tree, target).mutability == .variable
|
||||
) return ast.NodeId(maxval!(u32))
|
||||
arm := state.cells[usize(id)].narrowing_arm
|
||||
while (arm != ast.NodeId(maxval!(u32))) {
|
||||
owner :: state.cells[usize(arm)].match_owner
|
||||
@@ -4467,8 +4601,10 @@ has_narrowing proc(state @State, id ast.NodeId) bool {
|
||||
arm_names_variant proc(state @State, arm, name ast.NodeId) bool {
|
||||
for 0..ast.match_pattern_count(state.tree, arm) |i| {
|
||||
pattern :: ast.match_pattern(state.tree, arm, i)
|
||||
if (ast.expr_kind(state.tree, pattern) == .enum_literal
|
||||
and ast.name_id(state.tree, ast.enum_literal(state.tree, pattern)) == ast.name_id(state.tree, name)) return true
|
||||
if (
|
||||
ast.expr_kind(state.tree, pattern) == .enum_literal
|
||||
and ast.name_id(state.tree, ast.enum_literal(state.tree, pattern)) == ast.name_id(state.tree, name)
|
||||
) return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -54,14 +54,17 @@ Instance :: struct {
|
||||
@hide:file
|
||||
Rewrite :: struct { target ast.NodeId, saved ast.NodeId }
|
||||
|
||||
#! owns an undo log and retained checking obligations; generated nodes belong to the caller's AST arena.
|
||||
#! owns an undo log and retained checking obligations; deinit releases them with their retained allocator.
|
||||
#! borrows the caller's AST builder; call deinit before finishing or deinitializing that builder.
|
||||
#! generated nodes remain arena-owned and are released with the AST.
|
||||
Result :: struct {
|
||||
builder @mut ast.Builder
|
||||
changes arraylist.ArrayList(Rewrite)
|
||||
constraints arraylist.ArrayList(ast.NodeId)
|
||||
}
|
||||
|
||||
#! restores source nodes so the same compilation may be compiled again.
|
||||
#! restores rewritten source nodes in the borrowed builder and releases the undo log and checking obligations.
|
||||
#! does not remove generated arena nodes or restore resolution bindings.
|
||||
deinit proc(result @mut Result) void {
|
||||
index := result.changes.items.len
|
||||
while (index > 0) {
|
||||
@@ -94,8 +97,11 @@ State :: struct {
|
||||
type_execution_remaining usize = 0
|
||||
}
|
||||
|
||||
#! elaborates templates into cached concrete declarations in the caller-owned arena.
|
||||
#! resolution is extended to cover generated nodes; no template reaches checking or lowering.
|
||||
#! elaborates templates into cached concrete declarations in the caller-owned `builder` arena.
|
||||
#! extends `resolution` to cover generated nodes; no template reaches checking or lowering.
|
||||
#! `allocator` supplies returned logs, temporary storage released on every exit, and replacement resolution bindings.
|
||||
#! use the allocator retained by `resolution`; the returned Result borrows `builder` until the caller calls deinit.
|
||||
#! on failure, restores rewritten source nodes and releases logs; generated nodes remain arena-owned.
|
||||
elaborate proc(
|
||||
allocator mem.Allocator,
|
||||
builder @mut ast.Builder,
|
||||
@@ -105,7 +111,7 @@ elaborate proc(
|
||||
) Result ! Error {
|
||||
result := Result{ builder = builder, changes = arraylist.init(allocator), constraints = arraylist.init(allocator) }
|
||||
errdefer deinit(&result)
|
||||
# Explicit evaluation is useful even in packages without generic declarations.
|
||||
# explicit evaluation is useful even in packages without generic declarations.
|
||||
original :: ast.view(builder, root)
|
||||
needed := false
|
||||
for (0..ast.node_count(&original)) |i| {
|
||||
@@ -250,9 +256,11 @@ expand proc(state @mut State, id ast.NodeId) void ! Error {
|
||||
callee :: ast.call_expr(&state.tree, id).callee
|
||||
try expand(state, callee)
|
||||
resolved :: binding(state, callee)
|
||||
if (resolved.kind == .declaration
|
||||
if (
|
||||
resolved.kind == .declaration
|
||||
and ast.node_class(&state.tree, resolved.data.declaration) == .function
|
||||
and template(state, resolved.data.declaration)) {
|
||||
and template(state, resolved.data.declaration)
|
||||
) {
|
||||
try specialize_call(state, id, resolved.data.declaration)
|
||||
return
|
||||
}
|
||||
@@ -289,7 +297,7 @@ expand proc(state @mut State, id ast.NodeId) void ! Error {
|
||||
}
|
||||
}
|
||||
|
||||
# Only probe side-effect-free expressions. Ordinary calls stay runtime unless the
|
||||
# only probe side-effect-free expressions. ordinary calls stay runtime unless the
|
||||
# source requests evaluation with $(); probing must not execute FFI or emit traps.
|
||||
@hide:file
|
||||
branch_constant proc(state @State, id ast.NodeId, depth usize) bool {
|
||||
@@ -362,7 +370,7 @@ specialize_call proc(state @mut State, call, function ast.NodeId) void ! Error {
|
||||
state.depth += 1
|
||||
defer state.depth -= 1
|
||||
count :: ast.function_param_count(&state.tree, function)
|
||||
# A type result has no runtime call: every input participates in specialization.
|
||||
# a type result has no runtime call: every input participates in specialization.
|
||||
type_result :: is_type(state, ast.function_decl(&state.tree, function).return_type)
|
||||
runtime := usize(0)
|
||||
for (0..count) |i| {
|
||||
@@ -551,13 +559,15 @@ instantiate proc(state @mut State, index usize, call ast.NodeId) void ! Error {
|
||||
}
|
||||
for (0..count) |n| {
|
||||
resolved :: binding(state, ast.NodeId(n))
|
||||
if (resolved.kind == .declaration and resolved.data.declaration == parameter
|
||||
and ast.node_class(&state.tree, ast.NodeId(n)) == .expression) mapping[n] = replacement
|
||||
if (
|
||||
resolved.kind == .declaration and resolved.data.declaration == parameter
|
||||
and ast.node_class(&state.tree, ast.NodeId(n)) == .expression
|
||||
) mapping[n] = replacement
|
||||
}
|
||||
}
|
||||
clone :: try ast.clone_subtree(state.builder, function, mapping)
|
||||
try refresh(state)
|
||||
# Copy original resolution, remapping references to cloned locals and runtime parameters.
|
||||
# copy original resolution, remapping references to cloned locals and runtime parameters.
|
||||
for (0..count) |i| {
|
||||
target :: mapping[i]
|
||||
if (target == no_node or usize(target) < clone_start) continue
|
||||
@@ -655,7 +665,7 @@ reject_evaluation proc(state @mut State, call ast.NodeId) void ! Error {
|
||||
state.bindings.items[usize(call)] = sema.Binding{ kind = .invalid, data = sema.BindingData{ diagnostic = diagnostic } }
|
||||
}
|
||||
|
||||
# Failed deferred elaboration must not leave unchecked type expressions in the package.
|
||||
# failed deferred elaboration must not leave unchecked type expressions in the package.
|
||||
@hide:file
|
||||
reject_type_result proc(state @mut State, call, result ast.NodeId, constraints_start usize) void ! Error {
|
||||
try reject_evaluation(state, call)
|
||||
@@ -714,8 +724,10 @@ argument_annotation proc(
|
||||
value :: try materialize(state, arguments[i])
|
||||
for (0..mapping.len) |n| {
|
||||
resolved :: binding(state, ast.NodeId(n))
|
||||
if (resolved.kind == .declaration and resolved.data.declaration == parameter
|
||||
and ast.node_class(&state.tree, ast.NodeId(n)) == .expression) mapping[n] = value.node
|
||||
if (
|
||||
resolved.kind == .declaration and resolved.data.declaration == parameter
|
||||
and ast.node_class(&state.tree, ast.NodeId(n)) == .expression
|
||||
) mapping[n] = value.node
|
||||
}
|
||||
}
|
||||
start :: ast.node_count(&state.tree)
|
||||
@@ -738,8 +750,8 @@ evaluate_expected proc(state @mut State, id, annotation ast.NodeId) Value ! Erro
|
||||
return try evaluate(state, id)
|
||||
}
|
||||
|
||||
# Values leaving the temporary interpreter must own no interpreter addresses or module IDs.
|
||||
# Rebuild contextual source literals while the module and its escaped flat cells are alive.
|
||||
# values leaving the temporary interpreter must own no interpreter addresses or module IDs.
|
||||
# rebuild contextual source literals while the module and its escaped flat cells are alive.
|
||||
@hide:file
|
||||
serialize_value proc(
|
||||
state @mut State, module @ir.Module, checked @typecheck.Result,
|
||||
@@ -759,7 +771,7 @@ serialize_value proc(
|
||||
result = try ast.add_array_literal(state.builder, span, elements.items)
|
||||
} else if (type.kind == .record) {
|
||||
record :: typecheck.record_type(checked, type.record_type)
|
||||
# Untagged storage has no active member; choosing one could discard bytes.
|
||||
# untagged storage has no active member; choosing one could discard bytes.
|
||||
if (record.is_union and record.tag_type.kind != .enumeration) return no_node
|
||||
fields := arraylist.init(ast.NodeId, state.allocator)
|
||||
defer arraylist.deinit(&fields)
|
||||
@@ -786,7 +798,7 @@ serialize_value proc(
|
||||
child :: ir.aggregate_item(module, value, index)
|
||||
result = try serialize_value(state, module, checked, channel, child, span)
|
||||
if (result == no_node) return no_node
|
||||
# Keep named success/error types distinct even when their members overlap.
|
||||
# keep named success/error types distinct even when their members overlap.
|
||||
annotation := no_node
|
||||
if (channel.nominal != no_node) annotation = channel.nominal
|
||||
else if (channel.kind == .enumeration) annotation = typecheck.enum_type(checked, channel.enum_type).declaration
|
||||
@@ -927,7 +939,7 @@ evaluate_call proc(state @mut State, call ast.NodeId) Value ! Error {
|
||||
}
|
||||
try arraylist.append(&arguments, concrete.node)
|
||||
}
|
||||
# Reuse checking, typed IR and the interpreter: compile-time arithmetic and local
|
||||
# reuse checking, typed IR and the interpreter: compile-time arithmetic and local
|
||||
# inference must have exactly the same semantics as an ordinary runtime call.
|
||||
span :: ast.node_span(&state.tree, call)
|
||||
invocation :: try ast.add_call(state.builder, span, callee.node, arguments.items)
|
||||
@@ -1022,7 +1034,7 @@ TypeExecution :: struct {
|
||||
@hide:file
|
||||
TypeFlow :: enum { next, returned, break, continue, invalid }
|
||||
|
||||
# Type values cannot enter runtime IR. Execute their control flow here, substituting
|
||||
# type values cannot enter runtime IR. execute their control flow here, substituting
|
||||
# owned constants into snapshots; ordinary value operations still use checked IR.
|
||||
@hide:file
|
||||
return_type_value proc(state @mut State, block, first_owned ast.NodeId) ast.NodeId ! Error {
|
||||
@@ -1054,7 +1066,7 @@ type_step proc(state @mut State, at ast.NodeId) bool ! Error {
|
||||
@hide:file
|
||||
prepare_type_snapshot proc(state @mut State, execution @mut TypeExecution, id ast.NodeId) void {
|
||||
if (usize(id) >= execution.mapping.len) return
|
||||
# Substituted arguments belong to the caller, including nominal type identity.
|
||||
# substituted arguments belong to the caller, including nominal type identity.
|
||||
if (id < execution.first_owned) {
|
||||
execution.mapping[usize(id)] = id
|
||||
return
|
||||
@@ -1105,7 +1117,7 @@ nominal_type_value proc(state @State, id ast.NodeId) bool {
|
||||
return kind == .record_type or kind == .union_type or kind == .enum_type or kind == .distinct_type
|
||||
}
|
||||
|
||||
# Select with ordinary checked IR so patterns, overlap and exhaustiveness have
|
||||
# select with ordinary checked IR so patterns, overlap and exhaustiveness have
|
||||
# the same rules as runtime matching, without checking unexecuted type bodies.
|
||||
@hide:file
|
||||
type_match_arm proc(state @mut State, id ast.NodeId) usize ! Error {
|
||||
@@ -1117,7 +1129,7 @@ type_match_arm proc(state @mut State, id ast.NodeId) usize ! Error {
|
||||
for (0..ast.match_arm_count(&state.tree, id)) |i| {
|
||||
arm_id :: ast.match_arm(&state.tree, id, i)
|
||||
arm :: ast.match_arm_view(&state.tree, arm_id)
|
||||
# Selector bodies omit source capture uses, but must retain their resolver failures.
|
||||
# selector bodies omit source capture uses, but must retain their resolver failures.
|
||||
if (arm.capture) |capture| {
|
||||
resolved :: binding(state, capture)
|
||||
if (resolved.kind == .invalid) {
|
||||
@@ -1125,8 +1137,13 @@ type_match_arm proc(state @mut State, id ast.NodeId) usize ! Error {
|
||||
return no_index
|
||||
}
|
||||
}
|
||||
if (i > 0 and arm.expression != ast.match_arm_view(&state.tree,
|
||||
ast.match_arm(&state.tree, id, 0)).expression) {
|
||||
if (
|
||||
i > 0
|
||||
and arm.expression != ast.match_arm_view(
|
||||
&state.tree,
|
||||
ast.match_arm(&state.tree, id, 0),
|
||||
).expression
|
||||
) {
|
||||
state.evaluation_diagnostic = try diagnostics.add(state.diagnostics,
|
||||
.type_mismatch, ast.node_span(&state.tree, arm_id))
|
||||
return no_index
|
||||
@@ -1152,7 +1169,7 @@ type_match_arm proc(state @mut State, id ast.NodeId) usize ! Error {
|
||||
return usize(selected.magnitude)
|
||||
}
|
||||
|
||||
# Match checking has already validated every capture. Copy the active payload
|
||||
# match checking has already validated every capture. copy the active payload
|
||||
# out of owned constant storage, never an address in the temporary interpreter.
|
||||
@hide:file
|
||||
type_match_capture proc(state @mut State, id ast.NodeId) ast.NodeId ! Error {
|
||||
@@ -1163,8 +1180,10 @@ type_match_capture proc(state @mut State, id ast.NodeId) ast.NodeId ! Error {
|
||||
payload :: ast.field(&state.tree, ast.record_field(&state.tree, value.node, 0))
|
||||
for (0..ast.record_field_count(&state.tree, annotation)) |i| {
|
||||
field :: ast.field(&state.tree, ast.record_field(&state.tree, annotation, i))
|
||||
if (field.name != null and payload.name != null
|
||||
and ast.name_id(&state.tree, field.name?) == ast.name_id(&state.tree, payload.name?)) {
|
||||
if (
|
||||
field.name != null and payload.name != null
|
||||
and ast.name_id(&state.tree, field.name?) == ast.name_id(&state.tree, payload.name?)
|
||||
) {
|
||||
result :: try ast.add_typed_constant(state.builder,
|
||||
ast.node_span(&state.tree, subject), field.type?, payload.value?)
|
||||
try refresh(state)
|
||||
@@ -1208,8 +1227,8 @@ type_value_expression proc(state @mut State, input ast.NodeId) ast.NodeId ! Erro
|
||||
return expression
|
||||
}
|
||||
|
||||
# Normalize only selected expression arms before ordinary expansion can visit them.
|
||||
# Nominal results enter here after their instance declaration is cached.
|
||||
# normalize only selected expression arms before ordinary expansion can visit them.
|
||||
# nominal results enter here after their instance declaration is cached.
|
||||
@hide:file
|
||||
expand_type_matches proc(state @mut State, id ast.NodeId) bool ! Error {
|
||||
if (ast.node_class(&state.tree, id) == .expression and ast.expr_kind(&state.tree, id) == .match_expr) {
|
||||
@@ -1233,7 +1252,7 @@ type_execution_value proc(state @mut State, input, annotation ast.NodeId) ast.No
|
||||
expression :: try type_value_expression(state, input)
|
||||
if (expression == no_node) return no_node
|
||||
if (!try expand_type_matches(state, annotation)) return no_node
|
||||
# A recursive record needs its cached declaration before expanding its fields.
|
||||
# a recursive record needs its cached declaration before expanding its fields.
|
||||
if (is_type(state, annotation) and nominal_type_value(state, expression)) {
|
||||
concrete :: try materialize(state, Value{ kind = .type, node = expression })
|
||||
constraint :: try ast.add_type_operand(state.builder, ast.node_span(&state.tree, expression), concrete.node, true)
|
||||
@@ -1327,9 +1346,11 @@ execute_type_statement proc(state @mut State, execution @mut TypeExecution, stat
|
||||
if (local.declaration == resolved.data.declaration) index = i
|
||||
}
|
||||
}
|
||||
if (index == no_index
|
||||
if (
|
||||
index == no_index
|
||||
or ast.node_class(&state.tree, execution.locals.items[index].declaration) != .statement
|
||||
or ast.statement_decl(&state.tree, execution.locals.items[index].declaration).mutability != .variable) {
|
||||
or ast.statement_decl(&state.tree, execution.locals.items[index].declaration).mutability != .variable
|
||||
) {
|
||||
state.evaluation_diagnostic = try diagnostics.add(state.diagnostics,
|
||||
.invalid_assignment_target, ast.node_span(&state.tree, assignment.target))
|
||||
return .invalid
|
||||
@@ -1572,9 +1593,11 @@ evaluate_binary proc(state @mut State, id ast.NodeId) Value ! Error {
|
||||
result_type :: try builtin(state, id, kind)
|
||||
return try evaluate_ir(state, id, result_type)
|
||||
}
|
||||
if (left.annotation != no_node and right.annotation != no_node
|
||||
if (
|
||||
left.annotation != no_node and right.annotation != no_node
|
||||
and left.kind == .integer and right.kind == .integer
|
||||
and !try same_type(state, left.annotation, right.annotation)) return Value{}
|
||||
and !try same_type(state, left.annotation, right.annotation)
|
||||
) return Value{}
|
||||
if (view.operator == .equal or view.operator == .not_equal) {
|
||||
if (left.kind != right.kind) return Value{}
|
||||
equal :: try value_equal(state, left, right)
|
||||
@@ -1790,8 +1813,8 @@ type_hash proc(state @mut State, id ast.NodeId) usize ! Error {
|
||||
.optional_type: return mix(hash, try type_hash(state, ast.type_operand(&state.tree, node)))
|
||||
.sum_type: {
|
||||
view :: ast.binary_expr(&state.tree, node)
|
||||
# Set fingerprints must ignore ordering, grouping and repeated members.
|
||||
# Colliding fingerprints still use full type equality in the cache chain.
|
||||
# set fingerprints must ignore ordering, grouping and repeated members.
|
||||
# colliding fingerprints still use full type equality in the cache chain.
|
||||
return try type_hash(state, view.left) | try type_hash(state, view.right)
|
||||
}
|
||||
.fallible_type: {
|
||||
@@ -1914,9 +1937,11 @@ expression_type proc(state @mut State, id ast.NodeId, depth usize) ast.NodeId !
|
||||
}
|
||||
.binary: {
|
||||
view :: ast.binary_expr(&state.tree, id)
|
||||
if (view.operator == .equal or view.operator == .not_equal or view.operator == .less
|
||||
if (
|
||||
view.operator == .equal or view.operator == .not_equal or view.operator == .less
|
||||
or view.operator == .less_equal or view.operator == .greater or view.operator == .greater_equal
|
||||
or view.operator == .logical_and or view.operator == .logical_or) return try builtin(state, id, .bool)
|
||||
or view.operator == .logical_and or view.operator == .logical_or
|
||||
) return try builtin(state, id, .bool)
|
||||
return try expression_type(state, view.left, depth + 1)
|
||||
}
|
||||
.selection: {
|
||||
@@ -1927,8 +1952,12 @@ expression_type proc(state @mut State, id ast.NodeId, depth usize) ast.NodeId !
|
||||
if (base == no_node) return no_node
|
||||
member :: ast.source_text(&state.tree, ast.node_span(&state.tree, view.member))
|
||||
base_kind :: ast.expr_kind(&state.tree, base)
|
||||
if (mem.eql(member, "len") and (base_kind == .array_type or base_kind == .slice_type
|
||||
or (binding(state, base).kind == .builtin and binding(state, base).data.builtin == .string)))
|
||||
if (
|
||||
mem.eql(member, "len") and (
|
||||
base_kind == .array_type or base_kind == .slice_type
|
||||
or (binding(state, base).kind == .builtin and binding(state, base).data.builtin == .string)
|
||||
)
|
||||
)
|
||||
return try builtin(state, id, .usize)
|
||||
if (base_kind != .record_type and base_kind != .union_type) return no_node
|
||||
for (0..ast.record_field_count(&state.tree, base)) |i| {
|
||||
@@ -2062,11 +2091,13 @@ materialize proc(state @mut State, value Value) Value ! Error {
|
||||
result := value
|
||||
span :: ast.node_span(&state.tree, value.node)
|
||||
if (value.kind == .type and nominal_type_value(state, value.node)) {
|
||||
# Refer to declared nominal types, rather than reusing their definition as
|
||||
# refer to declared nominal types, rather than reusing their definition as
|
||||
# an annotation and turning its value-role checker cell into an annotation.
|
||||
for (state.declarations.items) |declaration| {
|
||||
if (ast.node_class(&state.tree, declaration) == .statement
|
||||
and ast.statement_decl(&state.tree, declaration).initializer == value.node) {
|
||||
if (
|
||||
ast.node_class(&state.tree, declaration) == .statement
|
||||
and ast.statement_decl(&state.tree, declaration).initializer == value.node
|
||||
) {
|
||||
result.node = try reference(state, value.node, declaration)
|
||||
return result
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user