Files
honey/source/sema/check/check.test.hon
T

2603 lines
132 KiB
Plaintext

import "@std/mem"
import "@std/testing"
import "@source/diagnostics"
import "@source/strpool"
import "@source/lexer"
import "@source/parser"
import "@source/ast"
sema :: import "@source/sema"
@hide:file
expect_kind proc(result @Result, id ast.NodeId, expected TypeKind) void ! testing.Error {
try testing.expect_equal(expected, type_of(result, id).kind)
try testing.expect_equal(diagnostics.no_diagnostic, type_of(result, id).diagnostic)
}
@hide:file
expect_error proc(
result @Result,
diagnostic_store @diagnostics.Store,
id ast.NodeId,
code diagnostics.Code,
span ast.Span,
) diagnostics.DiagnosticId ! testing.Error {
actual :: type_of(result, id)
try testing.expect_equal(TypeKind.invalid, actual.kind)
if (diagnostics.get(diagnostic_store, actual.diagnostic)) |diagnostic| {
try testing.expect_equal(code, diagnostic.code)
try testing.expect_equal(span.start, diagnostic.span.start)
try testing.expect_equal(span.end, diagnostic.span.end)
} else {
return .expectation_failed
}
return actual.diagnostic
}
@hide:file
code_count proc(diagnostic_store @diagnostics.Store, code diagnostics.Code) usize {
count := usize(0)
for 0..diagnostics.count(diagnostic_store) |index| {
if (diagnostics.get(diagnostic_store, diagnostics.DiagnosticId(index))) |diagnostic| {
if (diagnostic.code == code) count += 1
}
}
return count
}
propagates_numeric_context_before_defaulting_forward_and_mutable_aliases test {
strpool.strings = strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings)
diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store)
source ::
`forward float :: -alias * 2 + seed
`alias :: seed
`seed := 3
`backward :: 4
`demand float :: backward
`quotient :: -3 / 2 + 1
`mixed :: 3 / 2
`plain :: -3 + 2 * 4
`fraction :: 1.5
`text :: "honey"
`fixed int :: 7
`anchored :: fixed + 2
`nested float :: quotient / (alias + 1)
scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store)
tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree)
resolution := try sema.resolve(mem.c_allocator, &tree, &diagnostic_store)
defer sema.deinit(&resolution)
try testing.expect_equal(0, diagnostics.count(&diagnostic_store))
result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store)
defer deinit(&result)
expected [13]TypeKind :: [
.float, .float, .float, .float, .float, .float, .float,
.int, .float, .string, .int, .int, .float,
]
for expected |kind, index| {
declaration :: ast.package_decl(&tree, index)
view :: ast.statement_decl(&tree, declaration)
try expect_kind(&result, declaration, kind)
try expect_kind(&result, view.initializer, kind)
if (view.type) |annotation| try expect_kind(&result, annotation, kind)
}
forward :: ast.binary_expr(&tree, ast.statement_decl(&tree, ast.package_decl(&tree, 0)).initializer)
multiply :: ast.binary_expr(&tree, forward.left)
negate :: ast.unary_expr(&tree, multiply.left)
numeric_nodes [6]ast.NodeId :: [
forward.left, forward.right, multiply.left, multiply.right, negate.operand,
ast.statement_decl(&tree, ast.package_decl(&tree, 2)).initializer,
]
for (numeric_nodes) |id| try expect_kind(&result, id, .float)
quotient :: ast.binary_expr(&tree, ast.statement_decl(&tree, ast.package_decl(&tree, 5)).initializer)
mixed :: ast.binary_expr(&tree, ast.statement_decl(&tree, ast.package_decl(&tree, 6)).initializer)
division_operands [4]ast.NodeId :: [quotient.left, quotient.right, mixed.left, mixed.right]
for (division_operands) |id| try expect_kind(&result, id, .float)
try testing.expect_equal(0, diagnostics.count(&diagnostic_store))
}
rejects_fixed_family_conversion_integer_division_and_conflicting_demands test {
strpool.strings = strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings)
diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store)
source ::
`fixed int :: 1
`real float :: 2
`widen float :: fixed
`narrow int :: 1.5
`cross :: fixed + real
`division :: fixed / 2
`negated :: -"x"
`strings :: "a" + "b"
`first_int int :: conflict_a
`first_float float :: conflict_a
`conflict_a := 5
`reverse_float float :: conflict_b
`reverse_int int :: conflict_b
`conflict_b :: 6
`good :: 9
scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store)
tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree)
resolution := try sema.resolve(mem.c_allocator, &tree, &diagnostic_store)
defer sema.deinit(&resolution)
try testing.expect_equal(0, diagnostics.count(&diagnostic_store))
result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store)
defer deinit(&result)
try expect_kind(&result, ast.package_decl(&tree, 0), .int)
try expect_kind(&result, ast.package_decl(&tree, 1), .float)
codes [6]diagnostics.Code :: [
.type_mismatch, .type_mismatch, .invalid_operand,
.integer_division, .invalid_operand, .invalid_operand,
]
for codes |code, index| {
declaration :: ast.package_decl(&tree, index + 2)
expression :: ast.statement_decl(&tree, declaration).initializer
diagnostic :: try expect_error(&result, &diagnostic_store, expression, code, ast.node_span(&tree, expression))
try testing.expect_equal(TypeKind.invalid, type_of(&result, declaration).kind)
try testing.expect_equal(diagnostic, type_of(&result, declaration).diagnostic)
}
conflicts [2]ast.NodeId :: [ast.package_decl(&tree, 10), ast.package_decl(&tree, 13)]
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
}
try testing.expect_equal(diagnostic, type_of(&result, ast.statement_decl(&tree, declaration).initializer).diagnostic)
}
conflict_uses [4]usize :: [8, 9, 11, 12]
for conflict_uses |index| {
declaration :: ast.package_decl(&tree, index)
target :: if (index < 11) conflicts[0] else conflicts[1]
try testing.expect_equal(TypeKind.invalid, type_of(&result, declaration).kind)
try testing.expect_equal(type_of(&result, target).diagnostic, type_of(&result, declaration).diagnostic)
}
try expect_kind(&result, ast.package_decl(&tree, 14), .int)
try testing.expect_equal(0, code_count(&diagnostic_store, .cannot_infer_type))
}
checks_forward_alias_calls_grouped_parameters_and_mutual_recursion test {
strpool.strings = strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings)
diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store)
source ::
`alias :: alpha
`alpha proc(a, b int) void {
` (alias)(a, b)
` beta(a + b, seed)
`}
`beta proc(total int, amount float) void {
` alpha(total, 2)
`}
`seed := 3
`main proc() void {
` beta(1, 2)
` (alias)(3, 4)
`}
scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store)
tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree)
resolution := try sema.resolve(mem.c_allocator, &tree, &diagnostic_store)
defer sema.deinit(&resolution)
result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store)
defer deinit(&result)
alias_decl :: ast.package_decl(&tree, 0)
alpha :: ast.package_decl(&tree, 1)
beta :: ast.package_decl(&tree, 2)
seed :: ast.package_decl(&tree, 3)
main_function :: ast.package_decl(&tree, 4)
functions [3]ast.NodeId :: [alpha, beta, main_function]
for functions |function| {
try expect_kind(&result, function, .function)
try testing.expect_equal(function, type_of(&result, function).function)
try expect_kind(&result, ast.function_decl(&tree, function).return_type, .void)
}
try expect_kind(&result, alias_decl, .function)
try testing.expect_equal(alpha, type_of(&result, alias_decl).function)
try testing.expect_equal(alpha, type_of(&result, ast.statement_decl(&tree, alias_decl).initializer).function)
alpha_body :: ast.function_decl(&tree, alpha).body
beta_body :: ast.function_decl(&tree, beta).body
main_body :: ast.function_decl(&tree, main_function).body
calls [5]ast.NodeId :: [
ast.statement_expr(&tree, ast.block_statement(&tree, alpha_body, 0)),
ast.statement_expr(&tree, ast.block_statement(&tree, alpha_body, 1)),
ast.statement_expr(&tree, ast.block_statement(&tree, beta_body, 0)),
ast.statement_expr(&tree, ast.block_statement(&tree, main_body, 0)),
ast.statement_expr(&tree, ast.block_statement(&tree, main_body, 1)),
]
callees [5]ast.NodeId :: [alpha, beta, alpha, beta, alpha]
for calls |call, index| {
try expect_kind(&result, call, .void)
callee :: ast.call_expr(&tree, call).callee
try expect_kind(&result, callee, .function)
try testing.expect_equal(callees[index], type_of(&result, callee).function)
try expect_kind(&result, ast.call_arg(&tree, call, 0), .int)
expected TypeKind :: if (index == 1 or index == 3) .float else .int
try expect_kind(&result, ast.call_arg(&tree, call, 1), expected)
}
try expect_kind(&result, seed, .float)
try expect_kind(&result, ast.statement_decl(&tree, seed).initializer, .float)
a :: ast.function_param(&tree, alpha, 0)
b :: ast.function_param(&tree, alpha, 1)
try testing.expect_equal(ast.param(&tree, a).type, ast.param(&tree, b).type)
try expect_kind(&result, a, .int)
try expect_kind(&result, b, .int)
try expect_kind(&result, ast.param(&tree, a).type, .int)
try expect_kind(&result, ast.function_param(&tree, beta, 1), .float)
try testing.expect_equal(0, diagnostics.count(&diagnostic_store))
}
separates_type_annotations_from_values_and_restricts_void_storage test {
strpool.strings = strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings)
diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store)
source ::
`value :: 1
`bad value :: -"x"
`builtin :: int
`grouped proc(a, b value) value {}
`void_params proc(a, b void) void {}
`storage void :: 1
`noop proc() void {}
`sink proc(p int) void {}
`holder :: noop()
`operand :: noop() + 1
`main proc() void {
` noop()
` sink(noop())
`}
`good string :: "ok"
scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store)
tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree)
resolution := try sema.resolve(mem.c_allocator, &tree, &diagnostic_store)
defer sema.deinit(&resolution)
try testing.expect_equal(0, diagnostics.count(&diagnostic_store))
result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store)
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
}
_ = 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))
grouped :: ast.package_decl(&tree, 3)
a :: ast.function_param(&tree, grouped, 0)
b :: ast.function_param(&tree, grouped, 1)
shared_type :: ast.param(&tree, a).type
try testing.expect_equal(shared_type, ast.param(&tree, b).type)
shared_error :: try expect_error(&result, &diagnostic_store, shared_type, .expected_type, ast.node_span(&tree, shared_type))
try testing.expect_equal(shared_error, type_of(&result, a).diagnostic)
try testing.expect_equal(shared_error, type_of(&result, b).diagnostic)
return_type :: ast.function_decl(&tree, grouped).return_type
_ = try expect_error(&result, &diagnostic_store, return_type, .expected_type, ast.node_span(&tree, return_type))
void_function :: ast.package_decl(&tree, 4)
void_a :: ast.function_param(&tree, void_function, 0)
void_b :: ast.function_param(&tree, void_function, 1)
void_annotation :: ast.param(&tree, void_a).type
void_error :: try expect_error(&result, &diagnostic_store, void_annotation, .invalid_void_type, ast.node_span(&tree, void_annotation))
try testing.expect_equal(void_annotation, ast.param(&tree, void_b).type)
try testing.expect_equal(void_error, type_of(&result, void_a).diagnostic)
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
}
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
_ = try expect_error(&result, &diagnostic_store, operand, .invalid_operand, ast.node_span(&tree, operand))
main_body :: ast.function_decl(&tree, ast.package_decl(&tree, 10)).body
noop_call :: ast.statement_expr(&tree, ast.block_statement(&tree, main_body, 0))
try expect_kind(&result, noop_call, .void)
bad_call :: ast.statement_expr(&tree, ast.block_statement(&tree, main_body, 1))
argument :: ast.call_arg(&tree, bad_call, 0)
argument_error :: try expect_error(&result, &diagnostic_store, argument, .type_mismatch, ast.node_span(&tree, argument))
try testing.expect_equal(argument_error, type_of(&result, bad_call).diagnostic)
rejected [5]usize :: [1, 2, 5, 8, 9]
for rejected |index| {
try testing.expect_equal(TypeKind.invalid, type_of(&result, ast.package_decl(&tree, index)).kind)
}
try expect_kind(&result, storage.initializer, .int)
try expect_kind(&result, ast.package_decl(&tree, 0), .int)
try expect_kind(&result, ast.package_decl(&tree, 11), .string)
try testing.expect_equal(3, code_count(&diagnostic_store, .expected_type))
try testing.expect_equal(2, code_count(&diagnostic_store, .invalid_void_type))
try testing.expect_equal(1, code_count(&diagnostic_store, .type_not_value))
try testing.expect_equal(2, code_count(&diagnostic_store, .type_mismatch))
try testing.expect_equal(2, code_count(&diagnostic_store, .invalid_operand))
try testing.expect_equal(10, diagnostics.count(&diagnostic_store))
}
reports_nonvoid_fallthrough_without_poisoning_signatures_and_checks_unused_bodies test {
strpool.strings = strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings)
diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store)
source ::
`answer :: alias()
`alias :: make
`make proc() int {}
`unused proc() float {}
`broken proc() void {
` -"x"
`}
`main proc() void {
` make()
`}
scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store)
tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree)
resolution := try sema.resolve(mem.c_allocator, &tree, &diagnostic_store)
defer sema.deinit(&resolution)
result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store)
defer deinit(&result)
answer :: ast.package_decl(&tree, 0)
alias_decl :: ast.package_decl(&tree, 1)
make :: ast.package_decl(&tree, 2)
unused :: ast.package_decl(&tree, 3)
try expect_kind(&result, answer, .int)
try expect_kind(&result, ast.statement_decl(&tree, answer).initializer, .int)
try expect_kind(&result, alias_decl, .function)
try testing.expect_equal(make, type_of(&result, alias_decl).function)
functions [2]ast.NodeId :: [make, unused]
returns [2]TypeKind :: [.int, .float]
for functions |function, index| {
view :: ast.function_decl(&tree, function)
try expect_kind(&result, function, .function)
try testing.expect_equal(function, type_of(&result, function).function)
try expect_kind(&result, view.return_type, returns[index])
_ = try expect_error(&result, &diagnostic_store, view.body, .missing_return, ast.node_span(&tree, view.body))
}
broken :: ast.package_decl(&tree, 4)
try expect_kind(&result, broken, .function)
broken_body :: ast.function_decl(&tree, broken).body
bad_operand :: ast.statement_expr(&tree, ast.block_statement(&tree, broken_body, 0))
_ = try expect_error(&result, &diagnostic_store, bad_operand, .invalid_operand, ast.node_span(&tree, bad_operand))
main_body :: ast.function_decl(&tree, ast.package_decl(&tree, 5)).body
call :: ast.statement_expr(&tree, ast.block_statement(&tree, main_body, 0))
try expect_kind(&result, call, .int)
try testing.expect_equal(2, code_count(&diagnostic_store, .missing_return))
try testing.expect_equal(3, diagnostics.count(&diagnostic_store))
}
checks_arguments_even_for_wrong_arity_noncallable_and_chained_callees test {
strpool.strings = strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings)
diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store)
source ::
`sink proc(a int, b float) void {}
`noop proc() void {}
`fixed int :: 1
`main proc() void {
` sink(1, 2, -"extra")
` sink()
` fixed(-"arg")
` sink(1, "wrong")
` sink(1, fixed)
` noop()()
` sink(2, 3)
`}
scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store)
tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree)
resolution := try sema.resolve(mem.c_allocator, &tree, &diagnostic_store)
defer sema.deinit(&resolution)
result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store)
defer deinit(&result)
body :: ast.function_decl(&tree, ast.package_decl(&tree, 3)).body
for usize(0)..usize(2) |index| {
call :: ast.statement_expr(&tree, ast.block_statement(&tree, body, index))
_ = try expect_error(&result, &diagnostic_store, call, .argument_count_mismatch, ast.node_span(&tree, call))
}
long_call :: ast.statement_expr(&tree, ast.block_statement(&tree, body, 0))
extra :: ast.call_arg(&tree, long_call, 2)
_ = try expect_error(&result, &diagnostic_store, extra, .invalid_operand, ast.node_span(&tree, extra))
try expect_kind(&result, ast.call_arg(&tree, long_call, 0), .int)
try expect_kind(&result, ast.call_arg(&tree, long_call, 1), .float)
noncall :: ast.statement_expr(&tree, ast.block_statement(&tree, body, 2))
noncall_callee :: ast.call_expr(&tree, noncall).callee
_ = try expect_error(&result, &diagnostic_store, noncall, .not_callable, ast.node_span(&tree, noncall_callee))
bad_argument :: ast.call_arg(&tree, noncall, 0)
_ = try expect_error(&result, &diagnostic_store, bad_argument, .invalid_operand, ast.node_span(&tree, bad_argument))
for usize(3)..usize(5) |index| {
call :: ast.statement_expr(&tree, ast.block_statement(&tree, body, index))
argument :: ast.call_arg(&tree, call, 1)
diagnostic :: try expect_error(&result, &diagnostic_store, argument, .type_mismatch, ast.node_span(&tree, argument))
try testing.expect_equal(diagnostic, type_of(&result, call).diagnostic)
}
chained :: ast.statement_expr(&tree, ast.block_statement(&tree, body, 5))
inner :: ast.call_expr(&tree, chained).callee
try expect_kind(&result, inner, .void)
_ = try expect_error(&result, &diagnostic_store, chained, .not_callable, ast.node_span(&tree, inner))
good_call :: ast.statement_expr(&tree, ast.block_statement(&tree, body, 6))
try expect_kind(&result, good_call, .void)
try expect_kind(&result, ast.call_arg(&tree, good_call, 1), .float)
try expect_kind(&result, ast.package_decl(&tree, 2), .int)
try testing.expect_equal(2, code_count(&diagnostic_store, .argument_count_mismatch))
try testing.expect_equal(2, code_count(&diagnostic_store, .not_callable))
try testing.expect_equal(2, code_count(&diagnostic_store, .invalid_operand))
try testing.expect_equal(2, code_count(&diagnostic_store, .type_mismatch))
try testing.expect_equal(8, diagnostics.count(&diagnostic_store))
}
terminates_unanchored_cycles_and_keeps_grounded_cycles_typed test {
strpool.strings = strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings)
diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store)
source ::
`a :: b
`b :: a
`dependent :: a
`self :: self
`anchored float :: partner
`partner :: anchored
`fixed int :: fixed
`good :: 2
`numeric :: numeric + 1
`demand float :: contextual_a
`contextual_a :: contextual_b
`contextual_b :: contextual_a
scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store)
tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree)
resolution := try sema.resolve(mem.c_allocator, &tree, &diagnostic_store)
defer sema.deinit(&resolution)
try testing.expect_equal(0, diagnostics.count(&diagnostic_store))
result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store)
defer deinit(&result)
a :: ast.package_decl(&tree, 0)
b :: ast.package_decl(&tree, 1)
dependent :: ast.package_decl(&tree, 2)
shared :: type_of(&result, a).diagnostic
cycle [3]ast.NodeId :: [a, b, dependent]
for cycle |declaration| {
try testing.expect_equal(TypeKind.invalid, type_of(&result, declaration).kind)
try testing.expect_equal(shared, type_of(&result, declaration).diagnostic)
expression :: ast.statement_decl(&tree, declaration).initializer
try testing.expect_equal(TypeKind.invalid, type_of(&result, expression).kind)
try testing.expect_equal(shared, type_of(&result, expression).diagnostic)
}
if (diagnostics.get(&diagnostic_store, shared)) |diagnostic| {
try testing.expect_equal(diagnostics.Code.cannot_infer_type, diagnostic.code)
a_name :: ast.statement_decl(&tree, a).name
b_name :: ast.statement_decl(&tree, b).name
try testing.expect(
(diagnostic.span.start == a_name.start and diagnostic.span.end == a_name.end)
or (diagnostic.span.start == b_name.start and diagnostic.span.end == b_name.end),
)
} else {
return .expectation_failed
}
self :: ast.package_decl(&tree, 3)
self_error :: try expect_error(&result, &diagnostic_store, self, .cannot_infer_type, ast.statement_decl(&tree, self).name)
try testing.expect(self_error != shared)
try testing.expect_equal(self_error, type_of(&result, ast.statement_decl(&tree, self).initializer).diagnostic)
for usize(4)..usize(6) |index| {
declaration :: ast.package_decl(&tree, index)
try expect_kind(&result, declaration, .float)
try expect_kind(&result, ast.statement_decl(&tree, declaration).initializer, .float)
}
try expect_kind(&result, ast.package_decl(&tree, 6), .int)
try expect_kind(&result, ast.statement_decl(&tree, ast.package_decl(&tree, 6)).initializer, .int)
try expect_kind(&result, ast.package_decl(&tree, 7), .int)
numeric :: ast.package_decl(&tree, 8)
try expect_kind(&result, numeric, .int)
try expect_kind(&result, ast.statement_decl(&tree, numeric).initializer, .int)
for usize(9)..usize(12) |index| {
declaration :: ast.package_decl(&tree, index)
try expect_kind(&result, declaration, .float)
try expect_kind(&result, ast.statement_decl(&tree, declaration).initializer, .float)
}
try testing.expect_equal(2, diagnostics.count(&diagnostic_store))
}
preserves_existing_syntax_name_and_duplicate_ids_while_checking_siblings test {
strpool.strings = strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings)
diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store)
source ::
`@ broken
`main proc(a, b Missing) void {
` empty :: ()
` bad :: absent + -"sibling"
` unknown(-"argument")
` valid float :: later
` later := 1
` discarded(1 2)
`}
`same :: 1
`same :: -"initializer"
scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store)
tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree)
resolution := try sema.resolve(mem.c_allocator, &tree, &diagnostic_store)
defer sema.deinit(&resolution)
previous_count :: diagnostics.count(&diagnostic_store)
function :: ast.package_decl(&tree, 1)
body :: ast.function_decl(&tree, function).body
invalid_decl :: ast.package_decl(&tree, 0)
empty_decl :: ast.block_statement(&tree, body, 0)
empty :: ast.statement_decl(&tree, empty_decl).initializer
invalid_statement :: ast.block_statement(&tree, body, 5)
bad_decl :: ast.block_statement(&tree, body, 1)
bad_expr :: ast.statement_decl(&tree, bad_decl).initializer
bad :: ast.binary_expr(&tree, bad_expr)
unknown_call :: ast.statement_expr(&tree, ast.block_statement(&tree, body, 2))
unknown_callee :: ast.call_expr(&tree, unknown_call).callee
duplicate :: ast.package_decl(&tree, 3)
a :: ast.function_param(&tree, function, 0)
b :: ast.function_param(&tree, function, 1)
shared_type :: ast.param(&tree, a).type
try testing.expect_equal(shared_type, ast.param(&tree, b).type)
result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store)
defer deinit(&result)
syntax_nodes [3]ast.NodeId :: [invalid_decl, empty, invalid_statement]
for syntax_nodes |id| {
try testing.expect_equal(TypeKind.invalid, type_of(&result, id).kind)
try testing.expect_equal(ast.invalid(&tree, id).diagnostic, type_of(&result, id).diagnostic)
}
try testing.expect_equal(type_of(&result, empty).diagnostic, type_of(&result, empty_decl).diagnostic)
resolved_invalid [4]ast.NodeId :: [shared_type, bad.left, unknown_callee, duplicate]
for resolved_invalid |id| {
try testing.expect_equal(sema.BindingKind.invalid, sema.binding(&resolution, id).kind)
try testing.expect_equal(TypeKind.invalid, type_of(&result, id).kind)
try testing.expect_equal(sema.binding(&resolution, id).data.diagnostic, type_of(&result, id).diagnostic)
}
try testing.expect_equal(type_of(&result, shared_type).diagnostic, type_of(&result, a).diagnostic)
try testing.expect_equal(type_of(&result, shared_type).diagnostic, type_of(&result, b).diagnostic)
try testing.expect_equal(type_of(&result, bad.left).diagnostic, type_of(&result, bad_expr).diagnostic)
try testing.expect_equal(type_of(&result, bad.left).diagnostic, type_of(&result, bad_decl).diagnostic)
try testing.expect_equal(type_of(&result, unknown_callee).diagnostic, type_of(&result, unknown_call).diagnostic)
_ = try expect_error(&result, &diagnostic_store, bad.right, .invalid_operand, ast.node_span(&tree, bad.right))
argument :: ast.call_arg(&tree, unknown_call, 0)
_ = try expect_error(&result, &diagnostic_store, argument, .invalid_operand, ast.node_span(&tree, argument))
duplicate_initializer :: ast.statement_decl(&tree, duplicate).initializer
_ = try expect_error(&result, &diagnostic_store, duplicate_initializer, .invalid_operand, ast.node_span(&tree, duplicate_initializer))
for usize(3)..usize(5) |index| {
declaration :: ast.block_statement(&tree, body, index)
try expect_kind(&result, declaration, .float)
try expect_kind(&result, ast.statement_decl(&tree, declaration).initializer, .float)
}
try expect_kind(&result, ast.package_decl(&tree, 2), .int)
try testing.expect_equal(3, code_count(&diagnostic_store, .unknown_name))
try testing.expect_equal(1, code_count(&diagnostic_store, .duplicate_name))
try testing.expect_equal(3, code_count(&diagnostic_store, .invalid_operand))
try testing.expect_equal(previous_count + 3, diagnostics.count(&diagnostic_store))
}
returns_supply_numeric_context_and_resolve_forward_local_and_parameter_names test {
strpool.strings = strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings)
diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store)
source ::
`fraction proc() float {
` return -later / 2
` later := seed
`}
`identity proc(value int) int { return value }
`wrapper proc() void { return noop() }
`noop proc() void { return }
`text proc() string { return "honey" }
`seed :: 3
scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store)
tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree)
resolution := try sema.resolve(mem.c_allocator, &tree, &diagnostic_store)
defer sema.deinit(&resolution)
result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store)
defer deinit(&result)
expected [5]TypeKind :: [.float, .int, .void, .void, .string]
for (expected) |kind, index| {
function :: ast.package_decl(&tree, index)
view :: ast.function_decl(&tree, function)
try expect_kind(&result, function, .function)
try expect_kind(&result, view.return_type, kind)
statement :: ast.block_statement(&tree, view.body, 0)
try expect_kind(&result, statement, .void)
if (ast.return_stmt(&tree, statement).value) |value| try expect_kind(&result, value, kind)
}
fraction :: ast.function_decl(&tree, ast.package_decl(&tree, 0))
local :: ast.block_statement(&tree, fraction.body, 1)
try expect_kind(&result, local, .float)
try expect_kind(&result, ast.package_decl(&tree, 5), .float)
if (ast.return_stmt(&tree, ast.block_statement(&tree, fraction.body, 0)).value) |value| {
division :: ast.binary_expr(&tree, value)
negate :: ast.unary_expr(&tree, division.left)
try expect_kind(&result, division.left, .float)
try expect_kind(&result, division.right, .float)
try testing.expect_equal(local, sema.binding(&resolution, negate.operand).data.declaration)
} else {
return .expectation_failed
}
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
}
try testing.expect_equal(0, diagnostics.count(&diagnostic_store))
}
return_errors_preserve_identity_check_unreachable_code_and_suppress_missing_return_cascades test {
strpool.strings = strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings)
diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store)
source ::
`bare proc() int { return }
`wrong proc() int { return "text" }
`void_value proc() void { return 1 }
`unknown proc() int { return absent }
`syntax proc() int { return (1 }
`unreachable proc() int {
` return 7
` -"bad"
`}
`fallthrough proc() int {}
`recovered proc() int {
` return 1 stray
` valid :: 2
`}
scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store)
tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree)
resolution := try sema.resolve(mem.c_allocator, &tree, &diagnostic_store)
defer sema.deinit(&resolution)
result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store)
defer deinit(&result)
for (usize(0)..usize(3)) |index| {
function :: ast.package_decl(&tree, index)
view :: ast.function_decl(&tree, function)
try expect_kind(&result, function, .function)
statement :: ast.block_statement(&tree, view.body, 0)
returned :: ast.return_stmt(&tree, statement)
span :: if (returned.value) |value| ast.node_span(&tree, value) else returned.span
diagnostic :: try expect_error(&result, &diagnostic_store, statement, .return_type_mismatch, span)
if (returned.value) |value| try testing.expect_equal(diagnostic, type_of(&result, value).diagnostic)
}
for (usize(3)..usize(5)) |index| {
view :: ast.function_decl(&tree, ast.package_decl(&tree, index))
statement :: ast.block_statement(&tree, view.body, 0)
if (ast.return_stmt(&tree, statement).value) |value| {
original :: type_of(&result, value).diagnostic
try testing.expect(original != diagnostics.no_diagnostic)
try testing.expect_equal(original, type_of(&result, statement).diagnostic)
try testing.expect_equal(original, type_of(&result, view.body).diagnostic)
} else {
return .expectation_failed
}
}
unreachable_body :: ast.function_decl(&tree, ast.package_decl(&tree, 5)).body
try expect_kind(&result, ast.block_statement(&tree, unreachable_body, 0), .void)
bad :: ast.statement_expr(&tree, ast.block_statement(&tree, unreachable_body, 1))
_ = try expect_error(&result, &diagnostic_store, bad, .invalid_operand, ast.node_span(&tree, bad))
missing_body :: ast.function_decl(&tree, ast.package_decl(&tree, 6)).body
_ = try expect_error(&result, &diagnostic_store, missing_body, .missing_return, ast.node_span(&tree, missing_body))
recovered_body :: ast.function_decl(&tree, ast.package_decl(&tree, 7)).body
try expect_kind(&result, ast.block_statement(&tree, recovered_body, 1), .int)
try testing.expect_equal(3, code_count(&diagnostic_store, .return_type_mismatch))
try testing.expect_equal(1, code_count(&diagnostic_store, .missing_return))
try testing.expect_equal(1, code_count(&diagnostic_store, .unknown_name))
try testing.expect_equal(2, code_count(&diagnostic_store, .unexpected_token))
try testing.expect_equal(8, diagnostics.count(&diagnostic_store))
}
checks_bool_comparisons_recursive_returns_and_nested_numeric_context test {
strpool.strings = strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings)
diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store)
source ::
`flag bool :: true
`same :: flag == false
`different :: true != flag
`less :: alias < 4.0
`less_equal :: seed <= 5
`greater :: 6 > seed
`greater_equal :: seed >= 1
`equal :: seed == 3
`not_equal :: seed != 2
`alias :: seed
`seed := 3
`identity proc(value bool) bool { return value }
`factorial proc(n int) int {
` if n <= 1 { return 1 }
` return n * factorial(n - 1)
`}
`fraction proc(value bool) float {
` if value {
` before :: later
` later := 3
` return -before / 2
` } else if false {
` later :: 4
` return later
` } else {
` return 5
` }
`}
`main proc() bool { return identity(flag) }
scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store)
tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree)
resolution := try sema.resolve(mem.c_allocator, &tree, &diagnostic_store)
defer sema.deinit(&resolution)
result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store)
defer deinit(&result)
for (0..usize(9)) |i| {
declaration :: ast.package_decl(&tree, i)
try expect_kind(&result, declaration, .bool)
try expect_kind(&result, ast.statement_decl(&tree, declaration).initializer, .bool)
if (i < 3) continue
comparison_view :: ast.binary_expr(&tree, ast.statement_decl(&tree, declaration).initializer)
try expect_kind(&result, comparison_view.left, .float)
try expect_kind(&result, comparison_view.right, .float)
}
for (usize(9)..usize(11)) |i| try expect_kind(&result, ast.package_decl(&tree, i), .float)
identity :: ast.package_decl(&tree, 11)
try expect_kind(&result, ast.param(&tree, ast.function_param(&tree, identity, 0)).type, .bool)
try expect_kind(&result, ast.function_decl(&tree, identity).return_type, .bool)
factorial :: ast.package_decl(&tree, 12)
factorial_view :: ast.function_decl(&tree, factorial)
try expect_kind(&result, factorial, .function)
conditional :: ast.if_stmt(&tree, ast.block_statement(&tree, factorial_view.body, 0))
try expect_kind(&result, conditional.condition, .bool)
operands :: ast.binary_expr(&tree, conditional.condition)
try expect_kind(&result, operands.left, .int)
try expect_kind(&result, operands.right, .int)
fraction :: ast.function_decl(&tree, ast.package_decl(&tree, 13))
arms :: ast.if_stmt(&tree, ast.block_statement(&tree, fraction.body, 0))
for (0..usize(2)) |i| {
declaration :: ast.block_statement(&tree, arms.then_block, i)
try expect_kind(&result, declaration, .float)
try expect_kind(&result, ast.statement_decl(&tree, declaration).initializer, .float)
}
if (arms.else_block) |other| {
nested :: ast.if_stmt(&tree, ast.block_statement(&tree, other, 0))
try expect_kind(&result, ast.block_statement(&tree, nested.then_block, 0), .float)
} else {
return .expectation_failed
}
try testing.expect_equal(0, diagnostics.count(&diagnostic_store))
}
rejects_bool_numeric_mixing_and_preserves_invalid_condition_identity test {
strpool.strings = strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings)
diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store)
source ::
`sum :: true + false
`order :: true < false
`mixed :: true == 1
`strings :: "x" == "x"
`wrong_bool bool :: 1
`wrong_int int :: false
`main proc() void {
` if (1) { okay :: true }
` if "text" { okay :: false }
` if absent { okay :: true }
` if true + false { okay :: false }
` if true { return } else { -"bad" }
`}
scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store)
tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree)
resolution := try sema.resolve(mem.c_allocator, &tree, &diagnostic_store)
defer sema.deinit(&resolution)
result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store)
defer deinit(&result)
for (0..usize(4)) |i| {
expression :: ast.statement_decl(&tree, ast.package_decl(&tree, i)).initializer
_ = try expect_error(&result, &diagnostic_store, expression, .invalid_operand, ast.node_span(&tree, expression))
}
for (usize(4)..usize(6)) |i| {
expression :: ast.statement_decl(&tree, ast.package_decl(&tree, i)).initializer
_ = try expect_error(&result, &diagnostic_store, expression, .type_mismatch, ast.node_span(&tree, expression))
}
function :: ast.package_decl(&tree, 6)
body :: ast.function_decl(&tree, function).body
for (0..usize(2)) |i| {
statement :: ast.block_statement(&tree, body, i)
condition :: ast.if_stmt(&tree, statement).condition
diagnostic :: try expect_error(&result, &diagnostic_store, condition, .condition_not_bool, ast.node_span(&tree, condition))
try testing.expect_equal(diagnostic, type_of(&result, statement).diagnostic)
}
unknown :: ast.if_stmt(&tree, ast.block_statement(&tree, body, 2)).condition
original :: sema.binding(&resolution, unknown).data.diagnostic
try testing.expect_equal(original, type_of(&result, unknown).diagnostic)
failed :: ast.if_stmt(&tree, ast.block_statement(&tree, body, 3)).condition
_ = try expect_error(&result, &diagnostic_store, failed, .invalid_operand, ast.node_span(&tree, failed))
final_if :: ast.block_statement(&tree, body, 4)
try expect_kind(&result, final_if, .void)
final_view :: ast.if_stmt(&tree, final_if)
if (final_view.else_block) |other| {
error_expr :: ast.statement_expr(&tree, ast.block_statement(&tree, other, 0))
_ = try expect_error(&result, &diagnostic_store, error_expr, .invalid_operand, ast.node_span(&tree, error_expr))
} else {
return .expectation_failed
}
try expect_kind(&result, function, .function)
try testing.expect_equal(2, code_count(&diagnostic_store, .condition_not_bool))
try testing.expect_equal(6, code_count(&diagnostic_store, .invalid_operand))
try testing.expect_equal(2, code_count(&diagnostic_store, .type_mismatch))
try testing.expect_equal(1, code_count(&diagnostic_store, .unknown_name))
try testing.expect_equal(11, diagnostics.count(&diagnostic_store))
}
analyzes_return_paths_after_trap_propagation_without_poisoning_signatures test {
strpool.strings = strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings)
diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store)
source ::
`both proc(flag bool) int {
` if flag { return 1 } else { return 2 }
`}
`missing proc() int { if true { return 1 } }
`bad_arm proc(flag bool) int { if flag { -"bad" } }
`trap_and_return proc(flag bool) int {
` if flag { return 1 } else { bad int :: false }
`}
`invalid_condition proc() int { if 1 {} }
`invalid_expression proc() int { -"bad" }
`invalid_declaration proc() int { bad bool :: 1 }
`syntax_trap proc() int {
` return 1 stray
`}
`unreachable proc() int {
` return 1
` -"bad"
`}
`nested proc(a bool, b bool) int {
` if a { if b { return 1 } else { return 2 } } else { return 3 }
`}
`join proc(flag bool) int {
` if flag {} else { return 1 }
` return 2
`}
`answer :: bad_arm(false)
`wrong_return proc(flag bool) int {
` if flag { return false } else { return 1 }
`}
scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store)
tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree)
resolution := try sema.resolve(mem.c_allocator, &tree, &diagnostic_store)
defer sema.deinit(&resolution)
result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store)
defer deinit(&result)
for (0..usize(11)) |i| try expect_kind(&result, ast.package_decl(&tree, i), .function)
for (usize(1)..usize(3)) |i| {
body :: ast.function_decl(&tree, ast.package_decl(&tree, i)).body
_ = try expect_error(&result, &diagnostic_store, body, .missing_return, ast.node_span(&tree, body))
}
bad_body :: ast.function_decl(&tree, ast.package_decl(&tree, 2)).body
conditional :: ast.block_statement(&tree, bad_body, 0)
try expect_kind(&result, conditional, .void)
arm :: ast.if_stmt(&tree, conditional).then_block
operand :: ast.statement_expr(&tree, ast.block_statement(&tree, arm, 0))
original :: try expect_error(&result, &diagnostic_store, operand, .invalid_operand, ast.node_span(&tree, operand))
try testing.expect_equal(original, type_of(&result, arm).diagnostic)
try testing.expect(type_of(&result, bad_body).diagnostic != original)
condition_body :: ast.function_decl(&tree, ast.package_decl(&tree, 4)).body
condition :: ast.if_stmt(&tree, ast.block_statement(&tree, condition_body, 0)).condition
condition_diag :: try expect_error(&result, &diagnostic_store, condition, .condition_not_bool, ast.node_span(&tree, condition))
try testing.expect_equal(condition_diag, type_of(&result, condition_body).diagnostic)
recovered :: ast.function_decl(&tree, ast.package_decl(&tree, 7)).body
syntax :: ast.block_statement(&tree, recovered, 0)
try testing.expect_equal(ast.invalid(&tree, syntax).diagnostic, type_of(&result, recovered).diagnostic)
unreachable_body :: ast.function_decl(&tree, ast.package_decl(&tree, 8)).body
checked_after_return :: ast.statement_expr(&tree, ast.block_statement(&tree, unreachable_body, 1))
_ = try expect_error(&result, &diagnostic_store, checked_after_return, .invalid_operand, ast.node_span(&tree, checked_after_return))
try expect_kind(&result, ast.package_decl(&tree, 11), .int)
wrong_function :: ast.package_decl(&tree, 12)
try expect_kind(&result, wrong_function, .function)
wrong_body :: ast.function_decl(&tree, wrong_function).body
wrong_if :: ast.block_statement(&tree, wrong_body, 0)
try expect_kind(&result, wrong_if, .void)
wrong_arm :: ast.if_stmt(&tree, wrong_if).then_block
wrong_statement :: ast.block_statement(&tree, wrong_arm, 0)
if (ast.return_stmt(&tree, wrong_statement).value) |value| {
mismatch :: try expect_error(&result, &diagnostic_store, value, .return_type_mismatch, ast.node_span(&tree, value))
try testing.expect_equal(mismatch, type_of(&result, wrong_statement).diagnostic)
try testing.expect_equal(mismatch, type_of(&result, wrong_body).diagnostic)
} else {
return .expectation_failed
}
try testing.expect_equal(1, code_count(&diagnostic_store, .return_type_mismatch))
try testing.expect_equal(2, code_count(&diagnostic_store, .missing_return))
try testing.expect_equal(1, code_count(&diagnostic_store, .condition_not_bool))
}
assignments_preserve_established_types_and_contextualize_numeric_values test {
strpool.strings = strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings)
diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store)
source ::
`take_float proc(value float) int { return 1 }
`main proc() void {
` number := 0
` number = 1.5
` number = number + 1.5
` number = take_float(number)
` observed :: number
` quotient float := 0
` quotient = 3 / 2
` inferred := 0
` context float :: inferred
` inferred = 5 / 2
` rigid int := 1
` quotient = rigid
` flag := true
` flag = false
` text := "before"
` text = "after"
`}
scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store)
tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree)
resolution := try sema.resolve(mem.c_allocator, &tree, &diagnostic_store)
defer sema.deinit(&resolution)
try testing.expect_equal(0, diagnostics.count(&diagnostic_store))
result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store)
defer deinit(&result)
body :: ast.function_decl(&tree, ast.package_decl(&tree, 1)).body
number :: ast.block_statement(&tree, body, 0)
observed :: ast.block_statement(&tree, body, 4)
quotient :: ast.block_statement(&tree, body, 5)
inferred :: ast.block_statement(&tree, body, 7)
rigid :: ast.block_statement(&tree, body, 10)
declarations [3]ast.NodeId :: [number, observed, rigid]
for declarations |id| {
try expect_kind(&result, id, .int)
try expect_kind(&result, ast.statement_decl(&tree, id).initializer, .int)
}
try expect_kind(&result, quotient, .float)
try expect_kind(&result, inferred, .float)
bad_values [2]usize :: [1, 11]
for bad_values |index| {
statement :: ast.block_statement(&tree, body, index)
assignment :: ast.assignment_stmt(&tree, statement)
original :: try expect_error(&result, &diagnostic_store, assignment.value, .type_mismatch, ast.node_span(&tree, assignment.value))
try testing.expect_equal(original, type_of(&result, statement).diagnostic)
expected :: if (index == 1) TypeKind.int else TypeKind.float
try expect_kind(&result, assignment.target, expected)
}
arithmetic_statement :: ast.block_statement(&tree, body, 2)
arithmetic :: ast.assignment_stmt(&tree, arithmetic_statement)
original :: try expect_error(&result, &diagnostic_store, arithmetic.value, .invalid_operand, ast.node_span(&tree, arithmetic.value))
try testing.expect_equal(original, type_of(&result, arithmetic_statement).diagnostic)
try expect_kind(&result, arithmetic.target, .int)
try expect_kind(&result, ast.binary_expr(&tree, arithmetic.value).left, .int)
call_statement :: ast.block_statement(&tree, body, 3)
call :: ast.assignment_stmt(&tree, call_statement)
argument :: ast.call_arg(&tree, call.value, 0)
call_error :: try expect_error(&result, &diagnostic_store, argument, .type_mismatch, ast.node_span(&tree, argument))
try testing.expect_equal(call_error, type_of(&result, call_statement).diagnostic)
try expect_kind(&result, call.target, .int)
divisions [2]usize :: [6, 9]
for divisions |index| {
statement :: ast.block_statement(&tree, body, index)
assignment :: ast.assignment_stmt(&tree, statement)
division :: ast.binary_expr(&tree, assignment.value)
try expect_kind(&result, statement, .void)
try expect_kind(&result, assignment.target, .float)
try expect_kind(&result, assignment.value, .float)
try expect_kind(&result, division.left, .float)
try expect_kind(&result, division.right, .float)
}
scalar_assignments [2]usize :: [13, 15]
for scalar_assignments |index| {
statement :: ast.block_statement(&tree, body, index)
assignment :: ast.assignment_stmt(&tree, statement)
expected :: if (index == 13) TypeKind.bool else TypeKind.string
try expect_kind(&result, statement, .void)
try expect_kind(&result, assignment.target, expected)
try expect_kind(&result, assignment.value, expected)
}
try testing.expect_equal(3, code_count(&diagnostic_store, .type_mismatch))
try testing.expect_equal(1, code_count(&diagnostic_store, .invalid_operand))
try testing.expect_equal(0, code_count(&diagnostic_store, .integer_division))
}
assignment_target_errors_preserve_bindings_and_independent_rhs_diagnostics test {
strpool.strings = strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings)
diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store)
source ::
`global := 1
`named proc() void {}
`main proc(parameter int) void {
` constant :: 1
` constant = 2
` parameter = 2
` global = 2
` named = named
` int = 2
` absent = other_missing
` constant = -false
`}
scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store)
tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree)
resolution := try sema.resolve(mem.c_allocator, &tree, &diagnostic_store)
defer sema.deinit(&resolution)
result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store)
defer deinit(&result)
function :: ast.package_decl(&tree, 2)
body :: ast.function_decl(&tree, function).body
for usize(1)..usize(6) |index| {
statement :: ast.block_statement(&tree, body, index)
assignment :: ast.assignment_stmt(&tree, statement)
_ = try expect_error(&result, &diagnostic_store, statement, .invalid_assignment_target, ast.node_span(&tree, assignment.target))
expected :: if (index == 4) TypeKind.function else TypeKind.int
try expect_kind(&result, assignment.value, expected)
if (index != 5) try testing.expect(type_of(&result, assignment.target).kind != .invalid)
}
unknown_statement :: ast.block_statement(&tree, body, 6)
unknown :: ast.assignment_stmt(&tree, unknown_statement)
target_error :: sema.binding(&resolution, unknown.target).data.diagnostic
value_error :: sema.binding(&resolution, unknown.value).data.diagnostic
try testing.expect(target_error != value_error)
try testing.expect_equal(target_error, type_of(&result, unknown.target).diagnostic)
try testing.expect_equal(target_error, type_of(&result, unknown_statement).diagnostic)
try testing.expect_equal(value_error, type_of(&result, unknown.value).diagnostic)
independent_statement :: ast.block_statement(&tree, body, 7)
independent :: ast.assignment_stmt(&tree, independent_statement)
_ = try expect_error(&result, &diagnostic_store, independent.value, .invalid_operand, ast.node_span(&tree, independent.value))
_ = try expect_error(&result, &diagnostic_store, independent_statement, .invalid_assignment_target, ast.node_span(&tree, independent.target))
try expect_kind(&result, ast.block_statement(&tree, body, 0), .int)
try expect_kind(&result, ast.function_param(&tree, function, 0), .int)
try expect_kind(&result, ast.package_decl(&tree, 0), .int)
try expect_kind(&result, ast.package_decl(&tree, 1), .function)
try testing.expect_equal(6, code_count(&diagnostic_store, .invalid_assignment_target))
try testing.expect_equal(2, code_count(&diagnostic_store, .unknown_name))
try testing.expect_equal(0, code_count(&diagnostic_store, .type_not_value))
}
function_assignment_checks_complete_signatures_without_retargeting_calls test {
strpool.strings = strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings)
diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store)
source ::
`first proc(a int) int { return a }
`same proc(renamed int) int { return renamed + 1 }
`wrong_parameter proc(a float) int { return 1 }
`wrong_result proc(a int) bool { return true }
`wrong_count proc() int { return 2 }
`main proc() int {
` chosen := first
` chosen = same
` chosen = wrong_parameter
` chosen = wrong_result
` chosen = wrong_count
` answer :: chosen(7)
` return answer
`}
scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store)
tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree)
resolution := try sema.resolve(mem.c_allocator, &tree, &diagnostic_store)
defer sema.deinit(&resolution)
try testing.expect_equal(0, diagnostics.count(&diagnostic_store))
result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store)
defer deinit(&result)
first :: ast.package_decl(&tree, 0)
body :: ast.function_decl(&tree, ast.package_decl(&tree, 5)).body
chosen :: ast.block_statement(&tree, body, 0)
try expect_kind(&result, chosen, .function)
try testing.expect_equal(first, type_of(&result, chosen).function)
try expect_kind(&result, ast.block_statement(&tree, body, 1), .void)
for usize(2)..usize(5) |index| {
statement :: ast.block_statement(&tree, body, index)
assignment :: ast.assignment_stmt(&tree, statement)
original :: try expect_error(&result, &diagnostic_store, assignment.value, .type_mismatch, ast.node_span(&tree, assignment.value))
try testing.expect_equal(original, type_of(&result, statement).diagnostic)
try expect_kind(&result, assignment.target, .function)
try testing.expect_equal(first, type_of(&result, assignment.target).function)
}
answer :: ast.block_statement(&tree, body, 5)
call :: ast.statement_decl(&tree, answer).initializer
try expect_kind(&result, answer, .int)
try expect_kind(&result, call, .int)
try testing.expect_equal(first, type_of(&result, ast.call_expr(&tree, call).callee).function)
try testing.expect_equal(3, code_count(&diagnostic_store, .type_mismatch))
try testing.expect_equal(0, code_count(&diagnostic_store, .argument_count_mismatch))
}
while_checks_nested_bodies_without_claiming_a_zero_iteration_return test {
strpool.strings = strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings)
diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store)
source ::
`missing proc() int { while true { return 1 } }
`bad_body proc() int { while false { -"bad" } }
`continues proc() int {
` while false { -"bad" }
` return 7
`}
`bad_condition proc() int { while 1 { -"bad" } }
`unknown_condition proc() int { while absent { missing_body } }
`nested proc() void {
` counter := 2
` while counter > 0 {
` inner := 1
` while inner > 0 {
` inner = inner - 1
` counter = counter - 1
` }
` }
`}
scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store)
tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree)
resolution := try sema.resolve(mem.c_allocator, &tree, &diagnostic_store)
defer sema.deinit(&resolution)
result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store)
defer deinit(&result)
for usize(0)..usize(2) |index| {
body :: ast.function_decl(&tree, ast.package_decl(&tree, index)).body
_ = try expect_error(&result, &diagnostic_store, body, .missing_return, ast.node_span(&tree, body))
try expect_kind(&result, ast.block_statement(&tree, body, 0), .void)
}
continues_body :: ast.function_decl(&tree, ast.package_decl(&tree, 2)).body
loop_statement :: ast.block_statement(&tree, continues_body, 0)
loop :: ast.while_stmt(&tree, loop_statement)
invalid_body :: ast.statement_expr(&tree, ast.block_statement(&tree, loop.body, 0))
original :: try expect_error(&result, &diagnostic_store, invalid_body, .invalid_operand, ast.node_span(&tree, invalid_body))
try testing.expect_equal(original, type_of(&result, loop.body).diagnostic)
try testing.expect_equal(original, type_of(&result, continues_body).diagnostic)
try expect_kind(&result, loop_statement, .void)
try expect_kind(&result, ast.block_statement(&tree, continues_body, 1), .void)
bad_condition_body :: ast.function_decl(&tree, ast.package_decl(&tree, 3)).body
bad_condition_statement :: ast.block_statement(&tree, bad_condition_body, 0)
condition :: ast.while_stmt(&tree, bad_condition_statement).condition
condition_error :: try expect_error(&result, &diagnostic_store, condition, .condition_not_bool, ast.node_span(&tree, condition))
try testing.expect_equal(condition_error, type_of(&result, bad_condition_statement).diagnostic)
unknown_body :: ast.function_decl(&tree, ast.package_decl(&tree, 4)).body
unknown_statement :: ast.block_statement(&tree, unknown_body, 0)
unknown :: ast.while_stmt(&tree, unknown_statement).condition
try testing.expect_equal(sema.binding(&resolution, unknown).data.diagnostic, type_of(&result, unknown_statement).diagnostic)
nested_body :: ast.function_decl(&tree, ast.package_decl(&tree, 5)).body
try expect_kind(&result, nested_body, .none)
outer_loop :: ast.while_stmt(&tree, ast.block_statement(&tree, nested_body, 1))
inner_loop :: ast.while_stmt(&tree, ast.block_statement(&tree, outer_loop.body, 1))
for usize(0)..usize(2) |index| {
statement :: ast.block_statement(&tree, inner_loop.body, index)
assignment :: ast.assignment_stmt(&tree, statement)
try expect_kind(&result, statement, .void)
try expect_kind(&result, assignment.target, .int)
try expect_kind(&result, assignment.value, .int)
}
try testing.expect_equal(2, code_count(&diagnostic_store, .missing_return))
try testing.expect_equal(1, code_count(&diagnostic_store, .condition_not_bool))
try testing.expect_equal(3, code_count(&diagnostic_store, .invalid_operand))
}
logical_types_preserve_comparison_context_and_assignment_isolation test {
strpool.strings = strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings)
diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store)
source ::
`seed :: 3
`forward :: seed
`composed :: !(forward < 4.0) and true or false
`equal :: !(true and false) == (true or false)
`nested :: !!!true
`identity proc(value bool) bool { return value }
`main proc() bool {
` count := 1
` flag := true
` flag = !(count < 2) and identity(true) or false
` return flag
`}
scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store)
tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree)
resolution := try sema.resolve(mem.c_allocator, &tree, &diagnostic_store)
defer sema.deinit(&resolution)
result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store)
defer deinit(&result)
for (usize(0)..usize(2)) |index| try expect_kind(&result, ast.package_decl(&tree, index), .float)
for usize(2)..usize(5) |index| {
declaration :: ast.package_decl(&tree, index)
try expect_kind(&result, declaration, .bool)
try expect_kind(&result, ast.statement_decl(&tree, declaration).initializer, .bool)
}
composed :: ast.binary_expr(&tree, ast.statement_decl(&tree, ast.package_decl(&tree, 2)).initializer)
try testing.expect_equal(ast.BinaryOperator.logical_or, composed.operator)
conjunction :: ast.binary_expr(&tree, composed.left)
try testing.expect_equal(ast.BinaryOperator.logical_and, conjunction.operator)
inverted :: ast.unary_expr(&tree, conjunction.left)
try expect_kind(&result, conjunction.left, .bool)
comparison_view :: ast.binary_expr(&tree, inverted.operand)
try expect_kind(&result, comparison_view.left, .float)
try expect_kind(&result, comparison_view.right, .float)
nested := ast.statement_decl(&tree, ast.package_decl(&tree, 4)).initializer
for 0..3 |_| {
try expect_kind(&result, nested, .bool)
view :: ast.unary_expr(&tree, nested)
try testing.expect_equal(ast.UnaryOperator.logical_not, view.operator)
nested = view.operand
}
try expect_kind(&result, nested, .bool)
body :: ast.function_decl(&tree, ast.package_decl(&tree, 6)).body
count :: ast.block_statement(&tree, body, 0)
try expect_kind(&result, count, .int)
assignment :: ast.assignment_stmt(&tree, ast.block_statement(&tree, body, 2))
try expect_kind(&result, assignment.value, .bool)
disjunction :: ast.binary_expr(&tree, assignment.value)
assignment_conjunction :: ast.binary_expr(&tree, disjunction.left)
assignment_not :: ast.unary_expr(&tree, assignment_conjunction.left)
assignment_comparison :: ast.binary_expr(&tree, assignment_not.operand)
try expect_kind(&result, assignment_comparison.left, .int)
try expect_kind(&result, assignment_comparison.right, .int)
try testing.expect_equal(0, diagnostics.count(&diagnostic_store))
}
logical_operand_errors_are_independent_and_skipped_rhs_preserves_bool test {
strpool.strings = strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings)
diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store)
source ::
`number :: 1
`number_alias :: number
`skipped_unknown :: false and absent
`skipped_kind :: true or number
`both_bad :: 1 and "wrong"
`demanded_unknown :: missing or true
`demanded_not :: !number_alias
`nested :: !(false and absent_nested) == true
`independent :: left_missing and 1
`identity proc(value bool) bool { return value }
`skips proc() bool {
` local :: true or 1
` value := false
` value = false and absent_assignment
` identity(true or absent_argument)
` if true or 1 { value = true }
` while false and 1 { value = false }
` return false and absent_return
`}
scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store)
tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree)
resolution := try sema.resolve(mem.c_allocator, &tree, &diagnostic_store)
defer sema.deinit(&resolution)
result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store)
defer deinit(&result)
for usize(0)..usize(2) |index| {
declaration :: ast.package_decl(&tree, index)
try expect_kind(&result, declaration, .int)
try expect_kind(&result, ast.statement_decl(&tree, declaration).initializer, .int)
}
skipped_indices [3]usize :: [2, 3, 7]
for skipped_indices |index| {
declaration :: ast.package_decl(&tree, index)
try expect_kind(&result, declaration, .bool)
try expect_kind(&result, ast.statement_decl(&tree, declaration).initializer, .bool)
}
unknown :: ast.binary_expr(&tree, ast.statement_decl(&tree, ast.package_decl(&tree, 2)).initializer)
try testing.expect_equal(sema.binding(&resolution, unknown.right).data.diagnostic, type_of(&result, unknown.right).diagnostic)
_ = try expect_error(&result, &diagnostic_store, unknown.right, .unknown_name, ast.node_span(&tree, unknown.right))
wrong :: ast.binary_expr(&tree, ast.statement_decl(&tree, ast.package_decl(&tree, 3)).initializer)
_ = try expect_error(&result, &diagnostic_store, wrong.right, .invalid_operand, ast.node_span(&tree, wrong.right))
both_declaration :: ast.package_decl(&tree, 4)
both_id :: ast.statement_decl(&tree, both_declaration).initializer
both :: ast.binary_expr(&tree, both_id)
left_error :: try expect_error(&result, &diagnostic_store, both.left, .invalid_operand, ast.node_span(&tree, both.left))
right_error :: try expect_error(&result, &diagnostic_store, both.right, .invalid_operand, ast.node_span(&tree, both.right))
try testing.expect(left_error != right_error)
try testing.expect_equal(left_error, type_of(&result, both_id).diagnostic)
try testing.expect_equal(left_error, type_of(&result, both_declaration).diagnostic)
demanded_id :: ast.statement_decl(&tree, ast.package_decl(&tree, 5)).initializer
demanded :: ast.binary_expr(&tree, demanded_id)
try testing.expect_equal(sema.binding(&resolution, demanded.left).data.diagnostic, type_of(&result, demanded_id).diagnostic)
not_id :: ast.statement_decl(&tree, ast.package_decl(&tree, 6)).initializer
not_view :: ast.unary_expr(&tree, not_id)
not_error :: try expect_error(&result, &diagnostic_store, not_view.operand, .invalid_operand, ast.node_span(&tree, not_view.operand))
try testing.expect_equal(not_error, type_of(&result, not_id).diagnostic)
independent_id :: ast.statement_decl(&tree, ast.package_decl(&tree, 8)).initializer
independent :: ast.binary_expr(&tree, independent_id)
try testing.expect_equal(sema.binding(&resolution, independent.left).data.diagnostic, type_of(&result, independent_id).diagnostic)
_ = try expect_error(&result, &diagnostic_store, independent.right, .invalid_operand, ast.node_span(&tree, independent.right))
body :: ast.function_decl(&tree, ast.package_decl(&tree, 10)).body
try expect_kind(&result, body, .none)
try expect_kind(&result, ast.block_statement(&tree, body, 0), .bool)
assignment_id :: ast.block_statement(&tree, body, 2)
assignment :: ast.assignment_stmt(&tree, assignment_id)
try expect_kind(&result, assignment_id, .void)
try expect_kind(&result, assignment.value, .bool)
call :: ast.statement_expr(&tree, ast.block_statement(&tree, body, 3))
try expect_kind(&result, call, .bool)
try expect_kind(&result, ast.call_arg(&tree, call, 0), .bool)
conditional :: ast.if_stmt(&tree, ast.block_statement(&tree, body, 4))
loop :: ast.while_stmt(&tree, ast.block_statement(&tree, body, 5))
try expect_kind(&result, conditional.condition, .bool)
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
}
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))
}
logical_external_mismatches_and_always_demanded_not_preserve_errors test {
strpool.strings = strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings)
diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store)
source ::
`accept_bool proc(value bool) bool { return value }
`accept_int proc(value int) bool { return true }
`noop proc() void {}
`wrong_decl int :: true or 1
`wrong_return proc() int { return false and absent_return }
`wrong_argument proc() bool { return accept_int(false and absent_argument) }
`wrong_assignment proc() void {
` count := 1
` count = true or 1
`}
`unary_bad proc() void {
` !1.0
` !"text"
` !accept_bool
` !noop()
`}
scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store)
tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree)
resolution := try sema.resolve(mem.c_allocator, &tree, &diagnostic_store)
defer sema.deinit(&resolution)
result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store)
defer deinit(&result)
declaration :: ast.package_decl(&tree, 3)
initializer :: ast.statement_decl(&tree, declaration).initializer
declaration_error :: try expect_error(&result, &diagnostic_store, initializer, .type_mismatch, ast.node_span(&tree, initializer))
try testing.expect_equal(declaration_error, type_of(&result, declaration).diagnostic)
return_body :: ast.function_decl(&tree, ast.package_decl(&tree, 4)).body
return_id :: ast.block_statement(&tree, return_body, 0)
if (ast.return_stmt(&tree, return_id).value) |value| {
return_error :: try expect_error(&result, &diagnostic_store, value, .return_type_mismatch, ast.node_span(&tree, value))
try testing.expect_equal(return_error, type_of(&result, return_id).diagnostic)
operand :: ast.binary_expr(&tree, value).right
try testing.expect_equal(sema.binding(&resolution, operand).data.diagnostic, type_of(&result, operand).diagnostic)
try testing.expect(return_error != type_of(&result, operand).diagnostic)
} else {
return .expectation_failed
}
argument_body :: ast.function_decl(&tree, ast.package_decl(&tree, 5)).body
argument_return :: ast.block_statement(&tree, argument_body, 0)
if (ast.return_stmt(&tree, argument_return).value) |call| {
argument :: ast.call_arg(&tree, call, 0)
argument_error :: try expect_error(&result, &diagnostic_store, argument, .type_mismatch, ast.node_span(&tree, argument))
try testing.expect_equal(argument_error, type_of(&result, call).diagnostic)
try testing.expect_equal(argument_error, type_of(&result, argument_return).diagnostic)
} else {
return .expectation_failed
}
assignment_body :: ast.function_decl(&tree, ast.package_decl(&tree, 6)).body
assignment_id :: ast.block_statement(&tree, assignment_body, 1)
assignment :: ast.assignment_stmt(&tree, assignment_id)
assignment_error :: try expect_error(&result, &diagnostic_store, assignment.value, .type_mismatch, ast.node_span(&tree, assignment.value))
try testing.expect_equal(assignment_error, type_of(&result, assignment_id).diagnostic)
try expect_kind(&result, assignment.target, .int)
try expect_kind(&result, ast.block_statement(&tree, assignment_body, 0), .int)
unary_body :: ast.function_decl(&tree, ast.package_decl(&tree, 7)).body
for usize(0)..usize(4) |index| {
expression :: ast.statement_expr(&tree, ast.block_statement(&tree, unary_body, index))
operand :: ast.unary_expr(&tree, expression).operand
original :: try expect_error(&result, &diagnostic_store, operand, .invalid_operand, ast.node_span(&tree, operand))
try testing.expect_equal(original, type_of(&result, expression).diagnostic)
}
for (usize(0)..usize(3)) |index| try expect_kind(&result, ast.package_decl(&tree, index), .function)
try testing.expect_equal(6, code_count(&diagnostic_store, .invalid_operand))
try testing.expect_equal(2, code_count(&diagnostic_store, .unknown_name))
try testing.expect_equal(3, code_count(&diagnostic_store, .type_mismatch))
try testing.expect_equal(1, code_count(&diagnostic_store, .return_type_mismatch))
try testing.expect_equal(12, diagnostics.count(&diagnostic_store))
}
short_circuit_skip_paths_do_not_guarantee_a_return test {
strpool.strings = strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings)
diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store)
source ::
`expression proc() int { false and missing_expression }
`declaration proc() int { local :: true or 1 }
`assignment proc() int {
` value := false
` value = false and missing_assignment
`}
`call proc() int { take(true or 1) }
`conditional proc() int { if false and missing_condition { return 1 } }
`loop proc() int { while true or missing_guard { return 1 } }
`loop_body proc() int { while true { true or 1 } }
`arms proc(flag bool) int {
` if flag { false and missing_arm } else { return 1 }
`}
`demanded proc() int { missing_left or true }
`successful proc() bool { return true or missing_return }
`take proc(value bool) void {}
scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store)
tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree)
resolution := try sema.resolve(mem.c_allocator, &tree, &diagnostic_store)
defer sema.deinit(&resolution)
result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store)
defer deinit(&result)
for usize(0)..usize(8) |index| {
function :: ast.package_decl(&tree, index)
try expect_kind(&result, function, .function)
body :: ast.function_decl(&tree, function).body
_ = try expect_error(&result, &diagnostic_store, body, .missing_return, ast.node_span(&tree, body))
}
expression_body :: ast.function_decl(&tree, ast.package_decl(&tree, 0)).body
expression :: ast.statement_expr(&tree, ast.block_statement(&tree, expression_body, 0))
try expect_kind(&result, expression, .bool)
loop_body :: ast.function_decl(&tree, ast.package_decl(&tree, 6)).body
loop_id :: ast.block_statement(&tree, loop_body, 0)
try expect_kind(&result, loop_id, .void)
demanded_body :: ast.function_decl(&tree, ast.package_decl(&tree, 8)).body
demanded :: ast.statement_expr(&tree, ast.block_statement(&tree, demanded_body, 0))
left :: ast.binary_expr(&tree, demanded).left
original :: sema.binding(&resolution, left).data.diagnostic
try testing.expect_equal(original, type_of(&result, demanded).diagnostic)
try testing.expect_equal(original, type_of(&result, demanded_body).diagnostic)
successful_body :: ast.function_decl(&tree, ast.package_decl(&tree, 9)).body
try expect_kind(&result, successful_body, .none)
try expect_kind(&result, ast.block_statement(&tree, successful_body, 0), .void)
try testing.expect_equal(8, code_count(&diagnostic_store, .missing_return))
try testing.expect_equal(7, code_count(&diagnostic_store, .unknown_name))
try testing.expect_equal(3, code_count(&diagnostic_store, .invalid_operand))
try testing.expect_equal(18, diagnostics.count(&diagnostic_store))
}
loop_controls_follow_lexical_bodies_and_check_unreachable_code test {
strpool.strings = strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings)
diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store)
source ::
`scopes proc(flag bool) void {
` while flag {
` if (flag) break else continue
` while flag {
` if (flag) continue else break
` }
` absent
` }
` break
` continue
`}
`caller proc() void {
` while true {
` callee()
` break
` }
`}
`callee proc() void { continue }
`dead proc() void {
` return
` break
` continue
`}
`arms proc() void {
` if (false) break else continue
`}
scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store)
tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree)
resolution := try sema.resolve(mem.c_allocator, &tree, &diagnostic_store)
defer sema.deinit(&resolution)
result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store)
defer deinit(&result)
scopes_body :: ast.function_decl(&tree, ast.package_decl(&tree, 0)).body
outer_statement :: ast.block_statement(&tree, scopes_body, 0)
outer :: ast.while_stmt(&tree, outer_statement)
outer_if :: ast.if_stmt(&tree, ast.block_statement(&tree, outer.body, 0))
inner_statement :: ast.block_statement(&tree, outer.body, 1)
inner :: ast.while_stmt(&tree, inner_statement)
inner_if :: ast.if_stmt(&tree, ast.block_statement(&tree, inner.body, 0))
caller_body :: ast.function_decl(&tree, ast.package_decl(&tree, 1)).body
caller_loop :: ast.while_stmt(&tree, ast.block_statement(&tree, caller_body, 0))
valid [5]ast.NodeId :: [
ast.block_statement(&tree, outer_if.then_block, 0),
ast.block_statement(&tree, outer_if.else_block?, 0),
ast.block_statement(&tree, inner_if.then_block, 0),
ast.block_statement(&tree, inner_if.else_block?, 0),
ast.block_statement(&tree, caller_loop.body, 1),
]
for (valid) |statement| try expect_kind(&result, statement, .void)
try expect_kind(&result, outer_statement, .void)
try expect_kind(&result, inner_statement, .void)
unknown :: ast.statement_expr(&tree, ast.block_statement(&tree, outer.body, 2))
original :: sema.binding(&resolution, unknown).data.diagnostic
try testing.expect_equal(original, type_of(&result, unknown).diagnostic)
outside_break :: ast.block_statement(&tree, scopes_body, 1)
outside_continue :: ast.block_statement(&tree, scopes_body, 2)
_ = try expect_error(&result, &diagnostic_store, outside_break, .break_outside_loop, ast.node_span(&tree, outside_break))
_ = try expect_error(&result, &diagnostic_store, outside_continue, .continue_outside_loop, ast.node_span(&tree, outside_continue))
callee_body :: ast.function_decl(&tree, ast.package_decl(&tree, 2)).body
callee_control :: ast.block_statement(&tree, callee_body, 0)
_ = try expect_error(&result, &diagnostic_store, callee_control, .continue_outside_loop, ast.node_span(&tree, callee_control))
dead_body :: ast.function_decl(&tree, ast.package_decl(&tree, 3)).body
dead_break :: ast.block_statement(&tree, dead_body, 1)
dead_continue :: ast.block_statement(&tree, dead_body, 2)
_ = try expect_error(&result, &diagnostic_store, dead_break, .break_outside_loop, ast.node_span(&tree, dead_break))
_ = try expect_error(&result, &diagnostic_store, dead_continue, .continue_outside_loop, ast.node_span(&tree, dead_continue))
arms_body :: ast.function_decl(&tree, ast.package_decl(&tree, 4)).body
arms :: ast.if_stmt(&tree, ast.block_statement(&tree, arms_body, 0))
arm_break :: ast.block_statement(&tree, arms.then_block, 0)
arm_continue :: ast.block_statement(&tree, arms.else_block?, 0)
_ = try expect_error(&result, &diagnostic_store, arm_break, .break_outside_loop, ast.node_span(&tree, arm_break))
_ = try expect_error(&result, &diagnostic_store, arm_continue, .continue_outside_loop, ast.node_span(&tree, arm_continue))
for (usize(0)..usize(5)) |index| try expect_kind(&result, ast.package_decl(&tree, index), .function)
try testing.expect_equal(3, code_count(&diagnostic_store, .break_outside_loop))
try testing.expect_equal(4, code_count(&diagnostic_store, .continue_outside_loop))
try testing.expect_equal(1, code_count(&diagnostic_store, .unknown_name))
try testing.expect_equal(8, diagnostics.count(&diagnostic_store))
}
loop_controls_do_not_guarantee_function_returns test {
strpool.strings = strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings)
diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store)
source ::
`breaking proc() int { while true { break } }
`continuing proc() int { while false { continue } }
`mixed proc(flag bool) int {
` while flag {
` if (flag) return 42 else break
` }
`}
`closed proc(flag bool) int {
` while flag {
` if (flag) break else continue
` -"bad"
` }
` return 42
`}
`invalid proc() int { break }
scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store)
tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree)
resolution := try sema.resolve(mem.c_allocator, &tree, &diagnostic_store)
defer sema.deinit(&resolution)
result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store)
defer deinit(&result)
for (usize(0)..usize(3)) |index| {
body :: ast.function_decl(&tree, ast.package_decl(&tree, index)).body
_ = try expect_error(&result, &diagnostic_store, body, .missing_return, ast.node_span(&tree, body))
try expect_kind(&result, ast.block_statement(&tree, body, 0), .void)
}
closed_body :: ast.function_decl(&tree, ast.package_decl(&tree, 3)).body
loop_statement :: ast.block_statement(&tree, closed_body, 0)
loop :: ast.while_stmt(&tree, loop_statement)
dead_expression :: ast.statement_expr(&tree, ast.block_statement(&tree, loop.body, 1))
original :: try expect_error(&result, &diagnostic_store, dead_expression, .invalid_operand, ast.node_span(&tree, dead_expression))
try testing.expect_equal(original, type_of(&result, closed_body).diagnostic)
try expect_kind(&result, loop_statement, .void)
try expect_kind(&result, ast.block_statement(&tree, closed_body, 1), .void)
invalid_body :: ast.function_decl(&tree, ast.package_decl(&tree, 4)).body
invalid_control :: ast.block_statement(&tree, invalid_body, 0)
control_error :: try expect_error(&result, &diagnostic_store, invalid_control, .break_outside_loop, ast.node_span(&tree, invalid_control))
try testing.expect_equal(control_error, type_of(&result, invalid_body).diagnostic)
for (usize(0)..usize(5)) |index| try expect_kind(&result, ast.package_decl(&tree, index), .function)
try testing.expect_equal(3, code_count(&diagnostic_store, .missing_return))
try testing.expect_equal(1, code_count(&diagnostic_store, .invalid_operand))
try testing.expect_equal(1, code_count(&diagnostic_store, .break_outside_loop))
try testing.expect_equal(5, diagnostics.count(&diagnostic_store))
}
range_bounds_receive_integer_context_without_joining_the_range_or_capture test {
strpool.strings = strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings)
diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store)
source ::
`limit :: seed
`seed := 3
`endpoint proc() int { return 4 }
`consume proc(value int) void {}
`main proc() void {
` for (-2)..(limit + 1) |item| {
` consume(item)
` copy :: item
` while false { consume(copy) }
` }
` for (0..=endpoint()) |_| consume(1)
` for 0..2 |item| { consume(item) }
`}
scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store)
tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree)
resolution := try sema.resolve(mem.c_allocator, &tree, &diagnostic_store)
defer sema.deinit(&resolution)
result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store)
defer deinit(&result)
try expect_kind(&result, ast.package_decl(&tree, 0), .int)
try expect_kind(&result, ast.package_decl(&tree, 1), .int)
main_body :: ast.function_decl(&tree, ast.package_decl(&tree, 4)).body
for (usize(0)..usize(3)) |index| {
statement :: ast.block_statement(&tree, main_body, index)
loop :: ast.for_stmt(&tree, statement)
range_view :: ast.range_expr(&tree, loop.iterable)
try expect_kind(&result, statement, .void)
try expect_kind(&result, loop.iterable, .range)
try expect_kind(&result, range_view.start, .int)
try expect_kind(&result, range_view.end, .int)
if (loop.capture) |capture| {
try expect_kind(&result, capture, .int)
try testing.expect_equal(capture, sema.binding(&resolution, capture).data.declaration)
call :: ast.statement_expr(&tree, ast.block_statement(&tree, loop.body, 0))
item :: ast.call_arg(&tree, call, 0)
try expect_kind(&result, item, .int)
try testing.expect_equal(capture, sema.binding(&resolution, item).data.declaration)
}
}
first :: ast.for_stmt(&tree, ast.block_statement(&tree, main_body, 0))
first_range :: ast.range_expr(&tree, first.iterable)
try expect_kind(&result, ast.unary_expr(&tree, first_range.start).operand, .int)
upper :: ast.binary_expr(&tree, first_range.end)
try expect_kind(&result, upper.left, .int)
try expect_kind(&result, upper.right, .int)
try expect_kind(&result, ast.block_statement(&tree, first.body, 1), .int)
try testing.expect_equal(0, diagnostics.count(&diagnostic_store))
}
range_checks_both_bounds_and_preserves_fixed_declarations_and_source_errors test {
strpool.strings = strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings)
diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store)
source ::
`real float :: 2
`flag :: true
`alias :: flag
`nothing proc() void {}
`main proc() void {
` for 1.5..2.5 |_| {}
` for alias.."text" |_| {}
` for nothing..nothing() |_| {}
` for real..3 |_| {}
` for absent_start..absent_end |_| {}
` for 0..1 |_| { sibling :: 7 }
` for flexible..floating_alias |_| {}
`}
`floating float :: floating_alias
`floating_alias :: flexible
`flexible := 3
scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store)
tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree)
resolution := try sema.resolve(mem.c_allocator, &tree, &diagnostic_store)
defer sema.deinit(&resolution)
result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store)
defer deinit(&result)
try expect_kind(&result, ast.package_decl(&tree, 0), .float)
try expect_kind(&result, ast.package_decl(&tree, 1), .bool)
try expect_kind(&result, ast.package_decl(&tree, 2), .bool)
try expect_kind(&result, ast.package_decl(&tree, 3), .function)
main_body :: ast.function_decl(&tree, ast.package_decl(&tree, 4)).body
for (usize(0)..usize(3)) |index| {
statement :: ast.block_statement(&tree, main_body, index)
loop :: ast.for_stmt(&tree, statement)
range_view :: ast.range_expr(&tree, loop.iterable)
first_error :: try expect_error(&result, &diagnostic_store, range_view.start, .invalid_range_bound, ast.node_span(&tree, range_view.start))
second_error :: try expect_error(&result, &diagnostic_store, range_view.end, .invalid_range_bound, ast.node_span(&tree, range_view.end))
try testing.expect(first_error != second_error)
try testing.expect_equal(type_of(&result, loop.iterable).diagnostic, type_of(&result, statement).diagnostic)
}
mixed :: ast.for_stmt(&tree, ast.block_statement(&tree, main_body, 3))
mixed_range :: ast.range_expr(&tree, mixed.iterable)
_ = try expect_error(&result, &diagnostic_store, mixed_range.start, .invalid_range_bound, ast.node_span(&tree, mixed_range.start))
try expect_kind(&result, mixed_range.end, .int)
unknown :: ast.for_stmt(&tree, ast.block_statement(&tree, main_body, 4))
unknown_range :: ast.range_expr(&tree, unknown.iterable)
unknown_bounds [2]ast.NodeId :: [unknown_range.start, unknown_range.end]
for unknown_bounds |bound| {
original :: sema.binding(&resolution, bound).data.diagnostic
_ = try expect_error(&result, &diagnostic_store, bound, .unknown_name, ast.node_span(&tree, bound))
try testing.expect_equal(original, type_of(&result, bound).diagnostic)
}
sibling :: ast.for_stmt(&tree, ast.block_statement(&tree, main_body, 5))
try expect_kind(&result, sibling.iterable, .range)
try expect_kind(&result, ast.block_statement(&tree, sibling.body, 0), .int)
floating_loop :: ast.for_stmt(&tree, ast.block_statement(&tree, main_body, 6))
floating_range :: ast.range_expr(&tree, floating_loop.iterable)
floating_start_error :: try expect_error(&result, &diagnostic_store, floating_range.start, .invalid_range_bound, ast.node_span(&tree, floating_range.start))
floating_end_error :: try expect_error(&result, &diagnostic_store, floating_range.end, .invalid_range_bound, ast.node_span(&tree, floating_range.end))
try testing.expect(floating_start_error != floating_end_error)
for (usize(5)..usize(8)) |index| try expect_kind(&result, ast.package_decl(&tree, index), .float)
try testing.expect_equal(9, code_count(&diagnostic_store, .invalid_range_bound))
try testing.expect_equal(2, code_count(&diagnostic_store, .unknown_name))
try testing.expect_equal(0, code_count(&diagnostic_store, .invalid_for_iterable))
try testing.expect_equal(0, code_count(&diagnostic_store, .cannot_infer_type))
}
range_values_nonrange_iterables_and_capture_modes_have_explicit_errors test {
strpool.strings = strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings)
diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store)
source ::
`stored :: 0..3
`produced proc() int { return 0..3 }
`consume proc(value int) void {}
`scalar proc() int { return 2 }
`main proc() void {
` 0..3
` consume(0..3)
` local :: 0..3
` for 3 |_| { absent_scalar }
` for true |_| {}
` for "text" |_| {}
` for scalar() |_| {}
` for stored |_| {}
` for 0..3 |@item| { item }
` for 0..3 |item, index| { item + index }
` for 0..3 |_, _| {}
` for 0..3 |@item, _| {}
`}
scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store)
tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree)
resolution := try sema.resolve(mem.c_allocator, &tree, &diagnostic_store)
defer sema.deinit(&resolution)
result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store)
defer deinit(&result)
stored :: ast.package_decl(&tree, 0)
stored_value :: ast.statement_decl(&tree, stored).initializer
stored_error :: try expect_error(&result, &diagnostic_store, stored_value, .range_value_not_supported, ast.node_span(&tree, stored_value))
try testing.expect_equal(stored_error, type_of(&result, stored).diagnostic)
produced_body :: ast.function_decl(&tree, ast.package_decl(&tree, 1)).body
returned :: ast.return_stmt(&tree, ast.block_statement(&tree, produced_body, 0)).value?
_ = try expect_error(&result, &diagnostic_store, returned, .range_value_not_supported, ast.node_span(&tree, returned))
main_body :: ast.function_decl(&tree, ast.package_decl(&tree, 4)).body
standalone :: ast.statement_expr(&tree, ast.block_statement(&tree, main_body, 0))
call :: ast.statement_expr(&tree, ast.block_statement(&tree, main_body, 1))
local_value :: ast.statement_decl(&tree, ast.block_statement(&tree, main_body, 2)).initializer
values [3]ast.NodeId :: [standalone, ast.call_arg(&tree, call, 0), local_value]
for values |value| {
_ = try expect_error(&result, &diagnostic_store, value, .range_value_not_supported, ast.node_span(&tree, value))
}
for (usize(3)..usize(7)) |index| {
statement :: ast.block_statement(&tree, main_body, index)
loop :: ast.for_stmt(&tree, statement)
original :: try expect_error(&result, &diagnostic_store, loop.iterable, .invalid_for_iterable, ast.node_span(&tree, loop.iterable))
try testing.expect_equal(original, type_of(&result, statement).diagnostic)
}
stored_loop :: ast.block_statement(&tree, main_body, 7)
try testing.expect_equal(stored_error, type_of(&result, stored_loop).diagnostic)
modes [4]diagnostics.Code :: [.range_pointer_capture, .range_index_capture, .range_index_capture, .range_pointer_capture]
for modes |code, index| {
statement :: ast.block_statement(&tree, main_body, index + 8)
_ = try expect_error(&result, &diagnostic_store, statement, code, ast.node_span(&tree, statement))
}
indexed :: ast.for_stmt(&tree, ast.block_statement(&tree, main_body, 9))
try expect_kind(&result, indexed.capture?, .int)
try expect_kind(&result, indexed.index?, .int)
try expect_kind(&result, ast.statement_expr(&tree, ast.block_statement(&tree, indexed.body, 0)), .int)
try testing.expect_equal(5, code_count(&diagnostic_store, .range_value_not_supported))
try testing.expect_equal(4, code_count(&diagnostic_store, .invalid_for_iterable))
try testing.expect_equal(2, code_count(&diagnostic_store, .range_pointer_capture))
try testing.expect_equal(3, code_count(&diagnostic_store, .range_index_capture))
try testing.expect_equal(1, code_count(&diagnostic_store, .unknown_name))
try testing.expect_equal(0, code_count(&diagnostic_store, .return_type_mismatch))
try testing.expect_equal(0, code_count(&diagnostic_store, .type_mismatch))
}
range_captures_are_fixed_readonly_and_assignment_values_do_not_retarget_bounds test {
strpool.strings = strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings)
diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store)
source ::
`consume proc(value float) void {}
`returned proc() float {
` for 0..2 |item| { return item }
` return 1
`}
`main proc() void {
` limit := 3
` real := 1.5
` for 0..limit |item| {
` item = 2
` if true { item = 1.5 }
` while false { item = -"bad" }
` real = item
` real = limit
` consume(item)
` copy :: item
` consume(copy)
` }
`}
scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store)
tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree)
resolution := try sema.resolve(mem.c_allocator, &tree, &diagnostic_store)
defer sema.deinit(&resolution)
result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store)
defer deinit(&result)
returned_body :: ast.function_decl(&tree, ast.package_decl(&tree, 1)).body
returned_loop :: ast.for_stmt(&tree, ast.block_statement(&tree, returned_body, 0))
returned_value :: ast.return_stmt(&tree, ast.block_statement(&tree, returned_loop.body, 0)).value?
_ = try expect_error(&result, &diagnostic_store, returned_value, .return_type_mismatch, ast.node_span(&tree, returned_value))
try expect_kind(&result, returned_loop.capture?, .int)
main_body :: ast.function_decl(&tree, ast.package_decl(&tree, 2)).body
limit :: ast.block_statement(&tree, main_body, 0)
real :: ast.block_statement(&tree, main_body, 1)
loop_statement :: ast.block_statement(&tree, main_body, 2)
loop :: ast.for_stmt(&tree, loop_statement)
try expect_kind(&result, limit, .int)
try expect_kind(&result, real, .float)
try expect_kind(&result, loop_statement, .void)
try expect_kind(&result, loop.iterable, .range)
try expect_kind(&result, ast.range_expr(&tree, loop.iterable).end, .int)
try expect_kind(&result, loop.capture?, .int)
conditional :: ast.if_stmt(&tree, ast.block_statement(&tree, loop.body, 1))
inner :: ast.while_stmt(&tree, ast.block_statement(&tree, loop.body, 2))
readonly [3]ast.NodeId :: [
ast.block_statement(&tree, loop.body, 0),
ast.block_statement(&tree, conditional.then_block, 0),
ast.block_statement(&tree, inner.body, 0),
]
for readonly |statement| {
assignment :: ast.assignment_stmt(&tree, statement)
_ = try expect_error(&result, &diagnostic_store, statement, .invalid_assignment_target, ast.node_span(&tree, assignment.target))
try expect_kind(&result, assignment.target, .int)
}
dead_value :: ast.assignment_stmt(&tree, readonly[2]).value
_ = try expect_error(&result, &diagnostic_store, dead_value, .invalid_operand, ast.node_span(&tree, dead_value))
for (usize(3)..usize(5)) |index| {
assignment :: ast.assignment_stmt(&tree, ast.block_statement(&tree, loop.body, index))
_ = try expect_error(&result, &diagnostic_store, assignment.value, .type_mismatch, ast.node_span(&tree, assignment.value))
try expect_kind(&result, assignment.target, .float)
}
for (usize(5)..usize(8)) |index| {
if (index == 6) continue
call :: ast.statement_expr(&tree, ast.block_statement(&tree, loop.body, index))
argument :: ast.call_arg(&tree, call, 0)
_ = try expect_error(&result, &diagnostic_store, argument, .type_mismatch, ast.node_span(&tree, argument))
}
try expect_kind(&result, ast.block_statement(&tree, loop.body, 6), .int)
try testing.expect_equal(3, code_count(&diagnostic_store, .invalid_assignment_target))
try testing.expect_equal(4, code_count(&diagnostic_store, .type_mismatch))
try testing.expect_equal(1, code_count(&diagnostic_store, .return_type_mismatch))
try testing.expect_equal(1, code_count(&diagnostic_store, .invalid_operand))
try testing.expect_equal(0, code_count(&diagnostic_store, .invalid_range_bound))
try testing.expect_equal(0, code_count(&diagnostic_store, .missing_return))
}
range_loop_return_flow_and_controls_are_lexical_and_body_errors_are_isolated test {
strpool.strings = strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings)
diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store)
source ::
`missing proc() int { for 0..=0 |_| { return 1 } }
`bad_body proc() int { for 2..1 |_| { -"bad" } }
`breaking proc() int { for 0..2 |_| { break } }
`continuing proc() int { for 2..1 |_| { continue } }
`closed proc() int {
` for 2..1 |_| { -"bad" }
` return 7
`}
`scopes proc(flag bool) void {
` for 0..2 |item| {
` if (flag) break else continue
` while flag {
` if (flag) continue else break
` for 0..item |_| { continue }
` }
` absent
` }
` while flag { for 0..2 |_| { break } }
` break
` continue
` item
`}
`callee proc() void {
` break
` continue
`}
`caller proc() void {
` for 0..2 |_| {
` callee()
` break
` }
`}
scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store)
tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree)
resolution := try sema.resolve(mem.c_allocator, &tree, &diagnostic_store)
defer sema.deinit(&resolution)
result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store)
defer deinit(&result)
for (usize(0)..usize(4)) |index| {
function :: ast.package_decl(&tree, index)
body :: ast.function_decl(&tree, function).body
try expect_kind(&result, function, .function)
_ = try expect_error(&result, &diagnostic_store, body, .missing_return, ast.node_span(&tree, body))
try expect_kind(&result, ast.block_statement(&tree, body, 0), .void)
}
closed_body :: ast.function_decl(&tree, ast.package_decl(&tree, 4)).body
closed_statement :: ast.block_statement(&tree, closed_body, 0)
closed_loop :: ast.for_stmt(&tree, closed_statement)
dead_value :: ast.statement_expr(&tree, ast.block_statement(&tree, closed_loop.body, 0))
original :: try expect_error(&result, &diagnostic_store, dead_value, .invalid_operand, ast.node_span(&tree, dead_value))
try testing.expect_equal(original, type_of(&result, closed_body).diagnostic)
try expect_kind(&result, closed_statement, .void)
try expect_kind(&result, ast.block_statement(&tree, closed_body, 1), .void)
scopes_body :: ast.function_decl(&tree, ast.package_decl(&tree, 5)).body
outer_statement :: ast.block_statement(&tree, scopes_body, 0)
outer :: ast.for_stmt(&tree, outer_statement)
conditional :: ast.if_stmt(&tree, ast.block_statement(&tree, outer.body, 0))
inner_statement :: ast.block_statement(&tree, outer.body, 1)
inner :: ast.while_stmt(&tree, inner_statement)
inner_if :: ast.if_stmt(&tree, ast.block_statement(&tree, inner.body, 0))
nested_statement :: ast.block_statement(&tree, inner.body, 1)
nested :: ast.for_stmt(&tree, nested_statement)
controls [5]ast.NodeId :: [
ast.block_statement(&tree, conditional.then_block, 0),
ast.block_statement(&tree, conditional.else_block?, 0),
ast.block_statement(&tree, inner_if.then_block, 0),
ast.block_statement(&tree, inner_if.else_block?, 0),
ast.block_statement(&tree, nested.body, 0),
]
for (controls) |statement| try expect_kind(&result, statement, .void)
try expect_kind(&result, outer_statement, .void)
try expect_kind(&result, inner_statement, .void)
try expect_kind(&result, nested_statement, .void)
absent :: ast.statement_expr(&tree, ast.block_statement(&tree, outer.body, 2))
try testing.expect_equal(sema.binding(&resolution, absent).data.diagnostic, type_of(&result, absent).diagnostic)
escaped :: ast.statement_expr(&tree, ast.block_statement(&tree, scopes_body, 4))
try testing.expect_equal(sema.binding(&resolution, escaped).data.diagnostic, type_of(&result, escaped).diagnostic)
try testing.expect_equal(4, code_count(&diagnostic_store, .missing_return))
try testing.expect_equal(2, code_count(&diagnostic_store, .invalid_operand))
try testing.expect_equal(2, code_count(&diagnostic_store, .unknown_name))
try testing.expect_equal(2, code_count(&diagnostic_store, .break_outside_loop))
try testing.expect_equal(2, code_count(&diagnostic_store, .continue_outside_loop))
}
invalid_range_capture_bindings_keep_resolver_diagnostic_ids_without_erasing_headers test {
strpool.strings = strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings)
diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store)
source ::
`main proc() void {
` existing :: 7
` for 0..1 |existing| { sibling :: 1 }
` for 0..1 |int| { sibling :: 2 }
` for 0..1 |item| { item :: 3 }
` for 0..1 |item| { copy :: item }
`}
scan_state := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan_state)
try lexer.scan(&scan_state, source, &diagnostic_store)
tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
defer ast.deinit(&tree)
resolution := try sema.resolve(mem.c_allocator, &tree, &diagnostic_store)
defer sema.deinit(&resolution)
previous_count :: diagnostics.count(&diagnostic_store)
result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store)
defer deinit(&result)
function :: ast.package_decl(&tree, 0)
body :: ast.function_decl(&tree, function).body
try expect_kind(&result, function, .function)
try expect_kind(&result, ast.block_statement(&tree, body, 0), .int)
for (usize(1)..usize(3)) |index| {
statement :: ast.block_statement(&tree, body, index)
loop :: ast.for_stmt(&tree, statement)
capture :: loop.capture?
binding :: sema.binding(&resolution, capture)
try testing.expect_equal(sema.BindingKind.invalid, binding.kind)
try testing.expect_equal(TypeKind.invalid, type_of(&result, capture).kind)
try testing.expect_equal(binding.data.diagnostic, type_of(&result, capture).diagnostic)
try testing.expect_equal(binding.data.diagnostic, type_of(&result, loop.body).diagnostic)
try expect_kind(&result, statement, .void)
try expect_kind(&result, loop.iterable, .range)
try expect_kind(&result, ast.block_statement(&tree, loop.body, 0), .int)
}
duplicate_loop :: ast.for_stmt(&tree, ast.block_statement(&tree, body, 3))
duplicate :: ast.block_statement(&tree, duplicate_loop.body, 0)
try expect_kind(&result, duplicate_loop.capture?, .int)
try testing.expect_equal(sema.binding(&resolution, duplicate).data.diagnostic, type_of(&result, duplicate).diagnostic)
sibling_loop :: ast.for_stmt(&tree, ast.block_statement(&tree, body, 4))
try expect_kind(&result, sibling_loop.capture?, .int)
try expect_kind(&result, ast.block_statement(&tree, sibling_loop.body, 0), .int)
try testing.expect_equal(previous_count, diagnostics.count(&diagnostic_store))
}
@hide:file
ArrayFixture :: struct {
tree ast.Tree
resolution sema.Resolution
diagnostics diagnostics.Store
result Result
}
@hide:file
array_fixture proc(source []u8) ArrayFixture ! (mem.AllocError | strpool.InternError | diagnostics.CapacityError | lexer.SourceError) {
store := diagnostics.init(mem.c_allocator)
errdefer diagnostics.deinit(&store)
scan := lexer.init(mem.c_allocator)
defer lexer.deinit(&scan)
try lexer.scan(&scan, source, &store)
tree := try parser.parse(mem.c_allocator, scan.tokens.items, source, &store)
errdefer ast.deinit(&tree)
resolution := try sema.resolve(mem.c_allocator, &tree, &store)
errdefer sema.deinit(&resolution)
result :: try check(mem.c_allocator, &tree, &resolution, &store)
return ArrayFixture{ tree = tree, resolution = resolution, diagnostics = store, result = result }
}
@hide:file
deinit_array_fixture proc(fixture @mut ArrayFixture) void {
deinit(&fixture.result)
sema.deinit(&fixture.resolution)
ast.deinit(&fixture.tree)
diagnostics.deinit(&fixture.diagnostics)
}
@hide:file
expect_array proc(result @Result, id ast.NodeId, len usize, mutable bool, element TypeKind) ArrayType ! testing.Error {
try expect_kind(result, id, .array)
actual :: array_type(result, type_of(result, id).array_type)
try testing.expect_equal(len, actual.len)
try testing.expect_equal(mutable, actual.mutable)
try testing.expect_equal(element, actual.element.kind)
return actual
}
fixed_arrays_contextualize_forward_aliases_counts_nested_literals_and_signatures test {
strpool.strings = strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings)
source ::
`context [2]f64 :: alias
`alias :: seed
`seed := [1, 2]
`count :: later * 2 - 1
`later :: 2
`nested [_]mut [2]mut i64 := [[1, 2], [3, 4]]
`empty [_]bool :: []
`plain :: []
`copy :: nested
`index usize :: 0
`child float :: seed[index]
`produce proc(value [2]f64) [2]f64 { return value }
`called :: produce([5, 6])
`from_result [_]f64 :: called
`counted [count]i64 :: [1, 2, 3]
fixture := try array_fixture(source)
defer deinit_array_fixture(&fixture)
tree :: &fixture.tree
result :: &fixture.result
for (0..usize(3)) |i| _ = try expect_array(result, ast.package_decl(tree, i), 2, false, .float)
seed :: ast.statement_decl(tree, ast.package_decl(tree, 2)).initializer
for (0..ast.array_element_count(tree, seed)) |i| try expect_kind(result, ast.array_element(tree, seed, i), .float)
nested :: ast.package_decl(tree, 5)
shape :: try expect_array(result, nested, 2, true, .array)
row :: array_type(result, shape.element.array_type)
try testing.expect_equal(2, row.len)
try testing.expect(row.mutable)
try testing.expect_equal(TypeKind.int, row.element.kind)
annotation :: ast.statement_decl(tree, nested).type?
_ = try expect_array(result, annotation, 2, true, .array)
_ = try expect_array(result, ast.package_decl(tree, 6), 0, false, .bool)
_ = try expect_array(result, ast.package_decl(tree, 7), 0, false, .int)
try testing.expect_equal(type_of(result, nested).array_type, type_of(result, ast.package_decl(tree, 8)).array_type)
indexed :: ast.index_expr(tree, ast.statement_decl(tree, ast.package_decl(tree, 10)).initializer)
try expect_kind(result, indexed.index, .usize)
try expect_kind(result, ast.statement_decl(tree, ast.package_decl(tree, 10)).initializer, .float)
function :: ast.package_decl(tree, 11)
_ = try expect_array(result, ast.function_param(tree, function, 0), 2, false, .float)
_ = try expect_array(result, ast.function_decl(tree, function).return_type, 2, false, .float)
call :: ast.statement_decl(tree, ast.package_decl(tree, 12)).initializer
argument :: ast.call_arg(tree, call, 0)
_ = try expect_array(result, argument, 2, false, .float)
try expect_kind(result, ast.array_element(tree, argument, 0), .float)
_ = try expect_array(result, ast.package_decl(tree, 13), 2, false, .float)
_ = try expect_array(result, ast.package_decl(tree, 14), 3, false, .int)
try testing.expect(array_type_count(result) > 0)
try testing.expect_equal(0, diagnostics.count(&fixture.diagnostics))
}
nested_array_numeric_context_flows_through_index_children_before_defaults test {
strpool.strings = strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings)
source ::
`first float :: later[0][0]
`later := [[1, 2], [3, 4]]
`row :: later[0]
`mixed :: [[1, 2], [3.5, 4]]
`identity proc(value int) int { return value }
`another proc(value int) int { return value + 1 }
`functions :: [identity, another]
fixture := try array_fixture(source)
defer deinit_array_fixture(&fixture)
tree :: &fixture.tree
result :: &fixture.result
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)
}
}
matrix :: ast.statement_decl(tree, ast.package_decl(tree, 1)).initializer
for 0..ast.array_element_count(tree, matrix) |i| {
row :: ast.array_element(tree, matrix, i)
for (0..ast.array_element_count(tree, row)) |j| try expect_kind(result, ast.array_element(tree, row, j), .float)
}
outer :: ast.statement_decl(tree, ast.package_decl(tree, 0)).initializer
inner :: ast.index_expr(tree, outer).base
_ = try expect_array(result, inner, 2, false, .float)
try expect_kind(result, outer, .float)
try expect_kind(result, ast.index_expr(tree, outer).index, .usize)
try expect_kind(result, ast.index_expr(tree, inner).index, .usize)
_ = try expect_array(result, ast.package_decl(tree, 6), 2, false, .function)
try testing.expect_equal(0, diagnostics.count(&fixture.diagnostics))
}
fixed_index_errors_are_occurrence_local_and_preserve_sources_and_siblings test {
strpool.strings = strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings)
source ::
`fixed i64 :: 0
`inferred := 0
`source [2]i64 :: [10, 20]
`bad :: source[fixed]
`good :: source[inferred]
`boolean :: source[true]
`missing :: source[unknown]
`tail :: source[1]
fixture := try array_fixture(source)
defer deinit_array_fixture(&fixture)
tree :: &fixture.tree
result :: &fixture.result
try expect_kind(result, ast.package_decl(tree, 0), .int)
try expect_kind(result, ast.package_decl(tree, 1), .usize)
_ = try expect_array(result, ast.package_decl(tree, 2), 2, false, .int)
bad_id :: ast.statement_decl(tree, ast.package_decl(tree, 3)).initializer
bad :: ast.index_expr(tree, bad_id)
original :: try expect_error(result, &fixture.diagnostics, bad.index, .invalid_index_type, ast.node_span(tree, bad.index))
try testing.expect_equal(original, type_of(result, bad_id).diagnostic)
try testing.expect_equal(original, type_of(result, ast.package_decl(tree, 3)).diagnostic)
_ = try expect_array(result, bad.base, 2, false, .int)
try expect_kind(result, ast.package_decl(tree, 4), .int)
boolean :: ast.index_expr(tree, ast.statement_decl(tree, ast.package_decl(tree, 5)).initializer).index
_ = try expect_error(result, &fixture.diagnostics, boolean, .invalid_index_type, ast.node_span(tree, boolean))
missing :: ast.index_expr(tree, ast.statement_decl(tree, ast.package_decl(tree, 6)).initializer).index
try testing.expect_equal(sema.binding(&fixture.resolution, missing).data.diagnostic, type_of(result, missing).diagnostic)
try expect_kind(result, ast.package_decl(tree, 7), .int)
try testing.expect_equal(2, code_count(&fixture.diagnostics, .invalid_index_type))
try testing.expect_equal(1, code_count(&fixture.diagnostics, .unknown_name))
try testing.expect_equal(3, diagnostics.count(&fixture.diagnostics))
}
array_lengths_require_supported_constexpr_and_finalize_nested_inferred_counts test {
strpool.strings = strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings)
source ::
`two :: tail
`tail :: 2
`valid [two * 3 - 4]i64 :: [1, 2]
`zero [3 - 3]i64 :: []
`nested [_]mut [_]mut i64 := [[1, 2], [3, 4]]
`negative [-1]i64 :: []
`fraction [1.5]i64 :: []
`runtime_count := 2
`runtime_array [runtime_count]i64 :: [1, 2]
`count proc() int { return 2 }
`called [count()]i64 :: [1, 2]
`a :: b
`b :: a
`cycle [a]i64 :: []
`overflow [18446744073709551615 + 1]i64 :: []
`wrong [2]i64 :: [1]
`constraints [2]mut int := [1, 2]
`floats [0]float :: []
`voids [0]void :: []
`missing [absent]i64 :: []
fixture := try array_fixture(source)
defer deinit_array_fixture(&fixture)
tree :: &fixture.tree
result :: &fixture.result
_ = try expect_array(result, ast.package_decl(tree, 2), 2, false, .int)
_ = try expect_array(result, ast.package_decl(tree, 3), 0, false, .int)
nested :: ast.statement_decl(tree, ast.package_decl(tree, 4))
shape :: try expect_array(result, nested.type?, 2, true, .array)
child :: array_type(result, shape.element.array_type)
try testing.expect_equal(2, child.len)
try testing.expect(child.mutable)
_ = try expect_array(result, ast.array_type(tree, nested.type?).element, 2, true, .int)
invalid_lengths [6]usize :: [5, 6, 8, 10, 13, 14]
for invalid_lengths |i| {
annotation :: ast.statement_decl(tree, ast.package_decl(tree, i)).type?
count :: ast.array_type(tree, annotation).count?
_ = try expect_error(result, &fixture.diagnostics, annotation, .invalid_array_length, ast.node_span(tree, count))
}
wrong :: ast.statement_decl(tree, ast.package_decl(tree, 15)).initializer
_ = try expect_error(result, &fixture.diagnostics, wrong, .array_length_mismatch, ast.node_span(tree, wrong))
for usize(16)..usize(19) |i| {
annotation :: ast.statement_decl(tree, ast.package_decl(tree, i)).type?
child_id :: ast.array_type(tree, annotation).element
_ = try expect_error(result, &fixture.diagnostics, annotation, .invalid_array_element_type, ast.node_span(tree, child_id))
}
missing :: ast.statement_decl(tree, ast.package_decl(tree, 19)).type?
count :: ast.array_type(tree, missing).count?
try testing.expect_equal(sema.binding(&fixture.resolution, count).data.diagnostic, type_of(result, missing).diagnostic)
try testing.expect_equal(6, code_count(&fixture.diagnostics, .invalid_array_length))
try testing.expect_equal(1, code_count(&fixture.diagnostics, .array_length_mismatch))
try testing.expect_equal(3, code_count(&fixture.diagnostics, .invalid_array_element_type))
}
indexed_writes_require_mutable_local_root_and_each_array_level_but_copies_are_values test {
strpool.strings = strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings)
source ::
`global [1]mut i64 := [1]
`main proc(parameter [1]mut i64) void {
` mutable [1]mut i64 := [1]
` mutable[0] = 2
` copied := mutable
` copied[0] = 3
` inferred := [1]
` inferred[0] = 4
` inferred = [5]
` constant [1]mut i64 :: [1]
` constant[0] = 6
` parameter[0] = 7
` global[0] = 8
` outer_readonly [1][1]mut i64 := [[1]]
` (outer_readonly[0])[0] = 9
` inner_readonly [1]mut [1]i64 := [[1]]
` (inner_readonly[0])[0] = 10
` both [1]mut [1]mut i64 := [[1]]
` (both[0])[0] = 11
` both[0] = [12]
` mismatch [1]mut i64 := inner_readonly[0]
`}
fixture := try array_fixture(source)
defer deinit_array_fixture(&fixture)
tree :: &fixture.tree
result :: &fixture.result
body :: ast.function_decl(tree, ast.package_decl(tree, 1)).body
valid [6]usize :: [1, 3, 6, 16, 17, 2]
for valid |i| {
id :: ast.block_statement(tree, body, i)
kind TypeKind :: if (i == 2) .array else .void
try expect_kind(result, id, kind)
}
invalid [6]usize :: [5, 8, 9, 10, 12, 14]
for invalid |i| {
id :: ast.block_statement(tree, body, i)
target :: ast.assignment_stmt(tree, id).target
_ = try expect_error(result, &fixture.diagnostics, id, .invalid_index_assignment, ast.node_span(tree, target))
try expect_kind(result, target, .int)
try expect_kind(result, ast.index_expr(tree, target).index, .usize)
}
mismatch :: ast.statement_decl(tree, ast.block_statement(tree, body, 18)).initializer
_ = try expect_error(result, &fixture.diagnostics, mismatch, .type_mismatch, ast.node_span(tree, mismatch))
_ = try expect_array(result, ast.block_statement(tree, body, 4), 1, false, .int)
_ = try expect_array(result, ast.function_param(tree, ast.package_decl(tree, 1), 0), 1, true, .int)
try testing.expect_equal(6, code_count(&fixture.diagnostics, .invalid_index_assignment))
try testing.expect_equal(1, code_count(&fixture.diagnostics, .type_mismatch))
}
usize_arithmetic_ranges_and_readonly_captures_keep_the_bound_family test {
strpool.strings = strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings)
source ::
`limit usize :: 18446744073709551615
`arithmetic usize :: (limit - 2) * 1 + 1
`ordered :: 0 < limit
`equal :: limit == 18446744073709551615
`negated :: -limit
`divided :: limit / 2
`fixed i64 :: 1
`mixed :: fixed + limit
`main proc() void {
` source [2]i64 :: [1, 2]
` for 0..limit |index| {
` source[index]
` index = 1
` }
` for 0..2 |index| { source[index] }
` for limit..=limit |_| {}
`}
fixture := try array_fixture(source)
defer deinit_array_fixture(&fixture)
tree :: &fixture.tree
result :: &fixture.result
try expect_kind(result, ast.package_decl(tree, 0), .usize)
try expect_kind(result, ast.package_decl(tree, 1), .usize)
try expect_kind(result, ast.package_decl(tree, 2), .bool)
try expect_kind(result, ast.package_decl(tree, 3), .bool)
for usize(4)..usize(8) |i| {
if (i == 6) continue
value :: ast.statement_decl(tree, ast.package_decl(tree, i)).initializer
code diagnostics.Code :: if (i == 5) .integer_division else .invalid_operand
_ = try expect_error(result, &fixture.diagnostics, value, code, ast.node_span(tree, value))
}
body :: ast.function_decl(tree, ast.package_decl(tree, 8)).body
unsigned_loop :: ast.for_stmt(tree, ast.block_statement(tree, body, 1))
try expect_kind(result, unsigned_loop.capture?, .usize)
try testing.expect_equal(TypeKind.usize, type_of(result, unsigned_loop.iterable).range_element)
interval :: ast.range_expr(tree, unsigned_loop.iterable)
try expect_kind(result, interval.start, .usize)
try expect_kind(result, interval.end, .usize)
read :: ast.statement_expr(tree, ast.block_statement(tree, unsigned_loop.body, 0))
try expect_kind(result, read, .int)
try expect_kind(result, ast.index_expr(tree, read).index, .usize)
readonly :: ast.block_statement(tree, unsigned_loop.body, 1)
target :: ast.assignment_stmt(tree, readonly).target
_ = try expect_error(result, &fixture.diagnostics, readonly, .invalid_assignment_target, ast.node_span(tree, target))
try expect_kind(result, target, .usize)
signed_loop :: ast.for_stmt(tree, ast.block_statement(tree, body, 2))
try expect_kind(result, signed_loop.capture?, .int)
signed_read :: ast.statement_expr(tree, ast.block_statement(tree, signed_loop.body, 0))
bad_index :: ast.index_expr(tree, signed_read).index
_ = try expect_error(result, &fixture.diagnostics, bad_index, .invalid_index_type, ast.node_span(tree, bad_index))
try testing.expect_equal(TypeKind.int, type_of(result, signed_loop.iterable).range_element)
_ = try expect_array(result, ast.block_statement(tree, body, 0), 2, false, .int)
ignored :: ast.for_stmt(tree, ast.block_statement(tree, body, 3))
try testing.expect_equal(TypeKind.usize, type_of(result, ignored.iterable).range_element)
try testing.expect_equal(1, code_count(&fixture.diagnostics, .invalid_index_type))
try testing.expect_equal(0, code_count(&fixture.diagnostics, .invalid_range_bound))
}
array_assignment_rhs_is_contextual_but_does_not_retarget_established_sources test {
strpool.strings = strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings)
source ::
`main proc() void {
` target [2]mut f64 := [1, 2]
` seed := 3
` source [2]i64 :: [4, 5]
` target = [seed, 6]
` target = source
` target = [1]
` matrix [1]mut [2]mut i64 := [[1, 2]]
` matrix[0] = [7, 8]
` matrix[0] = [true, false]
` unknown [1]mut i64 := [absent]
` unknown[0] = 9
` good := 10
`}
fixture := try array_fixture(source)
defer deinit_array_fixture(&fixture)
tree :: &fixture.tree
result :: &fixture.result
body :: ast.function_decl(tree, ast.package_decl(tree, 0)).body
_ = try expect_array(result, ast.block_statement(tree, body, 0), 2, true, .float)
try expect_kind(result, ast.block_statement(tree, body, 1), .int)
_ = try expect_array(result, ast.block_statement(tree, body, 2), 2, false, .int)
contextual :: ast.assignment_stmt(tree, ast.block_statement(tree, body, 3)).value
_ = try expect_error(result, &fixture.diagnostics, ast.array_element(tree, contextual, 0), .type_mismatch, ast.node_span(tree, ast.array_element(tree, contextual, 0)))
try expect_kind(result, ast.array_element(tree, contextual, 1), .float)
copied :: ast.assignment_stmt(tree, ast.block_statement(tree, body, 4)).value
_ = try expect_error(result, &fixture.diagnostics, copied, .type_mismatch, ast.node_span(tree, copied))
wrong_len :: ast.assignment_stmt(tree, ast.block_statement(tree, body, 5)).value
_ = try expect_error(result, &fixture.diagnostics, wrong_len, .array_length_mismatch, ast.node_span(tree, wrong_len))
try expect_kind(result, ast.block_statement(tree, body, 7), .void)
literal :: ast.statement_decl(tree, ast.block_statement(tree, body, 9)).initializer
missing :: ast.array_element(tree, literal, 0)
original :: sema.binding(&fixture.resolution, missing).data.diagnostic
try testing.expect_equal(original, type_of(result, literal).diagnostic)
try testing.expect_equal(original, type_of(result, ast.block_statement(tree, body, 9)).diagnostic)
try testing.expect_equal(original, type_of(result, ast.block_statement(tree, body, 10)).diagnostic)
try expect_kind(result, ast.block_statement(tree, body, 11), .int)
}
array_signature_identity_includes_recursive_counts_and_mutability test {
strpool.strings = strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings)
source ::
`accept proc(value [1]mut i64) [1]mut i64 { return value }
`different_count proc(value [2]mut i64) [1]mut i64 { return [1] }
`different_mutability proc(value [1]i64) [1]mut i64 { return [1] }
`functions :: [accept, different_count, different_mutability]
`wrong_return proc() [1]mut i64 {
` value [1]i64 :: [1]
` return value
`}
`take proc(value [2]i64) void {}
`caller proc() void {
` take([1])
` take([true, false])
`}
fixture := try array_fixture(source)
defer deinit_array_fixture(&fixture)
tree :: &fixture.tree
result :: &fixture.result
functions :: ast.statement_decl(tree, ast.package_decl(tree, 3)).initializer
for usize(1)..usize(3) |i| {
value :: ast.array_element(tree, functions, i)
_ = try expect_error(result, &fixture.diagnostics, value, .type_mismatch, ast.node_span(tree, value))
try expect_kind(result, ast.package_decl(tree, i), .function)
}
wrong :: ast.package_decl(tree, 4)
wrong_body :: ast.function_decl(tree, wrong).body
value :: ast.return_stmt(tree, ast.block_statement(tree, wrong_body, 1)).value?
_ = try expect_error(result, &fixture.diagnostics, value, .return_type_mismatch, ast.node_span(tree, value))
_ = try expect_array(result, ast.block_statement(tree, wrong_body, 0), 1, false, .int)
try expect_kind(result, wrong, .function)
caller :: ast.function_decl(tree, ast.package_decl(tree, 6)).body
call :: ast.statement_expr(tree, ast.block_statement(tree, caller, 0))
argument :: ast.call_arg(tree, call, 0)
_ = try expect_error(result, &fixture.diagnostics, argument, .array_length_mismatch, ast.node_span(tree, argument))
second :: ast.statement_expr(tree, ast.block_statement(tree, caller, 1))
literal :: ast.call_arg(tree, second, 0)
for 0..ast.array_element_count(tree, literal) |i| {
child :: ast.array_element(tree, literal, i)
_ = try expect_error(result, &fixture.diagnostics, child, .type_mismatch, ast.node_span(tree, child))
}
try expect_kind(result, ast.package_decl(tree, 5), .function)
}
inferred_counts_follow_forward_fixed_aliases_empty_shapes_and_reject_local_counts test {
strpool.strings = strpool.init(mem.c_allocator)
defer strpool.deinit(&strpool.strings)
source ::
`forward [_]i64 :: later
`later [_]i64 :: [1, 2]
`zero [_]mut bool := []
`nested [_][_]bool :: [[], []]
`unsigned usize :: 5
`main proc() void {
` local_count :: 2
` unsupported [local_count]i64 :: [1, 2]
` values [2]i64 :: [1, 2]
` for 0..(unsigned + 1) |index| { values[index] }
`}
fixture := try array_fixture(source)
defer deinit_array_fixture(&fixture)
tree :: &fixture.tree
result :: &fixture.result
for 0..usize(2) |i| {
declaration :: ast.statement_decl(tree, ast.package_decl(tree, i))
_ = try expect_array(result, declaration.type?, 2, false, .int)
_ = try expect_array(result, ast.package_decl(tree, i), 2, false, .int)
}
_ = try expect_array(result, ast.package_decl(tree, 2), 0, true, .bool)
shape :: try expect_array(result, ast.package_decl(tree, 3), 2, false, .array)
child :: array_type(result, shape.element.array_type)
try testing.expect_equal(0, child.len)
try testing.expect_equal(TypeKind.bool, child.element.kind)
body :: ast.function_decl(tree, ast.package_decl(tree, 5)).body
unsupported :: ast.statement_decl(tree, ast.block_statement(tree, body, 1)).type?
count :: ast.array_type(tree, unsupported).count?
_ = try expect_error(result, &fixture.diagnostics, unsupported, .invalid_array_length, ast.node_span(tree, count))
try expect_kind(result, ast.block_statement(tree, body, 0), .int)
loop :: ast.for_stmt(tree, ast.block_statement(tree, body, 3))
try expect_kind(result, loop.capture?, .usize)
try testing.expect_equal(TypeKind.usize, type_of(result, loop.iterable).range_element)
read :: ast.statement_expr(tree, ast.block_statement(tree, loop.body, 0))
try expect_kind(result, read, .int)
try testing.expect_equal(1, code_count(&fixture.diagnostics, .invalid_array_length))
try testing.expect_equal(1, diagnostics.count(&fixture.diagnostics))
}