style: compact inner groups and simple guard bodies

This commit is contained in:
2026-10-27 12:40:41 +01:00
parent f062389ccc
commit 8d1a98f982
22 changed files with 165 additions and 430 deletions
+4 -10
View File
@@ -105,18 +105,12 @@ render_block proc(renderer @mut Renderer, tree @ast.Tree, block ast.NodeId, dept
try append_text(renderer, ast.source_text(tree, ast.node_span(tree, loop.iterable)))
try append_text(renderer, " |")
if (loop.pointer_capture) try append_text(renderer, "@")
if (loop.capture) |capture| {
try append_text(renderer, ast.source_text(tree, ast.node_span(tree, capture)))
} else {
try append_text(renderer, "_")
}
if (loop.capture) |capture| try append_text(renderer, ast.source_text(tree, ast.node_span(tree, capture)))
else try append_text(renderer, "_")
if (loop.index_capture) {
try append_text(renderer, ", ")
if (loop.index) |index| {
try append_text(renderer, ast.source_text(tree, ast.node_span(tree, index)))
} else {
try append_text(renderer, "_")
}
if (loop.index) |index| try append_text(renderer, ast.source_text(tree, ast.node_span(tree, index)))
else try append_text(renderer, "_")
}
try append_text(renderer, "|\n")
try render_block(renderer, tree, loop.body, depth + 1)
+3 -7
View File
@@ -347,9 +347,7 @@ compile proc(
for (0..ast.package_decl_count(&tree)) |i| try arraylist.append(&declarations, ast.package_decl(&tree, i))
}
}
if (compilation.module_root == null) {
compilation.module_root = try ast.add_package(&compilation.builder, diagnostics.Span{ start = 0, end = 0 }, declarations.items)
}
if (compilation.module_root == null) compilation.module_root = try ast.add_package(&compilation.builder, diagnostics.Span{ start = 0, end = 0 }, declarations.items)
imports := arraylist.init(sema.Import, compilation.allocator)
defer arraylist.deinit(&imports)
tree := ast.view(&compilation.builder, compilation.module_root?)
@@ -362,11 +360,9 @@ compile proc(
diagnostic.span.source == imported.path.source
and diagnostic.span.start == imported.path.start
and diagnostic.span.end == imported.path.end
and (
diagnostic.code == .package_unavailable
and (diagnostic.code == .package_unavailable
or diagnostic.code == .invalid_import_path
or diagnostic.code == .import_cycle
)
or diagnostic.code == .import_cycle)
) {
failure = diagnostics.DiagnosticId(index)
break
+19 -57
View File
@@ -170,9 +170,7 @@ execute proc(
for (frames.items) |frame| free_frame(allocator, frame)
arraylist.deinit(&frames)
}
if (module.functions.items[usize(entry)].external_name.len != 0) {
return try runtime_trap(diagnostic_store, .unsupported_foreign_call, module.functions.items[usize(entry)].span)
}
if (module.functions.items[usize(entry)].external_name.len != 0) return try runtime_trap(diagnostic_store, .unsupported_foreign_call, module.functions.items[usize(entry)].span)
try push_frame(allocator, module, &frames, entry, ir.no_value, null)
while frames.items.len != 0 {
@@ -204,9 +202,7 @@ execute proc(
continue
}
value := ir.Value{}
if terminator.value != ir.no_value {
value = frames.items[frame_index].values[usize(terminator.value)]
}
if (terminator.value != ir.no_value) value = frames.items[frame_index].values[usize(terminator.value)]
completed :: frames.items[frame_index]
# transfer leaves while the completed frame still owns its backing.
if completed.global |global_index| {
@@ -222,9 +218,7 @@ execute proc(
}
return Outcome{ value = value }
}
if completed.destination != ir.no_value {
copy_value(module, &frames.items[frame_index - 1].values[usize(completed.destination)], value)
}
if (completed.destination != ir.no_value) copy_value(module, &frames.items[frame_index - 1].values[usize(completed.destination)], value)
_ = arraylist.pop(&frames)
free_frame(allocator, completed)
continue
@@ -237,9 +231,7 @@ execute proc(
.constant: value = instruction.constant
.load_local: {
local :: &frames.items[frame_index].locals[usize(instruction.slot)]
if !local.initialized {
return try runtime_trap(diagnostic_store, .read_before_initialization, instruction.span)
}
if (!local.initialized) return try runtime_trap(diagnostic_store, .read_before_initialization, instruction.span)
value = local.value
}
.store_local: {
@@ -250,9 +242,7 @@ execute proc(
}
.assign_local: {
local :: &frames.items[frame_index].locals[usize(instruction.slot)]
if (!local.initialized) {
return try runtime_trap(diagnostic_store, .write_before_initialization, instruction.span)
}
if (!local.initialized) return try runtime_trap(diagnostic_store, .write_before_initialization, instruction.span)
copy_value(module, &local.value, frames.items[frame_index].values[usize(instruction.left)])
continue
}
@@ -283,13 +273,9 @@ execute proc(
}
.call: {
callable :: frames.items[frame_index].values[usize(instruction.left)]
if (callable.data.function == maxval!(ir.FunctionId)) {
return try runtime_trap(diagnostic_store, .null_unwrap, instruction.span)
}
if (callable.data.function == maxval!(ir.FunctionId)) return try runtime_trap(diagnostic_store, .null_unwrap, instruction.span)
callee :: callable.data.function
if (module.functions.items[usize(callee)].external_name.len != 0) {
return try runtime_trap(diagnostic_store, .unsupported_foreign_call, instruction.span)
}
if (module.functions.items[usize(callee)].external_name.len != 0) return try runtime_trap(diagnostic_store, .unsupported_foreign_call, instruction.span)
try push_frame(allocator, module, &frames, callee, instruction.destination, null)
# frame indices survive stack growth; pointers into frames.items do not.
for (0..instruction.arguments_count) |argument_index| {
@@ -346,9 +332,7 @@ execute proc(
start :: frames.items[frame_index].values[usize(instruction.left)].data.unsigned
end :: frames.items[frame_index].values[usize(instruction.right)].data.unsigned
if (start > end) return try runtime_trap(diagnostic_store, .invalid_slice_bounds, instruction.span)
if (instruction.extra != ir.no_value and end > frames.items[frame_index].values[usize(instruction.extra)].data.unsigned) {
return try runtime_trap(diagnostic_store, .invalid_slice_bounds, instruction.span)
}
if (instruction.extra != ir.no_value and end > frames.items[frame_index].values[usize(instruction.extra)].data.unsigned) return try runtime_trap(diagnostic_store, .invalid_slice_bounds, instruction.span)
continue
}
.optional_check: {
@@ -423,17 +407,13 @@ execute proc(
base :: frames.items[frame_index].values[usize(instruction.left)]
index :: frames.items[frame_index].values[usize(instruction.right)].data.unsigned
shape :: module.arrays.items[usize(base.array_type)]
if (index >= u64(shape.len)) {
return try runtime_trap(diagnostic_store, .index_out_of_bounds, instruction.span)
}
if (index >= u64(shape.len)) return try runtime_trap(diagnostic_store, .index_out_of_bounds, instruction.span)
value = ir.aggregate_item(module, base, usize(index))
}
.field: {
base :: frames.items[frame_index].values[usize(instruction.left)]
shape :: module.arrays.items[usize(base.array_type)]
if (shape.is_union and !ir.union_active(module, base, usize(instruction.slot))) {
return try runtime_trap(diagnostic_store, .inactive_union_field, instruction.span)
}
if (shape.is_union and !ir.union_active(module, base, usize(instruction.slot))) return try runtime_trap(diagnostic_store, .inactive_union_field, instruction.span)
value = ir.aggregate_item(module, base, usize(instruction.slot))
}
.address_local: {
@@ -489,9 +469,7 @@ execute proc(
base :: frames.items[frame_index].addresses[usize(base_id)]
index :: frames.items[frame_index].values[usize(instruction.right)].data.unsigned
shape :: module.arrays.items[usize(base.array_type)]
if (index >= u64(shape.len)) {
return try runtime_trap(diagnostic_store, .index_out_of_bounds, instruction.span)
}
if (index >= u64(shape.len)) return try runtime_trap(diagnostic_store, .index_out_of_bounds, instruction.span)
width :: if (base.raw or base.byte_view) ir.storage_size(module, shape.element, shape.element_array) else element_cells(module, shape)
start :: usize(index) * width
address := base
@@ -513,9 +491,7 @@ execute proc(
if base.initialized |initialized| {
if (!initialized^) return try runtime_trap(diagnostic_store, .read_before_initialization, instruction.span)
}
if (!ir.union_active(module, read_address(module, base, scratch), usize(instruction.slot))) {
return try runtime_trap(diagnostic_store, .inactive_union_field, instruction.span)
}
if (!ir.union_active(module, read_address(module, base, scratch), usize(instruction.slot))) return try runtime_trap(diagnostic_store, .inactive_union_field, instruction.span)
}
raw :: base.raw or shape.is_union
width :: if (raw or base.byte_view) ir.storage_size(module, field.type, field.array_type) else if (ir.aggregate(field.type)) module.arrays.items[usize(field.array_type)].cells else 1
@@ -548,9 +524,7 @@ execute proc(
}
.pointer_address: {
handle :: frames.items[frame_index].values[usize(instruction.left)].data.unsigned
if (handle >= u64(pointers.items.len)) {
return try runtime_trap(diagnostic_store, .invalid_pointer_access, instruction.span)
}
if (handle >= u64(pointers.items.len)) return try runtime_trap(diagnostic_store, .invalid_pointer_access, instruction.span)
location :: pointers.items[usize(handle)]
if (pointer_address(module, location, instruction.pointee, instruction.array_type)) |address| {
frames.items[frame_index].addresses[usize(instruction.destination)] = address
@@ -561,19 +535,13 @@ execute proc(
}
.pointer_offset: {
handle :: frames.items[frame_index].values[usize(instruction.left)].data.unsigned
if (handle >= u64(pointers.items.len)) {
return try runtime_trap(diagnostic_store, .invalid_pointer_access, instruction.span)
}
if (handle >= u64(pointers.items.len)) return try runtime_trap(diagnostic_store, .invalid_pointer_access, instruction.span)
address := pointers.items[usize(handle)]
index :: frames.items[frame_index].values[usize(instruction.right)].data.unsigned
stride :: ir.storage_size(module, instruction.pointee, instruction.array_type)
if (stride != 0 and index > u64(divtrunc!(maxval!(usize) - address.byte_offset, stride))) {
return try runtime_trap(diagnostic_store, .invalid_pointer_access, instruction.span)
}
if (stride != 0 and index > u64(divtrunc!(maxval!(usize) - address.byte_offset, stride))) return try runtime_trap(diagnostic_store, .invalid_pointer_access, instruction.span)
address.byte_offset += usize(index) * stride
if (address.byte_offset > pointer_size(module, address)) {
return try runtime_trap(diagnostic_store, .invalid_pointer_access, instruction.span)
}
if (address.byte_offset > pointer_size(module, address)) return try runtime_trap(diagnostic_store, .invalid_pointer_access, instruction.span)
value = try pointer_value(&pointers, address)
}
.load_address, .store_address: {
@@ -604,9 +572,7 @@ execute proc(
if instruction.type == .float {
value = ir.Value{ type = .float, data = ir.ValueData{ real = -operand.data.real } }
} else {
if operand.data.integer == ir.integer_min(instruction.type) {
return try runtime_trap(diagnostic_store, .integer_overflow, instruction.span)
}
if (operand.data.integer == ir.integer_min(instruction.type)) return try runtime_trap(diagnostic_store, .integer_overflow, instruction.span)
value = ir.Value{ type = instruction.type, data = ir.ValueData{ integer = -operand.data.integer } }
}
}
@@ -638,9 +604,7 @@ execute proc(
} else {
result :: checked_integer(instruction.op, left.data.integer, right.data.integer)
if result |result_integer| {
if (result_integer < ir.integer_min(instruction.type) or result_integer > i64(ir.integer_max(instruction.type))) {
return try runtime_trap(diagnostic_store, .integer_overflow, instruction.span)
}
if (result_integer < ir.integer_min(instruction.type) or result_integer > i64(ir.integer_max(instruction.type))) return try runtime_trap(diagnostic_store, .integer_overflow, instruction.span)
value = ir.Value{ type = instruction.type, data = ir.ValueData{ integer = result_integer } }
} else {
return try runtime_trap(diagnostic_store, .integer_overflow, instruction.span)
@@ -699,9 +663,7 @@ execute proc(
value = ir.Value{ type = .bool, data = ir.ValueData{ boolean = result } }
}
}
if instruction.destination != ir.no_value {
copy_value(module, &frames.items[frame_index].values[usize(instruction.destination)], value)
}
if (instruction.destination != ir.no_value) copy_value(module, &frames.items[frame_index].values[usize(instruction.destination)], value)
}
unreachable
}
+1 -3
View File
@@ -434,9 +434,7 @@ storage_size proc(module @Module, type Type, shape ArrayId) usize {
#! loads a scalar or borrows an aggregate from byte cells without allocating.
read_storage proc(module @Module, type Type, shape ArrayId, cells []mut Value, raw bool) Value {
if (aggregate(type)) {
return Value{ type = type, array_type = shape, raw = raw, data = ValueData{ elements = cells[..storage_size(module, type, shape)] } }
}
if (aggregate(type)) return Value{ type = type, array_type = shape, raw = raw, data = ValueData{ elements = cells[..storage_size(module, type, shape)] } }
bits u64 := 0
size :: storage_size(module, type, shape)
count :: if (size > 8) usize(8) else size
+3 -7
View File
@@ -358,14 +358,10 @@ scan_ident proc(
cursor := start + 1
while (
cursor < program.len
and (
is_alpha(program[cursor])
and (is_alpha(program[cursor])
or is_digit(program[cursor])
or program[cursor] == '_'
)
) {
cursor += 1
}
or program[cursor] == '_')
) cursor += 1
kind :: strmap.get(&keywords, program[start..cursor]) orelse .ident
# member contexts accept keywords and therefore need their interned spelling too.
+2 -3
View File
@@ -201,9 +201,8 @@ emit_instruction proc(state @mut State, block @ir.Block, instruction ir.Instruct
.optional_check: {
operand :: try load(state, instruction.left)
failed :: fresh(state)
if (state.values[usize(instruction.left)].kind == .bool) {
try io.print(" %r{} = xor i1 %r{}, true\n", output, {failed, operand})
} else try io.print(" %r{} = icmp eq ptr %r{}, null\n", output, {failed, operand})
if (state.values[usize(instruction.left)].kind == .bool) try io.print(" %r{} = xor i1 %r{}, true\n", output, {failed, operand})
else try io.print(" %r{} = icmp eq ptr %r{}, null\n", output, {failed, operand})
try check_failure(state, failed, .null_unwrap, instruction.span)
return
}
+1 -3
View File
@@ -104,9 +104,7 @@ emit proc(
mem.set(values, Type{})
for (function.blocks.items) |block| {
for (block.instructions.items) |instruction| {
if (instruction.destination != ir.no_value) {
values[usize(instruction.destination)] = Type{ kind = instruction.type, shape = instruction.array_type }
}
if (instruction.destination != ir.no_value) values[usize(instruction.destination)] = Type{ kind = instruction.type, shape = instruction.array_type }
}
}
state := State{ output = output, module = module, line_starts = line_starts.items, values = values }
+16 -41
View File
@@ -170,9 +170,7 @@ lower_module proc(
@hide:file
lower_type proc(state @State, type typecheck.Type) ir.Type {
if (type.kind == .enumeration) {
return lower_type(state, typecheck.Type{ kind = typecheck.enum_type(state.checked, type.enum_type).backing })
}
if (type.kind == .enumeration) return lower_type(state, typecheck.Type{ kind = typecheck.enum_type(state.checked, type.enum_type).backing })
if (type.kind == .slice) return .record
if (type.kind == .fallible) return .record
if (type.kind == .optional) return lower_optional_type(state, typecheck.array_type(state.checked, type.array_type).element)
@@ -562,11 +560,9 @@ lower_cleanups proc(state @mut State, depth usize, error_value ir.ValueId) void
id :: state.cleanups.items[index]
if (ast.stmt_kind(state.tree, id) == .errdefer_stmt and error_value == ir.no_value) continue
cleanup :: ast.cleanup_stmt(state.tree, id)
if (cleanup.capture) |capture| {
_ = try emit(state, ir.Instruction{
op = .store_local, left = error_value, slot = state.storage[usize(capture)].index, span = cleanup.span,
})
}
if (cleanup.capture) |capture| _ = try emit(state, ir.Instruction{
op = .store_local, left = error_value, slot = state.storage[usize(capture)].index, span = cleanup.span,
})
try lower_block(state, cleanup.body, true)
if (state.terminated) return
}
@@ -800,10 +796,8 @@ lower_statement proc(state @mut State, id ast.NodeId) void ! (mem.AllocError | d
if (
ast.expr_kind(tree, assignment.target) == .index
or ast.expr_kind(tree, assignment.target) == .unary
or (
ast.expr_kind(tree, assignment.target) == .selection
and sema.binding(state.resolution, assignment.target).kind == .none
)
or (ast.expr_kind(tree, assignment.target) == .selection
and sema.binding(state.resolution, assignment.target).kind == .none)
) {
value :: try lower_as(state, assignment.value, typecheck.type_of(state.checked, assignment.target))
if (state.terminated) return
@@ -865,9 +859,7 @@ lower_statement proc(state @mut State, id ast.NodeId) void ! (mem.AllocError | d
value := ir.no_value
if (statement.value) |expression| value = try lower_as(state, expression, state.return_type)
if (state.terminated or trap_type(state, id)) return
if (statement.value == null and state.return_type.kind == .fallible) {
value = try wrap_fallible(state, value, state.return_type, false, statement.span)
}
if (statement.value == null and state.return_type.kind == .fallible) value = try wrap_fallible(state, value, state.return_type, false, statement.span)
try lower_return(state, value, statement.span)
}
}
@@ -1080,11 +1072,8 @@ lower_union proc(state @mut State, id ast.NodeId) ir.ValueId ! (mem.AllocError |
count :: ast.record_field_count(tree, id)
contextual := false
if (view.type) |annotation| contextual = ast.expr_kind(tree, annotation) == .enum_literal
if (contextual and field.type == .record and (count != 1 or ast.field(tree, ast.record_field(tree, id, 0)).name != null)) {
payload = try lower_record_as(state, id, typecheck.union_payload_type(state.checked, id))
} else if (count != 0) {
payload = try lower_as(state, ast.field(tree, ast.record_field(tree, id, 0)).value?, typecheck.union_payload_type(state.checked, id))
}
if (contextual and field.type == .record and (count != 1 or ast.field(tree, ast.record_field(tree, id, 0)).name != null)) payload = try lower_record_as(state, id, typecheck.union_payload_type(state.checked, id))
else if (count != 0) payload = try lower_as(state, ast.field(tree, ast.record_field(tree, id, 0)).value?, typecheck.union_payload_type(state.checked, id))
}
if (state.terminated) return ir.no_value
return try emit(state, ir.Instruction{
@@ -1826,9 +1815,7 @@ lower_expression proc(state @mut State, id ast.NodeId) ir.ValueId ! (mem.AllocEr
if (!trap_type(state, id)) unreachable
}
.array_type, .pointer_type, .function_type, .record_type, .enum_type, .union_type, .distinct_type, .slice_type, .optional_type, .fallible_type, .sum_type: {
if (!trap_type(state, id)) {
trap(state, try diagnostics.add(state.diagnostics, .type_not_value, span), span)
}
if (!trap_type(state, id)) trap(state, try diagnostics.add(state.diagnostics, .type_not_value, span), span)
}
.alias_expr: return try lower_expression(state, ast.type_operand(tree, id))
.enum_literal: {
@@ -1843,9 +1830,7 @@ lower_expression proc(state @mut State, id ast.NodeId) ir.ValueId ! (mem.AllocEr
.builtin_call: return try lower_builtin(state, id)
.record_literal: {
checked :: typecheck.type_of(state.checked, id)
if (checked.kind == .record and typecheck.record_type(state.checked, checked.record_type).is_union) {
return try lower_union(state, id)
}
if (checked.kind == .record and typecheck.record_type(state.checked, checked.record_type).is_union) return try lower_union(state, id)
return try lower_record_as(state, id, checked)
}
.array_literal: {
@@ -2014,9 +1999,7 @@ lower_expression proc(state @mut State, id ast.NodeId) ir.ValueId ! (mem.AllocEr
text :: ast.literal_text(tree, expression.operand)
magnitude_limit :: ir.integer_max(type) + 1
if (integer_magnitude(text, magnitude_limit)) |magnitude| {
if (magnitude == magnitude_limit) {
return try constant(state, ir.Value{ type = type, data = ir.ValueData{ integer = ir.integer_min(type) } }, span)
}
if (magnitude == magnitude_limit) return try constant(state, ir.Value{ type = type, data = ir.ValueData{ integer = ir.integer_min(type) } }, span)
}
}
operand :: try lower_expression(state, expression.operand)
@@ -2030,9 +2013,7 @@ lower_expression proc(state @mut State, id ast.NodeId) ir.ValueId ! (mem.AllocEr
}
.binary: {
expression :: ast.binary_expr(tree, id)
if (expression.operator == .logical_and or expression.operator == .logical_or) {
return try lower_logical(state, id)
}
if (expression.operator == .logical_and or expression.operator == .logical_or) return try lower_logical(state, id)
left_type :: typecheck.type_of(state.checked, expression.left)
right_type :: typecheck.type_of(state.checked, expression.right)
if (
@@ -2100,9 +2081,7 @@ lower_expression proc(state @mut State, id ast.NodeId) ir.ValueId ! (mem.AllocEr
for (0..count) |i| {
signature :: typecheck.type_of(state.checked, ast.call_expr(tree, id).callee).function
expected := typecheck.Type{}
if (signature != ast.NodeId(maxval!(u32)) and i < ast.function_param_count(tree, signature)) {
expected = typecheck.type_of(state.checked, ast.param(tree, ast.function_param(tree, signature, i)).type)
}
if (signature != ast.NodeId(maxval!(u32)) and i < ast.function_param_count(tree, signature)) expected = typecheck.type_of(state.checked, ast.param(tree, ast.function_param(tree, signature, i)).type)
argument :: try lower_as(state, ast.call_arg(tree, id, i), expected)
if (state.terminated) return ir.no_value
state.arguments.items[scratch_start + i] = argument
@@ -2162,13 +2141,9 @@ lower_number proc(state @mut State, id ast.NodeId) ir.ValueId ! (mem.AllocError
text :: ast.literal_text(state.tree, id)
type :: lower_type(state, typecheck.type_of(state.checked, id))
if (ir.integer_bits(type) != 0 and !ir.unsigned_integer(type)) {
if (integer_magnitude(text, ir.integer_max(type))) |magnitude| {
return try constant(state, ir.Value{ type = type, data = ir.ValueData{ integer = i64(magnitude) } }, span)
}
if (integer_magnitude(text, ir.integer_max(type))) |magnitude| return try constant(state, ir.Value{ type = type, data = ir.ValueData{ integer = i64(magnitude) } }, span)
} else if (ir.unsigned_integer(type)) {
if (integer_magnitude(text, ir.integer_max(type))) |magnitude| {
return try constant(state, ir.Value{ type = type, data = ir.ValueData{ unsigned = magnitude } }, span)
}
if (integer_magnitude(text, ir.integer_max(type))) |magnitude| return try constant(state, ir.Value{ type = type, data = ir.ValueData{ unsigned = magnitude } }, span)
} else {
value :: strconv.parse_float(state.module.allocator, text) catch |err| {
match err {
+2 -5
View File
@@ -1695,11 +1695,8 @@ array_context_inference_empty_shapes_and_scalar_leaf_families test {
outcome := try interpreter.run(mem.c_allocator, &module, &diagnostic_store)
defer interpreter.deinit(&outcome)
try testing.expect(outcome.trap == null)
if (outcome.value.type == .string) {
try testing.expect(mem.eql(ir.string_bytes(&module, outcome.value.data.string), "two"))
} else {
try testing.expect(outcome.value.data.boolean)
}
if (outcome.value.type == .string) try testing.expect(mem.eql(ir.string_bytes(&module, outcome.value.data.string), "two"))
else try testing.expect(outcome.value.data.boolean)
}
}
+13 -33
View File
@@ -277,8 +277,7 @@ parse_block proc(state @mut State) ast.NodeId ! ParseError {
parse_statement proc(state @mut State, allow_else bool) ast.NodeId ! ParseError {
if (
next_is(state, .ident)
and (
next_n_is(state, 1, .ident)
and (next_n_is(state, 1, .ident)
or next_n_is(state, 1, .struct)
or next_n_is(state, 1, .enum) or next_n_is(state, 1, .union)
or next_n_is(state, 1, .distinct)
@@ -289,11 +288,8 @@ parse_statement proc(state @mut State, allow_else bool) ast.NodeId ! ParseError
or (next_n_is(state, 1, .star) and pointer_binding_starts(state))
or (next_n_is(state, 1, .open_bracket) and array_starts_type(state))
or next_n_is(state, 1, .double_colon)
or next_n_is(state, 1, .colon_equal)
)
) {
return try parse_statement_decl(state)
}
or next_n_is(state, 1, .colon_equal))
) return try parse_statement_decl(state)
match current(state).kind {
.if: return try parse_if(state)
.while: return try parse_while(state, allow_else)
@@ -343,17 +339,13 @@ parse_return proc(state @mut State, allow_else bool) ast.NodeId ! ParseError {
next_is(state, .newline) or next_is(state, .close_curly) or next_is(state, .eof)
or (state.match_arm_body and next_is(state, .comma))
or (allow_else and next_is(state, .else))
) {
return try ast.add_return_stmt(state.builder, keyword.span, null)
}
) return try ast.add_return_stmt(state.builder, keyword.span, null)
value :: try parse_expr(state)
if (
!next_is(state, .newline) and !next_is(state, .close_curly) and !next_is(state, .eof)
and !(allow_else and next_is(state, .else))
and !(state.match_arm_body and next_is(state, .comma))
) {
return .unexpected_token
}
) return .unexpected_token
# include consumed grouping punctuation, which has no separate expression node.
end :: state.tokens[state.next_token - 1].span.end
return try ast.add_return_stmt(
@@ -395,9 +387,7 @@ parse_assignment proc(state @mut State, target ast.NodeId, allow_else bool) ast.
and !next_is(state, .eof)
and !(allow_else and next_is(state, .else))
and !(state.match_arm_body and next_is(state, .comma))
) {
return .unexpected_token
}
) return .unexpected_token
end :: state.tokens[state.next_token - 1].span.end
return try ast.add_assignment_stmt(
state.builder,
@@ -1176,10 +1166,8 @@ parse_record_field proc(state @mut State, declaration bool) ast.NodeId ! ParseEr
value ?ast.NodeId := null
named :: (
member_name(current(state).kind)
and (
(!declaration and next_n_is(state, 1, .equal))
or (declaration and starts_type(state.tokens[state.next_token + 1].kind))
)
and ((!declaration and next_n_is(state, 1, .equal))
or (declaration and starts_type(state.tokens[state.next_token + 1].kind)))
)
if (named) {
token :: if (declaration) try consume(state, .ident) else try consume_member(state, false)
@@ -1596,14 +1584,12 @@ pointer_binding_starts proc(state @State) bool {
named_binding_starts proc(state @State) bool {
return (
next_is(state, .ident)
and (
next_n_is(state, 1, .double_colon) or next_n_is(state, 1, .colon_equal)
and (next_n_is(state, 1, .double_colon) or next_n_is(state, 1, .colon_equal)
or next_n_is(state, 1, .ident) or next_n_is(state, 1, .proc) or next_n_is(state, 1, .match)
or next_n_is(state, 1, .at)
or next_n_is(state, 1, .question)
or (next_n_is(state, 1, .open_paren) and pointer_binding_starts(state))
or (next_n_is(state, 1, .star) and pointer_binding_starts(state))
)
or (next_n_is(state, 1, .star) and pointer_binding_starts(state)))
)
}
@@ -1699,9 +1685,7 @@ parse_primary proc(state @mut State) ast.NodeId ! ParseError {
else: {
diagnostic :: try unexpected_diagnostic(state)
if !is_expression_recovery_boundary(token.kind) {
state.next_token += 1
}
if (!is_expression_recovery_boundary(token.kind)) state.next_token += 1
return try ast.add_invalid_expr(state.builder, token.span, diagnostic)
}
@@ -1846,9 +1830,7 @@ recovery_closes_delimiters proc(state @State, paren_depth, square_depth usize) b
.ident: {
if (i + 1 < state.tokens.len) {
next :: state.tokens[i + 1].kind
if (next == .double_colon or next == .colon_equal or next == .ident or next == .proc) {
return false
}
if (next == .double_colon or next == .colon_equal or next == .ident or next == .proc) return false
}
}
else: {}
@@ -1865,9 +1847,7 @@ recovery_closes_delimiters proc(state @State, paren_depth, square_depth usize) b
@hide:file
unexpected_diagnostic proc(state @mut State) diagnostics.DiagnosticId ! (mem.AllocError | diagnostics.CapacityError) {
token :: current(state)
if (token.kind == .invalid and token.data.diagnostic != diagnostics.no_diagnostic) {
return token.data.diagnostic
}
if (token.kind == .invalid and token.data.diagnostic != diagnostics.no_diagnostic) return token.data.diagnostic
return try diagnostics.add(state.diagnostics, .unexpected_token, token.span)
}
+4 -8
View File
@@ -442,11 +442,8 @@ parses_return_boundaries_and_recovers_trailing_tokens test {
try testing.expect_equal(ast.StmtKind.expression, ast.stmt_kind(&tree, ast.block_statement(&tree, body, 1)))
grouped :: ast.return_stmt(&tree, ast.block_statement(&tree, body, 2))
try testing.expect_equal("return (3 + 4)", ast.source_text(&tree, grouped.span))
if (grouped.value) |value| {
try testing.expect_equal(ast.ExprKind.binary, ast.expr_kind(&tree, value))
} else {
return .expectation_failed
}
if (grouped.value) |value| try testing.expect_equal(ast.ExprKind.binary, ast.expr_kind(&tree, value))
else return .expectation_failed
try testing.expect_equal(ast.StmtKind.invalid, ast.stmt_kind(&tree, ast.block_statement(&tree, body, 3)))
after :: ast.statement_decl(&tree, ast.block_statement(&tree, body, 4))
try testing.expect_equal("after", ast.source_text(&tree, after.name))
@@ -1146,9 +1143,8 @@ rejects_control_tails_and_preserves_following_declarations test {
}
conditional :: ast.if_stmt(&tree, ast.block_statement(&tree, loop.body, 4))
try testing.expect_equal(ast.StmtKind.invalid, ast.stmt_kind(&tree, ast.block_statement(&tree, conditional.then_block, 0)))
if (conditional.else_block) |other| {
try testing.expect_equal(ast.StmtKind.continue_stmt, ast.stmt_kind(&tree, ast.block_statement(&tree, other, 0)))
} else return .expectation_failed
if (conditional.else_block) |other| try testing.expect_equal(ast.StmtKind.continue_stmt, ast.stmt_kind(&tree, ast.block_statement(&tree, other, 0)))
else return .expectation_failed
try testing.expect_equal(ast.StmtKind.declaration, ast.stmt_kind(&tree, ast.block_statement(&tree, body, 1)))
try testing.expect_equal(ast.DeclKind.statement, ast.decl_kind(&tree, ast.package_decl(&tree, 5)))
}
+48 -123
View File
@@ -360,9 +360,7 @@ check_elaborated proc(
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)
}
) join(&state, id, binding.data.declaration)
}
# assignment inputs observe established declaration types, never contribute
@@ -844,10 +842,8 @@ check_annotation proc(state @mut State, id ast.NodeId, allow_void bool) Type ! (
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)
)
(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)))
@@ -1233,9 +1229,7 @@ collect_expression proc(state @mut State, id ast.NodeId) void ! (mem.AllocError
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)))
}
) state.cells[usize(id)].type = invalid_type(try diagnostics.add(state.diagnostics, .type_not_value, ast.node_span(tree, id)))
}
match binding.kind {
.invalid: state.cells[usize(id)].type = invalid_type(binding.data.diagnostic)
@@ -1464,13 +1458,11 @@ 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)
)
}
@@ -1739,10 +1731,8 @@ mutable_target proc(state @mut State, id ast.NodeId, allow_global bool) bool {
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)
)
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
@@ -1950,9 +1940,7 @@ validate proc(state @mut State, id ast.NodeId) void ! (mem.AllocError | diagnost
} 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)
}
) try fail(state, id, .return_type_mismatch, statement.span)
}
.expression: {
match ast.expr_kind(tree, id) {
@@ -1975,24 +1963,18 @@ validate proc(state @mut State, id ast.NodeId) void ! (mem.AllocError | diagnost
return
}
if (expression.operator == .unwrap) {
if (state.types[usize(expression.operand)].kind != .optional and state.types[usize(expression.operand)].kind != .invalid) {
try fail(state, id, .invalid_operand, expression.span)
}
if (state.types[usize(expression.operand)].kind != .optional and state.types[usize(expression.operand)].kind != .invalid) try fail(state, id, .invalid_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)
}
) 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))
}
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))
}
.binary: {
expression :: ast.binary_expr(tree, id)
@@ -2005,9 +1987,7 @@ 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 (pointer_addition(state, id)) {
if (right.kind != .usize or right.nominal != ast.NodeId(maxval!(u32))) {
try fail(state, id, .invalid_operand, expression.span)
}
if (right.kind != .usize or right.nominal != ast.NodeId(maxval!(u32))) try fail(state, id, .invalid_operand, expression.span)
return
}
if (
@@ -2019,11 +1999,8 @@ validate proc(state @mut State, id ast.NodeId) void ! (mem.AllocError | diagnost
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) {
try fail(state, id, .integer_division, expression.span)
}
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) try fail(state, id, .integer_division, expression.span)
}
.range: {
if (state.cells[usize(id)].match_pattern) return
@@ -2032,12 +2009,8 @@ validate proc(state @mut State, id ast.NodeId) void ! (mem.AllocError | diagnost
end_type :: state.types[usize(expression.end)]
family TypeKind :: if (integer_kind(start_type.kind)) start_type.kind
else if (integer_kind(end_type.kind)) end_type.kind else .int
if (start_type.kind != .invalid and (!integer_kind(start_type.kind) or start_type.kind != family)) {
try fail(state, expression.start, .invalid_range_bound, ast.node_span(tree, expression.start))
}
if (end_type.kind != .invalid and (!integer_kind(end_type.kind) or end_type.kind != family)) {
try fail(state, expression.end, .invalid_range_bound, ast.node_span(tree, expression.end))
}
if (start_type.kind != .invalid and (!integer_kind(start_type.kind) or start_type.kind != family)) try fail(state, expression.start, .invalid_range_bound, ast.node_span(tree, expression.start))
if (end_type.kind != .invalid and (!integer_kind(end_type.kind) or end_type.kind != family)) try fail(state, expression.end, .invalid_range_bound, ast.node_span(tree, expression.end))
}
.array_literal: {
actual :: state.types[usize(id)]
@@ -2045,11 +2018,8 @@ validate proc(state @mut State, id ast.NodeId) void ! (mem.AllocError | diagnost
element :: state.arrays.items[usize(actual.array_type)].element
for 0..ast.array_element_count(tree, id) |i| {
child :: ast.array_element(tree, id, i)
if (element.kind == .void or element.kind == .range) {
try fail(state, child, .invalid_array_element_type, ast.node_span(tree, child))
} else {
try compatible(state, child, element)
}
if (element.kind == .void or element.kind == .range) try fail(state, child, .invalid_array_element_type, ast.node_span(tree, child))
else try compatible(state, child, element)
}
if (array_cells(state, actual) == null) try fail(state, id, .array_size_overflow, ast.node_span(tree, id))
}
@@ -2057,9 +2027,7 @@ validate proc(state @mut State, id ast.NodeId) void ! (mem.AllocError | diagnost
actual :: state.types[usize(id)]
if (actual.kind != .record) return
descriptor := state.records.items[usize(actual.record_type)]
if (state.variant_slots[usize(id)] != no_index and state.backings[usize(id)].kind == .record) {
descriptor = state.records.items[usize(state.backings[usize(id)].record_type)]
}
if (state.variant_slots[usize(id)] != no_index and state.backings[usize(id)].kind == .record) descriptor = state.records.items[usize(state.backings[usize(id)].record_type)]
fields :: descriptor.fields
slot :: state.slots[usize(id)]
if (slot == no_index) return
@@ -2073,12 +2041,8 @@ validate proc(state @mut State, id ast.NodeId) void ! (mem.AllocError | diagnost
view :: ast.index_expr(tree, id)
base :: state.types[usize(view.base)]
index :: state.types[usize(view.index)]
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 !indexable_type(state, base)) {
try fail(state, id, .not_indexable, ast.node_span(tree, view.base))
}
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 !indexable_type(state, base)) try fail(state, id, .not_indexable, ast.node_span(tree, view.base))
}
.call: try validate_call(state, id)
.builtin_call: _ = try infer_builtin(state, id, true)
@@ -2116,13 +2080,9 @@ validate_call proc(state @mut State, id ast.NodeId) void ! (mem.AllocError | dia
backing :: immediate_backing(state, actual)
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))
}
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))
}
return
}
@@ -2283,9 +2243,7 @@ intern_pointer proc(state @mut State, descriptor PointerType) Type ! mem.AllocEr
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) }
}
) 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) }
@@ -2304,9 +2262,7 @@ check_pointer_type proc(state @mut State, id ast.NodeId) Type ! (mem.AllocError
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)))
}
) 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 })
}
@@ -2346,9 +2302,7 @@ intern_array proc(state @mut State, descriptor ArrayType) Type ! mem.AllocError
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)
) {
return Type{ kind = descriptor.kind, array_type = ArrayId(i) }
}
) return Type{ kind = descriptor.kind, array_type = ArrayId(i) }
}
try arraylist.append(&state.arrays, descriptor)
return Type{ kind = descriptor.kind, array_type = ArrayId(state.arrays.items.len - 1) }
@@ -3074,9 +3028,7 @@ apply_contexts proc(state @mut State, assignment_values, early bool) void ! (mem
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)
}
) try context_demand(state, ast.call_arg(tree, id, 0), backing, .type_mismatch, early)
continue
}
if (conversion_kind(state, id) != .none) {
@@ -3165,9 +3117,7 @@ prepare_ranges proc(state @mut State, assignment_values, constrain bool) void !
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))
}
) seed(state, id, group_type(state, binding.data.declaration))
}
}
@@ -3230,9 +3180,7 @@ specialize_annotations proc(state @mut State) void ! (mem.AllocError | diagnosti
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)
}
) seed(state, id, state.cells[usize(binding.data.declaration)].type)
}
}
@@ -3742,9 +3690,7 @@ check_union proc(state @mut State, id ast.NodeId, nominal bool) Type ! (mem.Allo
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))
}
) 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) {
return invalid_type(try diagnostics.add(state.diagnostics, .duplicate_field, field.span))
}
@@ -4201,21 +4147,13 @@ validate_match proc(state @mut State, id ast.NodeId) void ! (mem.AllocError | di
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)
)
)
) {
try fail(state, id, .invalid_match_pattern, ast.node_span(state.tree, pattern))
}
(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) {
if (interval.low.magnitude == 0) seen_false = true else seen_true = true
}
@@ -4348,20 +4286,14 @@ check_fallible_type proc(state @mut State, id ast.NodeId) Type ! (mem.AllocError
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
)
)
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)))
}
) 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 })
state.cells[usize(id)].type = type
return type
@@ -4536,14 +4468,10 @@ validate_fallible_expression proc(state @mut State, id ast.NodeId) void ! (mem.A
if (catching) {
expression :: ast.catch_expr(state.tree, id)
if (expression.handler_is_block) {
if (descriptor.element.kind != .void and block_falls_through(state, expression.handler)) {
try fail(state, expression.handler, .catch_type_mismatch, ast.node_span(state.tree, expression.handler))
}
if (descriptor.element.kind != .void and block_falls_through(state, expression.handler)) try fail(state, expression.handler, .catch_type_mismatch, ast.node_span(state.tree, expression.handler))
} else {
actual :: state.types[usize(expression.handler)]
if (actual.kind != .invalid and !assignable_value(state, expression.handler, actual, descriptor.element)) {
try fail(state, expression.handler, .catch_type_mismatch, ast.node_span(state.tree, expression.handler))
}
if (actual.kind != .invalid and !assignable_value(state, expression.handler, actual, descriptor.element)) try fail(state, expression.handler, .catch_type_mismatch, ast.node_span(state.tree, expression.handler))
}
return
}
@@ -4558,18 +4486,15 @@ validate_fallible_expression proc(state @mut State, id ast.NodeId) void ! (mem.A
}
result :: state.types[usize(annotation)]
if (result.kind == .invalid) return
if (result.kind != .fallible) {
try fail(state, id, .try_outside_fallible, ast.node_span(state.tree, id))
} else if (
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,
)
) {
try fail(state, id, .incompatible_propagated_error, ast.node_span(state.tree, id))
}
) try fail(state, id, .incompatible_propagated_error, ast.node_span(state.tree, id))
}
@hide:file
+11 -28
View File
@@ -168,11 +168,8 @@ rejects_fixed_family_conversion_integer_division_and_conflicting_demands test {
for conflicts |declaration| {
try testing.expect_equal(TypeKind.invalid, type_of(&result, declaration).kind)
diagnostic :: type_of(&result, declaration).diagnostic
if (diagnostics.get(&diagnostic_store, diagnostic)) |error| {
try testing.expect_equal(diagnostics.Code.type_mismatch, error.code)
} else {
return .expectation_failed
}
if (diagnostics.get(&diagnostic_store, diagnostic)) |error| try testing.expect_equal(diagnostics.Code.type_mismatch, error.code)
else return .expectation_failed
try testing.expect_equal(diagnostic, type_of(&result, ast.statement_decl(&tree, declaration).initializer).diagnostic)
}
conflict_uses [4]usize :: [8, 9, 11, 12]
@@ -294,11 +291,8 @@ separates_type_annotations_from_values_and_restricts_void_storage test {
defer deinit(&result)
bad :: ast.statement_decl(&tree, ast.package_decl(&tree, 1))
if (bad.type) |annotation| {
_ = try expect_error(&result, &diagnostic_store, annotation, .expected_type, ast.node_span(&tree, annotation))
} else {
return .expectation_failed
}
if (bad.type) |annotation| _ = try expect_error(&result, &diagnostic_store, annotation, .expected_type, ast.node_span(&tree, annotation))
else return .expectation_failed
_ = try expect_error(&result, &diagnostic_store, bad.initializer, .invalid_operand, ast.node_span(&tree, bad.initializer))
builtin :: ast.statement_decl(&tree, ast.package_decl(&tree, 2)).initializer
_ = try expect_error(&result, &diagnostic_store, builtin, .type_not_value, ast.node_span(&tree, builtin))
@@ -322,11 +316,8 @@ separates_type_annotations_from_values_and_restricts_void_storage test {
try testing.expect_equal(void_error, type_of(&result, void_b).diagnostic)
try expect_kind(&result, ast.function_decl(&tree, void_function).return_type, .void)
storage :: ast.statement_decl(&tree, ast.package_decl(&tree, 5))
if (storage.type) |annotation| {
_ = try expect_error(&result, &diagnostic_store, annotation, .invalid_void_type, ast.node_span(&tree, annotation))
} else {
return .expectation_failed
}
if (storage.type) |annotation| _ = try expect_error(&result, &diagnostic_store, annotation, .invalid_void_type, ast.node_span(&tree, annotation))
else return .expectation_failed
holder :: ast.statement_decl(&tree, ast.package_decl(&tree, 8)).initializer
_ = try expect_error(&result, &diagnostic_store, holder, .type_mismatch, ast.node_span(&tree, holder))
operand :: ast.statement_decl(&tree, ast.package_decl(&tree, 9)).initializer
@@ -677,11 +668,8 @@ returns_supply_numeric_context_and_resolve_forward_local_and_parameter_names tes
}
identity :: ast.package_decl(&tree, 1)
identity_body :: ast.function_decl(&tree, identity).body
if (ast.return_stmt(&tree, ast.block_statement(&tree, identity_body, 0)).value) |value| {
try testing.expect_equal(ast.function_param(&tree, identity, 0), sema.binding(&resolution, value).data.declaration)
} else {
return .expectation_failed
}
if (ast.return_stmt(&tree, ast.block_statement(&tree, identity_body, 0)).value) |value| try testing.expect_equal(ast.function_param(&tree, identity, 0), sema.binding(&resolution, value).data.declaration)
else return .expectation_failed
try testing.expect_equal(0, diagnostics.count(&diagnostic_store))
}
@@ -1429,11 +1417,8 @@ logical_operand_errors_are_independent_and_skipped_rhs_preserves_bool test {
try expect_kind(&result, loop.condition, .bool)
returned :: ast.block_statement(&tree, body, 6)
try expect_kind(&result, returned, .void)
if (ast.return_stmt(&tree, returned).value) |value| {
try expect_kind(&result, value, .bool)
} else {
return .expectation_failed
}
if (ast.return_stmt(&tree, returned).value) |value| try expect_kind(&result, value, .bool)
else return .expectation_failed
try testing.expect_equal(7, code_count(&diagnostic_store, .unknown_name))
try testing.expect_equal(8, code_count(&diagnostic_store, .invalid_operand))
try testing.expect_equal(15, diagnostics.count(&diagnostic_store))
@@ -2271,9 +2256,7 @@ nested_array_numeric_context_flows_through_index_children_before_defaults test {
for usize(1)..usize(4) |i| {
element_kind TypeKind :: if (i == 2) .float else .array
type :: try expect_array(result, ast.package_decl(tree, i), 2, false, element_kind)
if (type.element.kind == .array) {
try testing.expect_equal(TypeKind.float, array_type(result, type.element.array_type).element.kind)
}
if (type.element.kind == .array) try testing.expect_equal(TypeKind.float, array_type(result, type.element.array_type).element.kind)
}
matrix :: ast.statement_decl(tree, ast.package_decl(tree, 1)).initializer
for 0..ast.array_element_count(tree, matrix) |i| {
+6 -18
View File
@@ -349,9 +349,7 @@ resolve_function proc(
for (0..ast.function_param_count(tree, id)) |i| {
parameter :: ast.function_param(tree, id, i)
try resolve_expr(state, &signature, ast.param(tree, parameter).type)
if (ast.param(tree, parameter).comptime) {
try predeclare(state, &signature, parameter, ast.param(tree, parameter).name)
}
if (ast.param(tree, parameter).comptime) try predeclare(state, &signature, parameter, ast.param(tree, parameter).name)
}
try resolve_expr(state, &signature, function.return_type)
scope := Scope{
@@ -381,9 +379,7 @@ resolve_block proc(state @mut State, scope @mut Scope, id ast.NodeId) void ! (me
tree :: state.tree
for (0..ast.block_statement_count(tree, id)) |i| {
statement :: ast.block_statement(tree, id, i)
if ast.stmt_kind(tree, statement) == .declaration {
try predeclare(state, scope, statement, ast.statement_decl(tree, statement).name)
}
if (ast.stmt_kind(tree, statement) == .declaration) try predeclare(state, scope, statement, ast.statement_decl(tree, statement).name)
}
for (0..ast.block_statement_count(tree, id)) |i| {
statement :: ast.block_statement(tree, id, i)
@@ -423,12 +419,8 @@ resolve_block proc(state @mut State, scope @mut Scope, id ast.NodeId) void ! (me
parent = scope,
}
defer hashmap.deinit(&body_scope.names)
if (loop.capture) |capture| {
try predeclare(state, &body_scope, capture, ast.node_span(tree, capture))
}
if (loop.index) |index| {
try predeclare(state, &body_scope, index, ast.node_span(tree, index))
}
if (loop.capture) |capture| try predeclare(state, &body_scope, capture, ast.node_span(tree, capture))
if (loop.index) |index| try predeclare(state, &body_scope, index, ast.node_span(tree, index))
try resolve_block(state, &body_scope, loop.body)
}
.invalid: resolve_invalid(state, statement)
@@ -451,9 +443,7 @@ resolve_statement_decl proc(
id ast.NodeId,
) void ! (mem.AllocError | diagnostics.CapacityError) {
declaration :: ast.statement_decl(state.tree, id)
if (ast.expr_kind(state.tree, declaration.initializer) == .alias_expr) {
state.aliases[usize(id)] = ast.type_operand(state.tree, declaration.initializer)
}
if (ast.expr_kind(state.tree, declaration.initializer) == .alias_expr) state.aliases[usize(id)] = ast.type_operand(state.tree, declaration.initializer)
if (declaration.type) |type| try resolve_expr(state, scope, type)
try resolve_expr(state, scope, declaration.initializer)
}
@@ -562,9 +552,7 @@ resolve_expr proc(
try resolve_expr(state, scope, expression.operand)
handler_scope := Scope{ names = hashmap.init(state.allocator), parent = scope }
defer hashmap.deinit(&handler_scope.names)
if (expression.capture) |capture| {
try predeclare(state, &handler_scope, capture, ast.node_span(tree, capture))
}
if (expression.capture) |capture| try predeclare(state, &handler_scope, capture, ast.node_span(tree, capture))
if (expression.handler_is_block) try resolve_block(state, &handler_scope, expression.handler)
else try resolve_expr(state, &handler_scope, expression.handler)
}
+2 -3
View File
@@ -581,9 +581,8 @@ range_captures_preserve_duplicate_builtin_and_discard_errors test {
} else return .expectation_failed
}
indexed :: ast.for_stmt(&tree, ast.block_statement(&tree, body, 3))
if (indexed.index) |index| {
try testing.expect_equal(BindingKind.invalid, binding(&resolution, index).kind)
} else return .expectation_failed
if (indexed.index) |index| try testing.expect_equal(BindingKind.invalid, binding(&resolution, index).kind)
else return .expectation_failed
if (indexed.capture) |capture| {
reference :: ast.statement_expr(&tree, ast.block_statement(&tree, indexed.body, 0))
try testing.expect_equal(capture, binding(&resolution, reference).data.declaration)
+9 -21
View File
@@ -528,11 +528,8 @@ specialize_call proc(state @mut State, call, function ast.NodeId) void ! Error {
}
callee :: try reference(state, call, target)
replacement := no_node
if (state.instances.items[instance].type != no_node) {
replacement = try ast.add_type_operand(state.builder, ast.node_span(&state.tree, call), callee, true)
} else {
replacement = try ast.add_call(state.builder, ast.node_span(&state.tree, call), callee, runtime_arguments.items)
}
if (state.instances.items[instance].type != no_node) replacement = try ast.add_type_operand(state.builder, ast.node_span(&state.tree, call), callee, true)
else replacement = try ast.add_call(state.builder, ast.node_span(&state.tree, call), callee, runtime_arguments.items)
try replace(state, call, replacement)
try refresh(state)
}
@@ -1554,13 +1551,10 @@ evaluate proc(state @mut State, id ast.NodeId) Value ! Error {
.unary: {
view :: ast.unary_expr(&state.tree, id)
value := try evaluate(state, view.operand)
if (view.operator == .negate and value.kind == .integer) {
value.negative = value.magnitude != 0 and !value.negative
} else if (view.operator == .negate and value.kind == .real) {
value.magnitude = value.magnitude xor (u64(1) << 63)
} else if (view.operator == .logical_not and value.kind == .boolean) {
value.boolean = !value.boolean
} else return Value{}
if (view.operator == .negate and value.kind == .integer) value.negative = value.magnitude != 0 and !value.negative
else if (view.operator == .negate and value.kind == .real) value.magnitude = value.magnitude xor (u64(1) << 63)
else if (view.operator == .logical_not and value.kind == .boolean) value.boolean = !value.boolean
else return Value{}
value.node = id
return value
}
@@ -1642,9 +1636,7 @@ evaluate_binary proc(state @mut State, id ast.NodeId) Value ! Error {
if (left.magnitude > maxval!(u64) - right.magnitude) return Value{}
return try integer_value(state, id, left.magnitude + right.magnitude, left.negative, annotation)
}
if (left.magnitude < right.magnitude) {
return try integer_value(state, id, right.magnitude - left.magnitude, right.negative, annotation)
}
if (left.magnitude < right.magnitude) return try integer_value(state, id, right.magnitude - left.magnitude, right.negative, annotation)
return try integer_value(state, id, left.magnitude - right.magnitude,
left.magnitude != right.magnitude and left.negative, annotation)
}
@@ -1666,9 +1658,7 @@ same_type proc(state @mut State, left, right ast.NodeId) bool ! Error {
if (a == b) return true
x :: binding(state, a)
y :: binding(state, b)
if (x.kind == .builtin or y.kind == .builtin) {
return x.kind == .builtin and y.kind == .builtin and builtin_identity(x.data.builtin) == builtin_identity(y.data.builtin)
}
if (x.kind == .builtin or y.kind == .builtin) return x.kind == .builtin and y.kind == .builtin and builtin_identity(x.data.builtin) == builtin_identity(y.data.builtin)
kind :: ast.expr_kind(&state.tree, a)
if (kind == .sum_type or ast.expr_kind(&state.tree, b) == .sum_type)
return try type_subset(state, a, b) and try type_subset(state, b, a)
@@ -1908,9 +1898,7 @@ expression_type proc(state @mut State, id ast.NodeId, depth usize) ast.NodeId !
callee :: ast.call_expr(&state.tree, id).callee
destination :: binding(state, callee)
if (destination.kind == .builtin) return callee
if (destination.kind == .declaration and ast.node_class(&state.tree, destination.data.declaration) == .function) {
return try canonical_type(state, ast.function_decl(&state.tree, destination.data.declaration).return_type)
}
if (destination.kind == .declaration and ast.node_class(&state.tree, destination.data.declaration) == .function) return try canonical_type(state, ast.function_decl(&state.tree, destination.data.declaration).return_type)
signature :: try expression_type(state, callee, depth + 1)
if (signature != no_node and ast.expr_kind(&state.tree, signature) == .function_type)
return try canonical_type(state, ast.callable_result(&state.tree, signature))
+4 -11
View File
@@ -38,15 +38,10 @@ reserve proc(
new_capacity := 8
if list.capacity >= 8 {
half :: divtrunc!(list.capacity, 2)
if list.capacity > maxval!(usize) - half {
new_capacity = min_capacity
} else {
new_capacity = list.capacity + half
}
}
if new_capacity < min_capacity {
new_capacity = min_capacity
if (list.capacity > maxval!(usize) - half) new_capacity = min_capacity
else new_capacity = list.capacity + half
}
if (new_capacity < min_capacity) new_capacity = min_capacity
length :: list.items.len
allocation :: list.items.ptr[..list.capacity]
@@ -60,9 +55,7 @@ reserve proc(
append proc($T type, list @mut ArrayList(T), value T) void ! mem.AllocError {
length :: list.items.len
if length == maxval!(usize) {
return .out_of_memory
}
if (length == maxval!(usize)) return .out_of_memory
try reserve(list, length + 1)
list.items = list.items.ptr[..length + 1]
list.items[length] = value
+5 -13
View File
@@ -71,11 +71,8 @@ get proc(
if (map.count == 0) return null
hash usize := 0
if $(hash_key == null) {
hash = normalize(auto_hash(K, key))
} else {
hash = normalize(call_hash(K, hash_key?, key))
}
if ($(hash_key == null)) hash = normalize(auto_hash(K, key))
else hash = normalize(call_hash(K, hash_key?, key))
idx := hash & (map.entries.len - 1)
while true {
@@ -114,9 +111,7 @@ put proc(
if (entry.hash == 0) continue
idx := entry.hash & (new_entries.len - 1)
while new_entries[idx].hash != 0 {
idx = (idx + 1) & (new_entries.len - 1)
}
while (new_entries[idx].hash != 0) idx = (idx + 1) & (new_entries.len - 1)
new_entries[idx] = entry
}
@@ -126,11 +121,8 @@ put proc(
}
hash usize := 0
if $(hash_key == null) {
hash = normalize(auto_hash(K, key))
} else {
hash = normalize(call_hash(K, hash_key?, key))
}
if ($(hash_key == null)) hash = normalize(auto_hash(K, key))
else hash = normalize(call_hash(K, hash_key?, key))
idx := hash & (map.entries.len - 1)
while true {
+6 -18
View File
@@ -269,9 +269,7 @@ parse_format proc($N usize, $format []u8, $Args type) [N]mut FormatToken {
while cursor < format.len {
byte :: format[cursor]
if byte == '{' {
if cursor + 1 >= format.len {
compile_error!("io.print format has an unmatched '{'")
}
if (cursor + 1 >= format.len) compile_error!("io.print format has an unmatched '{'")
if cursor > literal_start {
tokens[token_count] = FormatToken{
kind = .literal,
@@ -298,9 +296,7 @@ parse_format proc($N usize, $format []u8, $Args type) [N]mut FormatToken {
kind FormatTokenKind := .default
width := 2
if next != '}' {
if cursor + 2 >= format.len or format[cursor + 2] != '}' {
compile_error!("io.print format expects a one-character specifier")
}
if (cursor + 2 >= format.len or format[cursor + 2] != '}') compile_error!("io.print format expects a one-character specifier")
width = 3
if (next == 's') kind = .string
@@ -314,9 +310,7 @@ parse_format proc($N usize, $format []u8, $Args type) [N]mut FormatToken {
else compile_error!("io.print format has an unknown specifier")
}
if argument_count >= field_count {
compile_error!("io.print format argument count does not match the tuple")
}
if (argument_count >= field_count) compile_error!("io.print format argument count does not match the tuple")
tokens[token_count] = FormatToken{
kind = kind,
@@ -333,9 +327,7 @@ parse_format proc($N usize, $format []u8, $Args type) [N]mut FormatToken {
}
if byte == '}' {
if cursor + 1 >= format.len or format[cursor + 1] != '}' {
compile_error!("io.print format has an unmatched '}'")
}
if (cursor + 1 >= format.len or format[cursor + 1] != '}') compile_error!("io.print format has an unmatched '}'")
if cursor > literal_start {
tokens[token_count] = FormatToken{
kind = .literal,
@@ -368,9 +360,7 @@ parse_format proc($N usize, $format []u8, $Args type) [N]mut FormatToken {
}
}
if argument_count != field_count {
compile_error!("io.print format argument count does not match the tuple")
}
if (argument_count != field_count) compile_error!("io.print format argument count does not match the tuple")
return tokens
}
@@ -466,9 +456,7 @@ write_decimal proc($T type, output Writer, value T) void ! WriteError {
write_character proc($T type, output Writer, value T) void ! WriteError {
match typeinfo!(T) {
.integer: {
if minval!(T) < 0 or maxval!(T) > 255 {
compile_error!("io.print '{c}' requires an unsigned integer that fits in u8")
}
if (minval!(T) < 0 or maxval!(T) > 255) compile_error!("io.print '{c}' requires an unsigned integer that fits in u8")
buffer [1]u8 :: [u8(value)]
try write_all(output, buffer[..])
}
+2 -6
View File
@@ -70,9 +70,7 @@ alloc proc($T type, allocator Allocator, count usize) []mut T ! AllocError {
element_size :: sizeof!(T)
if (element_size == 0) return empty_slice(T, count)
if count > divtrunc!(maxval!(usize), element_size) {
return .out_of_memory
}
if (count > divtrunc!(maxval!(usize), element_size)) return .out_of_memory
memory :: raw_alloc(allocator, count * element_size, alignof!(T))
if memory |bytes| {
@@ -194,9 +192,7 @@ c_realloc proc(
}
if memory |old_memory| {
if alignment <= malloc_alignment {
return ptrcast!(c.realloc(old_memory, c_ulong(new_size)))
}
if (alignment <= malloc_alignment) return ptrcast!(c.realloc(old_memory, c_ulong(new_size)))
new_memory :: c_alloc(null, new_size, alignment)
if new_memory |new_bytes| {
+1 -3
View File
@@ -58,9 +58,7 @@ parse_float proc(allocator mem.Allocator, text []u8) f64 ! (mem.AllocError | Par
magnitude >= u64(9218868437227405312)
or (magnitude == 0 and nonzero)
or (range_error and magnitude >= u64(4607182418800017408))
) {
return .out_of_range
}
) return .out_of_range
return value
}
+3 -9
View File
@@ -18,23 +18,17 @@ Pair proc($V type) type {
#! initializes a static string map from a list of key-value pairs (constructed at compile-time).
#! returned views use compile-time storage and require no cleanup.
init proc($V type, $N usize, $entries [N]Pair(V)) StaticStringMap(V) {
if N > usize(maxval!(u32)) {
compile_error!("static string map has too many entries")
}
if (N > usize(maxval!(u32))) compile_error!("static string map has too many entries")
keys [N]mut []u8 := undefined
values [N]mut V := undefined
# assert no duplicate keys
for entries |entry, i| {
if entry.0.len > usize(maxval!(u32)) {
compile_error!("static string map key is too long")
}
if (entry.0.len > usize(maxval!(u32))) compile_error!("static string map key is too long")
for (0..i) |prior| {
if mem.eql(u8, entry.0, entries[prior].0) {
compile_error!("duplicate static string map key")
}
if mem.eql(u8, entry.0, entries[prior].0) compile_error!("duplicate static string map key")
}
keys[i] = entry.0