diff --git a/toolchain/semantics/semantics_node.h b/toolchain/semantics/semantics_node.h index 616c44e39f65..0c3c2b53d9d2 100644 --- a/toolchain/semantics/semantics_node.h +++ b/toolchain/semantics/semantics_node.h @@ -77,6 +77,17 @@ class SemanticsNode { auto GetAsInvalid() const -> NoArgs { CARBON_FATAL() << "Invalid access"; } + static auto MakeAssign(ParseTree::Node parse_node, SemanticsNodeId type, + SemanticsNodeId lhs, SemanticsNodeId rhs) + -> SemanticsNode { + return SemanticsNode(parse_node, SemanticsNodeKind::Assign(), type, + lhs.index, rhs.index); + } + auto GetAsAssign() const -> std::pair { + CARBON_CHECK(kind_ == SemanticsNodeKind::Assign()); + return {SemanticsNodeId(arg0_), SemanticsNodeId(arg1_)}; + } + static auto MakeBinaryOperatorAdd(ParseTree::Node parse_node, SemanticsNodeId type, SemanticsNodeId lhs, SemanticsNodeId rhs) -> SemanticsNode { @@ -89,11 +100,11 @@ class SemanticsNode { return {SemanticsNodeId(arg0_), SemanticsNodeId(arg1_)}; } - static auto MakeBindName(ParseTree::Node parse_node, + static auto MakeBindName(ParseTree::Node parse_node, SemanticsNodeId type, SemanticsIdentifierId name, SemanticsNodeId node) -> SemanticsNode { - return SemanticsNode(parse_node, SemanticsNodeKind::BindName(), - SemanticsNodeId(), name.index, node.index); + return SemanticsNode(parse_node, SemanticsNodeKind::BindName(), type, + name.index, node.index); } auto GetAsBindName() const -> std::pair { @@ -193,6 +204,15 @@ class SemanticsNode { return SemanticsNodeId(arg0_); } + static auto MakeVarStorage(ParseTree::Node parse_node, SemanticsNodeId type) + -> SemanticsNode { + return SemanticsNode(parse_node, SemanticsNodeKind::VarStorage(), type); + } + auto GetAsVarStorage() const -> NoArgs { + CARBON_CHECK(kind_ == SemanticsNodeKind::VarStorage()); + return NoArgs(); + } + SemanticsNode() : SemanticsNode(ParseTree::Node(), SemanticsNodeKind::Invalid(), SemanticsNodeId()) {} diff --git a/toolchain/semantics/semantics_node_kind.def b/toolchain/semantics/semantics_node_kind.def index 86a37b969b3e..07d2dfc5c288 100644 --- a/toolchain/semantics/semantics_node_kind.def +++ b/toolchain/semantics/semantics_node_kind.def @@ -15,6 +15,7 @@ CARBON_SEMANTICS_NODE_KIND(Invalid) +CARBON_SEMANTICS_NODE_KIND(Assign) CARBON_SEMANTICS_NODE_KIND(BinaryOperatorAdd) CARBON_SEMANTICS_NODE_KIND(BindName) CARBON_SEMANTICS_NODE_KIND(Builtin) @@ -25,5 +26,6 @@ CARBON_SEMANTICS_NODE_KIND(IntegerLiteral) CARBON_SEMANTICS_NODE_KIND(RealLiteral) CARBON_SEMANTICS_NODE_KIND(Return) CARBON_SEMANTICS_NODE_KIND(ReturnExpression) +CARBON_SEMANTICS_NODE_KIND(VarStorage) #undef CARBON_SEMANTICS_NODE_KIND diff --git a/toolchain/semantics/semantics_parse_tree_handler.cpp b/toolchain/semantics/semantics_parse_tree_handler.cpp index fa8749f5bae6..a75f7248cab8 100644 --- a/toolchain/semantics/semantics_parse_tree_handler.cpp +++ b/toolchain/semantics/semantics_parse_tree_handler.cpp @@ -150,6 +150,45 @@ auto SemanticsParseTreeHandler::PopWithResult(ParseNodeKind pop_parse_kind) return node_id; } +auto SemanticsParseTreeHandler::PopWithResultIf(ParseNodeKind pop_parse_kind) + -> std::optional { + auto parse_kind = parse_tree_->node_kind(node_stack_.back().parse_node); + if (parse_kind != pop_parse_kind) { + return std::nullopt; + } + + auto back = node_stack_.pop_back_val(); + auto node_id = back.result_id; + CARBON_VLOG() << "Pop " << node_stack_.size() << ": " << pop_parse_kind + << ") -> " << node_id << "\n"; + CARBON_CHECK(node_id.is_valid()) + << "Invalid PopWithResult with " << parse_kind; + return node_id; +} + +auto SemanticsParseTreeHandler::TryTypeConversion(ParseTree::Node parse_node, + SemanticsNodeId lhs_id, + SemanticsNodeId rhs_id, + bool /*can_convert_lhs*/) + -> SemanticsNodeId { + auto block = node_block_stack_.back(); + auto lhs_type = semantics_->GetType(block, lhs_id); + auto rhs_type = semantics_->GetType(block, rhs_id); + // TODO: This should attempt a type conversion, but there's not enough + // implemented to do that right now. + if (lhs_type != rhs_type) { + auto invalid_type = SemanticsNodeId::MakeBuiltinReference( + SemanticsBuiltinKind::InvalidType()); + if (lhs_type != invalid_type && rhs_type != invalid_type) { + // TODO: This is a poor diagnostic, and should be expanded. + CARBON_DIAGNOSTIC(TypeMismatch, Error, "Type mismatch"); + emitter_->Emit(parse_tree_->node_token(parse_node), TypeMismatch); + } + return invalid_type; + } + return lhs_type; +} + auto SemanticsParseTreeHandler::AddIdentifier(ParseTree::Node decl_node) -> SemanticsIdentifierId { CARBON_CHECK(parse_tree_->node_kind(decl_node) == @@ -284,7 +323,9 @@ auto SemanticsParseTreeHandler::HandleFunctionDefinitionStart( Pop(ParseNodeKind::FunctionIntroducer()); auto decl_id = AddNode(SemanticsNode::MakeFunctionDeclaration(fn_node)); - AddNode(SemanticsNode::MakeBindName(name_node, name, decl_id)); + // TODO: Propagate the type of the function. + AddNode(SemanticsNode::MakeBindName(name_node, SemanticsNodeId::MakeInvalid(), + name, decl_id)); auto block_id = semantics_->AddNodeBlock(); AddNode(SemanticsNode::MakeFunctionDefinition(parse_node, decl_id, block_id)); node_block_stack_.push_back(block_id); @@ -321,23 +362,8 @@ auto SemanticsParseTreeHandler::HandleInfixOperator(ParseTree::Node parse_node) -> void { auto rhs_id = PopWithResult(); auto lhs_id = PopWithResult(); - - auto block = node_block_stack_.back(); - auto lhs_type = semantics_->GetType(block, lhs_id); - auto rhs_type = semantics_->GetType(block, rhs_id); - SemanticsNodeId result_type = lhs_type; - // TODO: This should attempt a type conversion, but there's not enough - // implemented to do that right now. - if (lhs_type != rhs_type) { - auto invalid_type = SemanticsNodeId::MakeBuiltinReference( - SemanticsBuiltinKind::InvalidType()); - if (lhs_type != invalid_type && rhs_type != invalid_type) { - // TODO: This is a poor diagnostic, and should be expanded. - CARBON_DIAGNOSTIC(TypeMismatch, Error, "Type mismatch"); - emitter_->Emit(parse_tree_->node_token(parse_node), TypeMismatch); - } - result_type = invalid_type; - } + SemanticsNodeId result_type = + TryTypeConversion(parse_node, lhs_id, rhs_id, /*can_convert_lhs=*/true); // Figure out the operator for the token. auto token = parse_tree_->node_token(parse_node); @@ -454,16 +480,23 @@ auto SemanticsParseTreeHandler::HandleParenExpressionOrTupleLiteralStart( auto SemanticsParseTreeHandler::HandlePatternBinding(ParseTree::Node parse_node) -> void { - // TODO: Create storage for the type, use that for the bind instead of the - // type itself. - auto type_id = PopWithResult(); + // Allocate storage. + auto type = node_stack_.pop_back_val(); + CARBON_CHECK(type.result_id.is_valid()); + auto storage_id = + AddNode(SemanticsNode::MakeVarStorage(parse_node, type.result_id)); - auto name_node = node_stack_.back().parse_node; - auto name = AddIdentifier(name_node); - node_stack_.pop_back(); + // Get the name. + auto name_node = node_stack_.pop_back_val().parse_node; + auto name_id = AddIdentifier(name_node); - Push(parse_node, - AddNode(SemanticsNode::MakeBindName(name_node, name, type_id))); + // Bind the name to storage. + AddNode(SemanticsNode::MakeBindName(name_node, type.result_id, name_id, + storage_id)); + + // If this node's result is used, it'll be for the storage address, so provide + // that. + Push(parse_node, storage_id); } auto SemanticsParseTreeHandler::HandlePostfixOperator( @@ -554,11 +587,16 @@ auto SemanticsParseTreeHandler::HandleTupleLiteralComma( auto SemanticsParseTreeHandler::HandleVariableDeclaration( ParseTree::Node parse_node) -> void { - // TODO: Initializers would assign to the PatternBinding, but this code - // doesn't handle it right now. - PopWithResult(); + auto init_id = PopWithResultIf(ParseNodeKind::VariableInitializer()); + auto storage_id = PopWithResult(ParseNodeKind::PatternBinding()); + if (init_id) { + auto storage_type = TryTypeConversion(parse_node, storage_id, *init_id, + /*can_convert_lhs=*/false); + AddNode(SemanticsNode::MakeAssign(parse_node, storage_type, storage_id, + *init_id)); + } Pop(ParseNodeKind::VariableIntroducer()); - Push(parse_node); + Push(parse_node, storage_id); } auto SemanticsParseTreeHandler::HandleVariableIntroducer( @@ -568,8 +606,9 @@ auto SemanticsParseTreeHandler::HandleVariableIntroducer( } auto SemanticsParseTreeHandler::HandleVariableInitializer( - ParseTree::Node /*parse_node*/) -> void { - CARBON_FATAL() << "TODO"; + ParseTree::Node parse_node) -> void { + // The child is the expression; propagate it for the parent. + Push(parse_node, PopWithResult()); } auto SemanticsParseTreeHandler::HandleWhileCondition( diff --git a/toolchain/semantics/semantics_parse_tree_handler.h b/toolchain/semantics/semantics_parse_tree_handler.h index b8fe7283014e..fbc222792deb 100644 --- a/toolchain/semantics/semantics_parse_tree_handler.h +++ b/toolchain/semantics/semantics_parse_tree_handler.h @@ -71,6 +71,18 @@ class SemanticsParseTreeHandler { // returning the result_id. Must only be called for nodes that have results. auto PopWithResult(ParseNodeKind pop_parse_kind) -> SemanticsNodeId; + // Pops the top of the stack, verifying that it's the expected kind and + // returning the result_id. Must only be called for nodes that have results. + auto PopWithResultIf(ParseNodeKind pop_parse_kind) + -> std::optional; + + // Attempts a type conversion between arguments of the two arguments with + // provided types, returning the result type. The result type will be invalid + // for errors; this handles printing diagnostics. + auto TryTypeConversion(ParseTree::Node parse_node, SemanticsNodeId lhs_id, + SemanticsNodeId rhs_id, bool can_convert_lhs) + -> SemanticsNodeId; + // Parse node handlers. #define CARBON_PARSE_NODE_KIND(Name) \ auto Handle##Name(ParseTree::Node parse_node)->void; diff --git a/toolchain/semantics/testdata/var/decl.carbon b/toolchain/semantics/testdata/var/decl.carbon index 4fa20f857dc4..020b59c0ce73 100644 --- a/toolchain/semantics/testdata/var/decl.carbon +++ b/toolchain/semantics/testdata/var/decl.carbon @@ -12,14 +12,23 @@ // CHECK:STDOUT: node_xref3 = xref(ir0, block0, node3); // CHECK:STDOUT: }, // CHECK:STDOUT: identifiers = { -// CHECK:STDOUT: ident0 = "x"; +// CHECK:STDOUT: ident0 = "Main"; +// CHECK:STDOUT: ident1 = "x"; // CHECK:STDOUT: }, // CHECK:STDOUT: integer_literals = { // CHECK:STDOUT: }, // CHECK:STDOUT: node_blocks = { // CHECK:STDOUT: block0 = { -// CHECK:STDOUT: node0 = BindName(ident0, node_xref2); +// CHECK:STDOUT: node0 = FunctionDeclaration(); +// CHECK:STDOUT: node1 = BindName(ident0, node0); +// CHECK:STDOUT: node2 = FunctionDefinition(node0, block1); +// CHECK:STDOUT: }, +// CHECK:STDOUT: block1 = { +// CHECK:STDOUT: node0 = VarStorage(): node_xref2; +// CHECK:STDOUT: node1 = BindName(ident1, node0): node_xref2; // CHECK:STDOUT: }, // CHECK:STDOUT: } -var x: i32; +fn Main() { + var x: i32; +} diff --git a/toolchain/semantics/testdata/var/decl_with_init.carbon b/toolchain/semantics/testdata/var/decl_with_init.carbon new file mode 100644 index 000000000000..c44551c6b87c --- /dev/null +++ b/toolchain/semantics/testdata/var/decl_with_init.carbon @@ -0,0 +1,37 @@ +// 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 +// RUN: %{carbon-run-semantics} +// CHECK:STDOUT: cross_reference_irs.size == 2, +// CHECK:STDOUT: cross_references = { +// CHECK:STDOUT: node_xref0 = xref(ir0, block0, node0); +// CHECK:STDOUT: node_xref1 = xref(ir0, block0, node1); +// CHECK:STDOUT: node_xref2 = xref(ir0, block0, node2); +// CHECK:STDOUT: node_xref3 = xref(ir0, block0, node3); +// CHECK:STDOUT: }, +// CHECK:STDOUT: identifiers = { +// CHECK:STDOUT: ident0 = "Main"; +// CHECK:STDOUT: ident1 = "x"; +// CHECK:STDOUT: }, +// CHECK:STDOUT: integer_literals = { +// CHECK:STDOUT: int0 = 0; +// CHECK:STDOUT: }, +// CHECK:STDOUT: node_blocks = { +// CHECK:STDOUT: block0 = { +// CHECK:STDOUT: node0 = FunctionDeclaration(); +// CHECK:STDOUT: node1 = BindName(ident0, node0); +// CHECK:STDOUT: node2 = FunctionDefinition(node0, block1); +// CHECK:STDOUT: }, +// CHECK:STDOUT: block1 = { +// CHECK:STDOUT: node0 = VarStorage(): node_xref2; +// CHECK:STDOUT: node1 = BindName(ident1, node0): node_xref2; +// CHECK:STDOUT: node2 = IntegerLiteral(int0): node_xref2; +// CHECK:STDOUT: node3 = Assign(node0, node2): node_xref2; +// CHECK:STDOUT: }, +// CHECK:STDOUT: } + +fn Main() { + var x: i32 = 0; +} diff --git a/toolchain/semantics/testdata/var/global_decl.carbon b/toolchain/semantics/testdata/var/global_decl.carbon index e4a29084cb5c..66ab5284e205 100644 --- a/toolchain/semantics/testdata/var/global_decl.carbon +++ b/toolchain/semantics/testdata/var/global_decl.carbon @@ -12,22 +12,15 @@ // CHECK:STDOUT: node_xref3 = xref(ir0, block0, node3); // CHECK:STDOUT: }, // CHECK:STDOUT: identifiers = { -// CHECK:STDOUT: ident0 = "Main"; -// CHECK:STDOUT: ident1 = "x"; +// CHECK:STDOUT: ident0 = "x"; // CHECK:STDOUT: }, // CHECK:STDOUT: integer_literals = { // CHECK:STDOUT: }, // CHECK:STDOUT: node_blocks = { // CHECK:STDOUT: block0 = { -// CHECK:STDOUT: node0 = FunctionDeclaration(); -// CHECK:STDOUT: node1 = BindName(ident0, node0); -// CHECK:STDOUT: node2 = FunctionDefinition(node0, block1); -// CHECK:STDOUT: }, -// CHECK:STDOUT: block1 = { -// CHECK:STDOUT: node0 = BindName(ident1, node_xref2); +// CHECK:STDOUT: node0 = VarStorage(): node_xref2; +// CHECK:STDOUT: node1 = BindName(ident0, node0): node_xref2; // CHECK:STDOUT: }, // CHECK:STDOUT: } -fn Main() { - var x: i32; -} +var x: i32; diff --git a/toolchain/semantics/testdata/var/global_decl_with_init.carbon b/toolchain/semantics/testdata/var/global_decl_with_init.carbon new file mode 100644 index 000000000000..8fbbb75aedbe --- /dev/null +++ b/toolchain/semantics/testdata/var/global_decl_with_init.carbon @@ -0,0 +1,29 @@ +// 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 +// RUN: %{carbon-run-semantics} +// CHECK:STDOUT: cross_reference_irs.size == 2, +// CHECK:STDOUT: cross_references = { +// CHECK:STDOUT: node_xref0 = xref(ir0, block0, node0); +// CHECK:STDOUT: node_xref1 = xref(ir0, block0, node1); +// CHECK:STDOUT: node_xref2 = xref(ir0, block0, node2); +// CHECK:STDOUT: node_xref3 = xref(ir0, block0, node3); +// CHECK:STDOUT: }, +// CHECK:STDOUT: identifiers = { +// CHECK:STDOUT: ident0 = "x"; +// CHECK:STDOUT: }, +// CHECK:STDOUT: integer_literals = { +// CHECK:STDOUT: int0 = 0; +// CHECK:STDOUT: }, +// CHECK:STDOUT: node_blocks = { +// CHECK:STDOUT: block0 = { +// CHECK:STDOUT: node0 = VarStorage(): node_xref2; +// CHECK:STDOUT: node1 = BindName(ident0, node0): node_xref2; +// CHECK:STDOUT: node2 = IntegerLiteral(int0): node_xref2; +// CHECK:STDOUT: node3 = Assign(node0, node2): node_xref2; +// CHECK:STDOUT: }, +// CHECK:STDOUT: } + +var x: i32 = 0; diff --git a/toolchain/semantics/testdata/var/init_type_mismatch.carbon b/toolchain/semantics/testdata/var/init_type_mismatch.carbon new file mode 100644 index 000000000000..73d8c8fb2a86 --- /dev/null +++ b/toolchain/semantics/testdata/var/init_type_mismatch.carbon @@ -0,0 +1,37 @@ +// 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 +// RUN: %{carbon-run-semantics} +// CHECK:STDOUT: cross_reference_irs.size == 2, +// CHECK:STDOUT: cross_references = { +// CHECK:STDOUT: node_xref0 = xref(ir0, block0, node0); +// CHECK:STDOUT: node_xref1 = xref(ir0, block0, node1); +// CHECK:STDOUT: node_xref2 = xref(ir0, block0, node2); +// CHECK:STDOUT: node_xref3 = xref(ir0, block0, node3); +// CHECK:STDOUT: }, +// CHECK:STDOUT: identifiers = { +// CHECK:STDOUT: ident0 = "Main"; +// CHECK:STDOUT: ident1 = "x"; +// CHECK:STDOUT: }, +// CHECK:STDOUT: integer_literals = { +// CHECK:STDOUT: }, +// CHECK:STDOUT: node_blocks = { +// CHECK:STDOUT: block0 = { +// CHECK:STDOUT: node0 = FunctionDeclaration(); +// CHECK:STDOUT: node1 = BindName(ident0, node0); +// CHECK:STDOUT: node2 = FunctionDefinition(node0, block1); +// CHECK:STDOUT: }, +// CHECK:STDOUT: block1 = { +// CHECK:STDOUT: node0 = VarStorage(): node_xref2; +// CHECK:STDOUT: node1 = BindName(ident1, node0): node_xref2; +// CHECK:STDOUT: node2 = RealLiteral(): node_xref3; +// CHECK:STDOUT: node3 = Assign(node0, node2): node_xref1; +// CHECK:STDOUT: }, +// CHECK:STDOUT: } + +fn Main() { + // CHECK:STDERR: {{.*}}/toolchain/semantics/testdata/var/init_type_mismatch.carbon:[[@LINE+1]]:19: Type mismatch + var x: i32 = 1.0; +}