while loops
This commit is contained in:
@@ -631,6 +631,13 @@ mark_block_imports_used :: proc(checker: ^Checker, statements: []ast.Stmt_Id, fi
|
||||
mark_expr_imports_used(checker, statement.expr, file)
|
||||
mark_block_imports_used(checker, statement.body, file)
|
||||
mark_block_imports_used(checker, statement.else_body, file)
|
||||
case .While:
|
||||
mark_expr_imports_used(checker, statement.expr, file)
|
||||
mark_block_imports_used(checker, statement.body, file)
|
||||
if statement.update != ast.INVALID_STMT {
|
||||
update := [1]ast.Stmt_Id{statement.update}
|
||||
mark_block_imports_used(checker, update[:], file)
|
||||
}
|
||||
case .Invalid:
|
||||
}
|
||||
}
|
||||
@@ -1424,6 +1431,13 @@ infer_statements :: proc(
|
||||
infer_statements(checker, statement.body, locals, pkg, file, demanded, result)
|
||||
infer_statements(checker, statement.else_body, locals, pkg, file, demanded, result)
|
||||
}
|
||||
case .While:
|
||||
_ = infer_expr(checker, statement.expr, locals^[:], pkg, file, demanded)
|
||||
infer_statements(checker, statement.body, locals, pkg, file, demanded, result)
|
||||
if statement.update != ast.INVALID_STMT {
|
||||
update := [1]ast.Stmt_Id{statement.update}
|
||||
infer_statements(checker, update[:], locals, pkg, file, demanded, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
resize(locals, scope_start)
|
||||
@@ -2460,7 +2474,13 @@ build_expr :: proc(
|
||||
append(&stack, Build_Expr_Frame{expr=expr.left, expected=types.INVALID, template=ast.INVALID_FUNCTION})
|
||||
case .Add:
|
||||
stack[frame_index].stage = 1
|
||||
append(&stack, Build_Expr_Frame{expr=expr.left, expected=types.INVALID, template=ast.INVALID_FUNCTION})
|
||||
// Preserve assignment/return context for literal operands, e.g.
|
||||
// assigning `i + 1` back into a `u32` local.
|
||||
left_expected := types.INVALID
|
||||
if types.is_concrete_scalar(frame.expected) && !types.is_bool(frame.expected) {
|
||||
left_expected = frame.expected
|
||||
}
|
||||
append(&stack, Build_Expr_Frame{expr=expr.left, expected=left_expected, template=ast.INVALID_FUNCTION})
|
||||
case .Call:
|
||||
if expr.left != ast.INVALID_EXPR {
|
||||
stack[frame_index].stage = 6
|
||||
@@ -2600,6 +2620,12 @@ build_expr :: proc(
|
||||
right_expected := types.INVALID
|
||||
if types.is_many_pointer(checker.module.exprs[last].type, &checker.module.types) {
|
||||
right_expected = types.USIZE
|
||||
} else if eval_constant(checker, expr.right).kind == .Value {
|
||||
// A constant RHS adopts the concrete LHS type before numeric
|
||||
// compatibility is checked.
|
||||
right_expected = checker.module.exprs[last].type
|
||||
} else if types.is_concrete_scalar(frame.expected) && !types.is_bool(frame.expected) {
|
||||
right_expected = frame.expected
|
||||
}
|
||||
append(&stack, Build_Expr_Frame{expr=expr.right, expected=right_expected, template=ast.INVALID_FUNCTION})
|
||||
continue
|
||||
@@ -3067,6 +3093,39 @@ build_block :: proc(ctx: ^Build_Ctx, statements: []ast.Stmt_Id) -> []hir.Stmt_Id
|
||||
diagnostic = source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
ctx.problematic^ = ctx.problematic^ || checker.module.exprs[condition].kind == .Invalid
|
||||
case .While:
|
||||
condition := build_expr(
|
||||
checker, statement.expr, ctx.locals^[:], ctx.global_reads, ctx.calls,
|
||||
types.BOOL, ctx.pkg, ctx.file,
|
||||
)
|
||||
if checker.module.exprs[condition].kind != .Invalid &&
|
||||
!types.is_bool(checker.module.exprs[condition].type) {
|
||||
id := source.add(checker.diagnostics, statement.span, "'while' condition must be a bool")
|
||||
condition = invalid_hir_expr(checker, statement.span, id, types.BOOL)
|
||||
ctx.problematic^ = true
|
||||
}
|
||||
loop_body := build_block(ctx, statement.body)
|
||||
update := hir.INVALID_STMT
|
||||
if statement.update != ast.INVALID_STMT {
|
||||
update_ast := [1]ast.Stmt_Id{statement.update}
|
||||
update_body := build_block(ctx, update_ast[:])
|
||||
if len(update_body) > 0 {
|
||||
update = update_body[0]
|
||||
}
|
||||
delete(update_body, checker.allocator)
|
||||
}
|
||||
append(&body, hir.stmt_id(len(checker.module.statements)))
|
||||
append(&checker.module.statements, hir.Stmt{
|
||||
kind=.While,
|
||||
span=statement.span,
|
||||
expr=condition,
|
||||
then_body=loop_body,
|
||||
update=update,
|
||||
local=hir.INVALID_LOCAL,
|
||||
target=hir.INVALID_EXPR,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
ctx.problematic^ = ctx.problematic^ || checker.module.exprs[condition].kind == .Invalid
|
||||
case .Invalid:
|
||||
append(&body, hir.stmt_id(len(checker.module.statements)))
|
||||
append(&checker.module.statements, hir.Stmt{
|
||||
@@ -3082,8 +3141,9 @@ build_block :: proc(ctx: ^Build_Ctx, statements: []ast.Stmt_Id) -> []hir.Stmt_Id
|
||||
|
||||
// Reports whether every control-flow path through `stmts` terminates (returns or traps),
|
||||
// so the end of the block is unreachable. A `.Return` or `.Trap` terminates outright; an
|
||||
// `.If` terminates only when it has an `else` and both arms terminate. Recursion into the
|
||||
// `then_body`/`else_body` slices handles nested ifs and `else if` chains.
|
||||
// `.If` terminates only when it has an `else` and both arms terminate. A literal
|
||||
// `while true` cannot fall through because the language has no `break` statement.
|
||||
// Recursion into the branch slices handles nested ifs and `else if` chains.
|
||||
all_paths_return :: proc(module: ^hir.Module, stmts: []hir.Stmt_Id) -> bool {
|
||||
for id in stmts {
|
||||
statement := module.statements[id]
|
||||
@@ -3096,6 +3156,13 @@ all_paths_return :: proc(module: ^hir.Module, stmts: []hir.Stmt_Id) -> bool {
|
||||
all_paths_return(module, statement.else_body) {
|
||||
return true
|
||||
}
|
||||
case .While:
|
||||
if statement.expr != hir.INVALID_EXPR && int(statement.expr) < len(module.exprs) {
|
||||
condition := module.exprs[statement.expr]
|
||||
if condition.kind == .Bool && condition.integer != 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
|
||||
Reference in New Issue
Block a user