report global cycle endpoints
This commit is contained in:
@@ -1,6 +1,10 @@
|
|||||||
# "quick" / "easy" fixes
|
# general todos
|
||||||
|
|
||||||
- for global initialization cycles, report also starting and ending lines
|
- sum-type ABI/layout polish
|
||||||
|
- consider dynamic tag-width shrinking after the fixed-`u16` ABI has real pressure
|
||||||
|
- consider all-void channel collapse after fallible channels are otherwise stable
|
||||||
|
- define cross-module/global-id ABI determinism before multi-module builds depend on it
|
||||||
|
- keep backed/C enum composition and must-consume fallible linting as later policy work
|
||||||
|
|
||||||
# milestones
|
# milestones
|
||||||
|
|
||||||
@@ -590,17 +594,9 @@
|
|||||||
spelling for void-payload variants
|
spelling for void-payload variants
|
||||||
- named fallible signatures can use inline unbacked enum and `union(enum)` error types
|
- named fallible signatures can use inline unbacked enum and `union(enum)` error types
|
||||||
after `!`
|
after `!`
|
||||||
- qualified same-name disambiguation remains deferred; existing ambiguous fallible return
|
|
||||||
diagnostics cover the current surface
|
|
||||||
|
|
||||||
23.7. anonymous struct payloads and keyed payload sugar
|
23.7. anonymous struct payloads and keyed payload sugar
|
||||||
|
|
||||||
23.8. sum-type ABI/layout polish
|
|
||||||
- consider dynamic tag-width shrinking after the fixed-`u16` ABI has real pressure
|
|
||||||
- consider all-void channel collapse after fallible channels are otherwise stable
|
|
||||||
- define cross-module/global-id ABI determinism before multi-module builds depend on it
|
|
||||||
- keep backed/C enum composition and must-consume fallible linting as later policy work
|
|
||||||
|
|
||||||
24. dynamic heap allocation
|
24. dynamic heap allocation
|
||||||
- see below for direction
|
- see below for direction
|
||||||
- notes below are too big in scope for a first pass and the language is not mature enough to support it yet
|
- notes below are too big in scope for a first pass and the language is not mature enough to support it yet
|
||||||
|
|||||||
@@ -7088,6 +7088,28 @@ Cycle_Frame :: struct {
|
|||||||
next_dependency: int,
|
next_dependency: int,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cycle_global_location :: proc(checker: ^Checker, global_id: hir.Global_Id) -> (name, path: string, line: int) {
|
||||||
|
name = "<unknown>"
|
||||||
|
path = "<unknown>"
|
||||||
|
line = 1
|
||||||
|
index := int(global_id)
|
||||||
|
if global_id == hir.INVALID_GLOBAL || index >= len(checker.module.globals) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
name = symbol_text(checker, checker.module.globals[global_id].name)
|
||||||
|
if index >= len(checker.ast_module.globals) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
span := checker.ast_module.globals[global_id].span
|
||||||
|
source_file := source.source_for_span(checker.diagnostics, span)
|
||||||
|
if source_file == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
path = source_file.path
|
||||||
|
line, _ = source.line_and_column(source_file, span.start)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
detect_global_cycles_visit :: proc(checker: ^Checker, global_id: hir.Global_Id, states: []u8) {
|
detect_global_cycles_visit :: proc(checker: ^Checker, global_id: hir.Global_Id, states: []u8) {
|
||||||
if states[global_id] == 2 {
|
if states[global_id] == 2 {
|
||||||
return
|
return
|
||||||
@@ -7120,11 +7142,18 @@ detect_global_cycles_visit :: proc(checker: ^Checker, global_id: hir.Global_Id,
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if states[dependency] == 1 {
|
if states[dependency] == 1 {
|
||||||
|
start_name, start_path, start_line := cycle_global_location(checker, dependency)
|
||||||
|
end_name, end_path, end_line := cycle_global_location(checker, frame.global)
|
||||||
id := source.addf(
|
id := source.addf(
|
||||||
checker.diagnostics,
|
checker.diagnostics,
|
||||||
checker.ast_module.globals[dependency].span,
|
checker.ast_module.globals[dependency].span,
|
||||||
"global initialization cycle involving '%s'",
|
"global initialization cycle from '%s' at %s:%d to '%s' at %s:%d",
|
||||||
symbol_text(checker, checker.module.globals[dependency].name),
|
start_name,
|
||||||
|
start_path,
|
||||||
|
start_line,
|
||||||
|
end_name,
|
||||||
|
end_path,
|
||||||
|
end_line,
|
||||||
)
|
)
|
||||||
checker.module.globals[dependency].diagnostic = id
|
checker.module.globals[dependency].diagnostic = id
|
||||||
checker.module.globals[dependency].problematic = true
|
checker.module.globals[dependency].problematic = true
|
||||||
|
|||||||
@@ -4684,6 +4684,33 @@ hundred_thousand_term_runtime_addition_uses_iterative_pipeline :: proc(t: ^testi
|
|||||||
testing.expect(t, instruction_count > 100_000)
|
testing.expect(t, instruction_count > 100_000)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(test)
|
||||||
|
global_cycle_diagnostic_reports_endpoint_lines :: proc(t: ^testing.T) {
|
||||||
|
text := `a i32 :: b
|
||||||
|
b i32 :: a
|
||||||
|
main func() void {}
|
||||||
|
`
|
||||||
|
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)
|
||||||
|
|
||||||
|
found := false
|
||||||
|
for _, diagnostic_index in diagnostics.items {
|
||||||
|
message := source.format(&diagnostics, source.diagnostic_id(diagnostic_index))
|
||||||
|
found = found || strings.contains(message, "global initialization cycle from 'a' at test.bro:1 to 'b' at test.bro:2")
|
||||||
|
delete(message)
|
||||||
|
}
|
||||||
|
testing.expect(t, found)
|
||||||
|
}
|
||||||
|
|
||||||
@(test)
|
@(test)
|
||||||
deep_global_cycle_detection_uses_iterative_dfs :: proc(t: ^testing.T) {
|
deep_global_cycle_detection_uses_iterative_dfs :: proc(t: ^testing.T) {
|
||||||
count := 50_000
|
count := 50_000
|
||||||
|
|||||||
Reference in New Issue
Block a user