favor return over return _ (void return); newline/closing terminates
This commit is contained in:
+2
-2
@@ -89,9 +89,9 @@ fields. `_` is not a keyword member name.
|
||||
- boolean `if` / `else if` / `else`, braceless single-statement branches, and optional parenthesized conditions
|
||||
- `while` loops with optional post-iteration update clauses
|
||||
- `for` loops over ranges, arrays, slices, and pointers-to-arrays with copy captures, pointer captures `|@item|`, and optional `usize` index captures
|
||||
- `break`, `continue`, labeled `break :label`, labeled `continue :label`, and labeled plain blocks
|
||||
- `break`, `continue`, labeled `break :label`, labeled `continue :label`, and labeled plain blocks; `break :label` can cross nested scopes to exit a labeled block
|
||||
- bare block scopes, `defer`, and fallible-function `errdefer` with optional error capture; cleanup is block-scoped and LIFO
|
||||
- value blocks, value `if`, value loops, value `match`, `yield`, and labeled `yield :label value`
|
||||
- bare void `return`, same-line `return value`, value blocks, value `if`, value loops, value `match`, and strictly value-producing `yield value` / `yield :label value`
|
||||
- `match` statements/expressions over enums, tagged unions, and scalars, including exhaustiveness checks, payload captures, pointer payload captures, multi-pattern arms, and scalar range patterns
|
||||
- fallible `try`, fallback `catch`, and `catch |e| { ... }` handler blocks
|
||||
- direct `return match ...` and `yield match ...` value-control-flow operands
|
||||
|
||||
@@ -733,8 +733,8 @@
|
||||
(needs string building), struct field defaults to drop `&[]` on empty lists
|
||||
|
||||
29. fix bugs (implemented)
|
||||
- `return _` was already the supported empty return for void functions; the original
|
||||
bare-`return` report was stale
|
||||
- bare `return` is the empty return for void functions; `yield` always requires a
|
||||
same-line, non-void value because `break` handles valueless scope exits
|
||||
- catch value blocks may end by returning from the function instead of yielding when
|
||||
every path exits
|
||||
- implicit-conversion diagnostics render source-level composite and named types instead
|
||||
@@ -834,6 +834,8 @@
|
||||
- add `debug.print` function making use of `std/io` to print values to the console
|
||||
- this may either require native variadic arguments or a tuple value to like zig's approach (consider pros and cons)
|
||||
|
||||
38. consider renaming `ptr_cast` to `ptrcast`
|
||||
|
||||
## A word on unchecked casts
|
||||
|
||||
For casts that bypass safety checks, Honey provides builtin functions:
|
||||
|
||||
@@ -7516,7 +7516,7 @@ build_block :: proc(
|
||||
local = hir.INVALID_LOCAL, diagnostic = source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
} else if !types.is_void(ctx.result) {
|
||||
id := source.add(checker.diagnostics, statement.span, "'return _' is only valid in a void function")
|
||||
id := source.add(checker.diagnostics, statement.span, "non-void function must return a value")
|
||||
append(&body, hir.stmt_id(len(checker.module.statements)))
|
||||
append(&checker.module.statements, hir.Stmt{
|
||||
kind = .Trap, span = statement.span, expr = hir.INVALID_EXPR,
|
||||
@@ -8118,6 +8118,16 @@ build_block :: proc(
|
||||
} else {
|
||||
yielded = build_expr(checker, statement.expr, ctx.locals^[:], ctx.global_reads, ctx.calls, target.slot_type, ctx.pkg, ctx.file)
|
||||
}
|
||||
if yielded != hir.INVALID_EXPR && types.is_void(checker.module.exprs[yielded].type) {
|
||||
id := source.add(checker.diagnostics, statement.span, "'yield' expression must produce a non-void value")
|
||||
append(&body, hir.stmt_id(len(checker.module.statements)))
|
||||
append(&checker.module.statements, hir.Stmt{
|
||||
kind = .Trap, span = statement.span, expr = hir.INVALID_EXPR,
|
||||
local = hir.INVALID_LOCAL, diagnostic = id,
|
||||
})
|
||||
ctx.problematic^ = true
|
||||
continue
|
||||
}
|
||||
yielded = resolve_loop_slot(ctx, target, yielded, checker.module.exprs[yielded].type if yielded != hir.INVALID_EXPR else types.INVALID, statement.span)
|
||||
if target.slot == hir.INVALID_LOCAL || yielded == hir.INVALID_EXPR {
|
||||
id := source.add(checker.diagnostics, statement.span,
|
||||
@@ -8291,9 +8301,19 @@ build_value_block :: proc(
|
||||
checker, yield_stmt.expr, ctx.locals^[:], ctx.global_reads, ctx.calls,
|
||||
expected, ctx.pkg, ctx.file,
|
||||
)
|
||||
value_type = checker.module.exprs[value].type
|
||||
}
|
||||
value_type = checker.module.exprs[value].type
|
||||
if is_runtime_type(checker, expected) {
|
||||
if types.is_void(value_type) {
|
||||
id := source.add(checker.diagnostics, yield_stmt.span, "'yield' expression must produce a non-void value")
|
||||
value = invalid_hir_expr(checker, yield_stmt.span, id)
|
||||
value_type = types.INVALID
|
||||
ctx.problematic^ = true
|
||||
} else if types.is_void(expected) {
|
||||
id := source.add(checker.diagnostics, yield_stmt.span, "void value context must fall through instead of yielding")
|
||||
value = invalid_hir_expr(checker, yield_stmt.span, id)
|
||||
value_type = types.INVALID
|
||||
ctx.problematic^ = true
|
||||
} else if is_runtime_type(checker, expected) {
|
||||
value = coerce_expr(checker, value, expected, yield_stmt.span)
|
||||
value_type = checker.module.exprs[value].type
|
||||
}
|
||||
@@ -9521,6 +9541,15 @@ build_value_loop :: proc(
|
||||
// The fall-through value initializes the slot before the loop (loop captures are
|
||||
// out of scope here), so the loop completing leaves it as the result.
|
||||
fall_value := build_expr(checker, fall_stmt.expr, ctx.locals^[:], ctx.global_reads, ctx.calls, target.slot_type, ctx.pkg, ctx.file)
|
||||
if fall_value != hir.INVALID_EXPR && types.is_void(checker.module.exprs[fall_value].type) {
|
||||
for s in loop_block {
|
||||
append(body, s)
|
||||
}
|
||||
delete(loop_block, checker.allocator)
|
||||
id := source.add(checker.diagnostics, fall_stmt.span, "'yield' expression must produce a non-void value")
|
||||
ctx.problematic^ = true
|
||||
return invalid_hir_expr(checker, fall_stmt.span, id), types.INVALID
|
||||
}
|
||||
fall_value = resolve_loop_slot(ctx, &target, fall_value, checker.module.exprs[fall_value].type if fall_value != hir.INVALID_EXPR else types.INVALID, fall_stmt.span)
|
||||
if target.slot == hir.INVALID_LOCAL || fall_value == hir.INVALID_EXPR {
|
||||
for s in loop_block {
|
||||
|
||||
@@ -2463,12 +2463,19 @@ ct_exec_statements :: proc(
|
||||
ok = ct_fail(state, .Not_Comptime, statement.span, "'yield' is only valid in a comptime value block")
|
||||
} else if statement.value_control_flow {
|
||||
flow, ok = ct_exec_statements(state, statement.body, true, depth+1)
|
||||
if ok && flow.kind == .Yield && types.is_void(state.values[flow.value].type) {
|
||||
ok = ct_fail(state, .Not_Comptime, statement.span, "'yield' expression must produce a non-void value")
|
||||
}
|
||||
} else {
|
||||
value, expr_flow, expr_ok := ct_eval_expr(state, statement.expr, types.INVALID, depth+1)
|
||||
ok = expr_ok
|
||||
flow = expr_flow
|
||||
if ok && flow.kind == .Normal {
|
||||
flow = ct_flow(.Yield, value, statement.label)
|
||||
if types.is_void(state.values[value].type) {
|
||||
ok = ct_fail(state, .Not_Comptime, statement.span, "'yield' expression must produce a non-void value")
|
||||
} else {
|
||||
flow = ct_flow(.Yield, value, statement.label)
|
||||
}
|
||||
}
|
||||
}
|
||||
case .If:
|
||||
|
||||
+17
-10
@@ -1282,14 +1282,11 @@ finish_statement :: proc(parser: ^Parser, allow_closing_brace := false) -> sourc
|
||||
|
||||
parse_return :: proc(parser: ^Parser) -> ast.Stmt_Id {
|
||||
start := advance(parser)
|
||||
skip_newlines(parser)
|
||||
if current(parser).kind == .Underscore {
|
||||
end := advance(parser)
|
||||
if current(parser).kind == .Newline || current(parser).kind == .Right_Brace || current(parser).kind == .Eof {
|
||||
id := ast.stmt_id(len(parser.module.statements))
|
||||
append(&parser.module.statements, ast.Stmt{
|
||||
kind=.Return,
|
||||
span=span_from(start.span, end.span),
|
||||
name=end.symbol,
|
||||
span=start.span,
|
||||
expr=ast.INVALID_EXPR,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
@@ -1321,12 +1318,10 @@ parse_return :: proc(parser: ^Parser) -> ast.Stmt_Id {
|
||||
return id
|
||||
}
|
||||
|
||||
// `yield <expr>` supplies the value of the enclosing value block. The checker
|
||||
// only accepts it as the final statement of a value block (a `{ ... }` on the
|
||||
// right of a declaration/assignment); it is the block analogue of `return`.
|
||||
// `yield <expr>` supplies a non-void value to an enclosing value construct.
|
||||
// The expression must start on the same line; `break` handles valueless exits.
|
||||
parse_yield :: proc(parser: ^Parser) -> ast.Stmt_Id {
|
||||
start := advance(parser) // consume 'yield'
|
||||
skip_newlines(parser)
|
||||
// `yield :blk x` targets the loop labeled `blk`; a bare `yield x` targets
|
||||
// the directly-enclosing value block / if branch. No expression starts with
|
||||
// ':', so a leading colon is unambiguously a label.
|
||||
@@ -1335,9 +1330,21 @@ parse_yield :: proc(parser: ^Parser) -> ast.Stmt_Id {
|
||||
if name, name_ok := allow(parser, .Identifier); name_ok {
|
||||
label = name.symbol
|
||||
} else {
|
||||
source.add(parser.diagnostics, current(parser).span, "expected a loop label after ':'")
|
||||
source.add(parser.diagnostics, current(parser).span, "expected a yield target label after ':'")
|
||||
}
|
||||
}
|
||||
if current(parser).kind == .Newline || current(parser).kind == .Right_Brace || current(parser).kind == .Eof {
|
||||
expr := invalid_expr(parser, start.span, "'yield' must produce a value")
|
||||
id := ast.stmt_id(len(parser.module.statements))
|
||||
append(&parser.module.statements, ast.Stmt{
|
||||
kind=.Yield,
|
||||
span=start.span,
|
||||
label=label,
|
||||
expr=expr,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
return id
|
||||
}
|
||||
if cf, is_cf := parse_value_control_flow(parser); is_cf {
|
||||
cf_span := parser.module.statements[cf].span
|
||||
body := make([]ast.Stmt_Id, 1, parser.module.allocator)
|
||||
|
||||
+91
-1
@@ -2289,7 +2289,7 @@ return_sink_and_unconsumed_values_have_distinct_hir :: proc(t: ^testing.T) {
|
||||
return 1
|
||||
}
|
||||
done func() void {
|
||||
return _
|
||||
return
|
||||
}
|
||||
main func() void {
|
||||
done()
|
||||
@@ -2327,6 +2327,96 @@ main func() void {
|
||||
testing.expect_value(t, hir_module.statements[main.body[2]].kind, hir.Stmt_Kind.Trap)
|
||||
}
|
||||
|
||||
@(test)
|
||||
bare_returns_and_strict_yields :: proc(t: ^testing.T) {
|
||||
text := `Failure :: enum { bad }
|
||||
noop func() void {}
|
||||
newline_return func() void {
|
||||
return
|
||||
}
|
||||
inline_return func() void { return }
|
||||
fallible_return func() void ! Failure { return }
|
||||
split_return func() i32 {
|
||||
return
|
||||
1
|
||||
}
|
||||
old_return func() void { return _ }
|
||||
missing_yield func() i32 {
|
||||
value :: {
|
||||
yield
|
||||
1
|
||||
}
|
||||
return value
|
||||
}
|
||||
missing_labeled_yield func() i32 {
|
||||
value :: block: {
|
||||
yield :block
|
||||
}
|
||||
return value
|
||||
}
|
||||
void_yield func() i32 {
|
||||
value :: { yield noop() }
|
||||
return value
|
||||
}
|
||||
sink_yield func() i32 {
|
||||
value :: { yield _ }
|
||||
return value
|
||||
}
|
||||
void_context func() void {
|
||||
fallible_return() catch |_| { yield 1 }
|
||||
}
|
||||
bad_comptime :: ${ yield noop() }
|
||||
main func() void {
|
||||
newline_return()
|
||||
inline_return()
|
||||
fallible_return() catch |_| {}
|
||||
_ = split_return()
|
||||
old_return()
|
||||
_ = missing_yield()
|
||||
_ = missing_labeled_yield()
|
||||
_ = void_yield()
|
||||
_ = sink_yield()
|
||||
void_context()
|
||||
}
|
||||
`
|
||||
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)
|
||||
|
||||
testing.expect_value(t, ast_module.statements[ast_module.functions[1].body[0]].expr, ast.INVALID_EXPR)
|
||||
testing.expect_value(t, ast_module.statements[ast_module.functions[2].body[0]].expr, ast.INVALID_EXPR)
|
||||
testing.expect_value(t, ast_module.statements[ast_module.functions[3].body[0]].expr, ast.INVALID_EXPR)
|
||||
testing.expect_value(t, len(ast_module.functions[4].body), 2)
|
||||
|
||||
missing_value := false
|
||||
non_void_function := false
|
||||
void_yields := 0
|
||||
sink_read := false
|
||||
void_context := false
|
||||
for diagnostic in diagnostics.items {
|
||||
missing_value = missing_value || strings.contains(diagnostic.message, "'yield' must produce a value")
|
||||
non_void_function = non_void_function || strings.contains(diagnostic.message, "non-void function must return a value")
|
||||
if strings.contains(diagnostic.message, "'yield' expression must produce a non-void value") {
|
||||
void_yields += 1
|
||||
}
|
||||
sink_read = sink_read || strings.contains(diagnostic.message, "'_' is a write-only sink and cannot be read")
|
||||
void_context = void_context || strings.contains(diagnostic.message, "void value context must fall through instead of yielding")
|
||||
}
|
||||
testing.expect(t, missing_value)
|
||||
testing.expect(t, non_void_function)
|
||||
testing.expect(t, void_yields >= 2)
|
||||
testing.expect(t, sink_read)
|
||||
testing.expect(t, void_context)
|
||||
}
|
||||
|
||||
@(test)
|
||||
unused_locals_and_params_warn_without_traps :: proc(t: ^testing.T) {
|
||||
text := `warn_only func(value i32, unused i32) i32 {
|
||||
|
||||
@@ -23,8 +23,6 @@ _fail_allocator mem.Allocator :: mem.Allocator {
|
||||
vtable = &_fail_vtable,
|
||||
}
|
||||
|
||||
_noop func() void {}
|
||||
|
||||
run func() i32 ! mem.AllocError {
|
||||
values std.ArrayList(i32) = arraylist.init(mem.c_allocator)
|
||||
defer arraylist.deinit(&values)
|
||||
@@ -65,7 +63,6 @@ run func() i32 ! mem.AllocError {
|
||||
failed_as_expected bool = false
|
||||
arraylist.append(&failed, 1) catch |_| {
|
||||
failed_as_expected = true
|
||||
yield _noop()
|
||||
}
|
||||
if (failed_as_expected == false or failed.items.len != 0 or failed.capacity != 0) return 9
|
||||
arraylist.deinit(&failed)
|
||||
|
||||
@@ -266,6 +266,19 @@ stmt_block_escape func() i32 {
|
||||
return hits # 4 + 1000 (defer) = 1004
|
||||
}
|
||||
|
||||
# A labeled block can also be exited through an ordinary nested block.
|
||||
stmt_block_nested func() i32 {
|
||||
hits i32 = 0
|
||||
outer: {
|
||||
{
|
||||
hits = 1
|
||||
break :outer
|
||||
}
|
||||
hits = 100 # skipped by break :outer
|
||||
}
|
||||
return hits
|
||||
}
|
||||
|
||||
# Item B: a `none` yielded before a concrete `yield :blk` that references a block local.
|
||||
lblock_local func() i32 {
|
||||
r :: blk: {
|
||||
@@ -319,6 +332,7 @@ main func() i32 {
|
||||
if (stmt_block(0) != 2) return 132
|
||||
if (stmt_block_escape() != 1004) return 133
|
||||
if (lblock_local() != 9) return 134
|
||||
if (stmt_block_nested() != 1) return 135
|
||||
|
||||
return 42
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ deinit func($T type, list @mut ArrayList(T)) void {
|
||||
|
||||
reserve func($T type, list @mut ArrayList(T), minimum_capacity usize) void ! mem.AllocError {
|
||||
if minimum_capacity <= list.capacity {
|
||||
return _
|
||||
return
|
||||
}
|
||||
|
||||
new_capacity usize = 8
|
||||
@@ -48,7 +48,7 @@ reserve func($T type, list @mut ArrayList(T), minimum_capacity usize) void ! mem
|
||||
}
|
||||
list.items = grown.ptr[..length]
|
||||
list.capacity = new_capacity
|
||||
return _
|
||||
return
|
||||
}
|
||||
|
||||
append func($T type, list @mut ArrayList(T), value T) void ! mem.AllocError {
|
||||
@@ -59,7 +59,7 @@ append func($T type, list @mut ArrayList(T), value T) void ! mem.AllocError {
|
||||
try reserve(list, length + 1)
|
||||
list.items = list.items.ptr[..length + 1]
|
||||
list.items[length] = value
|
||||
return _
|
||||
return
|
||||
}
|
||||
|
||||
clear func($T type, list @mut ArrayList(T)) void {
|
||||
|
||||
+1
-1
@@ -71,7 +71,7 @@ write_all func(writer Writer, bytes []u8) void ! WriteError {
|
||||
}
|
||||
offset += count
|
||||
}
|
||||
return _
|
||||
return
|
||||
}
|
||||
|
||||
_system_read func(_ ?*mut anyopaque, stream ReadStream, buffer []mut u8) usize ! ReadError {
|
||||
|
||||
@@ -44,7 +44,7 @@ _append func(tokens @mut std.ArrayList(Token), kind Kind, start, end usize) void
|
||||
length = end - start,
|
||||
kind = kind,
|
||||
})
|
||||
return _
|
||||
return
|
||||
}
|
||||
|
||||
lex func(source []u8, tokens @mut std.ArrayList(Token)) void ! mem.AllocError {
|
||||
@@ -92,7 +92,7 @@ lex func(source []u8, tokens @mut std.ArrayList(Token)) void ! mem.AllocError {
|
||||
}
|
||||
}
|
||||
try _append(tokens, .eof, cursor, cursor)
|
||||
return _
|
||||
return
|
||||
}
|
||||
|
||||
_kind_name func(kind Kind) *c_char {
|
||||
|
||||
@@ -25,7 +25,7 @@ deinit func($T type, list @mut ArrayList(T)) void {
|
||||
|
||||
reserve func($T type, list @mut ArrayList(T), minimum_capacity usize) void ! mem.AllocError {
|
||||
if minimum_capacity <= list.capacity {
|
||||
return _
|
||||
return
|
||||
}
|
||||
|
||||
new_capacity usize = 8
|
||||
@@ -48,7 +48,7 @@ reserve func($T type, list @mut ArrayList(T), minimum_capacity usize) void ! mem
|
||||
}
|
||||
list.items = grown.ptr[..length]
|
||||
list.capacity = new_capacity
|
||||
return _
|
||||
return
|
||||
}
|
||||
|
||||
append func($T type, list @mut ArrayList(T), value T) void ! mem.AllocError {
|
||||
@@ -59,7 +59,7 @@ append func($T type, list @mut ArrayList(T), value T) void ! mem.AllocError {
|
||||
try reserve(list, length + 1)
|
||||
list.items = list.items.ptr[..length + 1]
|
||||
list.items[length] = value
|
||||
return _
|
||||
return
|
||||
}
|
||||
|
||||
clear func($T type, list @mut ArrayList(T)) void {
|
||||
|
||||
@@ -71,7 +71,7 @@ write_all func(writer Writer, bytes []u8) void ! WriteError {
|
||||
}
|
||||
offset += count
|
||||
}
|
||||
return _
|
||||
return
|
||||
}
|
||||
|
||||
_system_read func(_ ?*mut anyopaque, stream ReadStream, buffer []mut u8) usize ! ReadError {
|
||||
|
||||
@@ -6,7 +6,7 @@ main func() void {
|
||||
|
||||
data :: mem.alloc(u8, allocator, 24) catch |_| {
|
||||
_ = c.printf("Failed to allocate memory\n")
|
||||
return _
|
||||
return
|
||||
}
|
||||
defer mem.free(u8, allocator, data)
|
||||
|
||||
|
||||
@@ -262,15 +262,13 @@ module.exports = grammar({
|
||||
|
||||
expression_statement: $ => $.expression,
|
||||
|
||||
return_statement: $ => seq(
|
||||
return_statement: $ => prec.right(seq(
|
||||
'return',
|
||||
repeat($._newline),
|
||||
field('value', $._value),
|
||||
),
|
||||
optional(field('value', $._value)),
|
||||
)),
|
||||
|
||||
yield_statement: $ => seq(
|
||||
'yield',
|
||||
repeat($._newline),
|
||||
optional(seq(':', field('label', $.identifier))),
|
||||
field('value', $._value),
|
||||
),
|
||||
|
||||
@@ -1714,28 +1714,33 @@
|
||||
"name": "expression"
|
||||
},
|
||||
"return_statement": {
|
||||
"type": "SEQ",
|
||||
"members": [
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "return"
|
||||
},
|
||||
{
|
||||
"type": "REPEAT",
|
||||
"content": {
|
||||
"type": "SYMBOL",
|
||||
"name": "_newline"
|
||||
"type": "PREC_RIGHT",
|
||||
"value": 0,
|
||||
"content": {
|
||||
"type": "SEQ",
|
||||
"members": [
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "return"
|
||||
},
|
||||
{
|
||||
"type": "CHOICE",
|
||||
"members": [
|
||||
{
|
||||
"type": "FIELD",
|
||||
"name": "value",
|
||||
"content": {
|
||||
"type": "SYMBOL",
|
||||
"name": "_value"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "BLANK"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "FIELD",
|
||||
"name": "value",
|
||||
"content": {
|
||||
"type": "SYMBOL",
|
||||
"name": "_value"
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"yield_statement": {
|
||||
"type": "SEQ",
|
||||
@@ -1744,13 +1749,6 @@
|
||||
"type": "STRING",
|
||||
"value": "yield"
|
||||
},
|
||||
{
|
||||
"type": "REPEAT",
|
||||
"content": {
|
||||
"type": "SYMBOL",
|
||||
"name": "_newline"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "CHOICE",
|
||||
"members": [
|
||||
|
||||
@@ -1667,7 +1667,7 @@
|
||||
"fields": {
|
||||
"value": {
|
||||
"multiple": false,
|
||||
"required": true,
|
||||
"required": false,
|
||||
"types": [
|
||||
{
|
||||
"type": "block",
|
||||
|
||||
+92004
-94856
File diff suppressed because it is too large
Load Diff
@@ -154,3 +154,56 @@ work func() i32 ! Failure {
|
||||
(return_statement
|
||||
(expression
|
||||
(integer)))))))
|
||||
|
||||
==================
|
||||
Bare return and value yield
|
||||
==================
|
||||
|
||||
done func() void {
|
||||
return
|
||||
}
|
||||
|
||||
inline func() void { return }
|
||||
|
||||
choose func() i32 {
|
||||
result :: { yield 1 }
|
||||
return result
|
||||
}
|
||||
|
||||
---
|
||||
|
||||
(source_file
|
||||
(function_declaration
|
||||
(identifier)
|
||||
(parameter_list)
|
||||
(type
|
||||
(builtin_type))
|
||||
(block
|
||||
(statement
|
||||
(return_statement))))
|
||||
(function_declaration
|
||||
(identifier)
|
||||
(parameter_list)
|
||||
(type
|
||||
(builtin_type))
|
||||
(block
|
||||
(statement
|
||||
(return_statement))))
|
||||
(function_declaration
|
||||
(identifier)
|
||||
(parameter_list)
|
||||
(type
|
||||
(builtin_type))
|
||||
(block
|
||||
(statement
|
||||
(constant_declaration
|
||||
(identifier)
|
||||
(block
|
||||
(statement
|
||||
(yield_statement
|
||||
(expression
|
||||
(integer)))))))
|
||||
(statement
|
||||
(return_statement
|
||||
(expression
|
||||
(identifier)))))))
|
||||
|
||||
Reference in New Issue
Block a user