favor return over return _ (void return); newline/closing terminates
This commit is contained in:
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user