mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-05 22:02:55 +01:00
Support for while statements. (#3258)
Basic support for type-checking and lowering of `while` statements, as well as `break` and `continue`.
This commit is contained in:
@@ -21,6 +21,12 @@ namespace Carbon::Check {
|
||||
// Context and shared functionality for semantics handlers.
|
||||
class Context {
|
||||
public:
|
||||
// A scope in which `break` and `continue` can be used.
|
||||
struct BreakContinueScope {
|
||||
SemIR::NodeBlockId break_target;
|
||||
SemIR::NodeBlockId continue_target;
|
||||
};
|
||||
|
||||
// Stores references for work.
|
||||
explicit Context(const Lex::TokenizedBuffer& tokens,
|
||||
DiagnosticEmitter<Parse::Node>& emitter,
|
||||
@@ -186,6 +192,10 @@ class Context {
|
||||
return return_scope_stack_;
|
||||
}
|
||||
|
||||
auto break_continue_stack() -> llvm::SmallVector<BreakContinueScope>& {
|
||||
return break_continue_stack_;
|
||||
}
|
||||
|
||||
auto declaration_name_stack() -> DeclarationNameStack& {
|
||||
return declaration_name_stack_;
|
||||
}
|
||||
@@ -273,6 +283,9 @@ class Context {
|
||||
// this will be a FunctionDeclaration.
|
||||
llvm::SmallVector<SemIR::NodeId> return_scope_stack_;
|
||||
|
||||
// A stack of `break` and `continue` targets.
|
||||
llvm::SmallVector<BreakContinueScope> break_continue_stack_;
|
||||
|
||||
// A stack for scope context.
|
||||
llvm::SmallVector<ScopeStackEntry> scope_stack_;
|
||||
|
||||
|
||||
@@ -3,25 +3,50 @@
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
#include "toolchain/check/context.h"
|
||||
#include "toolchain/check/convert.h"
|
||||
|
||||
namespace Carbon::Check {
|
||||
|
||||
auto HandleBreakStatement(Context& context, Parse::Node parse_node) -> bool {
|
||||
return context.TODO(parse_node, "HandleBreakStatement");
|
||||
auto HandleBreakStatement(Context& /*context*/, Parse::Node /*parse_node*/)
|
||||
-> bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
auto HandleBreakStatementStart(Context& context, Parse::Node parse_node)
|
||||
-> bool {
|
||||
return context.TODO(parse_node, "HandleBreakStatementStart");
|
||||
auto& stack = context.break_continue_stack();
|
||||
if (stack.empty()) {
|
||||
CARBON_DIAGNOSTIC(BreakOutsideLoop, Error,
|
||||
"`break` can only be used in a loop.");
|
||||
context.emitter().Emit(parse_node, BreakOutsideLoop);
|
||||
} else {
|
||||
context.AddNode(SemIR::Branch(parse_node, stack.back().break_target));
|
||||
}
|
||||
|
||||
context.node_block_stack().Pop();
|
||||
context.node_block_stack().PushUnreachable();
|
||||
return true;
|
||||
}
|
||||
|
||||
auto HandleContinueStatement(Context& context, Parse::Node parse_node) -> bool {
|
||||
return context.TODO(parse_node, "HandleContinueStatement");
|
||||
auto HandleContinueStatement(Context& /*context*/, Parse::Node /*parse_node*/)
|
||||
-> bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
auto HandleContinueStatementStart(Context& context, Parse::Node parse_node)
|
||||
-> bool {
|
||||
return context.TODO(parse_node, "HandleContinueStatementStart");
|
||||
auto& stack = context.break_continue_stack();
|
||||
if (stack.empty()) {
|
||||
CARBON_DIAGNOSTIC(ContinueOutsideLoop, Error,
|
||||
"`continue` can only be used in a loop.");
|
||||
context.emitter().Emit(parse_node, ContinueOutsideLoop);
|
||||
} else {
|
||||
context.AddNode(SemIR::Branch(parse_node, stack.back().continue_target));
|
||||
}
|
||||
|
||||
context.node_block_stack().Pop();
|
||||
context.node_block_stack().PushUnreachable();
|
||||
return true;
|
||||
}
|
||||
|
||||
auto HandleForHeader(Context& context, Parse::Node parse_node) -> bool {
|
||||
@@ -40,17 +65,59 @@ auto HandleForStatement(Context& context, Parse::Node parse_node) -> bool {
|
||||
return context.TODO(parse_node, "HandleForStatement");
|
||||
}
|
||||
|
||||
auto HandleWhileCondition(Context& context, Parse::Node parse_node) -> bool {
|
||||
return context.TODO(parse_node, "HandleWhileCondition");
|
||||
}
|
||||
|
||||
auto HandleWhileConditionStart(Context& context, Parse::Node parse_node)
|
||||
-> bool {
|
||||
return context.TODO(parse_node, "HandleWhileConditionStart");
|
||||
// Branch to the loop header block. Note that we create a new block here even
|
||||
// if the current block is empty; this ensures that the loop always has a
|
||||
// preheader block.
|
||||
auto loop_header_id = context.AddDominatedBlockAndBranch(parse_node);
|
||||
context.node_block_stack().Pop();
|
||||
|
||||
// Start emitting the loop header block.
|
||||
context.node_block_stack().Push(loop_header_id);
|
||||
context.AddCurrentCodeBlockToFunction();
|
||||
|
||||
context.node_stack().Push(parse_node, loop_header_id);
|
||||
return true;
|
||||
}
|
||||
|
||||
auto HandleWhileCondition(Context& context, Parse::Node parse_node) -> bool {
|
||||
auto cond_value_id = context.node_stack().PopExpression();
|
||||
auto loop_header_id =
|
||||
context.node_stack().Peek<Parse::NodeKind::WhileConditionStart>();
|
||||
cond_value_id = ConvertToBoolValue(context, parse_node, cond_value_id);
|
||||
|
||||
// Branch to either the loop body or the loop exit block.
|
||||
auto loop_body_id =
|
||||
context.AddDominatedBlockAndBranchIf(parse_node, cond_value_id);
|
||||
auto loop_exit_id = context.AddDominatedBlockAndBranch(parse_node);
|
||||
context.node_block_stack().Pop();
|
||||
|
||||
// Start emitting the loop body.
|
||||
context.node_block_stack().Push(loop_body_id);
|
||||
context.AddCurrentCodeBlockToFunction();
|
||||
context.break_continue_stack().push_back(
|
||||
{.break_target = loop_exit_id, .continue_target = loop_header_id});
|
||||
|
||||
context.node_stack().Push(parse_node, loop_exit_id);
|
||||
return true;
|
||||
}
|
||||
|
||||
auto HandleWhileStatement(Context& context, Parse::Node parse_node) -> bool {
|
||||
return context.TODO(parse_node, "HandleWhileStatement");
|
||||
auto loop_exit_id =
|
||||
context.node_stack().Pop<Parse::NodeKind::WhileCondition>();
|
||||
auto loop_header_id =
|
||||
context.node_stack().Pop<Parse::NodeKind::WhileConditionStart>();
|
||||
context.break_continue_stack().pop_back();
|
||||
|
||||
// Add the loop backedge.
|
||||
context.AddNode(SemIR::Branch(parse_node, loop_header_id));
|
||||
context.node_block_stack().Pop();
|
||||
|
||||
// Start emitting the loop exit block.
|
||||
context.node_block_stack().Push(loop_exit_id);
|
||||
context.AddCurrentCodeBlockToFunction();
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Carbon::Check
|
||||
|
||||
@@ -264,6 +264,8 @@ class NodeStack {
|
||||
case Parse::NodeKind::IfCondition:
|
||||
case Parse::NodeKind::IfExpressionIf:
|
||||
case Parse::NodeKind::ParameterList:
|
||||
case Parse::NodeKind::WhileCondition:
|
||||
case Parse::NodeKind::WhileConditionStart:
|
||||
return IdKind::NodeBlockId;
|
||||
case Parse::NodeKind::FunctionDefinitionStart:
|
||||
return IdKind::FunctionId;
|
||||
|
||||
@@ -0,0 +1,154 @@
|
||||
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
// AUTOUPDATE
|
||||
|
||||
fn A() -> bool;
|
||||
fn B() -> bool;
|
||||
fn C() -> bool;
|
||||
fn D() -> bool;
|
||||
fn E() -> bool;
|
||||
fn F() -> bool;
|
||||
fn G() -> bool;
|
||||
fn H() -> bool;
|
||||
|
||||
fn While() {
|
||||
while (A()) {
|
||||
if (B()) { continue; }
|
||||
if (C()) { break; }
|
||||
while (D()) {
|
||||
if (E()) { continue; }
|
||||
if (F()) { break; }
|
||||
}
|
||||
if (G()) { continue; }
|
||||
if (H()) { break; }
|
||||
}
|
||||
}
|
||||
|
||||
// CHECK:STDOUT: file "break_continue.carbon" {
|
||||
// CHECK:STDOUT: %A = fn_decl @A
|
||||
// CHECK:STDOUT: %B = fn_decl @B
|
||||
// CHECK:STDOUT: %C = fn_decl @C
|
||||
// CHECK:STDOUT: %D = fn_decl @D
|
||||
// CHECK:STDOUT: %E = fn_decl @E
|
||||
// CHECK:STDOUT: %F = fn_decl @F
|
||||
// CHECK:STDOUT: %G = fn_decl @G
|
||||
// CHECK:STDOUT: %H = fn_decl @H
|
||||
// CHECK:STDOUT: %While = fn_decl @While
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @A() -> bool;
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @B() -> bool;
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @C() -> bool;
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @D() -> bool;
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @E() -> bool;
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @F() -> bool;
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @G() -> bool;
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @H() -> bool;
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @While() {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: br !while.cond.loc17
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !while.cond.loc17:
|
||||
// CHECK:STDOUT: %A.ref = name_reference_untyped "A", package.%A
|
||||
// CHECK:STDOUT: %.loc17_11.1: init bool = call @A()
|
||||
// CHECK:STDOUT: %.loc17_11.2: ref bool = temporary_storage
|
||||
// CHECK:STDOUT: %.loc17_11.3: ref bool = temporary %.loc17_11.2, %.loc17_11.1
|
||||
// CHECK:STDOUT: %.loc17_11.4: bool = bind_value %.loc17_11.3
|
||||
// CHECK:STDOUT: if %.loc17_11.4 br !while.body.loc17 else br !while.done.loc17
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !while.body.loc17:
|
||||
// CHECK:STDOUT: %B.ref = name_reference_untyped "B", package.%B
|
||||
// CHECK:STDOUT: %.loc18_10.1: init bool = call @B()
|
||||
// CHECK:STDOUT: %.loc18_10.2: ref bool = temporary_storage
|
||||
// CHECK:STDOUT: %.loc18_10.3: ref bool = temporary %.loc18_10.2, %.loc18_10.1
|
||||
// CHECK:STDOUT: %.loc18_10.4: bool = bind_value %.loc18_10.3
|
||||
// CHECK:STDOUT: if %.loc18_10.4 br !if.then.loc18 else br !if.else.loc18
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !if.then.loc18:
|
||||
// CHECK:STDOUT: br !while.cond.loc17
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !if.else.loc18:
|
||||
// CHECK:STDOUT: %C.ref = name_reference_untyped "C", package.%C
|
||||
// CHECK:STDOUT: %.loc19_10.1: init bool = call @C()
|
||||
// CHECK:STDOUT: %.loc19_10.2: ref bool = temporary_storage
|
||||
// CHECK:STDOUT: %.loc19_10.3: ref bool = temporary %.loc19_10.2, %.loc19_10.1
|
||||
// CHECK:STDOUT: %.loc19_10.4: bool = bind_value %.loc19_10.3
|
||||
// CHECK:STDOUT: if %.loc19_10.4 br !if.then.loc19 else br !if.else.loc19
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !if.then.loc19:
|
||||
// CHECK:STDOUT: br !while.done.loc17
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !if.else.loc19:
|
||||
// CHECK:STDOUT: br !while.cond.loc20
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !while.cond.loc20:
|
||||
// CHECK:STDOUT: %D.ref = name_reference_untyped "D", package.%D
|
||||
// CHECK:STDOUT: %.loc20_13.1: init bool = call @D()
|
||||
// CHECK:STDOUT: %.loc20_13.2: ref bool = temporary_storage
|
||||
// CHECK:STDOUT: %.loc20_13.3: ref bool = temporary %.loc20_13.2, %.loc20_13.1
|
||||
// CHECK:STDOUT: %.loc20_13.4: bool = bind_value %.loc20_13.3
|
||||
// CHECK:STDOUT: if %.loc20_13.4 br !while.body.loc20 else br !while.done.loc20
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !while.body.loc20:
|
||||
// CHECK:STDOUT: %E.ref = name_reference_untyped "E", package.%E
|
||||
// CHECK:STDOUT: %.loc21_12.1: init bool = call @E()
|
||||
// CHECK:STDOUT: %.loc21_12.2: ref bool = temporary_storage
|
||||
// CHECK:STDOUT: %.loc21_12.3: ref bool = temporary %.loc21_12.2, %.loc21_12.1
|
||||
// CHECK:STDOUT: %.loc21_12.4: bool = bind_value %.loc21_12.3
|
||||
// CHECK:STDOUT: if %.loc21_12.4 br !if.then.loc21 else br !if.else.loc21
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !if.then.loc21:
|
||||
// CHECK:STDOUT: br !while.cond.loc20
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !if.else.loc21:
|
||||
// CHECK:STDOUT: %F.ref = name_reference_untyped "F", package.%F
|
||||
// CHECK:STDOUT: %.loc22_12.1: init bool = call @F()
|
||||
// CHECK:STDOUT: %.loc22_12.2: ref bool = temporary_storage
|
||||
// CHECK:STDOUT: %.loc22_12.3: ref bool = temporary %.loc22_12.2, %.loc22_12.1
|
||||
// CHECK:STDOUT: %.loc22_12.4: bool = bind_value %.loc22_12.3
|
||||
// CHECK:STDOUT: if %.loc22_12.4 br !if.then.loc22 else br !if.else.loc22
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !if.then.loc22:
|
||||
// CHECK:STDOUT: br !while.done.loc20
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !if.else.loc22:
|
||||
// CHECK:STDOUT: br !while.cond.loc20
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !while.done.loc20:
|
||||
// CHECK:STDOUT: %G.ref = name_reference_untyped "G", package.%G
|
||||
// CHECK:STDOUT: %.loc24_10.1: init bool = call @G()
|
||||
// CHECK:STDOUT: %.loc24_10.2: ref bool = temporary_storage
|
||||
// CHECK:STDOUT: %.loc24_10.3: ref bool = temporary %.loc24_10.2, %.loc24_10.1
|
||||
// CHECK:STDOUT: %.loc24_10.4: bool = bind_value %.loc24_10.3
|
||||
// CHECK:STDOUT: if %.loc24_10.4 br !if.then.loc24 else br !if.else.loc24
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !if.then.loc24:
|
||||
// CHECK:STDOUT: br !while.cond.loc17
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !if.else.loc24:
|
||||
// CHECK:STDOUT: %H.ref = name_reference_untyped "H", package.%H
|
||||
// CHECK:STDOUT: %.loc25_10.1: init bool = call @H()
|
||||
// CHECK:STDOUT: %.loc25_10.2: ref bool = temporary_storage
|
||||
// CHECK:STDOUT: %.loc25_10.3: ref bool = temporary %.loc25_10.2, %.loc25_10.1
|
||||
// CHECK:STDOUT: %.loc25_10.4: bool = bind_value %.loc25_10.3
|
||||
// CHECK:STDOUT: if %.loc25_10.4 br !if.then.loc25 else br !if.else.loc25
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !if.then.loc25:
|
||||
// CHECK:STDOUT: br !while.done.loc17
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !if.else.loc25:
|
||||
// CHECK:STDOUT: br !while.cond.loc17
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !while.done.loc17:
|
||||
// CHECK:STDOUT: return
|
||||
// CHECK:STDOUT: }
|
||||
@@ -0,0 +1,31 @@
|
||||
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
// AUTOUPDATE
|
||||
|
||||
fn While() {
|
||||
// CHECK:STDERR: fail_bad_condition.carbon:[[@LINE+3]]:17: ERROR: Cannot implicitly convert from `String` to `bool`.
|
||||
// CHECK:STDERR: while ("Hello") {}
|
||||
// CHECK:STDERR: ^
|
||||
while ("Hello") {}
|
||||
}
|
||||
|
||||
// CHECK:STDOUT: file "fail_bad_condition.carbon" {
|
||||
// CHECK:STDOUT: %While = fn_decl @While
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @While() {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: br !while.cond
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !while.cond:
|
||||
// CHECK:STDOUT: %.loc11: String = string_literal "Hello"
|
||||
// CHECK:STDOUT: if <error> br !while.body else br !while.done
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !while.body:
|
||||
// CHECK:STDOUT: br !while.cond
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !while.done:
|
||||
// CHECK:STDOUT: return
|
||||
// CHECK:STDOUT: }
|
||||
@@ -0,0 +1,36 @@
|
||||
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
// AUTOUPDATE
|
||||
|
||||
fn While() {
|
||||
// CHECK:STDERR: fail_break_continue.carbon:[[@LINE+3]]:3: ERROR: `continue` can only be used in a loop.
|
||||
// CHECK:STDERR: continue;
|
||||
// CHECK:STDERR: ^
|
||||
continue;
|
||||
// CHECK:STDERR: fail_break_continue.carbon:[[@LINE+3]]:3: ERROR: `break` can only be used in a loop.
|
||||
// CHECK:STDERR: break;
|
||||
// CHECK:STDERR: ^
|
||||
break;
|
||||
while (false) {
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
// CHECK:STDERR: fail_break_continue.carbon:[[@LINE+3]]:3: ERROR: `continue` can only be used in a loop.
|
||||
// CHECK:STDERR: continue;
|
||||
// CHECK:STDERR: ^
|
||||
continue;
|
||||
// CHECK:STDERR: fail_break_continue.carbon:[[@LINE+3]]:3: ERROR: `break` can only be used in a loop.
|
||||
// CHECK:STDERR: break;
|
||||
// CHECK:STDERR: ^
|
||||
break;
|
||||
}
|
||||
|
||||
// CHECK:STDOUT: file "fail_break_continue.carbon" {
|
||||
// CHECK:STDOUT: %While = fn_decl @While
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @While() {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: }
|
||||
@@ -0,0 +1,62 @@
|
||||
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
// AUTOUPDATE
|
||||
|
||||
fn Cond() -> bool;
|
||||
|
||||
fn F();
|
||||
fn G();
|
||||
fn H();
|
||||
|
||||
fn While() {
|
||||
F();
|
||||
while (Cond()) {
|
||||
G();
|
||||
return;
|
||||
}
|
||||
H();
|
||||
}
|
||||
|
||||
// CHECK:STDOUT: file "unreachable_end.carbon" {
|
||||
// CHECK:STDOUT: %Cond = fn_decl @Cond
|
||||
// CHECK:STDOUT: %F = fn_decl @F
|
||||
// CHECK:STDOUT: %G = fn_decl @G
|
||||
// CHECK:STDOUT: %H = fn_decl @H
|
||||
// CHECK:STDOUT: %While = fn_decl @While
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @Cond() -> bool;
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @F();
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @G();
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @H();
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @While() {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %F.ref = name_reference_untyped "F", package.%F
|
||||
// CHECK:STDOUT: %.loc14_4.1: type = tuple_type ()
|
||||
// CHECK:STDOUT: %.loc14_4.2: init () = call @F()
|
||||
// CHECK:STDOUT: br !while.cond
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !while.cond:
|
||||
// CHECK:STDOUT: %Cond.ref = name_reference_untyped "Cond", package.%Cond
|
||||
// CHECK:STDOUT: %.loc15_14.1: init bool = call @Cond()
|
||||
// CHECK:STDOUT: %.loc15_14.2: ref bool = temporary_storage
|
||||
// CHECK:STDOUT: %.loc15_14.3: ref bool = temporary %.loc15_14.2, %.loc15_14.1
|
||||
// CHECK:STDOUT: %.loc15_14.4: bool = bind_value %.loc15_14.3
|
||||
// CHECK:STDOUT: if %.loc15_14.4 br !while.body else br !while.done
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !while.body:
|
||||
// CHECK:STDOUT: %G.ref = name_reference_untyped "G", package.%G
|
||||
// CHECK:STDOUT: %.loc16: init () = call @G()
|
||||
// CHECK:STDOUT: return
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !while.done:
|
||||
// CHECK:STDOUT: %H.ref = name_reference_untyped "H", package.%H
|
||||
// CHECK:STDOUT: %.loc19: init () = call @H()
|
||||
// CHECK:STDOUT: return
|
||||
// CHECK:STDOUT: }
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
// AUTOUPDATE
|
||||
|
||||
fn Cond() -> bool;
|
||||
|
||||
fn F();
|
||||
fn G();
|
||||
fn H();
|
||||
|
||||
fn While() {
|
||||
F();
|
||||
while (Cond()) {
|
||||
G();
|
||||
}
|
||||
H();
|
||||
}
|
||||
|
||||
// CHECK:STDOUT: file "while.carbon" {
|
||||
// CHECK:STDOUT: %Cond = fn_decl @Cond
|
||||
// CHECK:STDOUT: %F = fn_decl @F
|
||||
// CHECK:STDOUT: %G = fn_decl @G
|
||||
// CHECK:STDOUT: %H = fn_decl @H
|
||||
// CHECK:STDOUT: %While = fn_decl @While
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @Cond() -> bool;
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @F();
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @G();
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @H();
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @While() {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %F.ref = name_reference_untyped "F", package.%F
|
||||
// CHECK:STDOUT: %.loc14_4.1: type = tuple_type ()
|
||||
// CHECK:STDOUT: %.loc14_4.2: init () = call @F()
|
||||
// CHECK:STDOUT: br !while.cond
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !while.cond:
|
||||
// CHECK:STDOUT: %Cond.ref = name_reference_untyped "Cond", package.%Cond
|
||||
// CHECK:STDOUT: %.loc15_14.1: init bool = call @Cond()
|
||||
// CHECK:STDOUT: %.loc15_14.2: ref bool = temporary_storage
|
||||
// CHECK:STDOUT: %.loc15_14.3: ref bool = temporary %.loc15_14.2, %.loc15_14.1
|
||||
// CHECK:STDOUT: %.loc15_14.4: bool = bind_value %.loc15_14.3
|
||||
// CHECK:STDOUT: if %.loc15_14.4 br !while.body else br !while.done
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !while.body:
|
||||
// CHECK:STDOUT: %G.ref = name_reference_untyped "G", package.%G
|
||||
// CHECK:STDOUT: %.loc16: init () = call @G()
|
||||
// CHECK:STDOUT: br !while.cond
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !while.done:
|
||||
// CHECK:STDOUT: %H.ref = name_reference_untyped "H", package.%H
|
||||
// CHECK:STDOUT: %.loc18: init () = call @H()
|
||||
// CHECK:STDOUT: return
|
||||
// CHECK:STDOUT: }
|
||||
@@ -114,6 +114,8 @@ CARBON_DIAGNOSTIC_KIND(AddressOfNonReference)
|
||||
CARBON_DIAGNOSTIC_KIND(ArrayInitFromLiteralArgCountMismatch)
|
||||
CARBON_DIAGNOSTIC_KIND(ArrayInitFromExpressionArgCountMismatch)
|
||||
CARBON_DIAGNOSTIC_KIND(AssignmentToNonAssignable)
|
||||
CARBON_DIAGNOSTIC_KIND(BreakOutsideLoop)
|
||||
CARBON_DIAGNOSTIC_KIND(ContinueOutsideLoop)
|
||||
CARBON_DIAGNOSTIC_KIND(DereferenceOfNonPointer)
|
||||
CARBON_DIAGNOSTIC_KIND(DereferenceOfType)
|
||||
CARBON_DIAGNOSTIC_KIND(NameNotFound)
|
||||
|
||||
@@ -186,6 +186,14 @@ auto FileContext::BuildFunctionDefinition(SemIR::FunctionId function_id)
|
||||
function_lowering.builder().SetInsertPoint(llvm_block);
|
||||
function_lowering.LowerBlock(block_id);
|
||||
}
|
||||
|
||||
// LLVM requires that the entry block has no predecessors.
|
||||
auto* entry_block = &llvm_function->getEntryBlock();
|
||||
if (entry_block->hasNPredecessorsOrMore(1)) {
|
||||
auto* new_entry_block = llvm::BasicBlock::Create(
|
||||
llvm_context(), "entry", llvm_function, entry_block);
|
||||
llvm::BranchInst::Create(entry_block, new_entry_block);
|
||||
}
|
||||
}
|
||||
|
||||
auto FileContext::BuildType(SemIR::NodeId node_id) -> llvm::Type* {
|
||||
|
||||
@@ -74,6 +74,7 @@ auto HandleBoolLiteral(FunctionContext& context, SemIR::NodeId node_id,
|
||||
auto HandleBranch(FunctionContext& context, SemIR::NodeId /*node_id*/,
|
||||
SemIR::Branch node) -> void {
|
||||
// Opportunistically avoid creating a BasicBlock that contains just a branch.
|
||||
// TODO: Don't do this if it would remove a loop preheader block.
|
||||
llvm::BasicBlock* block = context.builder().GetInsertBlock();
|
||||
if (block->empty() && context.TryToReuseBlock(node.target_id, block)) {
|
||||
// Reuse this block as the branch target.
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
// AUTOUPDATE
|
||||
|
||||
fn A() -> bool;
|
||||
fn B() -> bool;
|
||||
fn C() -> bool;
|
||||
|
||||
fn While() {
|
||||
while (A()) {
|
||||
if (B()) { continue; }
|
||||
if (C()) { break; }
|
||||
}
|
||||
}
|
||||
|
||||
// CHECK:STDOUT: ; ModuleID = 'break_continue.carbon'
|
||||
// CHECK:STDOUT: source_filename = "break_continue.carbon"
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: declare i1 @A()
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: declare i1 @B()
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: declare i1 @C()
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: define void @While() {
|
||||
// CHECK:STDOUT: entry:
|
||||
// CHECK:STDOUT: br label %0
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: 0: ; preds = %entry, %8, %4
|
||||
// CHECK:STDOUT: %A = call i1 @A()
|
||||
// CHECK:STDOUT: %temp = alloca i1, align 1
|
||||
// CHECK:STDOUT: store i1 %A, ptr %temp, align 1
|
||||
// CHECK:STDOUT: %1 = load i1, ptr %temp, align 1
|
||||
// CHECK:STDOUT: br i1 %1, label %2, label %9
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: 2: ; preds = %0
|
||||
// CHECK:STDOUT: %B = call i1 @B()
|
||||
// CHECK:STDOUT: %temp1 = alloca i1, align 1
|
||||
// CHECK:STDOUT: store i1 %B, ptr %temp1, align 1
|
||||
// CHECK:STDOUT: %3 = load i1, ptr %temp1, align 1
|
||||
// CHECK:STDOUT: br i1 %3, label %4, label %5
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: 4: ; preds = %2
|
||||
// CHECK:STDOUT: br label %0
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: 5: ; preds = %2
|
||||
// CHECK:STDOUT: %C = call i1 @C()
|
||||
// CHECK:STDOUT: %temp2 = alloca i1, align 1
|
||||
// CHECK:STDOUT: store i1 %C, ptr %temp2, align 1
|
||||
// CHECK:STDOUT: %6 = load i1, ptr %temp2, align 1
|
||||
// CHECK:STDOUT: br i1 %6, label %7, label %8
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: 7: ; preds = %5
|
||||
// CHECK:STDOUT: br label %9
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: 8: ; preds = %5
|
||||
// CHECK:STDOUT: br label %0
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: 9: ; preds = %7, %0
|
||||
// CHECK:STDOUT: ret void
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: ; uselistorder directives
|
||||
// CHECK:STDOUT: uselistorder label %0, { 1, 2, 0 }
|
||||
// CHECK:STDOUT: }
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
// AUTOUPDATE
|
||||
|
||||
fn Cond() -> bool;
|
||||
|
||||
fn F();
|
||||
fn G();
|
||||
|
||||
// TODO: It would be better to preserve the loop preheader blocks from SemIR
|
||||
// into LLVM IR. If we don't, LLVM will recreate them for us as part of loop
|
||||
// canonicalization.
|
||||
fn While() {
|
||||
while (Cond()) {
|
||||
F();
|
||||
}
|
||||
|
||||
if (Cond()) {
|
||||
while (Cond()) {
|
||||
G();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CHECK:STDOUT: ; ModuleID = 'preheader.carbon'
|
||||
// CHECK:STDOUT: source_filename = "preheader.carbon"
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: declare i1 @Cond()
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: declare void @F()
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: declare void @G()
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: define void @While() {
|
||||
// CHECK:STDOUT: entry:
|
||||
// CHECK:STDOUT: br label %0
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: 0: ; preds = %entry, %2
|
||||
// CHECK:STDOUT: %Cond = call i1 @Cond()
|
||||
// CHECK:STDOUT: %temp = alloca i1, align 1
|
||||
// CHECK:STDOUT: store i1 %Cond, ptr %temp, align 1
|
||||
// CHECK:STDOUT: %1 = load i1, ptr %temp, align 1
|
||||
// CHECK:STDOUT: br i1 %1, label %2, label %3
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: 2: ; preds = %0
|
||||
// CHECK:STDOUT: call void @F()
|
||||
// CHECK:STDOUT: br label %0
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: 3: ; preds = %0
|
||||
// CHECK:STDOUT: %Cond1 = call i1 @Cond()
|
||||
// CHECK:STDOUT: %temp2 = alloca i1, align 1
|
||||
// CHECK:STDOUT: store i1 %Cond1, ptr %temp2, align 1
|
||||
// CHECK:STDOUT: %4 = load i1, ptr %temp2, align 1
|
||||
// CHECK:STDOUT: br i1 %4, label %5, label %9
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: 5: ; preds = %7, %3
|
||||
// CHECK:STDOUT: %Cond3 = call i1 @Cond()
|
||||
// CHECK:STDOUT: %temp4 = alloca i1, align 1
|
||||
// CHECK:STDOUT: store i1 %Cond3, ptr %temp4, align 1
|
||||
// CHECK:STDOUT: %6 = load i1, ptr %temp4, align 1
|
||||
// CHECK:STDOUT: br i1 %6, label %7, label %8
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: 7: ; preds = %5
|
||||
// CHECK:STDOUT: call void @G()
|
||||
// CHECK:STDOUT: br label %5
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: 8: ; preds = %5
|
||||
// CHECK:STDOUT: br label %9
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: 9: ; preds = %8, %3
|
||||
// CHECK:STDOUT: ret void
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: ; uselistorder directives
|
||||
// CHECK:STDOUT: uselistorder label %0, { 1, 0 }
|
||||
// CHECK:STDOUT: }
|
||||
@@ -0,0 +1,51 @@
|
||||
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
// AUTOUPDATE
|
||||
|
||||
fn Cond() -> bool;
|
||||
|
||||
fn F();
|
||||
fn G();
|
||||
fn H();
|
||||
|
||||
fn While() {
|
||||
F();
|
||||
while (Cond()) {
|
||||
G();
|
||||
return;
|
||||
}
|
||||
H();
|
||||
}
|
||||
|
||||
// CHECK:STDOUT: ; ModuleID = 'unreachable_end.carbon'
|
||||
// CHECK:STDOUT: source_filename = "unreachable_end.carbon"
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: declare i1 @Cond()
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: declare void @F()
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: declare void @G()
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: declare void @H()
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: define void @While() {
|
||||
// CHECK:STDOUT: call void @F()
|
||||
// CHECK:STDOUT: br label %1
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: 1: ; preds = %0
|
||||
// CHECK:STDOUT: %Cond = call i1 @Cond()
|
||||
// CHECK:STDOUT: %temp = alloca i1, align 1
|
||||
// CHECK:STDOUT: store i1 %Cond, ptr %temp, align 1
|
||||
// CHECK:STDOUT: %2 = load i1, ptr %temp, align 1
|
||||
// CHECK:STDOUT: br i1 %2, label %3, label %4
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: 3: ; preds = %1
|
||||
// CHECK:STDOUT: call void @G()
|
||||
// CHECK:STDOUT: ret void
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: 4: ; preds = %1
|
||||
// CHECK:STDOUT: call void @H()
|
||||
// CHECK:STDOUT: ret void
|
||||
// CHECK:STDOUT: }
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
// AUTOUPDATE
|
||||
|
||||
fn Cond() -> bool;
|
||||
|
||||
fn F();
|
||||
fn G();
|
||||
fn H();
|
||||
|
||||
fn While() {
|
||||
F();
|
||||
while (Cond()) {
|
||||
G();
|
||||
}
|
||||
H();
|
||||
}
|
||||
|
||||
// CHECK:STDOUT: ; ModuleID = 'while.carbon'
|
||||
// CHECK:STDOUT: source_filename = "while.carbon"
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: declare i1 @Cond()
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: declare void @F()
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: declare void @G()
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: declare void @H()
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: define void @While() {
|
||||
// CHECK:STDOUT: call void @F()
|
||||
// CHECK:STDOUT: br label %1
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: 1: ; preds = %3, %0
|
||||
// CHECK:STDOUT: %Cond = call i1 @Cond()
|
||||
// CHECK:STDOUT: %temp = alloca i1, align 1
|
||||
// CHECK:STDOUT: store i1 %Cond, ptr %temp, align 1
|
||||
// CHECK:STDOUT: %2 = load i1, ptr %temp, align 1
|
||||
// CHECK:STDOUT: br i1 %2, label %3, label %4
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: 3: ; preds = %1
|
||||
// CHECK:STDOUT: call void @G()
|
||||
// CHECK:STDOUT: br label %1
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: 4: ; preds = %1
|
||||
// CHECK:STDOUT: call void @H()
|
||||
// CHECK:STDOUT: ret void
|
||||
// CHECK:STDOUT: }
|
||||
@@ -315,6 +315,23 @@ class NodeNamer {
|
||||
break;
|
||||
}
|
||||
|
||||
case Parse::NodeKind::WhileConditionStart:
|
||||
name = "while.cond";
|
||||
break;
|
||||
|
||||
case Parse::NodeKind::WhileCondition:
|
||||
switch (node.kind()) {
|
||||
case NodeKind::BranchIf:
|
||||
name = "while.body";
|
||||
break;
|
||||
case NodeKind::Branch:
|
||||
name = "while.done";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user