make record fields resolve type-factory calls before runtime validation
This commit is contained in:
@@ -1189,7 +1189,12 @@ type_from_syntax :: proc(
|
||||
}
|
||||
return composed
|
||||
case .Type_Call:
|
||||
return resolve_type_factory_call(checker, ast.Expr_Id(item.count_expr), pkg, file)
|
||||
expr_id := ast.Expr_Id(item.count_expr)
|
||||
call_file := file
|
||||
if expr_id != ast.INVALID_EXPR && int(expr_id) < len(checker.ast_module.exprs) {
|
||||
call_file = ast.File_Id(checker.ast_module.exprs[expr_id].span.file)
|
||||
}
|
||||
return resolve_type_factory_call(checker, expr_id, pkg, call_file)
|
||||
}
|
||||
if changed {
|
||||
return types.intern(store, item)
|
||||
@@ -4072,6 +4077,25 @@ validate_meta_schema :: proc(checker: ^Checker) {
|
||||
}
|
||||
|
||||
validate_type_nodes :: proc(checker: ^Checker) {
|
||||
node_count := len(checker.module.types.nodes)
|
||||
for index in 0..<node_count {
|
||||
item := checker.module.types.nodes[index]
|
||||
if item.kind != .Struct && item.kind != .Union {
|
||||
continue
|
||||
}
|
||||
start := int(item.field_start)
|
||||
end := start+int(item.field_count)
|
||||
if start < 0 || end > len(checker.module.types.fields) {
|
||||
continue
|
||||
}
|
||||
for slot in start..<end {
|
||||
resolved := type_from_syntax(
|
||||
checker, checker.module.types.fields[slot].type,
|
||||
ast.Package_Id(item.pkg), ast.File_Id(item.file),
|
||||
)
|
||||
checker.module.types.fields[slot].type = resolved
|
||||
}
|
||||
}
|
||||
validate_meta_schema(checker)
|
||||
for item, index in checker.module.types.nodes {
|
||||
id := types.DYNAMIC_START+types.Type(index)
|
||||
|
||||
@@ -2176,7 +2176,11 @@ parse_if :: proc(parser: ^Parser) -> ast.Stmt_Id {
|
||||
else_body: []ast.Stmt_Id = nil
|
||||
saved_cursor := parser.cursor
|
||||
skip_newlines(parser)
|
||||
if current(parser).kind == .Keyword_Else {
|
||||
// `else:` (and the reserved `else |...|:` shape) starts the next match arm;
|
||||
// it is not the else-branch of a brace-less if used as the previous arm body.
|
||||
match_arm_else := current(parser).kind == .Keyword_Else &&
|
||||
(peek(parser).kind == .Colon || peek(parser).kind == .Pipe)
|
||||
if current(parser).kind == .Keyword_Else && !match_arm_else {
|
||||
advance(parser)
|
||||
skip_newlines(parser)
|
||||
if current(parser).kind == .Keyword_If {
|
||||
|
||||
@@ -9648,6 +9648,38 @@ main func() void {
|
||||
testing.expect_value(t, len(unwrap_if.body), 1)
|
||||
}
|
||||
|
||||
@(test)
|
||||
match_else_arm_is_not_captured_by_braceless_if :: proc(t: ^testing.T) {
|
||||
text := `ready func() bool { return true }
|
||||
main func() void {
|
||||
value i32 = 0
|
||||
match value {
|
||||
0: if ready() _ = value
|
||||
else: _ = value
|
||||
}
|
||||
}
|
||||
`
|
||||
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)
|
||||
module := parser.parse(&stream, &source_file, &diagnostics)
|
||||
defer ast.destroy_module(&module)
|
||||
|
||||
testing.expect_value(t, len(diagnostics.items), 0)
|
||||
match_statement := module.statements[module.functions[1].body[1]]
|
||||
testing.expect_value(t, len(match_statement.body), 2)
|
||||
first_arm := module.statements[match_statement.body[0]]
|
||||
first_if := module.statements[first_arm.body[0]]
|
||||
testing.expect_value(t, first_if.kind, ast.Stmt_Kind.If)
|
||||
testing.expect_value(t, len(first_if.else_body), 0)
|
||||
else_arm := module.statements[match_statement.body[1]]
|
||||
testing.expect_value(t, len(else_arm.patterns), 0)
|
||||
}
|
||||
|
||||
@(test)
|
||||
parser_diagnoses_braceless_if_without_parens_or_call :: proc(t: ^testing.T) {
|
||||
text := `main func() void {
|
||||
|
||||
@@ -2,6 +2,14 @@ arraylist :: import "@std/arraylist"
|
||||
mem :: import "@std/mem"
|
||||
std :: import "@std"
|
||||
|
||||
Token :: struct { value i32 }
|
||||
ScanDiagnostic :: struct { value i32 }
|
||||
|
||||
State :: struct {
|
||||
tokens std.ArrayList(Token)
|
||||
diagnostics std.ArrayList(ScanDiagnostic)
|
||||
}
|
||||
|
||||
hide fail_alloc func(_ ?@mut anyopaque, _ usize, _ usize) ?*mut u8 {
|
||||
return none
|
||||
}
|
||||
|
||||
@@ -34,19 +34,15 @@ expect_equal func($T type, expected, actual T, location SourceLocation) void ! E
|
||||
return .expectation_failed
|
||||
}
|
||||
}
|
||||
.slice: {
|
||||
if !mem.eql(expected, actual) {
|
||||
.slice: if !mem.eql(expected, actual) {
|
||||
debug.print("{s}:{d}:{d}: expected and actual slices differ\n", {location.file, location.line, location.column})
|
||||
return .expectation_failed
|
||||
}
|
||||
}
|
||||
else: {
|
||||
if expected != actual {
|
||||
else: if expected != actual {
|
||||
debug.print("{s}:{d}:{d}: expected {}, found {}\n", {location.file, location.line, location.column, expected, actual})
|
||||
return .expectation_failed
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
expect_type func($Expected, $Actual type, _ Actual, location SourceLocation) void ! Error {
|
||||
|
||||
Reference in New Issue
Block a user