diff --git a/toolchain/lowering/lowering_handle.cpp b/toolchain/lowering/lowering_handle.cpp index 4b9a795465e2..5e9a5a5e413a 100644 --- a/toolchain/lowering/lowering_handle.cpp +++ b/toolchain/lowering/lowering_handle.cpp @@ -128,12 +128,6 @@ auto LoweringHandleCall(LoweringFunctionContext& context, context.SetLocal(node_id, value); } -auto LoweringHandleCodeBlock(LoweringFunctionContext& /*context*/, - SemanticsNodeId /*node_id*/, SemanticsNode node) - -> void { - CARBON_FATAL() << "TODO: Add support: " << node; -} - auto LoweringHandleFunctionDeclaration(LoweringFunctionContext& /*context*/, SemanticsNodeId /*node_id*/, SemanticsNode node) -> void { diff --git a/toolchain/lowering/testdata/if/else.carbon b/toolchain/lowering/testdata/if/else.carbon new file mode 100644 index 000000000000..7b0729f3b276 --- /dev/null +++ b/toolchain/lowering/testdata/if/else.carbon @@ -0,0 +1,46 @@ +// 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 +// CHECK:STDOUT: ; ModuleID = 'else.carbon' +// CHECK:STDOUT: source_filename = "else.carbon" +// CHECK:STDOUT: +// CHECK:STDOUT: %EmptyTupleType = type {} +// CHECK:STDOUT: +// CHECK:STDOUT: define %EmptyTupleType @F() { +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define %EmptyTupleType @G() { +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define %EmptyTupleType @H() { +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define %EmptyTupleType @If(i1 %b) { +// CHECK:STDOUT: br i1 %b, label %1, label %2 +// CHECK:STDOUT: +// CHECK:STDOUT: 1: ; preds = %0 +// CHECK:STDOUT: %F = call %EmptyTupleType @F() +// CHECK:STDOUT: br label %3 +// CHECK:STDOUT: +// CHECK:STDOUT: 2: ; preds = %0 +// CHECK:STDOUT: %G = call %EmptyTupleType @G() +// CHECK:STDOUT: br label %3 +// CHECK:STDOUT: +// CHECK:STDOUT: 3: ; preds = %2, %1 +// CHECK:STDOUT: %H = call %EmptyTupleType @H() +// CHECK:STDOUT: } + +fn F() {} +fn G() {} +fn H() {} + +fn If(b: bool) { + if (b) { + F(); + } else { + G(); + } + H(); +} diff --git a/toolchain/lowering/testdata/if/no_else.carbon b/toolchain/lowering/testdata/if/no_else.carbon new file mode 100644 index 000000000000..44e6c4ca4fb1 --- /dev/null +++ b/toolchain/lowering/testdata/if/no_else.carbon @@ -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 +// CHECK:STDOUT: ; ModuleID = 'no_else.carbon' +// CHECK:STDOUT: source_filename = "no_else.carbon" +// CHECK:STDOUT: +// CHECK:STDOUT: %EmptyTupleType = type {} +// CHECK:STDOUT: +// CHECK:STDOUT: define %EmptyTupleType @F() { +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define %EmptyTupleType @G() { +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define %EmptyTupleType @If(i1 %b) { +// CHECK:STDOUT: br i1 %b, label %1, label %2 +// CHECK:STDOUT: +// CHECK:STDOUT: 1: ; preds = %0 +// CHECK:STDOUT: %F = call %EmptyTupleType @F() +// CHECK:STDOUT: br label %2 +// CHECK:STDOUT: +// CHECK:STDOUT: 2: ; preds = %1, %0 +// CHECK:STDOUT: %G = call %EmptyTupleType @G() +// CHECK:STDOUT: } + +fn F() {} +fn G() {} + +fn If(b: bool) { + if (b) { + F(); + } + G(); +} diff --git a/toolchain/semantics/semantics_handle.cpp b/toolchain/semantics/semantics_handle.cpp index 15dc9cc890cb..2fcd6540eec0 100644 --- a/toolchain/semantics/semantics_handle.cpp +++ b/toolchain/semantics/semantics_handle.cpp @@ -22,16 +22,6 @@ auto SemanticsHandleBreakStatementStart(SemanticsContext& context, return context.TODO(parse_node, "HandleBreakStatementStart"); } -auto SemanticsHandleCodeBlock(SemanticsContext& context, - ParseTree::Node parse_node) -> bool { - return context.TODO(parse_node, "HandleCodeBlock"); -} - -auto SemanticsHandleCodeBlockStart(SemanticsContext& context, - ParseTree::Node parse_node) -> bool { - return context.TODO(parse_node, "HandleCodeBlockStart"); -} - auto SemanticsHandleContinueStatement(SemanticsContext& context, ParseTree::Node parse_node) -> bool { return context.TODO(parse_node, "HandleContinueStatement"); @@ -119,21 +109,19 @@ auto SemanticsHandleDesignatorExpression(SemanticsContext& context, return true; } -auto SemanticsHandleEmptyDeclaration(SemanticsContext& context, - ParseTree::Node parse_node) -> bool { - // Empty declarations have no actions associated, but we still balance the - // tree. - context.node_stack().Push(parse_node); +auto SemanticsHandleEmptyDeclaration(SemanticsContext& /*context*/, + ParseTree::Node /*parse_node*/) -> bool { + // Empty declarations have no actions associated. return true; } auto SemanticsHandleExpressionStatement(SemanticsContext& context, - ParseTree::Node parse_node) -> bool { + ParseTree::Node /*parse_node*/) + -> bool { // Pop the expression without investigating its contents. // TODO: This will probably eventually need to do some "do not discard" // analysis. context.node_stack().PopAndDiscardId(); - context.node_stack().Push(parse_node); return true; } @@ -168,26 +156,6 @@ auto SemanticsHandleGenericPatternBinding(SemanticsContext& context, return context.TODO(parse_node, "GenericPatternBinding"); } -auto SemanticsHandleIfCondition(SemanticsContext& context, - ParseTree::Node parse_node) -> bool { - return context.TODO(parse_node, "HandleIfCondition"); -} - -auto SemanticsHandleIfConditionStart(SemanticsContext& context, - ParseTree::Node parse_node) -> bool { - return context.TODO(parse_node, "HandleIfConditionStart"); -} - -auto SemanticsHandleIfStatement(SemanticsContext& context, - ParseTree::Node parse_node) -> bool { - return context.TODO(parse_node, "HandleIfStatement"); -} - -auto SemanticsHandleIfStatementElse(SemanticsContext& context, - ParseTree::Node parse_node) -> bool { - return context.TODO(parse_node, "HandleIfStatementElse"); -} - auto SemanticsHandleInfixOperator(SemanticsContext& context, ParseTree::Node parse_node) -> bool { auto rhs_id = context.node_stack().Pop(); @@ -484,7 +452,7 @@ auto SemanticsHandleReturnStatement(SemanticsContext& context, .Emit(); } - context.AddNodeAndPush(parse_node, SemanticsNode::Return::Make(parse_node)); + context.AddNode(SemanticsNode::Return::Make(parse_node)); } else { auto arg = context.node_stack().Pop(); context.node_stack().PopAndDiscardSoloParseNode( @@ -505,10 +473,8 @@ auto SemanticsHandleReturnStatement(SemanticsContext& context, context.ImplicitAsRequired(parse_node, arg, callable.return_type_id); } - context.AddNodeAndPush( - parse_node, - SemanticsNode::ReturnExpression::Make( - parse_node, context.semantics_ir().GetNode(arg).type_id(), arg)); + context.AddNode(SemanticsNode::ReturnExpression::Make( + parse_node, context.semantics_ir().GetNode(arg).type_id(), arg)); } return true; } @@ -634,7 +600,6 @@ auto SemanticsHandleVariableDeclaration(SemanticsContext& context, context.node_stack().PopAndDiscardSoloParseNode( ParseNodeKind::VariableIntroducer); - context.node_stack().Push(parse_node); return true; } diff --git a/toolchain/semantics/semantics_handle_codeblock.cpp b/toolchain/semantics/semantics_handle_codeblock.cpp new file mode 100644 index 000000000000..c9eea8405021 --- /dev/null +++ b/toolchain/semantics/semantics_handle_codeblock.cpp @@ -0,0 +1,24 @@ +// 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 + +#include "toolchain/semantics/semantics_context.h" +#include "toolchain/semantics/semantics_node.h" + +namespace Carbon { + +auto SemanticsHandleCodeBlockStart(SemanticsContext& context, + ParseTree::Node parse_node) -> bool { + context.node_stack().Push(parse_node); + context.PushScope(); + return true; +} + +auto SemanticsHandleCodeBlock(SemanticsContext& context, + ParseTree::Node /*parse_node*/) -> bool { + context.PopScope(); + context.node_stack().PopForSoloParseNode(ParseNodeKind::CodeBlockStart); + return true; +} + +} // namespace Carbon diff --git a/toolchain/semantics/semantics_handle_function.cpp b/toolchain/semantics/semantics_handle_function.cpp index 029429dfc9a9..0d59ad154fe9 100644 --- a/toolchain/semantics/semantics_handle_function.cpp +++ b/toolchain/semantics/semantics_handle_function.cpp @@ -13,11 +13,6 @@ auto SemanticsHandleFunctionDeclaration(SemanticsContext& context, auto SemanticsHandleFunctionDefinition(SemanticsContext& context, ParseTree::Node /*parse_node*/) -> bool { - // Merges code block children up under the FunctionDefinitionStart. - while (context.parse_tree().node_kind(context.node_stack().PeekParseNode()) != - ParseNodeKind::FunctionDefinitionStart) { - context.node_stack().PopAndIgnore(); - } context.node_stack().PopAndDiscardId(ParseNodeKind::FunctionDefinitionStart); context.return_scope_stack().pop_back(); context.PopScope(); diff --git a/toolchain/semantics/semantics_handle_if.cpp b/toolchain/semantics/semantics_handle_if.cpp new file mode 100644 index 000000000000..1404e3c5b6f6 --- /dev/null +++ b/toolchain/semantics/semantics_handle_if.cpp @@ -0,0 +1,88 @@ +// 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 + +#include "toolchain/semantics/semantics_context.h" +#include "toolchain/semantics/semantics_node.h" + +namespace Carbon { + +auto SemanticsHandleIfConditionStart(SemanticsContext& /*context*/, + ParseTree::Node /*parse_node*/) -> bool { + return true; +} + +auto SemanticsHandleIfCondition(SemanticsContext& context, + ParseTree::Node parse_node) -> bool { + // Convert the condition to `bool`. + auto cond_value_id = context.node_stack().Pop(); + cond_value_id = context.ImplicitAsBool(parse_node, cond_value_id); + + // Create the else block and the then block, and branch to the right one. If + // there is no `else`, the then block will terminate with a branch to the + // else block, which will be reused as the resumption block. + auto if_block_id = context.node_block_stack().PopForAdd(); + auto else_block_id = context.node_block_stack().PushForAdd(); + auto then_block_id = context.node_block_stack().PushForAdd(); + + // Branch to the appropriate block. + context.AddNodeToBlock( + if_block_id, + SemanticsNode::BranchIf::Make(parse_node, then_block_id, cond_value_id)); + context.AddNodeToBlock( + if_block_id, SemanticsNode::Branch::Make(parse_node, else_block_id)); + + context.node_stack().Push(parse_node); + return true; +} + +auto SemanticsHandleIfStatementElse(SemanticsContext& context, + ParseTree::Node parse_node) -> bool { + context.node_stack().PopAndDiscardSoloParseNode(ParseNodeKind::IfCondition); + + // Switch to emitting the else block. + auto then_block_id = context.node_block_stack().PopForAdd(); + context.node_stack().Push(parse_node, then_block_id); + return true; +} + +auto SemanticsHandleIfStatement(SemanticsContext& context, + ParseTree::Node parse_node) -> bool { + // Either the then or else block, depending on whether there's an `else` node + // on the top of the node stack. + auto sub_block_id = context.node_block_stack().PopForAdd(); + + switch (auto kind = context.parse_tree().node_kind( + context.node_stack().PeekParseNode())) { + case ParseNodeKind::IfCondition: { + // Branch from then block to else block. + context.node_stack().PopAndDiscardSoloParseNode( + ParseNodeKind::IfCondition); + context.AddNodeToBlock( + sub_block_id, + SemanticsNode::Branch::Make(parse_node, + context.node_block_stack().PeekForAdd())); + break; + } + + case ParseNodeKind::IfStatementElse: { + // Branch from the then and else blocks to a new resumption block. + auto then_block_id = context.node_stack().Pop( + ParseNodeKind::IfStatementElse); + auto resume_block_id = context.node_block_stack().PushForAdd(); + context.AddNodeToBlock(then_block_id, SemanticsNode::Branch::Make( + parse_node, resume_block_id)); + context.AddNodeToBlock(sub_block_id, SemanticsNode::Branch::Make( + parse_node, resume_block_id)); + break; + } + + default: { + CARBON_FATAL() << "Unexpected parse node at start of `if`: " << kind; + } + } + + return true; +} + +} // namespace Carbon diff --git a/toolchain/semantics/semantics_ir.cpp b/toolchain/semantics/semantics_ir.cpp index fcb410b41112..f708899b301f 100644 --- a/toolchain/semantics/semantics_ir.cpp +++ b/toolchain/semantics/semantics_ir.cpp @@ -205,7 +205,6 @@ auto SemanticsIR::StringifyType(SemanticsTypeId type_id) -> std::string { case SemanticsNodeKind::BranchWithArg: case SemanticsNodeKind::Builtin: case SemanticsNodeKind::Call: - case SemanticsNodeKind::CodeBlock: case SemanticsNodeKind::CrossReference: case SemanticsNodeKind::FunctionDeclaration: case SemanticsNodeKind::IntegerLiteral: diff --git a/toolchain/semantics/semantics_node.h b/toolchain/semantics/semantics_node.h index a74840ec9887..d94ff92035af 100644 --- a/toolchain/semantics/semantics_node.h +++ b/toolchain/semantics/semantics_node.h @@ -322,9 +322,6 @@ class SemanticsNode { Factory; - using CodeBlock = FactoryNoType; - class CrossReference : public FactoryBase i32 { + if (b) { + var n: i32 = 2; + return n; + } + // CHECK:STDERR: fail_scope.carbon:[[@LINE+1]]:10: Name n not found + return n; +} diff --git a/toolchain/semantics/testdata/if/no_else.carbon b/toolchain/semantics/testdata/if/no_else.carbon new file mode 100644 index 000000000000..bffe38e9927e --- /dev/null +++ b/toolchain/semantics/testdata/if/no_else.carbon @@ -0,0 +1,78 @@ +// 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 +// CHECK:STDOUT: cross_reference_irs_size: 1 +// CHECK:STDOUT: functions: [ +// CHECK:STDOUT: {name: str0, param_refs: block0, body: block2}, +// CHECK:STDOUT: {name: str1, param_refs: block0, body: block3}, +// CHECK:STDOUT: {name: str3, param_refs: block5, body: block6}, +// CHECK:STDOUT: ] +// CHECK:STDOUT: integer_literals: [ +// CHECK:STDOUT: ] +// CHECK:STDOUT: real_literals: [ +// CHECK:STDOUT: ] +// CHECK:STDOUT: strings: [ +// CHECK:STDOUT: F, +// CHECK:STDOUT: G, +// CHECK:STDOUT: b, +// CHECK:STDOUT: If, +// CHECK:STDOUT: ] +// CHECK:STDOUT: types: [ +// CHECK:STDOUT: nodeEmptyTupleType, +// CHECK:STDOUT: nodeBoolType, +// CHECK:STDOUT: ] +// CHECK:STDOUT: nodes: [ +// CHECK:STDOUT: {kind: FunctionDeclaration, arg0: function0}, +// CHECK:STDOUT: {kind: FunctionDeclaration, arg0: function1}, +// CHECK:STDOUT: {kind: VarStorage, type: type1}, +// CHECK:STDOUT: {kind: BindName, arg0: str2, arg1: node+2, type: type1}, +// CHECK:STDOUT: {kind: FunctionDeclaration, arg0: function2}, +// CHECK:STDOUT: {kind: BranchIf, arg0: block8, arg1: node+2}, +// CHECK:STDOUT: {kind: Branch, arg0: block7}, +// CHECK:STDOUT: {kind: Call, arg0: block0, arg1: function0}, +// CHECK:STDOUT: {kind: Branch, arg0: block7}, +// CHECK:STDOUT: {kind: Call, arg0: block0, arg1: function1}, +// CHECK:STDOUT: ] +// CHECK:STDOUT: node_blocks: [ +// CHECK:STDOUT: [ +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+0, +// CHECK:STDOUT: node+1, +// CHECK:STDOUT: node+4, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+2, +// CHECK:STDOUT: node+3, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+3, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+5, +// CHECK:STDOUT: node+6, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+9, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+7, +// CHECK:STDOUT: node+8, +// CHECK:STDOUT: ], +// CHECK:STDOUT: ] + +fn F() {} +fn G() {} + +fn If(b: bool) { + if (b) { + F(); + } + G(); +}