bug fix
This commit is contained in:
@@ -11570,7 +11570,7 @@ build_block :: proc(
|
|||||||
item_local := append_build_local(ctx, statement.name, capture_type, false, statement.span)
|
item_local := append_build_local(ctx, statement.name, capture_type, false, statement.span)
|
||||||
index_local := hir.INVALID_LOCAL
|
index_local := hir.INVALID_LOCAL
|
||||||
if symbol.is_valid(statement.index_name) {
|
if symbol.is_valid(statement.index_name) {
|
||||||
if statement.index_name == statement.name {
|
if statement.index_name == statement.name && statement.name != checker.sink_symbol {
|
||||||
diagnostic = source.add(checker.diagnostics, statement.span, "for-loop captures must have distinct names")
|
diagnostic = source.add(checker.diagnostics, statement.span, "for-loop captures must have distinct names")
|
||||||
valid_loop = false
|
valid_loop = false
|
||||||
} else if id := add_shadow_diagnostic(
|
} else if id := add_shadow_diagnostic(
|
||||||
|
|||||||
@@ -2480,15 +2480,17 @@ parse_for :: proc(parser: ^Parser) -> ast.Stmt_Id {
|
|||||||
if _, ok := allow(parser, .At); ok {
|
if _, ok := allow(parser, .At); ok {
|
||||||
pointer_capture = true
|
pointer_capture = true
|
||||||
}
|
}
|
||||||
item, item_ok := allow(parser, .Identifier)
|
item := current(parser)
|
||||||
if item_ok {
|
if item.kind == .Identifier || item.kind == .Underscore {
|
||||||
|
advance(parser)
|
||||||
item_name = item.symbol
|
item_name = item.symbol
|
||||||
} else {
|
} else {
|
||||||
source.add(parser.diagnostics, current(parser).span, "expected a for-loop item capture")
|
source.add(parser.diagnostics, current(parser).span, "expected a for-loop item capture")
|
||||||
}
|
}
|
||||||
if _, ok := allow(parser, .Comma); ok {
|
if _, ok := allow(parser, .Comma); ok {
|
||||||
index, index_ok := allow(parser, .Identifier)
|
index := current(parser)
|
||||||
if index_ok {
|
if index.kind == .Identifier || index.kind == .Underscore {
|
||||||
|
advance(parser)
|
||||||
index_name = index.symbol
|
index_name = index.symbol
|
||||||
} else {
|
} else {
|
||||||
source.add(parser.diagnostics, current(parser).span, "expected an index capture after ','")
|
source.add(parser.diagnostics, current(parser).span, "expected an index capture after ','")
|
||||||
@@ -2538,7 +2540,11 @@ parse_while :: proc(parser: ^Parser) -> ast.Stmt_Id {
|
|||||||
skip_newlines(parser)
|
skip_newlines(parser)
|
||||||
}
|
}
|
||||||
label := parse_optional_loop_label(parser)
|
label := parse_optional_loop_label(parser)
|
||||||
body := parse_block(parser)
|
body := parse_control_body(
|
||||||
|
parser,
|
||||||
|
condition,
|
||||||
|
"a brace-less 'while' body requires the condition to be parenthesized unless it is a function call",
|
||||||
|
)
|
||||||
|
|
||||||
id := ast.stmt_id(len(parser.module.statements))
|
id := ast.stmt_id(len(parser.module.statements))
|
||||||
append(&parser.module.statements, ast.Stmt{
|
append(&parser.module.statements, ast.Stmt{
|
||||||
|
|||||||
+127
-2
@@ -10670,6 +10670,88 @@ parser_diagnoses_braceless_if_without_parens_or_call :: proc(t: ^testing.T) {
|
|||||||
testing.expect(t, strings.contains(diagnostics.items[0].message, "parenthesized"))
|
testing.expect(t, strings.contains(diagnostics.items[0].message, "parenthesized"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(test)
|
||||||
|
parser_accepts_braceless_while_bodies :: proc(t: ^testing.T) {
|
||||||
|
text := `ready func() bool { return false }
|
||||||
|
main func() void {
|
||||||
|
cursor i32 = 0
|
||||||
|
while (cursor < 1) cursor += 1
|
||||||
|
while ready() cursor += 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)
|
||||||
|
module := parser.parse(&stream, &source_file, &diagnostics)
|
||||||
|
defer ast.destroy_module(&module)
|
||||||
|
|
||||||
|
testing.expect_value(t, len(diagnostics.items), 0)
|
||||||
|
main := module.functions[1]
|
||||||
|
testing.expect_value(t, len(main.body), 3)
|
||||||
|
|
||||||
|
parenthesized := module.statements[main.body[1]]
|
||||||
|
testing.expect_value(t, parenthesized.kind, ast.Stmt_Kind.While)
|
||||||
|
testing.expect_value(t, len(parenthesized.body), 1)
|
||||||
|
testing.expect(t, module.exprs[parenthesized.expr].parenthesized)
|
||||||
|
|
||||||
|
call := module.statements[main.body[2]]
|
||||||
|
testing.expect_value(t, call.kind, ast.Stmt_Kind.While)
|
||||||
|
testing.expect_value(t, len(call.body), 1)
|
||||||
|
testing.expect_value(t, module.exprs[call.expr].kind, ast.Expr_Kind.Call)
|
||||||
|
}
|
||||||
|
|
||||||
|
@(test)
|
||||||
|
parser_diagnoses_braceless_while_without_parens_or_call :: proc(t: ^testing.T) {
|
||||||
|
text := `main func() void {
|
||||||
|
cursor i32 = 0
|
||||||
|
limit i32 = 1
|
||||||
|
while cursor < limit cursor += 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)
|
||||||
|
module := parser.parse(&stream, &source_file, &diagnostics)
|
||||||
|
defer ast.destroy_module(&module)
|
||||||
|
|
||||||
|
testing.expect_value(t, len(diagnostics.items), 1)
|
||||||
|
testing.expect_value(
|
||||||
|
t,
|
||||||
|
diagnostics.items[0].message,
|
||||||
|
"a brace-less 'while' body requires the condition to be parenthesized unless it is a function call",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@(test)
|
||||||
|
braceless_while_compiles_and_runs :: proc(t: ^testing.T) {
|
||||||
|
directory := "/tmp/brolang-test-braceless-while"
|
||||||
|
main_path := "/tmp/brolang-test-braceless-while/main.bro"
|
||||||
|
output := "/tmp/brolang-test-braceless-while-output"
|
||||||
|
text := `main func() i32 {
|
||||||
|
cursor i32 = 0
|
||||||
|
while (cursor < 42) cursor += 1
|
||||||
|
return cursor
|
||||||
|
}
|
||||||
|
`
|
||||||
|
_ = os2.remove_all(directory)
|
||||||
|
defer _ = os2.remove_all(directory)
|
||||||
|
defer _ = os.remove(output)
|
||||||
|
testing.expect(t, os.make_directory(directory) == nil)
|
||||||
|
testing.expect(t, os.write_entire_file(main_path, transmute([]byte)text))
|
||||||
|
status := compiler_core.compile_package(directory, output)
|
||||||
|
testing.expect_value(t, status, 0)
|
||||||
|
state := run_executable(output)
|
||||||
|
testing.expect_value(t, state.exit_code, 42)
|
||||||
|
}
|
||||||
|
|
||||||
@(test)
|
@(test)
|
||||||
parser_diagnoses_braceless_for_and_unwrap_without_parens_or_call :: proc(t: ^testing.T) {
|
parser_diagnoses_braceless_for_and_unwrap_without_parens_or_call :: proc(t: ^testing.T) {
|
||||||
text := `main func() void {
|
text := `main func() void {
|
||||||
@@ -11179,6 +11261,49 @@ for_loop_tokens_and_parser_capture_range_shape :: proc(t: ^testing.T) {
|
|||||||
testing.expect_value(t, module.exprs[third.expr].integer, u64(1))
|
testing.expect_value(t, module.exprs[third.expr].integer, u64(1))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(test)
|
||||||
|
for_loop_sink_captures_are_throwaways :: proc(t: ^testing.T) {
|
||||||
|
text := `main func() void {
|
||||||
|
for 0..1 |_| {}
|
||||||
|
for [1] |_, _| {}
|
||||||
|
for 0..1 |unused_capture| {}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
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)
|
||||||
|
|
||||||
|
found_unused_capture := false
|
||||||
|
warning_count := 0
|
||||||
|
error_count := 0
|
||||||
|
for diagnostic in diagnostics.items {
|
||||||
|
if diagnostic.severity == source.Severity.Warning {
|
||||||
|
warning_count += 1
|
||||||
|
} else {
|
||||||
|
error_count += 1
|
||||||
|
}
|
||||||
|
found_unused_capture =
|
||||||
|
found_unused_capture ||
|
||||||
|
strings.contains(diagnostic.message, "unused local 'unused_capture'")
|
||||||
|
testing.expect(t, !strings.contains(diagnostic.message, "unused local '_'"))
|
||||||
|
testing.expect(t, !strings.contains(diagnostic.message, "distinct names"))
|
||||||
|
testing.expect(t, !strings.contains(diagnostic.message, "expected a for-loop"))
|
||||||
|
}
|
||||||
|
testing.expect_value(t, warning_count, 1)
|
||||||
|
testing.expect_value(t, error_count, 0)
|
||||||
|
testing.expect(t, found_unused_capture)
|
||||||
|
}
|
||||||
|
|
||||||
@(test)
|
@(test)
|
||||||
parser_accepts_braceless_for_bodies :: proc(t: ^testing.T) {
|
parser_accepts_braceless_for_bodies :: proc(t: ^testing.T) {
|
||||||
text := `make_items func() [2]i32 { return [1, 2] }
|
text := `make_items func() [2]i32 { return [1, 2] }
|
||||||
@@ -14956,12 +15081,12 @@ main func() i32 {
|
|||||||
})
|
})
|
||||||
if !map.present[0] or !map.present[1] or map.present[2] { return 9 }
|
if !map.present[0] or !map.present[1] or map.present[2] { return 9 }
|
||||||
if map.values[0].len != 10 or map.values[1].len != 7 { return 18 }
|
if map.values[0].len != 10 or map.values[1].len != 7 { return 18 }
|
||||||
ident :: get(TokenKind, []u8, &map, TokenKind.ident)
|
ident :: get(&map, TokenKind.ident)
|
||||||
if ident == null or null == ident { return 19 }
|
if ident == null or null == ident { return 19 }
|
||||||
if ident |value| {
|
if ident |value| {
|
||||||
if value.len != 10 { return 19 }
|
if value.len != 10 { return 19 }
|
||||||
} else { return 20 }
|
} else { return 20 }
|
||||||
eof :: get(TokenKind, []u8, &map, TokenKind.eof)
|
eof :: get(&map, TokenKind.eof)
|
||||||
if eof != null or null != eof { return 21 }
|
if eof != null or null != eof { return 21 }
|
||||||
location testing.SourceLocation = {file = "test.bro", line = 1, column = 1}
|
location testing.SourceLocation = {file = "test.bro", line = 1, column = 1}
|
||||||
testing.expect_equal(null, eof, location) catch |_| { return 24 }
|
testing.expect_equal(null, eof, location) catch |_| { return 24 }
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ module.exports = grammar({
|
|||||||
[$.field_initializer, $.expression],
|
[$.field_initializer, $.expression],
|
||||||
[$.capture_list, $.expression],
|
[$.capture_list, $.expression],
|
||||||
[$.match_capture, $.expression],
|
[$.match_capture, $.expression],
|
||||||
|
[$.statement, $.labeled_block],
|
||||||
],
|
],
|
||||||
|
|
||||||
rules: {
|
rules: {
|
||||||
@@ -366,8 +367,8 @@ module.exports = grammar({
|
|||||||
seq('(', repeat($._newline), choice($.assignment_statement, $.expression_statement), repeat($._newline), ')'),
|
seq('(', repeat($._newline), choice($.assignment_statement, $.expression_statement), repeat($._newline), ')'),
|
||||||
)))),
|
)))),
|
||||||
repeat($._newline),
|
repeat($._newline),
|
||||||
optional(seq(field('label', $.identifier), ':', repeat($._newline))),
|
optional(prec(1, seq(field('label', $.identifier), ':', repeat($._newline)))),
|
||||||
field('body', $.block),
|
field('body', $._branch_body),
|
||||||
),
|
),
|
||||||
|
|
||||||
for_statement: $ => seq(
|
for_statement: $ => seq(
|
||||||
@@ -378,8 +379,8 @@ module.exports = grammar({
|
|||||||
repeat($._newline),
|
repeat($._newline),
|
||||||
$._for_capture_bar,
|
$._for_capture_bar,
|
||||||
optional('@'),
|
optional('@'),
|
||||||
field('item', $.identifier),
|
field('item', choice($.identifier, $.sink)),
|
||||||
optional(seq(',', field('index', $.identifier))),
|
optional(seq(',', field('index', choice($.identifier, $.sink)))),
|
||||||
$._for_capture_bar,
|
$._for_capture_bar,
|
||||||
repeat($._newline),
|
repeat($._newline),
|
||||||
optional(seq(field('label', $.identifier), ':', repeat($._newline))),
|
optional(seq(field('label', $.identifier), ':', repeat($._newline))),
|
||||||
|
|||||||
@@ -2581,28 +2581,32 @@
|
|||||||
"type": "CHOICE",
|
"type": "CHOICE",
|
||||||
"members": [
|
"members": [
|
||||||
{
|
{
|
||||||
"type": "SEQ",
|
"type": "PREC",
|
||||||
"members": [
|
"value": 1,
|
||||||
{
|
"content": {
|
||||||
"type": "FIELD",
|
"type": "SEQ",
|
||||||
"name": "label",
|
"members": [
|
||||||
"content": {
|
{
|
||||||
"type": "SYMBOL",
|
"type": "FIELD",
|
||||||
"name": "identifier"
|
"name": "label",
|
||||||
|
"content": {
|
||||||
|
"type": "SYMBOL",
|
||||||
|
"name": "identifier"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "STRING",
|
||||||
|
"value": ":"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "REPEAT",
|
||||||
|
"content": {
|
||||||
|
"type": "SYMBOL",
|
||||||
|
"name": "_newline"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
]
|
||||||
{
|
}
|
||||||
"type": "STRING",
|
|
||||||
"value": ":"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "REPEAT",
|
|
||||||
"content": {
|
|
||||||
"type": "SYMBOL",
|
|
||||||
"name": "_newline"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "BLANK"
|
"type": "BLANK"
|
||||||
@@ -2614,7 +2618,7 @@
|
|||||||
"name": "body",
|
"name": "body",
|
||||||
"content": {
|
"content": {
|
||||||
"type": "SYMBOL",
|
"type": "SYMBOL",
|
||||||
"name": "block"
|
"name": "_branch_body"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -2680,8 +2684,17 @@
|
|||||||
"type": "FIELD",
|
"type": "FIELD",
|
||||||
"name": "item",
|
"name": "item",
|
||||||
"content": {
|
"content": {
|
||||||
"type": "SYMBOL",
|
"type": "CHOICE",
|
||||||
"name": "identifier"
|
"members": [
|
||||||
|
{
|
||||||
|
"type": "SYMBOL",
|
||||||
|
"name": "identifier"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "SYMBOL",
|
||||||
|
"name": "sink"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -2698,8 +2711,17 @@
|
|||||||
"type": "FIELD",
|
"type": "FIELD",
|
||||||
"name": "index",
|
"name": "index",
|
||||||
"content": {
|
"content": {
|
||||||
"type": "SYMBOL",
|
"type": "CHOICE",
|
||||||
"name": "identifier"
|
"members": [
|
||||||
|
{
|
||||||
|
"type": "SYMBOL",
|
||||||
|
"name": "identifier"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "SYMBOL",
|
||||||
|
"name": "sink"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -5520,6 +5542,10 @@
|
|||||||
[
|
[
|
||||||
"match_capture",
|
"match_capture",
|
||||||
"expression"
|
"expression"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"statement",
|
||||||
|
"labeled_block"
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
"precedences": [],
|
"precedences": [],
|
||||||
|
|||||||
@@ -994,6 +994,10 @@
|
|||||||
{
|
{
|
||||||
"type": "identifier",
|
"type": "identifier",
|
||||||
"named": true
|
"named": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "sink",
|
||||||
|
"named": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -1004,6 +1008,10 @@
|
|||||||
{
|
{
|
||||||
"type": "identifier",
|
"type": "identifier",
|
||||||
"named": true
|
"named": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "sink",
|
||||||
|
"named": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -2469,7 +2477,7 @@
|
|||||||
"required": true,
|
"required": true,
|
||||||
"types": [
|
"types": [
|
||||||
{
|
{
|
||||||
"type": "block",
|
"type": "statement",
|
||||||
"named": true
|
"named": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
+1873105
-998460
File diff suppressed because it is too large
Load Diff
@@ -800,3 +800,43 @@ clear func($K, $V type) void {
|
|||||||
(identifier)))
|
(identifier)))
|
||||||
(expression
|
(expression
|
||||||
(integer))))))))))
|
(integer))))))))))
|
||||||
|
|
||||||
|
==================
|
||||||
|
Sink for captures and braceless while
|
||||||
|
==================
|
||||||
|
|
||||||
|
main func() void {
|
||||||
|
for [1] |_, _| {}
|
||||||
|
while (true) _ = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
(source_file
|
||||||
|
(function_declaration
|
||||||
|
(identifier)
|
||||||
|
(parameter_list)
|
||||||
|
(type
|
||||||
|
(builtin_type))
|
||||||
|
(block
|
||||||
|
(statement
|
||||||
|
(for_statement
|
||||||
|
(expression
|
||||||
|
(array_literal
|
||||||
|
(expression
|
||||||
|
(integer))))
|
||||||
|
(sink)
|
||||||
|
(sink)
|
||||||
|
(block)))
|
||||||
|
(statement
|
||||||
|
(while_statement
|
||||||
|
(expression
|
||||||
|
(parenthesized_expression
|
||||||
|
(expression
|
||||||
|
(boolean))))
|
||||||
|
(statement
|
||||||
|
(assignment_statement
|
||||||
|
(expression
|
||||||
|
(sink))
|
||||||
|
(expression
|
||||||
|
(integer)))))))))
|
||||||
|
|||||||
Reference in New Issue
Block a user