runtime mutable global state
This commit is contained in:
+156
-9
@@ -5480,6 +5480,157 @@ valid_runtime_global_initializes_before_main :: proc(t: ^testing.T) {
|
||||
testing.expect_value(t, state.exit_code, 0)
|
||||
}
|
||||
|
||||
@(test)
|
||||
mutable_runtime_globals_compile_and_run :: proc(t: ^testing.T) {
|
||||
output := "/tmp/brolang-test-mutable-global"
|
||||
defer _ = os.remove(output)
|
||||
status := compiler_core.compile_package("examples/programs/mutable_global", output)
|
||||
testing.expect_value(t, status, 0)
|
||||
state := run_executable(output)
|
||||
testing.expect_value(t, state.exit_code, 52)
|
||||
}
|
||||
|
||||
@(test)
|
||||
mutable_globals_infer_constraints_and_emit_writable_storage :: proc(t: ^testing.T) {
|
||||
text := `Point :: struct {
|
||||
x i32
|
||||
}
|
||||
counter int = 0
|
||||
ratio float = 1
|
||||
span range = 0..2
|
||||
point Point = Point { x = 1 }
|
||||
values [_]mut i32 = [10, 20]
|
||||
main func() void {
|
||||
counter = 1
|
||||
counter += 1
|
||||
point.x = counter
|
||||
values[1] = point.x
|
||||
}
|
||||
`
|
||||
source_file := source.Source{path="test.bro", text=text}
|
||||
diagnostics := source.init_diagnostics(&source_file)
|
||||
defer source.destroy_diagnostics(&diagnostics)
|
||||
symbols := symbol.init_table()
|
||||
defer symbol.destroy_table(&symbols)
|
||||
stream := lexer.lex(&source_file, &diagnostics, &symbols)
|
||||
defer delete(stream.items)
|
||||
ast_module := parser.parse(&stream, &source_file, &diagnostics)
|
||||
defer ast.destroy_module(&ast_module)
|
||||
hir_module := checker.check(&ast_module, &diagnostics, &symbols)
|
||||
defer hir.destroy_module(&hir_module)
|
||||
ir_module := lower.lower(&hir_module)
|
||||
defer ir.destroy_module(&ir_module)
|
||||
llvm_text := llvm.emit(&ir_module, &diagnostics, &symbols)
|
||||
defer delete(llvm_text)
|
||||
|
||||
testing.expect_value(t, len(diagnostics.items), 0)
|
||||
found_counter := false
|
||||
found_ratio := false
|
||||
found_span := false
|
||||
found_values := false
|
||||
for global in hir_module.globals {
|
||||
name := symbol.resolve(&symbols, global.name)
|
||||
if name == "counter" {
|
||||
found_counter = global.writable && !global.is_static && types.equal(global.type, types.I8)
|
||||
} else if name == "ratio" {
|
||||
found_ratio = global.writable && !global.is_static && types.equal(global.type, types.F64)
|
||||
} else if name == "span" {
|
||||
found_span = global.writable && !global.is_static && types.is_range(global.type, &hir_module.types)
|
||||
} else if name == "values" {
|
||||
item, ok := types.node(&hir_module.types, global.type)
|
||||
found_values = global.writable && !global.is_static && ok && item.kind == .Array && item.count == 2
|
||||
}
|
||||
}
|
||||
testing.expect(t, found_counter)
|
||||
testing.expect(t, found_ratio)
|
||||
testing.expect(t, found_span)
|
||||
testing.expect(t, found_values)
|
||||
testing.expect(t, strings.contains(llvm_text, "internal global"))
|
||||
testing.expect(t, !strings.contains(llvm_text, "internal constant i8 0"))
|
||||
}
|
||||
|
||||
@(test)
|
||||
mutable_global_diagnostics_and_no_shadowing :: proc(t: ^testing.T) {
|
||||
text := `ID :: distinct i32
|
||||
OtherID :: distinct i32
|
||||
ID i32 = 0
|
||||
counter = 0
|
||||
bad i32 = undefined
|
||||
runtime i32 = 1
|
||||
immutable :: 1
|
||||
foo func(foo i32) void {}
|
||||
main func() void {
|
||||
_ = $runtime
|
||||
immutable = 2
|
||||
OtherID i32 = 0
|
||||
local i32 = 0
|
||||
if true {
|
||||
local i32 = 1
|
||||
}
|
||||
for 0..1 |local| {}
|
||||
scope i32 = 0
|
||||
scope: {}
|
||||
mark: {
|
||||
mark i32 = 0
|
||||
}
|
||||
value i32 = value_label: {
|
||||
value_label i32 = 1
|
||||
yield :value_label 1
|
||||
}
|
||||
}
|
||||
`
|
||||
source_file := source.Source{path="test.bro", text=text}
|
||||
diagnostics := source.init_diagnostics(&source_file)
|
||||
defer source.destroy_diagnostics(&diagnostics)
|
||||
symbols := symbol.init_table()
|
||||
defer symbol.destroy_table(&symbols)
|
||||
stream := lexer.lex(&source_file, &diagnostics, &symbols)
|
||||
defer delete(stream.items)
|
||||
ast_module := parser.parse(&stream, &source_file, &diagnostics)
|
||||
defer ast.destroy_module(&ast_module)
|
||||
hir_module := checker.check(&ast_module, &diagnostics, &symbols)
|
||||
defer hir.destroy_module(&hir_module)
|
||||
|
||||
missing_type := false
|
||||
undefined_global := false
|
||||
not_comptime := false
|
||||
immutable_write := false
|
||||
global_type_shadow := false
|
||||
param_shadow := false
|
||||
local_type_shadow := false
|
||||
local_shadow := false
|
||||
capture_shadow := false
|
||||
label_shadow := false
|
||||
local_label_shadow := false
|
||||
value_label_shadow := false
|
||||
for diagnostic in diagnostics.items {
|
||||
missing_type = missing_type || strings.contains(diagnostic.message, "mutable global 'counter' requires a type annotation")
|
||||
undefined_global = undefined_global || strings.contains(diagnostic.message, "'undefined' is only valid as a mutable local declaration initializer")
|
||||
not_comptime = not_comptime || strings.contains(diagnostic.message, "global 'runtime' is not comptime-known")
|
||||
immutable_write = immutable_write || strings.contains(diagnostic.message, "assignment target is not writable")
|
||||
global_type_shadow = global_type_shadow || strings.contains(diagnostic.message, "global 'ID' shadows visible type")
|
||||
param_shadow = param_shadow || strings.contains(diagnostic.message, "parameter 'foo' shadows visible function")
|
||||
local_type_shadow = local_type_shadow || strings.contains(diagnostic.message, "local 'OtherID' shadows visible type")
|
||||
local_shadow = local_shadow || strings.contains(diagnostic.message, "local 'local' shadows visible local")
|
||||
capture_shadow = capture_shadow || strings.contains(diagnostic.message, "capture 'local' shadows visible local")
|
||||
label_shadow = label_shadow || strings.contains(diagnostic.message, "label 'scope' shadows visible local")
|
||||
local_label_shadow = local_label_shadow || strings.contains(diagnostic.message, "local 'mark' shadows visible label")
|
||||
value_label_shadow = value_label_shadow || strings.contains(diagnostic.message, "local 'value_label' shadows visible label")
|
||||
}
|
||||
testing.expect(t, missing_type)
|
||||
testing.expect(t, undefined_global)
|
||||
testing.expect(t, not_comptime)
|
||||
testing.expect(t, immutable_write)
|
||||
testing.expect(t, global_type_shadow)
|
||||
testing.expect(t, param_shadow)
|
||||
testing.expect(t, local_type_shadow)
|
||||
testing.expect(t, local_shadow)
|
||||
testing.expect(t, capture_shadow)
|
||||
testing.expect(t, label_shadow)
|
||||
testing.expect(t, local_label_shadow)
|
||||
testing.expect(t, value_label_shadow)
|
||||
}
|
||||
|
||||
@(test)
|
||||
unused_global_cycle_is_deferred :: proc(t: ^testing.T) {
|
||||
output := "/tmp/brolang-test-cycle-unused"
|
||||
@@ -8153,7 +8304,7 @@ main func() void {
|
||||
}
|
||||
|
||||
@(test)
|
||||
distinct_type_construction_defers_to_callable_names :: proc(t: ^testing.T) {
|
||||
type_and_function_names_cannot_shadow :: proc(t: ^testing.T) {
|
||||
text := `Value :: distinct u32
|
||||
Value func(value i32) i32 {
|
||||
return value
|
||||
@@ -8174,15 +8325,11 @@ main func() i32 {
|
||||
hir_module := checker.check(&ast_module, &diagnostics, &symbols)
|
||||
defer hir.destroy_module(&hir_module)
|
||||
|
||||
found_call := false
|
||||
found_retype := false
|
||||
for expr in hir_module.exprs {
|
||||
found_call = found_call || expr.kind == .Call
|
||||
found_retype = found_retype || expr.kind == .Retype
|
||||
found := false
|
||||
for diagnostic in diagnostics.items {
|
||||
found = found || strings.contains(diagnostic.message, "function 'Value' shadows visible type")
|
||||
}
|
||||
testing.expect_value(t, len(diagnostics.items), 0)
|
||||
testing.expect(t, found_call)
|
||||
testing.expect(t, !found_retype)
|
||||
testing.expect(t, found)
|
||||
}
|
||||
|
||||
@(test)
|
||||
|
||||
Reference in New Issue
Block a user