From 77385aa3440de658fe1a7253d353fb7f207f107a Mon Sep 17 00:00:00 2001 From: Jon Ross-Perkins Date: Mon, 15 May 2023 14:45:16 -0700 Subject: [PATCH] Refactor semantics logic into separate files. (#2819) This echoes #2818 and the philosophy is mostly covered there. Versus parsing, semantics uses fewer separate handler files (for now) because the logic has been shorter. However, the design is still intended to make it easy to split files along boundaries similar to the parser, as I've done for a couple more complex/inter-related sections. --- toolchain/common/BUILD | 8 + .../common/pretty_stack_trace_function.h | 28 + toolchain/semantics/BUILD | 11 +- toolchain/semantics/semantics_context.cpp | 366 +++++ ...rse_tree_handler.h => semantics_context.h} | 226 +-- toolchain/semantics/semantics_handle.cpp | 535 +++++++ .../semantics_handle_call_expression.cpp | 68 + .../semantics/semantics_handle_class.cpp | 29 + .../semantics/semantics_handle_function.cpp | 77 + .../semantics/semantics_handle_interface.cpp | 30 + .../semantics/semantics_handle_package.cpp | 34 + .../semantics/semantics_handle_struct.cpp | 128 ++ toolchain/semantics/semantics_ir.cpp | 36 +- toolchain/semantics/semantics_ir.h | 114 +- .../semantics_parse_tree_handler.cpp | 1249 ----------------- 15 files changed, 1523 insertions(+), 1416 deletions(-) create mode 100644 toolchain/common/pretty_stack_trace_function.h create mode 100644 toolchain/semantics/semantics_context.cpp rename toolchain/semantics/{semantics_parse_tree_handler.h => semantics_context.h} (77%) create mode 100644 toolchain/semantics/semantics_handle.cpp create mode 100644 toolchain/semantics/semantics_handle_call_expression.cpp create mode 100644 toolchain/semantics/semantics_handle_class.cpp create mode 100644 toolchain/semantics/semantics_handle_function.cpp create mode 100644 toolchain/semantics/semantics_handle_interface.cpp create mode 100644 toolchain/semantics/semantics_handle_package.cpp create mode 100644 toolchain/semantics/semantics_handle_struct.cpp delete mode 100644 toolchain/semantics/semantics_parse_tree_handler.cpp diff --git a/toolchain/common/BUILD b/toolchain/common/BUILD index 55aed41553ba..d5f43c698c83 100644 --- a/toolchain/common/BUILD +++ b/toolchain/common/BUILD @@ -13,6 +13,14 @@ cc_library( ], ) +cc_library( + name = "pretty_stack_trace_function", + hdrs = ["pretty_stack_trace_function.h"], + deps = [ + "@llvm-project//llvm:Support", + ], +) + cc_library( name = "yaml_test_helpers", testonly = 1, diff --git a/toolchain/common/pretty_stack_trace_function.h b/toolchain/common/pretty_stack_trace_function.h new file mode 100644 index 000000000000..a6b7e9a29745 --- /dev/null +++ b/toolchain/common/pretty_stack_trace_function.h @@ -0,0 +1,28 @@ +// 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 + +#ifndef CARBON_TOOLCHAIN_COMMON_PRETTY_STACK_TRACE_FUNCTION_H_ +#define CARBON_TOOLCHAIN_COMMON_PRETTY_STACK_TRACE_FUNCTION_H_ + +#include + +#include "llvm/Support/PrettyStackTrace.h" + +namespace Carbon { + +class PrettyStackTraceFunction : public llvm::PrettyStackTraceEntry { + public: + explicit PrettyStackTraceFunction(std::function fn) + : fn_(std::move(fn)) {} + ~PrettyStackTraceFunction() override = default; + + auto print(llvm::raw_ostream& output) const -> void override { fn_(output); } + + private: + const std::function fn_; +}; + +} // namespace Carbon + +#endif // CARBON_TOOLCHAIN_COMMON_PRETTY_STACK_TRACE_FUNCTION_H_ diff --git a/toolchain/semantics/BUILD b/toolchain/semantics/BUILD index 34be5123f637..a2619b89d8a7 100644 --- a/toolchain/semantics/BUILD +++ b/toolchain/semantics/BUILD @@ -69,11 +69,15 @@ cc_library( name = "semantics_ir", srcs = [ "semantics_ir.cpp", - "semantics_parse_tree_handler.cpp", - ], + "semantics_context.cpp", + ] + + # Glob handler files to avoid missing anyway. + glob([ + "semantics_handle*.cpp", + ]), hdrs = [ + "semantics_context.h", "semantics_ir.h", - "semantics_parse_tree_handler.h", ], deps = [ ":semantics_builtin_kind", @@ -83,6 +87,7 @@ cc_library( "//common:check", "//common:ostream", "//common:vlog", + "//toolchain/common:pretty_stack_trace_function", "//toolchain/diagnostics:diagnostic_kind", "//toolchain/lexer:numeric_literal", "//toolchain/lexer:token_kind", diff --git a/toolchain/semantics/semantics_context.cpp b/toolchain/semantics/semantics_context.cpp new file mode 100644 index 000000000000..63a4fafb7b16 --- /dev/null +++ b/toolchain/semantics/semantics_context.cpp @@ -0,0 +1,366 @@ +// 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 + +#include "common/vlog.h" +#include "toolchain/diagnostics/diagnostic_kind.h" +#include "toolchain/lexer/token_kind.h" +#include "toolchain/lexer/tokenized_buffer.h" +#include "toolchain/parser/parse_node_kind.h" +#include "toolchain/semantics/semantics_ir.h" +#include "toolchain/semantics/semantics_node.h" +#include "toolchain/semantics/semantics_node_block_stack.h" + +namespace Carbon { + +SemanticsContext::SemanticsContext(const TokenizedBuffer& tokens, + DiagnosticEmitter& emitter, + const ParseTree& parse_tree, + SemanticsIR& semantics, + llvm::raw_ostream* vlog_stream) + : tokens_(&tokens), + emitter_(&emitter), + parse_tree_(&parse_tree), + semantics_(&semantics), + vlog_stream_(vlog_stream), + node_stack_(parse_tree, vlog_stream), + node_block_stack_("node_block_stack_", semantics.node_blocks(), + vlog_stream), + params_or_args_stack_("params_or_args_stack_", semantics.node_blocks(), + vlog_stream), + args_type_info_stack_("args_type_info_stack_", semantics.node_blocks(), + vlog_stream) {} + +auto SemanticsContext::TODO(ParseTree::Node parse_node, std::string label) + -> bool { + CARBON_DIAGNOSTIC(SemanticsTodo, Error, "Semantics TODO: {0}", std::string); + emitter_->Emit(parse_node, SemanticsTodo, std::move(label)); + return false; +} + +auto SemanticsContext::VerifyOnFinish() -> void { + // Information in all the various context objects should be cleaned up as + // various pieces of context go out of scope. At this point, nothing should + // remain. + // node_stack_ will still contain top-level entities. + CARBON_CHECK(name_lookup_.empty()) << name_lookup_.size(); + CARBON_CHECK(scope_stack_.empty()) << scope_stack_.size(); + CARBON_CHECK(node_block_stack_.empty()) << node_block_stack_.size(); + CARBON_CHECK(params_or_args_stack_.empty()) << params_or_args_stack_.size(); +} + +auto SemanticsContext::AddNode(SemanticsNode node) -> SemanticsNodeId { + auto block = node_block_stack_.PeekForAdd(); + CARBON_VLOG() << "AddNode " << block << ": " << node << "\n"; + return semantics_->AddNode(block, node); +} + +auto SemanticsContext::AddNodeAndPush(ParseTree::Node parse_node, + SemanticsNode node) -> void { + auto node_id = AddNode(node); + node_stack_.Push(parse_node, node_id); +} + +auto SemanticsContext::AddNameToLookup(ParseTree::Node name_node, + SemanticsStringId name_id, + SemanticsNodeId target_id) -> void { + auto [it, inserted] = current_scope().names.insert(name_id); + if (inserted) { + name_lookup_[name_id].push_back(target_id); + } else { + CARBON_DIAGNOSTIC(NameRedefined, Error, "Redefining {0} in the same scope.", + llvm::StringRef); + CARBON_DIAGNOSTIC(PreviousDefinition, Note, "Previous definition is here."); + auto prev_def_id = name_lookup_[name_id].back(); + auto prev_def = semantics_->GetNode(prev_def_id); + + emitter_->Build(name_node, NameRedefined, semantics_->GetString(name_id)) + .Note(prev_def.parse_node(), PreviousDefinition) + .Emit(); + } +} + +auto SemanticsContext::BindName(ParseTree::Node name_node, + SemanticsNodeId type_id, + SemanticsNodeId target_id) + -> SemanticsStringId { + CARBON_CHECK(parse_tree_->node_kind(name_node) == ParseNodeKind::DeclaredName) + << parse_tree_->node_kind(name_node); + auto name_str = parse_tree_->GetNodeText(name_node); + auto name_id = semantics_->AddString(name_str); + + AddNode( + SemanticsNode::BindName::Make(name_node, type_id, name_id, target_id)); + AddNameToLookup(name_node, name_id, target_id); + return name_id; +} + +auto SemanticsContext::TempRemoveLatestNameFromLookup() -> SemanticsNodeId { + // Save the storage ID. + auto it = name_lookup_.find( + node_stack_.PeekForNameId(ParseNodeKind::PatternBinding)); + CARBON_CHECK(it != name_lookup_.end()); + CARBON_CHECK(!it->second.empty()); + auto storage_id = it->second.back(); + + // Pop the name from lookup. + if (it->second.size() == 1) { + // Erase names that no longer resolve. + name_lookup_.erase(it); + } else { + it->second.pop_back(); + } + return storage_id; +} + +auto SemanticsContext::LookupName(ParseTree::Node parse_node, + llvm::StringRef name) -> SemanticsNodeId { + CARBON_DIAGNOSTIC(NameNotFound, Error, "Name {0} not found", llvm::StringRef); + + auto name_id = semantics_->GetStringID(name); + if (!name_id) { + emitter_->Emit(parse_node, NameNotFound, name); + return SemanticsNodeId::BuiltinInvalidType; + } + + auto it = name_lookup_.find(*name_id); + if (it == name_lookup_.end()) { + emitter_->Emit(parse_node, NameNotFound, name); + return SemanticsNodeId::BuiltinInvalidType; + } + CARBON_CHECK(!it->second.empty()) << "Should have been erased: " << name; + + // TODO: Check for ambiguous lookups. + return it->second.back(); +} + +auto SemanticsContext::PushScope() -> void { scope_stack_.push_back({}); } + +auto SemanticsContext::PopScope() -> void { + auto scope = scope_stack_.pop_back_val(); + for (const auto& str_id : scope.names) { + auto it = name_lookup_.find(str_id); + if (it->second.size() == 1) { + // Erase names that no longer resolve. + name_lookup_.erase(it); + } else { + it->second.pop_back(); + } + } +} + +auto SemanticsContext::ImplicitAsForArgs( + SemanticsNodeBlockId /*arg_ir_id*/, SemanticsNodeBlockId arg_refs_id, + ParseTree::Node param_parse_node, SemanticsNodeBlockId param_refs_id, + DiagnosticEmitter::DiagnosticBuilder* diagnostic) -> bool { + // If both arguments and parameters are empty, return quickly. Otherwise, + // we'll fetch both so that errors are consistent. + if (arg_refs_id == SemanticsNodeBlockId::Empty && + param_refs_id == SemanticsNodeBlockId::Empty) { + return true; + } + + auto arg_refs = semantics_->GetNodeBlock(arg_refs_id); + auto param_refs = semantics_->GetNodeBlock(param_refs_id); + + // If sizes mismatch, fail early. + if (arg_refs.size() != param_refs.size()) { + CARBON_CHECK(diagnostic != nullptr) << "Should have validated first"; + CARBON_DIAGNOSTIC(CallArgCountMismatch, Note, + "Callable cannot be used: Received {0} argument(s), but " + "require {1} argument(s).", + int, int); + diagnostic->Note(param_parse_node, CallArgCountMismatch, arg_refs.size(), + param_refs.size()); + return false; + } + + // Check type conversions per-element. + // TODO: arg_ir_id is passed so that implicit conversions can be inserted. + // It's currently not supported, but will be needed. + for (size_t i = 0; i < arg_refs.size(); ++i) { + auto value_id = arg_refs[i]; + auto as_type_id = semantics_->GetNode(param_refs[i]).type_id(); + if (ImplicitAsImpl(value_id, as_type_id, + diagnostic == nullptr ? &value_id : nullptr) == + ImplicitAsKind::Incompatible) { + CARBON_CHECK(diagnostic != nullptr) << "Should have validated first"; + CARBON_DIAGNOSTIC(CallArgTypeMismatch, Note, + "Callable cannot be used: Cannot implicityly convert " + "argument {0} from `{1}` to `{2}`.", + size_t, std::string, std::string); + diagnostic->Note( + param_parse_node, CallArgTypeMismatch, i, + semantics_->StringifyNode(semantics_->GetNode(value_id).type_id()), + semantics_->StringifyNode(as_type_id)); + return false; + } + } + + return true; +} + +auto SemanticsContext::ImplicitAsRequired(ParseTree::Node parse_node, + SemanticsNodeId value_id, + SemanticsNodeId as_type_id) + -> SemanticsNodeId { + SemanticsNodeId output_value_id = value_id; + if (ImplicitAsImpl(value_id, as_type_id, &output_value_id) == + ImplicitAsKind::Incompatible) { + // Only error when the system is trying to use the result. + CARBON_DIAGNOSTIC(ImplicitAsConversionFailure, Error, + "Cannot implicitly convert from `{0}` to `{1}`.", + std::string, std::string); + emitter_ + ->Build( + parse_node, ImplicitAsConversionFailure, + semantics_->StringifyNode(semantics_->GetNode(value_id).type_id()), + semantics_->StringifyNode(as_type_id)) + .Emit(); + } + return output_value_id; +} + +auto SemanticsContext::ImplicitAsImpl(SemanticsNodeId value_id, + SemanticsNodeId as_type_id, + SemanticsNodeId* output_value_id) + -> ImplicitAsKind { + // Start by making sure both sides are valid. If any part is invalid, the + // result is invalid and we shouldn't error. + if (value_id == SemanticsNodeId::BuiltinInvalidType) { + // If the value is invalid, we can't do much, but do "succeed". + return ImplicitAsKind::Identical; + } + auto value_type_id = semantics_->GetNode(value_id).type_id(); + if (value_type_id == SemanticsNodeId::BuiltinInvalidType) { + return ImplicitAsKind::Identical; + } + if (as_type_id == SemanticsNodeId::BuiltinInvalidType) { + // Although the target type is invalid, this still changes the value. + if (output_value_id != nullptr) { + *output_value_id = SemanticsNodeId::BuiltinInvalidType; + } + return ImplicitAsKind::Compatible; + } + + if (value_type_id == as_type_id) { + // Type doesn't need to change. + return ImplicitAsKind::Identical; + } + + // When converting to a Type, there are some automatic conversions that can be + // done. + if (as_type_id == SemanticsNodeId::BuiltinTypeType) { + if (value_id == SemanticsNodeId::BuiltinEmptyTuple) { + if (output_value_id != nullptr) { + *output_value_id = SemanticsNodeId::BuiltinEmptyTupleType; + } + return ImplicitAsKind::Compatible; + } + if (value_id == SemanticsNodeId::BuiltinEmptyStruct) { + if (output_value_id != nullptr) { + *output_value_id = SemanticsNodeId::BuiltinEmptyStructType; + } + return ImplicitAsKind::Compatible; + } + } + + auto value_type = semantics_->GetNode(value_type_id); + auto as_type = semantics_->GetNode(as_type_id); + if (CanImplicitAsStruct(value_type, as_type)) { + // Under the current implementation, struct types are only allowed to + // ImplicitAs when they're equivalent. What's really missing is type + // consolidation such that this would fall under the above `value_type_id == + // as_type_id` case. In the future, this will need to handle actual + // conversions. + return ImplicitAsKind::Identical; + } + + if (output_value_id != nullptr) { + *output_value_id = SemanticsNodeId::BuiltinInvalidType; + } + return ImplicitAsKind::Incompatible; +} + +auto SemanticsContext::CanImplicitAsStruct(SemanticsNode value_type, + SemanticsNode as_type) -> bool { + if (value_type.kind() != SemanticsNodeKind::StructType || + as_type.kind() != SemanticsNodeKind::StructType) { + return false; + } + auto value_type_refs = + semantics_->GetNodeBlock(value_type.GetAsStructType().second); + auto as_type_refs = + semantics_->GetNodeBlock(as_type.GetAsStructType().second); + if (value_type_refs.size() != as_type_refs.size()) { + return false; + } + + for (int i = 0; i < static_cast(value_type_refs.size()); ++i) { + auto value_type_field = semantics_->GetNode(value_type_refs[i]); + auto as_type_field = semantics_->GetNode(as_type_refs[i]); + if (value_type_field.type_id() != as_type_field.type_id() || + value_type_field.GetAsStructTypeField() != + as_type_field.GetAsStructTypeField()) { + return false; + } + } + return true; +} + +auto SemanticsContext::ParamOrArgStart() -> void { + params_or_args_stack_.Push(); + node_block_stack_.Push(); +} + +auto SemanticsContext::ParamOrArgComma(bool for_args) -> void { + ParamOrArgSave(for_args); +} + +auto SemanticsContext::ParamOrArgEnd(bool for_args, ParseNodeKind start_kind) + -> std::pair { + if (parse_tree_->node_kind(node_stack_.PeekParseNode()) != start_kind) { + ParamOrArgSave(for_args); + } + return {node_block_stack_.Pop(), params_or_args_stack_.Pop()}; +} + +auto SemanticsContext::ParamOrArgSave(bool for_args) -> void { + SemanticsNodeId param_or_arg_id = SemanticsNodeId::Invalid; + if (for_args) { + // For an argument, we add a stub reference to the expression on the top of + // the stack. There may not be anything on the IR prior to this. + auto [entry_parse_node, entry_node_id] = + node_stack_.PopForParseNodeAndNodeId(); + param_or_arg_id = AddNode(SemanticsNode::StubReference::Make( + entry_parse_node, semantics_->GetNode(entry_node_id).type_id(), + entry_node_id)); + } else { + // For a parameter, there should always be something in the IR. + node_stack_.PopAndIgnore(); + auto ir_id = node_block_stack_.Peek(); + CARBON_CHECK(ir_id.is_valid()); + auto& ir = semantics_->GetNodeBlock(ir_id); + CARBON_CHECK(!ir.empty()) << "Should have had a param"; + param_or_arg_id = ir.back(); + } + + // Save the param or arg ID. + auto& params_or_args = + semantics_->GetNodeBlock(params_or_args_stack_.PeekForAdd()); + params_or_args.push_back(param_or_arg_id); +} + +auto SemanticsContext::PrintForStackDump(llvm::raw_ostream& output) const + -> void { + node_stack_.PrintForStackDump(output); + node_block_stack_.PrintForStackDump(output); + params_or_args_stack_.PrintForStackDump(output); + args_type_info_stack_.PrintForStackDump(output); +} + +} // namespace Carbon diff --git a/toolchain/semantics/semantics_parse_tree_handler.h b/toolchain/semantics/semantics_context.h similarity index 77% rename from toolchain/semantics/semantics_parse_tree_handler.h rename to toolchain/semantics/semantics_context.h index 15f5e070f686..91010059084b 100644 --- a/toolchain/semantics/semantics_parse_tree_handler.h +++ b/toolchain/semantics/semantics_context.h @@ -2,8 +2,8 @@ // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -#ifndef CARBON_TOOLCHAIN_SEMANTICS_SEMANTICS_PARSE_TREE_HANDLER_H_ -#define CARBON_TOOLCHAIN_SEMANTICS_SEMANTICS_PARSE_TREE_HANDLER_H_ +#ifndef CARBON_TOOLCHAIN_SEMANTICS_SEMANTICS_CONTEXT_H_ +#define CARBON_TOOLCHAIN_SEMANTICS_SEMANTICS_CONTEXT_H_ #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseSet.h" @@ -16,36 +16,127 @@ namespace Carbon { -// Handles processing of a ParseTree for semantics. -class SemanticsParseTreeHandler { +// Context and shared functionality for semantics handlers. +class SemanticsContext { public: // Stores references for work. - explicit SemanticsParseTreeHandler( - const TokenizedBuffer& tokens, - DiagnosticEmitter& emitter, const ParseTree& parse_tree, - SemanticsIR& semantics, llvm::raw_ostream* vlog_stream) - : tokens_(&tokens), - emitter_(&emitter), - parse_tree_(&parse_tree), - semantics_(&semantics), - vlog_stream_(vlog_stream), - node_stack_(parse_tree, vlog_stream), - node_block_stack_("node_block_stack_", semantics.node_blocks_, - vlog_stream), - params_or_args_stack_("params_or_args_stack_", semantics.node_blocks_, - vlog_stream), - args_type_info_stack_("args_type_info_stack_", semantics.node_blocks_, - vlog_stream) {} + explicit SemanticsContext(const TokenizedBuffer& tokens, + DiagnosticEmitter& emitter, + const ParseTree& parse_tree, SemanticsIR& semantics, + llvm::raw_ostream* vlog_stream); - // Outputs the ParseTree information into SemanticsIR. - auto Build() -> void; + // Marks an implementation TODO. Always returns false. + auto TODO(ParseTree::Node parse_node, std::string label) -> bool; + + // Runs verification that the processing cleanly finished. + auto VerifyOnFinish() -> void; + + // Adds a node to the current block, returning the produced ID. + auto AddNode(SemanticsNode node) -> SemanticsNodeId; + + // Pushes a parse tree node onto the stack, storing the SemanticsNode as the + // result. + auto AddNodeAndPush(ParseTree::Node parse_node, SemanticsNode node) -> void; + + // Adds a name to name lookup. + auto AddNameToLookup(ParseTree::Node name_node, SemanticsStringId name_id, + SemanticsNodeId target_id) -> void; + + // Binds a DeclaredName to a target node with the given type. + auto BindName(ParseTree::Node name_node, SemanticsNodeId type_id, + SemanticsNodeId target_id) -> SemanticsStringId; + + // Temporarily remove name lookup entries added by the `var`. These will be + // restored by `VariableDeclaration` using `ReaddNameToLookup`. + auto TempRemoveLatestNameFromLookup() -> SemanticsNodeId; + + // Re-adds a name to name lookup. This is typically done through BindName, but + // can also be used to restore removed names. + auto ReaddNameToLookup(SemanticsStringId name_id, SemanticsNodeId storage_id) + -> void { + name_lookup_[name_id].push_back(storage_id); + } + + // Lookup up a name, returning the referenced node. + auto LookupName(ParseTree::Node parse_node, llvm::StringRef name) + -> SemanticsNodeId; + + // Pushes a new scope onto scope_stack_. + auto PushScope() -> void; + + // Pops the top scope from scope_stack_, cleaning up names from name_lookup_. + auto PopScope() -> void; + + // Runs ImplicitAsImpl for a set of arguments and parameters. + // + // This will eventually need to support checking against multiple possible + // overloads, multiple of which may be possible but not "best". While this can + // currently be done by calling twice, toggling `apply_implicit_as`, in the + // future we may want to remember the right implicit conversions to do for + // valid cases in order to efficiently handle generics. + auto ImplicitAsForArgs( + SemanticsNodeBlockId arg_ir_id, SemanticsNodeBlockId arg_refs_id, + ParseTree::Node param_parse_node, SemanticsNodeBlockId param_refs_id, + DiagnosticEmitter::DiagnosticBuilder* diagnostic) + -> bool; + + // Runs ImplicitAsImpl for a situation where a cast is required, returning the + // updated `value_id`. Prints a diagnostic and returns an InvalidType if + // unsupported. + auto ImplicitAsRequired(ParseTree::Node parse_node, SemanticsNodeId value_id, + SemanticsNodeId as_type_id) -> SemanticsNodeId; + + // Starts handling parameters or arguments. + auto ParamOrArgStart() -> void; + + // On a comma, pushes the entry. On return, the top of node_stack_ will be + // start_kind. + auto ParamOrArgComma(bool for_args) -> void; + + // Detects whether there's an entry to push. On return, the top of + // node_stack_ will be start_kind, and the caller should do type-specific + // processing. Returns a pair of {ir_id, refs_id}. + auto ParamOrArgEnd(bool for_args, ParseNodeKind start_kind) + -> std::pair; + + // Saves a parameter from the top block in node_stack_ to the top block in + // params_or_args_stack_. If for_args, adds a StubReference of the previous + // node's result to the IR. + // + // This should only be called by other ParamOrArg functions, not directly. + auto ParamOrArgSave(bool for_args) -> void; + + // Prints information for a stack dump. + auto PrintForStackDump(llvm::raw_ostream& output) const -> void; + + auto tokens() -> const TokenizedBuffer& { return *tokens_; } + + auto emitter() -> DiagnosticEmitter& { return *emitter_; } + + auto parse_tree() -> const ParseTree& { return *parse_tree_; } + + auto semantics() -> SemanticsIR& { return *semantics_; } + + auto node_stack() -> SemanticsNodeStack& { return node_stack_; } + + auto node_block_stack() -> SemanticsNodeBlockStack& { + return node_block_stack_; + } + + auto args_type_info_stack() -> SemanticsNodeBlockStack& { + return args_type_info_stack_; + } + + auto finished_params_stack() -> llvm::SmallVector< + std::pair>& { + return finished_params_stack_; + } + + auto return_scope_stack() -> llvm::SmallVector& { + return return_scope_stack_; + } private: - // Prints the node_stack_ on stack dumps. - class PrettyStackTraceNodeStack; - // Prints the node_block_stack_ on stack dumps. - class PrettyStackTraceNodeBlockStack; - // For CanImplicitAs, the detected conversion to apply. enum ImplicitAsKind { // Incompatible types. @@ -84,53 +175,6 @@ class SemanticsParseTreeHandler { // TODO: This likely needs to track things which need to be destructed. }; - // Adds a node to the current block, returning the produced ID. - auto AddNode(SemanticsNode node) -> SemanticsNodeId; - - // Pushes a parse tree node onto the stack, storing the SemanticsNode as the - // result. - auto AddNodeAndPush(ParseTree::Node parse_node, SemanticsNode node) -> void; - - // Adds a name to name lookup. - auto AddNameToLookup(ParseTree::Node name_node, SemanticsStringId name_id, - SemanticsNodeId target_id) -> void; - - // Re-adds a name to name lookup. This is typically done through BindName, but - // can also be used to restore removed names. - auto ReaddNameToLookup(SemanticsStringId name_id, SemanticsNodeId storage_id) - -> void { - name_lookup_[name_id].push_back(storage_id); - } - - // Binds a DeclaredName to a target node with the given type. - auto BindName(ParseTree::Node name_node, SemanticsNodeId type_id, - SemanticsNodeId target_id) -> SemanticsStringId; - - // Pushes a new scope onto scope_stack_. - auto PushScope() -> void; - - // Pops the top scope from scope_stack_, cleaning up names from name_lookup_. - auto PopScope() -> void; - - // Runs ImplicitAsImpl for a set of arguments and parameters. - // - // This will eventually need to support checking against multiple possible - // overloads, multiple of which may be possible but not "best". While this can - // currently be done by calling twice, toggling `apply_implicit_as`, in the - // future we may want to remember the right implicit conversions to do for - // valid cases in order to efficiently handle generics. - auto ImplicitAsForArgs( - SemanticsNodeBlockId arg_ir_id, SemanticsNodeBlockId arg_refs_id, - ParseTree::Node param_parse_node, SemanticsNodeBlockId param_refs_id, - DiagnosticEmitter::DiagnosticBuilder* diagnostic) - -> bool; - - // Runs ImplicitAsImpl for a situation where a cast is required, returning the - // updated `value_id`. Prints a diagnostic and returns an InvalidType if - // unsupported. - auto ImplicitAsRequired(ParseTree::Node parse_node, SemanticsNodeId value_id, - SemanticsNodeId as_type_id) -> SemanticsNodeId; - // Runs ImplicitAs behavior to convert `value` to `as_type`, returning the // result type. The result will be the node to use to replace `value`. // @@ -147,31 +191,6 @@ class SemanticsParseTreeHandler { auto CanImplicitAsStruct(SemanticsNode value_type, SemanticsNode as_type) -> bool; - // Starts handling parameters or arguments. - auto ParamOrArgStart() -> void; - - // On a comma, pushes the entry. On return, the top of node_stack_ will be - // start_kind. - auto ParamOrArgComma(bool for_args) -> void; - - // Detects whether there's an entry to push. On return, the top of - // node_stack_ will be start_kind, and the caller should do type-specific - // processing. Returns a pair of {ir_id, refs_id}. - auto ParamOrArgEnd(bool for_args, ParseNodeKind start_kind) - -> std::pair; - - // Saves a parameter from the top block in node_stack_ to the top block in - // params_or_args_stack_. If for_args, adds a StubReference of the previous - // node's result to the IR. - // - // This should only be called by other ParamOrArg functions, not directly. - auto ParamOrArgSave(bool for_args) -> void; - - // Parse node handlers. Returns false for unrecoverable errors. -#define CARBON_PARSE_NODE_KIND(Name) \ - auto Handle##Name(ParseTree::Node parse_node)->bool; -#include "toolchain/parser/parse_node_kind.def" - auto current_scope() -> ScopeStackEntry& { return scope_stack_.back(); } // Tokens for getting data on literals. @@ -231,6 +250,13 @@ class SemanticsParseTreeHandler { name_lookup_; }; +// Parse node handlers. Returns false for unrecoverable errors. +#define CARBON_PARSE_NODE_KIND(Name) \ + auto SemanticsHandle##Name(SemanticsContext& context, \ + ParseTree::Node parse_node) \ + ->bool; +#include "toolchain/parser/parse_node_kind.def" + } // namespace Carbon -#endif // CARBON_TOOLCHAIN_SEMANTICS_SEMANTICS_PARSE_TREE_HANDLER_H_ +#endif // CARBON_TOOLCHAIN_SEMANTICS_SEMANTICS_CONTEXT_H_ diff --git a/toolchain/semantics/semantics_handle.cpp b/toolchain/semantics/semantics_handle.cpp new file mode 100644 index 000000000000..7699b37fcd62 --- /dev/null +++ b/toolchain/semantics/semantics_handle.cpp @@ -0,0 +1,535 @@ +// 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" + +namespace Carbon { + +auto SemanticsHandleAddress(SemanticsContext& context, + ParseTree::Node parse_node) -> bool { + return context.TODO(parse_node, "HandleAddress"); +} + +auto SemanticsHandleBreakStatement(SemanticsContext& context, + ParseTree::Node parse_node) -> bool { + return context.TODO(parse_node, "HandleBreakStatement"); +} + +auto SemanticsHandleBreakStatementStart(SemanticsContext& context, + ParseTree::Node parse_node) -> bool { + 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"); +} + +auto SemanticsHandleContinueStatementStart(SemanticsContext& context, + ParseTree::Node parse_node) -> bool { + return context.TODO(parse_node, "HandleContinueStatementStart"); +} + +auto SemanticsHandleDeclaredName(SemanticsContext& context, + ParseTree::Node parse_node) -> bool { + // The parent is responsible for binding the name. + context.node_stack().Push(parse_node); + return true; +} + +auto SemanticsHandleDeducedParameterList(SemanticsContext& context, + ParseTree::Node parse_node) -> bool { + return context.TODO(parse_node, "HandleDeducedParameterList"); +} + +auto SemanticsHandleDeducedParameterListStart(SemanticsContext& context, + ParseTree::Node parse_node) + -> bool { + return context.TODO(parse_node, "HandleDeducedParameterListStart"); +} + +auto SemanticsHandleDesignatedName(SemanticsContext& context, + ParseTree::Node parse_node) -> bool { + auto name_str = context.parse_tree().GetNodeText(parse_node); + auto name_id = context.semantics().AddString(name_str); + // The parent is responsible for binding the name. + context.node_stack().Push(parse_node, name_id); + return true; +} + +auto SemanticsHandleDesignatorExpression(SemanticsContext& context, + ParseTree::Node parse_node) -> bool { + auto [_, name_id] = context.node_stack().PopForParseNodeAndNameId( + ParseNodeKind::DesignatedName); + + auto base_id = context.node_stack().PopForNodeId(); + auto base = context.semantics().GetNode(base_id); + auto base_type = context.semantics().GetNode(base.type_id()); + + switch (base_type.kind()) { + case SemanticsNodeKind::StructType: { + auto refs = + context.semantics().GetNodeBlock(base_type.GetAsStructType().second); + // TODO: Do we need to optimize this with a lookup table for O(1)? + for (int i = 0; i < static_cast(refs.size()); ++i) { + auto ref = context.semantics().GetNode(refs[i]); + if (name_id == ref.GetAsStructTypeField()) { + context.AddNodeAndPush( + parse_node, + SemanticsNode::StructMemberAccess::Make( + parse_node, ref.type_id(), base_id, SemanticsMemberIndex(i))); + return true; + } + } + CARBON_DIAGNOSTIC(DesignatorExpressionNameNotFound, Error, + "Type `{0}` does not have a member `{1}`.", std::string, + llvm::StringRef); + context.emitter().Emit(parse_node, DesignatorExpressionNameNotFound, + context.semantics().StringifyNode(base.type_id()), + context.semantics().GetString(name_id)); + break; + } + default: { + CARBON_DIAGNOSTIC(DesignatorExpressionUnsupported, Error, + "Type `{0}` does not support designator expressions.", + std::string); + context.emitter().Emit(parse_node, DesignatorExpressionUnsupported, + context.semantics().StringifyNode(base.type_id())); + break; + } + } + + // Should only be reached on error. + context.node_stack().Push(parse_node, SemanticsNodeId::BuiltinInvalidType); + 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); + return true; +} + +auto SemanticsHandleExpressionStatement(SemanticsContext& context, + 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; +} + +auto SemanticsHandleFileEnd(SemanticsContext& /*context*/, + ParseTree::Node /*parse_node*/) -> bool { + // Do nothing, no need to balance this node. + return true; +} + +auto SemanticsHandleForHeader(SemanticsContext& context, + ParseTree::Node parse_node) -> bool { + return context.TODO(parse_node, "HandleForHeader"); +} + +auto SemanticsHandleForHeaderStart(SemanticsContext& context, + ParseTree::Node parse_node) -> bool { + return context.TODO(parse_node, "HandleForHeaderStart"); +} + +auto SemanticsHandleForIn(SemanticsContext& context, ParseTree::Node parse_node) + -> bool { + return context.TODO(parse_node, "HandleForIn"); +} + +auto SemanticsHandleForStatement(SemanticsContext& context, + ParseTree::Node parse_node) -> bool { + return context.TODO(parse_node, "HandleForStatement"); +} + +auto SemanticsHandleGenericPatternBinding(SemanticsContext& context, + ParseTree::Node parse_node) -> bool { + 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().PopForNodeId(); + auto lhs_id = context.node_stack().PopForNodeId(); + + // TODO: This should search for a compatible interface. For now, it's a very + // trivial check of validity on the operation. + lhs_id = context.ImplicitAsRequired( + parse_node, lhs_id, context.semantics().GetNode(rhs_id).type_id()); + + // Figure out the operator for the token. + auto token = context.parse_tree().node_token(parse_node); + switch (auto token_kind = context.tokens().GetKind(token)) { + case TokenKind::Plus: + context.AddNodeAndPush( + parse_node, + SemanticsNode::BinaryOperatorAdd::Make( + parse_node, context.semantics().GetNode(lhs_id).type_id(), lhs_id, + rhs_id)); + break; + default: + return context.TODO(parse_node, llvm::formatv("Handle {0}", token_kind)); + } + + return true; +} + +auto SemanticsHandleInvalidParse(SemanticsContext& context, + ParseTree::Node parse_node) -> bool { + return context.TODO(parse_node, "HandleInvalidParse"); +} + +auto SemanticsHandleLiteral(SemanticsContext& context, + ParseTree::Node parse_node) -> bool { + auto token = context.parse_tree().node_token(parse_node); + switch (auto token_kind = context.tokens().GetKind(token)) { + case TokenKind::IntegerLiteral: { + auto id = context.semantics().AddIntegerLiteral( + context.tokens().GetIntegerLiteral(token)); + context.AddNodeAndPush( + parse_node, SemanticsNode::IntegerLiteral::Make(parse_node, id)); + break; + } + case TokenKind::RealLiteral: { + auto token_value = context.tokens().GetRealLiteral(token); + auto id = context.semantics().AddRealLiteral( + {.mantissa = token_value.Mantissa(), + .exponent = token_value.Exponent(), + .is_decimal = token_value.IsDecimal()}); + context.AddNodeAndPush(parse_node, + SemanticsNode::RealLiteral::Make(parse_node, id)); + break; + } + case TokenKind::StringLiteral: { + auto id = context.semantics().AddString( + context.tokens().GetStringLiteral(token)); + context.AddNodeAndPush( + parse_node, SemanticsNode::StringLiteral::Make(parse_node, id)); + break; + } + case TokenKind::IntegerTypeLiteral: { + auto text = context.tokens().GetTokenText(token); + if (text != "i32") { + return context.TODO(parse_node, "Currently only i32 is allowed"); + } + context.node_stack().Push(parse_node, + SemanticsNodeId::BuiltinIntegerType); + break; + } + case TokenKind::FloatingPointTypeLiteral: { + auto text = context.tokens().GetTokenText(token); + if (text != "f64") { + return context.TODO(parse_node, "Currently only f64 is allowed"); + } + context.node_stack().Push(parse_node, + SemanticsNodeId::BuiltinFloatingPointType); + break; + } + case TokenKind::StringTypeLiteral: { + context.node_stack().Push(parse_node, SemanticsNodeId::BuiltinStringType); + break; + } + default: { + return context.TODO(parse_node, llvm::formatv("Handle {0}", token_kind)); + } + } + + return true; +} + +auto SemanticsHandleNameReference(SemanticsContext& context, + ParseTree::Node parse_node) -> bool { + auto name = context.parse_tree().GetNodeText(parse_node); + context.node_stack().Push(parse_node, context.LookupName(parse_node, name)); + return true; +} + +auto SemanticsHandleNamedConstraintDeclaration(SemanticsContext& context, + ParseTree::Node parse_node) + -> bool { + return context.TODO(parse_node, "HandleNamedConstraintDeclaration"); +} + +auto SemanticsHandleNamedConstraintDefinition(SemanticsContext& context, + ParseTree::Node parse_node) + -> bool { + return context.TODO(parse_node, "HandleNamedConstraintDefinition"); +} + +auto SemanticsHandleNamedConstraintDefinitionStart(SemanticsContext& context, + ParseTree::Node parse_node) + -> bool { + return context.TODO(parse_node, "HandleNamedConstraintDefinitionStart"); +} + +auto SemanticsHandleNamedConstraintIntroducer(SemanticsContext& context, + ParseTree::Node parse_node) + -> bool { + return context.TODO(parse_node, "HandleNamedConstraintIntroducer"); +} + +auto SemanticsHandleParameterList(SemanticsContext& context, + ParseTree::Node parse_node) -> bool { + auto [ir_id, refs_id] = context.ParamOrArgEnd( + /*for_args=*/false, ParseNodeKind::ParameterListStart); + + context.PopScope(); + context.node_stack().PopAndDiscardSoloParseNode( + ParseNodeKind::ParameterListStart); + context.finished_params_stack().push_back({ir_id, refs_id}); + context.node_stack().Push(parse_node); + return true; +} + +auto SemanticsHandleParameterListComma(SemanticsContext& context, + ParseTree::Node /*parse_node*/) -> bool { + context.ParamOrArgComma(/*for_args=*/false); + return true; +} + +auto SemanticsHandleParameterListStart(SemanticsContext& context, + ParseTree::Node parse_node) -> bool { + context.PushScope(); + context.node_stack().Push(parse_node); + context.ParamOrArgStart(); + return true; +} + +auto SemanticsHandleParenExpression(SemanticsContext& context, + ParseTree::Node parse_node) -> bool { + return context.TODO(parse_node, "HandleParenExpression"); +} + +auto SemanticsHandleParenExpressionOrTupleLiteralStart( + SemanticsContext& context, ParseTree::Node parse_node) -> bool { + return context.TODO(parse_node, "HandleParenExpressionOrTupleLiteralStart"); +} + +auto SemanticsHandlePatternBinding(SemanticsContext& context, + ParseTree::Node parse_node) -> bool { + auto [type_node, parsed_type_id] = + context.node_stack().PopForParseNodeAndNodeId(); + SemanticsNodeId cast_type_id = context.ImplicitAsRequired( + type_node, parsed_type_id, SemanticsNodeId::BuiltinTypeType); + + // Get the name. + auto name_node = context.node_stack().PopForSoloParseNode(); + + // Allocate storage, linked to the name for error locations. + auto storage_id = + context.AddNode(SemanticsNode::VarStorage::Make(name_node, cast_type_id)); + + // Bind the name to storage. + auto name_id = context.BindName(name_node, cast_type_id, storage_id); + + // If this node's result is used, it'll be for either the name or the + // storage address. The storage address can be found through the name, so we + // push the name. + context.node_stack().Push(parse_node, name_id); + + return true; +} + +auto SemanticsHandlePostfixOperator(SemanticsContext& context, + ParseTree::Node parse_node) -> bool { + return context.TODO(parse_node, "HandlePostfixOperator"); +} + +auto SemanticsHandlePrefixOperator(SemanticsContext& context, + ParseTree::Node parse_node) -> bool { + return context.TODO(parse_node, "HandlePrefixOperator"); +} + +auto SemanticsHandleReturnStatement(SemanticsContext& context, + ParseTree::Node parse_node) -> bool { + CARBON_CHECK(!context.return_scope_stack().empty()); + const auto& fn_node = + context.semantics().GetNode(context.return_scope_stack().back()); + const auto callable = context.semantics().GetCallable( + fn_node.GetAsFunctionDeclaration().second); + + if (context.parse_tree().node_kind(context.node_stack().PeekParseNode()) == + ParseNodeKind::ReturnStatementStart) { + context.node_stack().PopAndDiscardSoloParseNode( + ParseNodeKind::ReturnStatementStart); + + if (callable.return_type_id.is_valid()) { + // TODO: Add a note pointing at the return type's parse node. + CARBON_DIAGNOSTIC(ReturnStatementMissingExpression, Error, + "Must return a {0}.", std::string); + context.emitter() + .Build(parse_node, ReturnStatementMissingExpression, + context.semantics().StringifyNode(callable.return_type_id)) + .Emit(); + } + + context.AddNodeAndPush(parse_node, SemanticsNode::Return::Make(parse_node)); + } else { + auto arg = context.node_stack().PopForNodeId(); + context.node_stack().PopAndDiscardSoloParseNode( + ParseNodeKind::ReturnStatementStart); + + if (!callable.return_type_id.is_valid()) { + CARBON_DIAGNOSTIC( + ReturnStatementDisallowExpression, Error, + "No return expression should be provided in this context."); + CARBON_DIAGNOSTIC(ReturnStatementImplicitNote, Note, + "There was no return type provided."); + context.emitter() + .Build(parse_node, ReturnStatementDisallowExpression) + .Note(fn_node.parse_node(), ReturnStatementImplicitNote) + .Emit(); + } else { + arg = + context.ImplicitAsRequired(parse_node, arg, callable.return_type_id); + } + + context.AddNodeAndPush( + parse_node, + SemanticsNode::ReturnExpression::Make( + parse_node, context.semantics().GetNode(arg).type_id(), arg)); + } + return true; +} + +auto SemanticsHandleReturnStatementStart(SemanticsContext& context, + ParseTree::Node parse_node) -> bool { + // No action, just a bracketing node. + context.node_stack().Push(parse_node); + return true; +} + +auto SemanticsHandleReturnType(SemanticsContext& context, + ParseTree::Node parse_node) -> bool { + // Propagate the type expression. + auto [type_parse_node, type_node_id] = + context.node_stack().PopForParseNodeAndNodeId(); + auto cast_node_id = context.ImplicitAsRequired( + type_parse_node, type_node_id, SemanticsNodeId::BuiltinTypeType); + context.node_stack().Push(parse_node, cast_node_id); + return true; +} + +auto SemanticsHandleSelfTypeIdentifier(SemanticsContext& context, + ParseTree::Node parse_node) -> bool { + return context.TODO(parse_node, "HandleSelfTypeIdentifier"); +} + +auto SemanticsHandleSelfValueIdentifier(SemanticsContext& context, + ParseTree::Node parse_node) -> bool { + return context.TODO(parse_node, "HandleSelfValueIdentifier"); +} + +auto SemanticsHandleTemplate(SemanticsContext& context, + ParseTree::Node parse_node) -> bool { + return context.TODO(parse_node, "HandleTemplate"); +} + +auto SemanticsHandleTupleLiteral(SemanticsContext& context, + ParseTree::Node parse_node) -> bool { + return context.TODO(parse_node, "HandleTupleLiteral"); +} + +auto SemanticsHandleTupleLiteralComma(SemanticsContext& context, + ParseTree::Node parse_node) -> bool { + return context.TODO(parse_node, "HandleTupleLiteralComma"); +} + +auto SemanticsHandleVariableDeclaration(SemanticsContext& context, + ParseTree::Node parse_node) -> bool { + auto [last_parse_node, last_node_id] = + context.node_stack().PopForParseNodeAndNodeId(); + + if (context.parse_tree().node_kind(last_parse_node) != + ParseNodeKind::PatternBinding) { + auto storage_id = + context.node_stack().PopForNodeId(ParseNodeKind::VariableInitializer); + + auto binding = context.node_stack().PopForParseNodeAndNameId( + ParseNodeKind::PatternBinding); + + // Restore the name now that the initializer is complete. + context.ReaddNameToLookup(binding.second, storage_id); + + auto cast_value_id = context.ImplicitAsRequired( + parse_node, last_node_id, + context.semantics().GetNode(storage_id).type_id()); + context.AddNode(SemanticsNode::Assign::Make( + parse_node, context.semantics().GetNode(cast_value_id).type_id(), + storage_id, cast_value_id)); + } + + context.node_stack().PopAndDiscardSoloParseNode( + ParseNodeKind::VariableIntroducer); + context.node_stack().Push(parse_node); + + return true; +} + +auto SemanticsHandleVariableIntroducer(SemanticsContext& context, + ParseTree::Node parse_node) -> bool { + // No action, just a bracketing node. + context.node_stack().Push(parse_node); + return true; +} + +auto SemanticsHandleVariableInitializer(SemanticsContext& context, + ParseTree::Node parse_node) -> bool { + auto storage_id = context.TempRemoveLatestNameFromLookup(); + context.node_stack().Push(parse_node, storage_id); + return true; +} + +auto SemanticsHandleWhileCondition(SemanticsContext& context, + ParseTree::Node parse_node) -> bool { + return context.TODO(parse_node, "HandleWhileCondition"); +} + +auto SemanticsHandleWhileConditionStart(SemanticsContext& context, + ParseTree::Node parse_node) -> bool { + return context.TODO(parse_node, "HandleWhileConditionStart"); +} + +auto SemanticsHandleWhileStatement(SemanticsContext& context, + ParseTree::Node parse_node) -> bool { + return context.TODO(parse_node, "HandleWhileStatement"); +} + +} // namespace Carbon diff --git a/toolchain/semantics/semantics_handle_call_expression.cpp b/toolchain/semantics/semantics_handle_call_expression.cpp new file mode 100644 index 000000000000..a3646622e220 --- /dev/null +++ b/toolchain/semantics/semantics_handle_call_expression.cpp @@ -0,0 +1,68 @@ +// 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" + +namespace Carbon { + +auto SemanticsHandleCallExpression(SemanticsContext& context, + ParseTree::Node parse_node) -> bool { + auto [ir_id, refs_id] = context.ParamOrArgEnd( + /*for_args=*/true, ParseNodeKind::CallExpressionStart); + + // TODO: Convert to call expression. + auto [call_expr_parse_node, name_id] = + context.node_stack().PopForParseNodeAndNodeId( + ParseNodeKind::CallExpressionStart); + auto name_node = context.semantics().GetNode(name_id); + if (name_node.kind() != SemanticsNodeKind::FunctionDeclaration) { + // TODO: Work on error. + context.TODO(parse_node, "Not a callable name"); + context.node_stack().Push(parse_node, name_id); + return true; + } + + auto [_, callable_id] = name_node.GetAsFunctionDeclaration(); + auto callable = context.semantics().GetCallable(callable_id); + + CARBON_DIAGNOSTIC(NoMatchingCall, Error, "No matching callable was found."); + auto diagnostic = + context.emitter().Build(call_expr_parse_node, NoMatchingCall); + if (!context.ImplicitAsForArgs(ir_id, refs_id, name_node.parse_node(), + callable.param_refs_id, &diagnostic)) { + diagnostic.Emit(); + context.node_stack().Push(parse_node, SemanticsNodeId::BuiltinInvalidType); + return true; + } + + CARBON_CHECK(context.ImplicitAsForArgs(ir_id, refs_id, name_node.parse_node(), + callable.param_refs_id, + /*diagnostic=*/nullptr)); + + auto call_id = context.semantics().AddCall({ir_id, refs_id}); + // TODO: Propagate return types from callable. + auto call_node_id = context.AddNode(SemanticsNode::Call::Make( + call_expr_parse_node, callable.return_type_id, call_id, callable_id)); + + context.node_stack().Push(parse_node, call_node_id); + return true; +} + +auto SemanticsHandleCallExpressionComma(SemanticsContext& context, + ParseTree::Node /*parse_node*/) + -> bool { + context.ParamOrArgComma(/*for_args=*/true); + return true; +} + +auto SemanticsHandleCallExpressionStart(SemanticsContext& context, + ParseTree::Node parse_node) -> bool { + auto name_id = + context.node_stack().PopForNodeId(ParseNodeKind::NameReference); + context.node_stack().Push(parse_node, name_id); + context.ParamOrArgStart(); + return true; +} + +} // namespace Carbon diff --git a/toolchain/semantics/semantics_handle_class.cpp b/toolchain/semantics/semantics_handle_class.cpp new file mode 100644 index 000000000000..7b422f2fbdbd --- /dev/null +++ b/toolchain/semantics/semantics_handle_class.cpp @@ -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 + +#include "toolchain/semantics/semantics_context.h" + +namespace Carbon { + +auto SemanticsHandleClassDeclaration(SemanticsContext& context, + ParseTree::Node parse_node) -> bool { + return context.TODO(parse_node, "HandleClassDeclaration"); +} + +auto SemanticsHandleClassDefinition(SemanticsContext& context, + ParseTree::Node parse_node) -> bool { + return context.TODO(parse_node, "HandleClassDefinition"); +} + +auto SemanticsHandleClassDefinitionStart(SemanticsContext& context, + ParseTree::Node parse_node) -> bool { + return context.TODO(parse_node, "HandleClassDefinitionStart"); +} + +auto SemanticsHandleClassIntroducer(SemanticsContext& context, + ParseTree::Node parse_node) -> bool { + return context.TODO(parse_node, "HandleClassIntroducer"); +} + +} // namespace Carbon diff --git a/toolchain/semantics/semantics_handle_function.cpp b/toolchain/semantics/semantics_handle_function.cpp new file mode 100644 index 000000000000..53899d03f667 --- /dev/null +++ b/toolchain/semantics/semantics_handle_function.cpp @@ -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 + +#include "toolchain/semantics/semantics_context.h" + +namespace Carbon { + +auto SemanticsHandleFunctionDeclaration(SemanticsContext& context, + ParseTree::Node parse_node) -> bool { + return context.TODO(parse_node, "HandleFunctionDeclaration"); +} + +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(); + } + auto decl_id = + context.node_stack().PopForNodeId(ParseNodeKind::FunctionDefinitionStart); + + context.return_scope_stack().pop_back(); + context.PopScope(); + auto block_id = context.node_block_stack().Pop(); + context.AddNode( + SemanticsNode::FunctionDefinition::Make(parse_node, decl_id, block_id)); + context.node_stack().Push(parse_node); + + return true; +} + +auto SemanticsHandleFunctionDefinitionStart(SemanticsContext& context, + ParseTree::Node parse_node) + -> bool { + SemanticsNodeId return_type_id = SemanticsNodeId::Invalid; + if (context.parse_tree().node_kind(context.node_stack().PeekParseNode()) == + ParseNodeKind::ReturnType) { + return_type_id = + context.node_stack().PopForNodeId(ParseNodeKind::ReturnType); + } + context.node_stack().PopForSoloParseNode(ParseNodeKind::ParameterList); + auto [param_ir_id, param_refs_id] = + context.finished_params_stack().pop_back_val(); + auto name_node = + context.node_stack().PopForSoloParseNode(ParseNodeKind::DeclaredName); + auto fn_node = context.node_stack().PopForSoloParseNode( + ParseNodeKind::FunctionIntroducer); + + auto name_str = context.parse_tree().GetNodeText(name_node); + auto name_id = context.semantics().AddString(name_str); + + auto callable_id = + context.semantics().AddCallable({.param_ir_id = param_ir_id, + .param_refs_id = param_refs_id, + .return_type_id = return_type_id}); + auto decl_id = context.AddNode( + SemanticsNode::FunctionDeclaration::Make(fn_node, name_id, callable_id)); + context.AddNameToLookup(name_node, name_id, decl_id); + + context.node_block_stack().Push(); + context.PushScope(); + context.return_scope_stack().push_back(decl_id); + context.node_stack().Push(parse_node, decl_id); + + return true; +} + +auto SemanticsHandleFunctionIntroducer(SemanticsContext& context, + ParseTree::Node parse_node) -> bool { + // No action, just a bracketing node. + context.node_stack().Push(parse_node); + return true; +} + +} // namespace Carbon diff --git a/toolchain/semantics/semantics_handle_interface.cpp b/toolchain/semantics/semantics_handle_interface.cpp new file mode 100644 index 000000000000..bca7b51a746b --- /dev/null +++ b/toolchain/semantics/semantics_handle_interface.cpp @@ -0,0 +1,30 @@ +// 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" + +namespace Carbon { + +auto SemanticsHandleInterfaceDeclaration(SemanticsContext& context, + ParseTree::Node parse_node) -> bool { + return context.TODO(parse_node, "HandleInterfaceDeclaration"); +} + +auto SemanticsHandleInterfaceDefinition(SemanticsContext& context, + ParseTree::Node parse_node) -> bool { + return context.TODO(parse_node, "HandleInterfaceDefinition"); +} + +auto SemanticsHandleInterfaceDefinitionStart(SemanticsContext& context, + ParseTree::Node parse_node) + -> bool { + return context.TODO(parse_node, "HandleInterfaceDefinitionStart"); +} + +auto SemanticsHandleInterfaceIntroducer(SemanticsContext& context, + ParseTree::Node parse_node) -> bool { + return context.TODO(parse_node, "HandleInterfaceIntroducer"); +} + +} // namespace Carbon diff --git a/toolchain/semantics/semantics_handle_package.cpp b/toolchain/semantics/semantics_handle_package.cpp new file mode 100644 index 000000000000..285d92565802 --- /dev/null +++ b/toolchain/semantics/semantics_handle_package.cpp @@ -0,0 +1,34 @@ +// 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" + +namespace Carbon { + +auto SemanticsHandlePackageApi(SemanticsContext& context, + ParseTree::Node parse_node) -> bool { + return context.TODO(parse_node, "HandlePackageApi"); +} + +auto SemanticsHandlePackageDirective(SemanticsContext& context, + ParseTree::Node parse_node) -> bool { + return context.TODO(parse_node, "HandlePackageDirective"); +} + +auto SemanticsHandlePackageImpl(SemanticsContext& context, + ParseTree::Node parse_node) -> bool { + return context.TODO(parse_node, "HandlePackageImpl"); +} + +auto SemanticsHandlePackageIntroducer(SemanticsContext& context, + ParseTree::Node parse_node) -> bool { + return context.TODO(parse_node, "HandlePackageIntroducer"); +} + +auto SemanticsHandlePackageLibrary(SemanticsContext& context, + ParseTree::Node parse_node) -> bool { + return context.TODO(parse_node, "HandlePackageLibrary"); +} + +} // namespace Carbon diff --git a/toolchain/semantics/semantics_handle_struct.cpp b/toolchain/semantics/semantics_handle_struct.cpp new file mode 100644 index 000000000000..35de3856f91a --- /dev/null +++ b/toolchain/semantics/semantics_handle_struct.cpp @@ -0,0 +1,128 @@ +// 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" + +namespace Carbon { + +auto SemanticsHandleStructComma(SemanticsContext& context, + ParseTree::Node /*parse_node*/) -> bool { + context.ParamOrArgComma( + /*for_args=*/context.parse_tree().node_kind( + context.node_stack().PeekParseNode()) != + ParseNodeKind::StructFieldType); + return true; +} + +auto SemanticsHandleStructFieldDesignator(SemanticsContext& context, + ParseTree::Node /*parse_node*/) + -> bool { + // This leaves the designated name on top because the `.` isn't interesting. + CARBON_CHECK( + context.parse_tree().node_kind(context.node_stack().PeekParseNode()) == + ParseNodeKind::DesignatedName); + return true; +} + +auto SemanticsHandleStructFieldType(SemanticsContext& context, + ParseTree::Node parse_node) -> bool { + auto [type_node, type_id] = context.node_stack().PopForParseNodeAndNodeId(); + SemanticsNodeId cast_type_id = context.ImplicitAsRequired( + type_node, type_id, SemanticsNodeId::BuiltinTypeType); + + auto [name_node, name_id] = context.node_stack().PopForParseNodeAndNameId( + ParseNodeKind::DesignatedName); + + context.AddNode( + SemanticsNode::StructTypeField::Make(name_node, cast_type_id, name_id)); + context.node_stack().Push(parse_node); + return true; +} + +auto SemanticsHandleStructFieldUnknown(SemanticsContext& context, + ParseTree::Node parse_node) -> bool { + return context.TODO(parse_node, "HandleStructFieldUnknown"); +} + +auto SemanticsHandleStructFieldValue(SemanticsContext& context, + ParseTree::Node parse_node) -> bool { + auto [value_parse_node, value_node_id] = + context.node_stack().PopForParseNodeAndNodeId(); + auto [_, name_id] = context.node_stack().PopForParseNodeAndNameId( + ParseNodeKind::DesignatedName); + + // Store the name for the type. + auto type_block_id = context.args_type_info_stack().PeekForAdd(); + context.semantics().AddNode( + type_block_id, + SemanticsNode::StructTypeField::Make( + parse_node, context.semantics().GetNode(value_node_id).type_id(), + name_id)); + + // Push the value back on the stack as an argument. + context.node_stack().Push(parse_node, value_node_id); + return true; +} + +auto SemanticsHandleStructLiteral(SemanticsContext& context, + ParseTree::Node parse_node) -> bool { + auto [ir_id, refs_id] = context.ParamOrArgEnd( + /*for_args=*/true, ParseNodeKind::StructLiteralOrStructTypeLiteralStart); + + context.PopScope(); + context.node_stack().PopAndDiscardSoloParseNode( + ParseNodeKind::StructLiteralOrStructTypeLiteralStart); + auto type_block_id = context.args_type_info_stack().Pop(); + + // Special-case `{}`. + if (refs_id == SemanticsNodeBlockId::Empty) { + context.node_stack().Push(parse_node, SemanticsNodeId::BuiltinEmptyStruct); + return true; + } + + // Construct a type for the literal. Each field is one node, so ir_id and + // refs_id match. + auto refs = context.semantics().GetNodeBlock(refs_id); + auto type_id = context.AddNode(SemanticsNode::StructType::Make( + parse_node, type_block_id, type_block_id)); + + auto value_id = context.AddNode( + SemanticsNode::StructValue::Make(parse_node, type_id, ir_id, refs_id)); + context.node_stack().Push(parse_node, value_id); + return true; +} + +auto SemanticsHandleStructLiteralOrStructTypeLiteralStart( + SemanticsContext& context, ParseTree::Node parse_node) -> bool { + context.PushScope(); + context.node_stack().Push(parse_node); + // At this point we aren't sure whether this will be a value or type literal, + // so we push onto args irrespective. It just won't be used for a type + // literal. + context.args_type_info_stack().Push(); + context.ParamOrArgStart(); + return true; +} + +auto SemanticsHandleStructTypeLiteral(SemanticsContext& context, + ParseTree::Node parse_node) -> bool { + auto [ir_id, refs_id] = context.ParamOrArgEnd( + /*for_args=*/false, ParseNodeKind::StructLiteralOrStructTypeLiteralStart); + + context.PopScope(); + context.node_stack().PopAndDiscardSoloParseNode( + ParseNodeKind::StructLiteralOrStructTypeLiteralStart); + // This is only used for value literals. + context.args_type_info_stack().Pop(); + + CARBON_CHECK(refs_id != SemanticsNodeBlockId::Empty) + << "{} is handled by StructLiteral."; + + auto type_id = context.AddNode( + SemanticsNode::StructType::Make(parse_node, ir_id, refs_id)); + context.node_stack().Push(parse_node, type_id); + return true; +} + +} // namespace Carbon diff --git a/toolchain/semantics/semantics_ir.cpp b/toolchain/semantics/semantics_ir.cpp index 7389350fd857..7e3af85b35b3 100644 --- a/toolchain/semantics/semantics_ir.cpp +++ b/toolchain/semantics/semantics_ir.cpp @@ -5,10 +5,11 @@ #include "toolchain/semantics/semantics_ir.h" #include "common/check.h" +#include "toolchain/common/pretty_stack_trace_function.h" #include "toolchain/parser/parse_tree_node_location_translator.h" #include "toolchain/semantics/semantics_builtin_kind.h" +#include "toolchain/semantics/semantics_context.h" #include "toolchain/semantics/semantics_node.h" -#include "toolchain/semantics/semantics_parse_tree_handler.h" namespace Carbon { @@ -52,8 +53,37 @@ auto SemanticsIR::MakeFromParseTree(const SemanticsIR& builtin_ir, ParseTreeNodeLocationTranslator translator(&tokens, &parse_tree); ErrorTrackingDiagnosticConsumer err_tracker(consumer); DiagnosticEmitter emitter(translator, err_tracker); - SemanticsParseTreeHandler(tokens, emitter, parse_tree, semantics, vlog_stream) - .Build(); + + SemanticsContext context(tokens, emitter, parse_tree, semantics, vlog_stream); + PrettyStackTraceFunction context_dumper( + [&](llvm::raw_ostream& output) { context.PrintForStackDump(output); }); + + // Add a block for the ParseTree. + context.node_block_stack().Push(); + context.PushScope(); + + // Loops over all nodes in the tree. On some errors, this may return early, + // for example if an unrecoverable state is encountered. + for (auto parse_node : parse_tree.postorder()) { + switch (auto parse_kind = parse_tree.node_kind(parse_node)) { +#define CARBON_PARSE_NODE_KIND(Name) \ + case ParseNodeKind::Name: { \ + if (!SemanticsHandle##Name(context, parse_node)) { \ + semantics.has_errors_ = true; \ + return semantics; \ + } \ + break; \ + } +#include "toolchain/parser/parse_node_kind.def" + } + } + + // Pop information for the file-level scope. + semantics.top_node_block_id_ = context.node_block_stack().Pop(); + context.PopScope(); + + context.VerifyOnFinish(); + semantics.has_errors_ = err_tracker.seen_error(); return semantics; } diff --git a/toolchain/semantics/semantics_ir.h b/toolchain/semantics/semantics_ir.h index 9a717908b13f..ab213df5ddc8 100644 --- a/toolchain/semantics/semantics_ir.h +++ b/toolchain/semantics/semantics_ir.h @@ -10,10 +10,6 @@ #include "toolchain/parser/parse_tree.h" #include "toolchain/semantics/semantics_node.h" -namespace Carbon::Testing { -class SemanticsIRForTest; -} // namespace Carbon::Testing - namespace Carbon { // A call. @@ -84,57 +80,6 @@ class SemanticsIR { } auto Print(llvm::raw_ostream& out, bool include_builtins) const -> void; - // Returns the requested callable. - auto GetCallable(SemanticsCallableId callable_id) const -> SemanticsCallable { - return callables_[callable_id.index]; - } - - // Returns the requested integer literal. - auto GetIntegerLiteral(SemanticsIntegerLiteralId int_id) const - -> const llvm::APInt& { - return integer_literals_[int_id.index]; - } - - // Returns the requested node. - auto GetNode(SemanticsNodeId node_id) const -> SemanticsNode { - return nodes_[node_id.index]; - } - - // Returns the requested node block. - auto GetNodeBlock(SemanticsNodeBlockId block_id) const - -> const llvm::SmallVector& { - return node_blocks_[block_id.index]; - } - - // Returns the requested real literal. - auto GetRealLiteral(SemanticsRealLiteralId int_id) const - -> const SemanticsRealLiteral& { - return real_literals_[int_id.index]; - } - - // Returns the requested string. - auto GetString(SemanticsStringId string_id) const -> llvm::StringRef { - return strings_[string_id.index]; - } - - auto nodes_size() const -> int { return nodes_.size(); } - - auto top_node_block_id() const -> SemanticsNodeBlockId { - return top_node_block_id_; - } - - // Returns true if there were errors creating the semantics IR. - auto has_errors() const -> bool { return has_errors_; } - - private: - friend class SemanticsParseTreeHandler; - - explicit SemanticsIR(const SemanticsIR* builtin_ir) - : cross_reference_irs_({builtin_ir == nullptr ? this : builtin_ir}) { - // For SemanticsNodeBlockId::Empty. - node_blocks_.resize(1); - } - // Adds a call, returning an ID to reference it. auto AddCall(SemanticsCall call) -> SemanticsCallId { SemanticsCallId id(calls_.size()); @@ -149,6 +94,11 @@ class SemanticsIR { return id; } + // Returns the requested callable. + auto GetCallable(SemanticsCallableId callable_id) const -> SemanticsCallable { + return callables_[callable_id.index]; + } + // Adds an integer literal, returning an ID to reference it. auto AddIntegerLiteral(llvm::APInt integer_literal) -> SemanticsIntegerLiteralId { @@ -157,6 +107,12 @@ class SemanticsIR { return id; } + // Returns the requested integer literal. + auto GetIntegerLiteral(SemanticsIntegerLiteralId int_id) const + -> const llvm::APInt& { + return integer_literals_[int_id.index]; + } + // Adds a node to a specified block, returning an ID to reference the node. auto AddNode(SemanticsNodeBlockId block_id, SemanticsNode node) -> SemanticsNodeId { @@ -166,14 +122,18 @@ class SemanticsIR { return node_id; } - // Adds an empty new node block, returning an ID to reference it and add - // items. - auto AddNodeBlock() -> SemanticsNodeBlockId { - SemanticsNodeBlockId id(node_blocks_.size()); - node_blocks_.resize(node_blocks_.size() + 1); - return id; + // Returns the requested node. + auto GetNode(SemanticsNodeId node_id) const -> SemanticsNode { + return nodes_[node_id.index]; } + // Returns the requested node block. + auto GetNodeBlock(SemanticsNodeBlockId block_id) const + -> const llvm::SmallVector& { + return node_blocks_[block_id.index]; + } + + // Returns the requested node block. auto GetNodeBlock(SemanticsNodeBlockId block_id) -> llvm::SmallVector& { return node_blocks_[block_id.index]; @@ -187,6 +147,12 @@ class SemanticsIR { return id; } + // Returns the requested real literal. + auto GetRealLiteral(SemanticsRealLiteralId int_id) const + -> const SemanticsRealLiteral& { + return real_literals_[int_id.index]; + } + // Adds an string, returning an ID to reference it. auto AddString(llvm::StringRef str) -> SemanticsStringId { // If the string has already been stored, return the corresponding ID. @@ -201,6 +167,11 @@ class SemanticsIR { return id; } + // Returns the requested string. + auto GetString(SemanticsStringId string_id) const -> llvm::StringRef { + return strings_[string_id.index]; + } + // Returns an ID for the string if it's previously been stored. auto GetStringID(llvm::StringRef str) -> std::optional { auto str_find = string_to_id_.find(str); @@ -213,6 +184,27 @@ class SemanticsIR { // Produces a string version of a node. auto StringifyNode(SemanticsNodeId node_id) -> std::string; + auto nodes_size() const -> int { return nodes_.size(); } + + // The node blocks, for direct mutation. + auto node_blocks() -> llvm::SmallVector>& { + return node_blocks_; + } + + auto top_node_block_id() const -> SemanticsNodeBlockId { + return top_node_block_id_; + } + + // Returns true if there were errors creating the semantics IR. + auto has_errors() const -> bool { return has_errors_; } + + private: + explicit SemanticsIR(const SemanticsIR* builtin_ir) + : cross_reference_irs_({builtin_ir == nullptr ? this : builtin_ir}) { + // For SemanticsNodeBlockId::Empty. + node_blocks_.resize(1); + } + bool has_errors_ = false; // Storage for call objects. diff --git a/toolchain/semantics/semantics_parse_tree_handler.cpp b/toolchain/semantics/semantics_parse_tree_handler.cpp deleted file mode 100644 index 9b6977150365..000000000000 --- a/toolchain/semantics/semantics_parse_tree_handler.cpp +++ /dev/null @@ -1,1249 +0,0 @@ -// 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_parse_tree_handler.h" - -#include -#include - -#include "common/vlog.h" -#include "llvm/Support/PrettyStackTrace.h" -#include "toolchain/diagnostics/diagnostic_kind.h" -#include "toolchain/lexer/token_kind.h" -#include "toolchain/lexer/tokenized_buffer.h" -#include "toolchain/parser/parse_node_kind.h" -#include "toolchain/semantics/semantics_ir.h" -#include "toolchain/semantics/semantics_node.h" -#include "toolchain/semantics/semantics_node_block_stack.h" - -namespace Carbon { - -CARBON_DIAGNOSTIC(SemanticsTodo, Error, "Semantics TODO: {0}", std::string); - -class PrettyStackTraceFunction : public llvm::PrettyStackTraceEntry { - public: - explicit PrettyStackTraceFunction(std::function fn) - : fn_(std::move(fn)) {} - ~PrettyStackTraceFunction() override = default; - - auto print(llvm::raw_ostream& output) const -> void override { fn_(output); } - - private: - const std::function fn_; -}; - -auto SemanticsParseTreeHandler::Build() -> void { - PrettyStackTraceFunction pretty_node_stack([&](llvm::raw_ostream& output) { - node_stack_.PrintForStackDump(output); - }); - PrettyStackTraceFunction pretty_node_block_stack( - [&](llvm::raw_ostream& output) { - node_block_stack_.PrintForStackDump(output); - }); - - // Add a block for the ParseTree. - node_block_stack_.Push(); - PushScope(); - - // Loops over all nodes in the tree. On some errors, this may return early, - // for example if an unrecoverable state is encountered. - for (auto parse_node : parse_tree_->postorder()) { - switch (auto parse_kind = parse_tree_->node_kind(parse_node)) { -#define CARBON_PARSE_NODE_KIND(Name) \ - case ParseNodeKind::Name: { \ - if (!Handle##Name(parse_node)) { \ - return; \ - } \ - break; \ - } -#include "toolchain/parser/parse_node_kind.def" - } - } - - // Pop information for the file-level scope. - semantics_->top_node_block_id_ = node_block_stack_.Pop(); - PopScope(); - - // Information in all the various context objects should be cleaned up as - // various pieces of context go out of scope. At this point, nothing should - // remain. - // node_stack_ will still contain top-level entities. - CARBON_CHECK(name_lookup_.empty()) << name_lookup_.size(); - CARBON_CHECK(scope_stack_.empty()) << scope_stack_.size(); - CARBON_CHECK(node_block_stack_.empty()) << node_block_stack_.size(); - CARBON_CHECK(params_or_args_stack_.empty()) << params_or_args_stack_.size(); -} - -auto SemanticsParseTreeHandler::AddNode(SemanticsNode node) -> SemanticsNodeId { - auto block = node_block_stack_.PeekForAdd(); - CARBON_VLOG() << "AddNode " << block << ": " << node << "\n"; - return semantics_->AddNode(block, node); -} - -auto SemanticsParseTreeHandler::AddNodeAndPush(ParseTree::Node parse_node, - SemanticsNode node) -> void { - auto node_id = AddNode(node); - node_stack_.Push(parse_node, node_id); -} - -auto SemanticsParseTreeHandler::AddNameToLookup(ParseTree::Node name_node, - SemanticsStringId name_id, - SemanticsNodeId target_id) - -> void { - auto [it, inserted] = current_scope().names.insert(name_id); - if (inserted) { - name_lookup_[name_id].push_back(target_id); - } else { - CARBON_DIAGNOSTIC(NameRedefined, Error, "Redefining {0} in the same scope.", - llvm::StringRef); - CARBON_DIAGNOSTIC(PreviousDefinition, Note, "Previous definition is here."); - auto prev_def_id = name_lookup_[name_id].back(); - auto prev_def = semantics_->GetNode(prev_def_id); - - emitter_->Build(name_node, NameRedefined, semantics_->GetString(name_id)) - .Note(prev_def.parse_node(), PreviousDefinition) - .Emit(); - } -} - -auto SemanticsParseTreeHandler::BindName(ParseTree::Node name_node, - SemanticsNodeId type_id, - SemanticsNodeId target_id) - -> SemanticsStringId { - CARBON_CHECK(parse_tree_->node_kind(name_node) == ParseNodeKind::DeclaredName) - << parse_tree_->node_kind(name_node); - auto name_str = parse_tree_->GetNodeText(name_node); - auto name_id = semantics_->AddString(name_str); - - AddNode( - SemanticsNode::BindName::Make(name_node, type_id, name_id, target_id)); - AddNameToLookup(name_node, name_id, target_id); - return name_id; -} - -auto SemanticsParseTreeHandler::PushScope() -> void { - scope_stack_.push_back({}); -} - -auto SemanticsParseTreeHandler::PopScope() -> void { - auto scope = scope_stack_.pop_back_val(); - for (const auto& str_id : scope.names) { - auto it = name_lookup_.find(str_id); - if (it->second.size() == 1) { - // Erase names that no longer resolve. - name_lookup_.erase(it); - } else { - it->second.pop_back(); - } - } -} - -auto SemanticsParseTreeHandler::ImplicitAsForArgs( - SemanticsNodeBlockId /*arg_ir_id*/, SemanticsNodeBlockId arg_refs_id, - ParseTree::Node param_parse_node, SemanticsNodeBlockId param_refs_id, - DiagnosticEmitter::DiagnosticBuilder* diagnostic) -> bool { - // If both arguments and parameters are empty, return quickly. Otherwise, - // we'll fetch both so that errors are consistent. - if (arg_refs_id == SemanticsNodeBlockId::Empty && - param_refs_id == SemanticsNodeBlockId::Empty) { - return true; - } - - auto arg_refs = semantics_->GetNodeBlock(arg_refs_id); - auto param_refs = semantics_->GetNodeBlock(param_refs_id); - - // If sizes mismatch, fail early. - if (arg_refs.size() != param_refs.size()) { - CARBON_CHECK(diagnostic != nullptr) << "Should have validated first"; - CARBON_DIAGNOSTIC(CallArgCountMismatch, Note, - "Callable cannot be used: Received {0} argument(s), but " - "require {1} argument(s).", - int, int); - diagnostic->Note(param_parse_node, CallArgCountMismatch, arg_refs.size(), - param_refs.size()); - return false; - } - - // Check type conversions per-element. - // TODO: arg_ir_id is passed so that implicit conversions can be inserted. - // It's currently not supported, but will be needed. - for (size_t i = 0; i < arg_refs.size(); ++i) { - auto value_id = arg_refs[i]; - auto as_type_id = semantics_->GetNode(param_refs[i]).type_id(); - if (ImplicitAsImpl(value_id, as_type_id, - diagnostic == nullptr ? &value_id : nullptr) == - ImplicitAsKind::Incompatible) { - CARBON_CHECK(diagnostic != nullptr) << "Should have validated first"; - CARBON_DIAGNOSTIC(CallArgTypeMismatch, Note, - "Callable cannot be used: Cannot implicityly convert " - "argument {0} from `{1}` to `{2}`.", - size_t, std::string, std::string); - diagnostic->Note( - param_parse_node, CallArgTypeMismatch, i, - semantics_->StringifyNode(semantics_->GetNode(value_id).type_id()), - semantics_->StringifyNode(as_type_id)); - return false; - } - } - - return true; -} - -auto SemanticsParseTreeHandler::ImplicitAsRequired(ParseTree::Node parse_node, - SemanticsNodeId value_id, - SemanticsNodeId as_type_id) - -> SemanticsNodeId { - SemanticsNodeId output_value_id = value_id; - if (ImplicitAsImpl(value_id, as_type_id, &output_value_id) == - ImplicitAsKind::Incompatible) { - // Only error when the system is trying to use the result. - CARBON_DIAGNOSTIC(ImplicitAsConversionFailure, Error, - "Cannot implicitly convert from `{0}` to `{1}`.", - std::string, std::string); - emitter_ - ->Build( - parse_node, ImplicitAsConversionFailure, - semantics_->StringifyNode(semantics_->GetNode(value_id).type_id()), - semantics_->StringifyNode(as_type_id)) - .Emit(); - } - return output_value_id; -} - -auto SemanticsParseTreeHandler::ImplicitAsImpl(SemanticsNodeId value_id, - SemanticsNodeId as_type_id, - SemanticsNodeId* output_value_id) - -> ImplicitAsKind { - // Start by making sure both sides are valid. If any part is invalid, the - // result is invalid and we shouldn't error. - if (value_id == SemanticsNodeId::BuiltinInvalidType) { - // If the value is invalid, we can't do much, but do "succeed". - return ImplicitAsKind::Identical; - } - auto value_type_id = semantics_->GetNode(value_id).type_id(); - if (value_type_id == SemanticsNodeId::BuiltinInvalidType) { - return ImplicitAsKind::Identical; - } - if (as_type_id == SemanticsNodeId::BuiltinInvalidType) { - // Although the target type is invalid, this still changes the value. - if (output_value_id != nullptr) { - *output_value_id = SemanticsNodeId::BuiltinInvalidType; - } - return ImplicitAsKind::Compatible; - } - - if (value_type_id == as_type_id) { - // Type doesn't need to change. - return ImplicitAsKind::Identical; - } - - // When converting to a Type, there are some automatic conversions that can be - // done. - if (as_type_id == SemanticsNodeId::BuiltinTypeType) { - if (value_id == SemanticsNodeId::BuiltinEmptyTuple) { - if (output_value_id != nullptr) { - *output_value_id = SemanticsNodeId::BuiltinEmptyTupleType; - } - return ImplicitAsKind::Compatible; - } - if (value_id == SemanticsNodeId::BuiltinEmptyStruct) { - if (output_value_id != nullptr) { - *output_value_id = SemanticsNodeId::BuiltinEmptyStructType; - } - return ImplicitAsKind::Compatible; - } - } - - auto value_type = semantics_->GetNode(value_type_id); - auto as_type = semantics_->GetNode(as_type_id); - if (CanImplicitAsStruct(value_type, as_type)) { - // Under the current implementation, struct types are only allowed to - // ImplicitAs when they're equivalent. What's really missing is type - // consolidation such that this would fall under the above `value_type_id == - // as_type_id` case. In the future, this will need to handle actual - // conversions. - return ImplicitAsKind::Identical; - } - - if (output_value_id != nullptr) { - *output_value_id = SemanticsNodeId::BuiltinInvalidType; - } - return ImplicitAsKind::Incompatible; -} - -auto SemanticsParseTreeHandler::CanImplicitAsStruct(SemanticsNode value_type, - SemanticsNode as_type) - -> bool { - if (value_type.kind() != SemanticsNodeKind::StructType || - as_type.kind() != SemanticsNodeKind::StructType) { - return false; - } - auto value_type_refs = - semantics_->GetNodeBlock(value_type.GetAsStructType().second); - auto as_type_refs = - semantics_->GetNodeBlock(as_type.GetAsStructType().second); - if (value_type_refs.size() != as_type_refs.size()) { - return false; - } - - for (int i = 0; i < static_cast(value_type_refs.size()); ++i) { - auto value_type_field = semantics_->GetNode(value_type_refs[i]); - auto as_type_field = semantics_->GetNode(as_type_refs[i]); - if (value_type_field.type_id() != as_type_field.type_id() || - value_type_field.GetAsStructTypeField() != - as_type_field.GetAsStructTypeField()) { - return false; - } - } - return true; -} - -auto SemanticsParseTreeHandler::ParamOrArgStart() -> void { - params_or_args_stack_.Push(); - node_block_stack_.Push(); -} - -auto SemanticsParseTreeHandler::ParamOrArgComma(bool for_args) -> void { - ParamOrArgSave(for_args); -} - -auto SemanticsParseTreeHandler::ParamOrArgEnd(bool for_args, - ParseNodeKind start_kind) - -> std::pair { - if (parse_tree_->node_kind(node_stack_.PeekParseNode()) != start_kind) { - ParamOrArgSave(for_args); - } - return {node_block_stack_.Pop(), params_or_args_stack_.Pop()}; -} - -auto SemanticsParseTreeHandler::ParamOrArgSave(bool for_args) -> void { - SemanticsNodeId param_or_arg_id = SemanticsNodeId::Invalid; - if (for_args) { - // For an argument, we add a stub reference to the expression on the top of - // the stack. There may not be anything on the IR prior to this. - auto [entry_parse_node, entry_node_id] = - node_stack_.PopForParseNodeAndNodeId(); - param_or_arg_id = AddNode(SemanticsNode::StubReference::Make( - entry_parse_node, semantics_->GetNode(entry_node_id).type_id(), - entry_node_id)); - } else { - // For a parameter, there should always be something in the IR. - node_stack_.PopAndIgnore(); - auto ir_id = node_block_stack_.Peek(); - CARBON_CHECK(ir_id.is_valid()); - auto& ir = semantics_->GetNodeBlock(ir_id); - CARBON_CHECK(!ir.empty()) << "Should have had a param"; - param_or_arg_id = ir.back(); - } - - // Save the param or arg ID. - auto& params_or_args = - semantics_->GetNodeBlock(params_or_args_stack_.PeekForAdd()); - params_or_args.push_back(param_or_arg_id); -} - -auto SemanticsParseTreeHandler::HandleAddress(ParseTree::Node parse_node) - -> bool { - emitter_->Emit(parse_node, SemanticsTodo, "HandleAddress"); - return false; -} - -auto SemanticsParseTreeHandler::HandleBreakStatement(ParseTree::Node parse_node) - -> bool { - emitter_->Emit(parse_node, SemanticsTodo, "HandleBreakStatement"); - return false; -} - -auto SemanticsParseTreeHandler::HandleBreakStatementStart( - ParseTree::Node parse_node) -> bool { - emitter_->Emit(parse_node, SemanticsTodo, "HandleBreakStatementStart"); - return false; -} - -auto SemanticsParseTreeHandler::HandleCallExpression(ParseTree::Node parse_node) - -> bool { - auto [ir_id, refs_id] = - ParamOrArgEnd(/*for_args=*/true, ParseNodeKind::CallExpressionStart); - - // TODO: Convert to call expression. - auto [call_expr_parse_node, name_id] = - node_stack_.PopForParseNodeAndNodeId(ParseNodeKind::CallExpressionStart); - auto name_node = semantics_->GetNode(name_id); - if (name_node.kind() != SemanticsNodeKind::FunctionDeclaration) { - // TODO: Work on error. - emitter_->Emit(parse_node, SemanticsTodo, "Not a callable name"); - node_stack_.Push(parse_node, name_id); - return true; - } - - auto [_, callable_id] = name_node.GetAsFunctionDeclaration(); - auto callable = semantics_->GetCallable(callable_id); - - CARBON_DIAGNOSTIC(NoMatchingCall, Error, "No matching callable was found."); - auto diagnostic = emitter_->Build(call_expr_parse_node, NoMatchingCall); - if (!ImplicitAsForArgs(ir_id, refs_id, name_node.parse_node(), - callable.param_refs_id, &diagnostic)) { - diagnostic.Emit(); - node_stack_.Push(parse_node, SemanticsNodeId::BuiltinInvalidType); - return true; - } - - CARBON_CHECK(ImplicitAsForArgs(ir_id, refs_id, name_node.parse_node(), - callable.param_refs_id, - /*diagnostic=*/nullptr)); - - auto call_id = semantics_->AddCall({ir_id, refs_id}); - // TODO: Propagate return types from callable. - auto call_node_id = AddNode(SemanticsNode::Call::Make( - call_expr_parse_node, callable.return_type_id, call_id, callable_id)); - - node_stack_.Push(parse_node, call_node_id); - return true; -} - -auto SemanticsParseTreeHandler::HandleCallExpressionComma( - ParseTree::Node /*parse_node*/) -> bool { - ParamOrArgComma(/*for_args=*/true); - return true; -} - -auto SemanticsParseTreeHandler::HandleCallExpressionStart( - ParseTree::Node parse_node) -> bool { - auto name_id = node_stack_.PopForNodeId(ParseNodeKind::NameReference); - node_stack_.Push(parse_node, name_id); - ParamOrArgStart(); - return true; -} - -auto SemanticsParseTreeHandler::HandleClassDeclaration( - ParseTree::Node parse_node) -> bool { - emitter_->Emit(parse_node, SemanticsTodo, "HandleClassDeclaration"); - return false; -} - -auto SemanticsParseTreeHandler::HandleClassDefinition( - ParseTree::Node parse_node) -> bool { - emitter_->Emit(parse_node, SemanticsTodo, "HandleClassDefinition"); - return false; -} - -auto SemanticsParseTreeHandler::HandleClassDefinitionStart( - ParseTree::Node parse_node) -> bool { - emitter_->Emit(parse_node, SemanticsTodo, "HandleClassDefinitionStart"); - return false; -} - -auto SemanticsParseTreeHandler::HandleClassIntroducer( - ParseTree::Node parse_node) -> bool { - emitter_->Emit(parse_node, SemanticsTodo, "HandleClassIntroducer"); - return false; -} - -auto SemanticsParseTreeHandler::HandleCodeBlock(ParseTree::Node parse_node) - -> bool { - emitter_->Emit(parse_node, SemanticsTodo, "HandleCodeBlock"); - return false; -} - -auto SemanticsParseTreeHandler::HandleCodeBlockStart(ParseTree::Node parse_node) - -> bool { - emitter_->Emit(parse_node, SemanticsTodo, "HandleCodeBlockStart"); - return false; -} - -auto SemanticsParseTreeHandler::HandleContinueStatement( - ParseTree::Node parse_node) -> bool { - emitter_->Emit(parse_node, SemanticsTodo, "HandleContinueStatement"); - return false; -} - -auto SemanticsParseTreeHandler::HandleContinueStatementStart( - ParseTree::Node parse_node) -> bool { - emitter_->Emit(parse_node, SemanticsTodo, "HandleContinueStatementStart"); - return false; -} - -auto SemanticsParseTreeHandler::HandleDeclaredName(ParseTree::Node parse_node) - -> bool { - // The parent is responsible for binding the name. - node_stack_.Push(parse_node); - return true; -} - -auto SemanticsParseTreeHandler::HandleDeducedParameterList( - ParseTree::Node parse_node) -> bool { - emitter_->Emit(parse_node, SemanticsTodo, "HandleDeducedParameterList"); - return false; -} - -auto SemanticsParseTreeHandler::HandleDeducedParameterListStart( - ParseTree::Node parse_node) -> bool { - emitter_->Emit(parse_node, SemanticsTodo, "HandleDeducedParameterListStart"); - return false; -} - -auto SemanticsParseTreeHandler::HandleDesignatedName(ParseTree::Node parse_node) - -> bool { - auto name_str = parse_tree_->GetNodeText(parse_node); - auto name_id = semantics_->AddString(name_str); - // The parent is responsible for binding the name. - node_stack_.Push(parse_node, name_id); - return true; -} - -auto SemanticsParseTreeHandler::HandleDesignatorExpression( - ParseTree::Node parse_node) -> bool { - auto [_, name_id] = - node_stack_.PopForParseNodeAndNameId(ParseNodeKind::DesignatedName); - - auto base_id = node_stack_.PopForNodeId(); - auto base = semantics_->GetNode(base_id); - auto base_type = semantics_->GetNode(base.type_id()); - - switch (base_type.kind()) { - case SemanticsNodeKind::StructType: { - auto refs = semantics_->GetNodeBlock(base_type.GetAsStructType().second); - // TODO: Do we need to optimize this with a lookup table for O(1)? - for (int i = 0; i < static_cast(refs.size()); ++i) { - auto ref = semantics_->GetNode(refs[i]); - if (name_id == ref.GetAsStructTypeField()) { - AddNodeAndPush(parse_node, SemanticsNode::StructMemberAccess::Make( - parse_node, ref.type_id(), base_id, - SemanticsMemberIndex(i))); - return true; - } - } - CARBON_DIAGNOSTIC(DesignatorExpressionNameNotFound, Error, - "Type `{0}` does not have a member `{1}`.", std::string, - llvm::StringRef); - emitter_->Emit(parse_node, DesignatorExpressionNameNotFound, - semantics_->StringifyNode(base.type_id()), - semantics_->GetString(name_id)); - break; - } - default: { - CARBON_DIAGNOSTIC(DesignatorExpressionUnsupported, Error, - "Type `{0}` does not support designator expressions.", - std::string); - emitter_->Emit(parse_node, DesignatorExpressionUnsupported, - semantics_->StringifyNode(base.type_id())); - break; - } - } - - // Should only be reached on error. - node_stack_.Push(parse_node, SemanticsNodeId::BuiltinInvalidType); - return true; -} - -auto SemanticsParseTreeHandler::HandleEmptyDeclaration( - ParseTree::Node parse_node) -> bool { - // Empty declarations have no actions associated, but we still balance the - // tree. - node_stack_.Push(parse_node); - return true; -} - -auto SemanticsParseTreeHandler::HandleExpressionStatement( - 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. - node_stack_.PopAndDiscardId(); - node_stack_.Push(parse_node); - return true; -} - -auto SemanticsParseTreeHandler::HandleFileEnd(ParseTree::Node /*parse_node*/) - -> bool { - // Do nothing, no need to balance this node. - return true; -} - -auto SemanticsParseTreeHandler::HandleForHeader(ParseTree::Node parse_node) - -> bool { - emitter_->Emit(parse_node, SemanticsTodo, "HandleForHeader"); - return false; -} - -auto SemanticsParseTreeHandler::HandleForHeaderStart(ParseTree::Node parse_node) - -> bool { - emitter_->Emit(parse_node, SemanticsTodo, "HandleForHeaderStart"); - return false; -} - -auto SemanticsParseTreeHandler::HandleForIn(ParseTree::Node parse_node) - -> bool { - emitter_->Emit(parse_node, SemanticsTodo, "HandleForIn"); - return false; -} - -auto SemanticsParseTreeHandler::HandleForStatement(ParseTree::Node parse_node) - -> bool { - emitter_->Emit(parse_node, SemanticsTodo, "HandleForStatement"); - return false; -} - -auto SemanticsParseTreeHandler::HandleFunctionDeclaration( - ParseTree::Node parse_node) -> bool { - emitter_->Emit(parse_node, SemanticsTodo, "HandleFunctionDeclaration"); - return false; -} - -auto SemanticsParseTreeHandler::HandleFunctionDefinition( - ParseTree::Node parse_node) -> bool { - // Merges code block children up under the FunctionDefinitionStart. - while (parse_tree_->node_kind(node_stack_.PeekParseNode()) != - ParseNodeKind::FunctionDefinitionStart) { - node_stack_.PopAndIgnore(); - } - auto decl_id = - node_stack_.PopForNodeId(ParseNodeKind::FunctionDefinitionStart); - - return_scope_stack_.pop_back(); - PopScope(); - auto block_id = node_block_stack_.Pop(); - AddNode( - SemanticsNode::FunctionDefinition::Make(parse_node, decl_id, block_id)); - node_stack_.Push(parse_node); - - return true; -} - -auto SemanticsParseTreeHandler::HandleFunctionDefinitionStart( - ParseTree::Node parse_node) -> bool { - SemanticsNodeId return_type_id = SemanticsNodeId::Invalid; - if (parse_tree_->node_kind(node_stack_.PeekParseNode()) == - ParseNodeKind::ReturnType) { - return_type_id = node_stack_.PopForNodeId(ParseNodeKind::ReturnType); - } - node_stack_.PopForSoloParseNode(ParseNodeKind::ParameterList); - auto [param_ir_id, param_refs_id] = finished_params_stack_.pop_back_val(); - auto name_node = node_stack_.PopForSoloParseNode(ParseNodeKind::DeclaredName); - auto fn_node = - node_stack_.PopForSoloParseNode(ParseNodeKind::FunctionIntroducer); - - auto name_str = parse_tree_->GetNodeText(name_node); - auto name_id = semantics_->AddString(name_str); - - auto callable_id = - semantics_->AddCallable({.param_ir_id = param_ir_id, - .param_refs_id = param_refs_id, - .return_type_id = return_type_id}); - auto decl_id = AddNode( - SemanticsNode::FunctionDeclaration::Make(fn_node, name_id, callable_id)); - AddNameToLookup(name_node, name_id, decl_id); - - node_block_stack_.Push(); - PushScope(); - return_scope_stack_.push_back(decl_id); - node_stack_.Push(parse_node, decl_id); - - return true; -} - -auto SemanticsParseTreeHandler::HandleFunctionIntroducer( - ParseTree::Node parse_node) -> bool { - // No action, just a bracketing node. - node_stack_.Push(parse_node); - return true; -} - -auto SemanticsParseTreeHandler::HandleGenericPatternBinding( - ParseTree::Node parse_node) -> bool { - emitter_->Emit(parse_node, SemanticsTodo, "GenericPatternBinding"); - return false; -} - -auto SemanticsParseTreeHandler::HandleIfCondition(ParseTree::Node parse_node) - -> bool { - emitter_->Emit(parse_node, SemanticsTodo, "HandleIfCondition"); - return false; -} - -auto SemanticsParseTreeHandler::HandleIfConditionStart( - ParseTree::Node parse_node) -> bool { - emitter_->Emit(parse_node, SemanticsTodo, "HandleIfConditionStart"); - return false; -} - -auto SemanticsParseTreeHandler::HandleIfStatement(ParseTree::Node parse_node) - -> bool { - emitter_->Emit(parse_node, SemanticsTodo, "HandleIfStatement"); - return false; -} - -auto SemanticsParseTreeHandler::HandleIfStatementElse( - ParseTree::Node parse_node) -> bool { - emitter_->Emit(parse_node, SemanticsTodo, "HandleIfStatementElse"); - return false; -} - -auto SemanticsParseTreeHandler::HandleInfixOperator(ParseTree::Node parse_node) - -> bool { - auto rhs_id = node_stack_.PopForNodeId(); - auto lhs_id = node_stack_.PopForNodeId(); - - // TODO: This should search for a compatible interface. For now, it's a very - // trivial check of validity on the operation. - lhs_id = ImplicitAsRequired(parse_node, lhs_id, - semantics_->GetNode(rhs_id).type_id()); - - // Figure out the operator for the token. - auto token = parse_tree_->node_token(parse_node); - switch (auto token_kind = tokens_->GetKind(token)) { - case TokenKind::Plus: - AddNodeAndPush(parse_node, - SemanticsNode::BinaryOperatorAdd::Make( - parse_node, semantics_->GetNode(lhs_id).type_id(), - lhs_id, rhs_id)); - break; - default: - emitter_->Emit(parse_node, SemanticsTodo, - llvm::formatv("Handle {0}", token_kind)); - return false; - } - - return true; -} - -auto SemanticsParseTreeHandler::HandleInterfaceDeclaration( - ParseTree::Node parse_node) -> bool { - emitter_->Emit(parse_node, SemanticsTodo, "HandleInterfaceDeclaration"); - return false; -} - -auto SemanticsParseTreeHandler::HandleInterfaceDefinition( - ParseTree::Node parse_node) -> bool { - emitter_->Emit(parse_node, SemanticsTodo, "HandleInterfaceDefinition"); - return false; -} - -auto SemanticsParseTreeHandler::HandleInterfaceDefinitionStart( - ParseTree::Node parse_node) -> bool { - emitter_->Emit(parse_node, SemanticsTodo, "HandleInterfaceDefinitionStart"); - return false; -} - -auto SemanticsParseTreeHandler::HandleInterfaceIntroducer( - ParseTree::Node parse_node) -> bool { - emitter_->Emit(parse_node, SemanticsTodo, "HandleInterfaceIntroducer"); - return false; -} - -auto SemanticsParseTreeHandler::HandleInvalidParse(ParseTree::Node parse_node) - -> bool { - emitter_->Emit(parse_node, SemanticsTodo, "HandleInvalidParse"); - return false; -} - -auto SemanticsParseTreeHandler::HandleLiteral(ParseTree::Node parse_node) - -> bool { - auto token = parse_tree_->node_token(parse_node); - switch (auto token_kind = tokens_->GetKind(token)) { - case TokenKind::IntegerLiteral: { - auto id = - semantics_->AddIntegerLiteral(tokens_->GetIntegerLiteral(token)); - AddNodeAndPush(parse_node, - SemanticsNode::IntegerLiteral::Make(parse_node, id)); - break; - } - case TokenKind::RealLiteral: { - auto token_value = tokens_->GetRealLiteral(token); - auto id = - semantics_->AddRealLiteral({.mantissa = token_value.Mantissa(), - .exponent = token_value.Exponent(), - .is_decimal = token_value.IsDecimal()}); - AddNodeAndPush(parse_node, - SemanticsNode::RealLiteral::Make(parse_node, id)); - break; - } - case TokenKind::StringLiteral: { - auto id = semantics_->AddString(tokens_->GetStringLiteral(token)); - AddNodeAndPush(parse_node, - SemanticsNode::StringLiteral::Make(parse_node, id)); - break; - } - case TokenKind::IntegerTypeLiteral: { - auto text = tokens_->GetTokenText(token); - if (text != "i32") { - emitter_->Emit(parse_node, SemanticsTodo, - "Currently only i32 is allowed"); - return false; - } - node_stack_.Push(parse_node, SemanticsNodeId::BuiltinIntegerType); - break; - } - case TokenKind::FloatingPointTypeLiteral: { - auto text = tokens_->GetTokenText(token); - if (text != "f64") { - emitter_->Emit(parse_node, SemanticsTodo, - "Currently only f64 is allowed"); - return false; - } - node_stack_.Push(parse_node, SemanticsNodeId::BuiltinFloatingPointType); - break; - } - case TokenKind::StringTypeLiteral: { - node_stack_.Push(parse_node, SemanticsNodeId::BuiltinStringType); - break; - } - default: { - emitter_->Emit(parse_node, SemanticsTodo, - llvm::formatv("Handle {0}", token_kind)); - return false; - } - } - - return true; -} - -auto SemanticsParseTreeHandler::HandleNameReference(ParseTree::Node parse_node) - -> bool { - auto name_str = parse_tree_->GetNodeText(parse_node); - - auto name_not_found = [&] { - CARBON_DIAGNOSTIC(NameNotFound, Error, "Name {0} not found", - llvm::StringRef); - emitter_->Emit(parse_node, NameNotFound, name_str); - node_stack_.Push(parse_node, SemanticsNodeId::BuiltinInvalidType); - }; - - auto name_id = semantics_->GetStringID(name_str); - if (!name_id) { - name_not_found(); - return true; - } - - auto it = name_lookup_.find(*name_id); - if (it == name_lookup_.end()) { - name_not_found(); - return true; - } - CARBON_CHECK(!it->second.empty()) << "Should have been erased: " << name_str; - - // TODO: Check for ambiguous lookups. - node_stack_.Push(parse_node, it->second.back()); - - return true; -} - -auto SemanticsParseTreeHandler::HandleNamedConstraintDeclaration( - ParseTree::Node parse_node) -> bool { - emitter_->Emit(parse_node, SemanticsTodo, "HandleNamedConstraintDeclaration"); - return false; -} - -auto SemanticsParseTreeHandler::HandleNamedConstraintDefinition( - ParseTree::Node parse_node) -> bool { - emitter_->Emit(parse_node, SemanticsTodo, "HandleNamedConstraintDefinition"); - return false; -} - -auto SemanticsParseTreeHandler::HandleNamedConstraintDefinitionStart( - ParseTree::Node parse_node) -> bool { - emitter_->Emit(parse_node, SemanticsTodo, - "HandleNamedConstraintDefinitionStart"); - return false; -} - -auto SemanticsParseTreeHandler::HandleNamedConstraintIntroducer( - ParseTree::Node parse_node) -> bool { - emitter_->Emit(parse_node, SemanticsTodo, "HandleNamedConstraintIntroducer"); - return false; -} - -auto SemanticsParseTreeHandler::HandlePackageApi(ParseTree::Node parse_node) - -> bool { - emitter_->Emit(parse_node, SemanticsTodo, "HandlePackageApi"); - return false; -} - -auto SemanticsParseTreeHandler::HandlePackageDirective( - ParseTree::Node parse_node) -> bool { - emitter_->Emit(parse_node, SemanticsTodo, "HandlePackageDirective"); - return false; -} - -auto SemanticsParseTreeHandler::HandlePackageImpl(ParseTree::Node parse_node) - -> bool { - emitter_->Emit(parse_node, SemanticsTodo, "HandlePackageImpl"); - return false; -} - -auto SemanticsParseTreeHandler::HandlePackageIntroducer( - ParseTree::Node parse_node) -> bool { - emitter_->Emit(parse_node, SemanticsTodo, "HandlePackageIntroducer"); - return false; -} - -auto SemanticsParseTreeHandler::HandlePackageLibrary(ParseTree::Node parse_node) - -> bool { - emitter_->Emit(parse_node, SemanticsTodo, "HandlePackageLibrary"); - return false; -} - -auto SemanticsParseTreeHandler::HandleParameterList(ParseTree::Node parse_node) - -> bool { - auto [ir_id, refs_id] = - ParamOrArgEnd(/*for_args=*/false, ParseNodeKind::ParameterListStart); - - PopScope(); - node_stack_.PopAndDiscardSoloParseNode(ParseNodeKind::ParameterListStart); - finished_params_stack_.push_back({ir_id, refs_id}); - node_stack_.Push(parse_node); - return true; -} - -auto SemanticsParseTreeHandler::HandleParameterListComma( - ParseTree::Node /*parse_node*/) -> bool { - ParamOrArgComma(/*for_args=*/false); - return true; -} - -auto SemanticsParseTreeHandler::HandleParameterListStart( - ParseTree::Node parse_node) -> bool { - PushScope(); - node_stack_.Push(parse_node); - ParamOrArgStart(); - return true; -} - -auto SemanticsParseTreeHandler::HandleParenExpression( - ParseTree::Node parse_node) -> bool { - emitter_->Emit(parse_node, SemanticsTodo, "HandleParenExpression"); - return false; -} - -auto SemanticsParseTreeHandler::HandleParenExpressionOrTupleLiteralStart( - ParseTree::Node parse_node) -> bool { - emitter_->Emit(parse_node, SemanticsTodo, - "HandleParenExpressionOrTupleLiteralStart"); - return false; -} - -auto SemanticsParseTreeHandler::HandlePatternBinding(ParseTree::Node parse_node) - -> bool { - auto [type_node, parsed_type_id] = node_stack_.PopForParseNodeAndNodeId(); - SemanticsNodeId cast_type_id = ImplicitAsRequired( - type_node, parsed_type_id, SemanticsNodeId::BuiltinTypeType); - - // Get the name. - auto name_node = node_stack_.PopForSoloParseNode(); - - // Allocate storage, linked to the name for error locations. - auto storage_id = - AddNode(SemanticsNode::VarStorage::Make(name_node, cast_type_id)); - - // Bind the name to storage. - auto name_id = BindName(name_node, cast_type_id, storage_id); - - // If this node's result is used, it'll be for either the name or the - // storage address. The storage address can be found through the name, so we - // push the name. - node_stack_.Push(parse_node, name_id); - - return true; -} - -auto SemanticsParseTreeHandler::HandlePostfixOperator( - ParseTree::Node parse_node) -> bool { - emitter_->Emit(parse_node, SemanticsTodo, "HandlePostfixOperator"); - return false; -} - -auto SemanticsParseTreeHandler::HandlePrefixOperator(ParseTree::Node parse_node) - -> bool { - emitter_->Emit(parse_node, SemanticsTodo, "HandlePrefixOperator"); - return false; -} - -auto SemanticsParseTreeHandler::HandleReturnStatement( - ParseTree::Node parse_node) -> bool { - CARBON_CHECK(!return_scope_stack_.empty()); - const auto& fn_node = semantics_->GetNode(return_scope_stack_.back()); - const auto callable = - semantics_->GetCallable(fn_node.GetAsFunctionDeclaration().second); - - if (parse_tree_->node_kind(node_stack_.PeekParseNode()) == - ParseNodeKind::ReturnStatementStart) { - node_stack_.PopAndDiscardSoloParseNode(ParseNodeKind::ReturnStatementStart); - - if (callable.return_type_id.is_valid()) { - // TODO: Add a note pointing at the return type's parse node. - CARBON_DIAGNOSTIC(ReturnStatementMissingExpression, Error, - "Must return a {0}.", std::string); - emitter_ - ->Build(parse_node, ReturnStatementMissingExpression, - semantics_->StringifyNode(callable.return_type_id)) - .Emit(); - } - - AddNodeAndPush(parse_node, SemanticsNode::Return::Make(parse_node)); - } else { - auto arg = node_stack_.PopForNodeId(); - node_stack_.PopAndDiscardSoloParseNode(ParseNodeKind::ReturnStatementStart); - - if (!callable.return_type_id.is_valid()) { - CARBON_DIAGNOSTIC( - ReturnStatementDisallowExpression, Error, - "No return expression should be provided in this context."); - CARBON_DIAGNOSTIC(ReturnStatementImplicitNote, Note, - "There was no return type provided."); - emitter_->Build(parse_node, ReturnStatementDisallowExpression) - .Note(fn_node.parse_node(), ReturnStatementImplicitNote) - .Emit(); - } else { - arg = ImplicitAsRequired(parse_node, arg, callable.return_type_id); - } - - AddNodeAndPush(parse_node, - SemanticsNode::ReturnExpression::Make( - parse_node, semantics_->GetNode(arg).type_id(), arg)); - } - return true; -} - -auto SemanticsParseTreeHandler::HandleReturnStatementStart( - ParseTree::Node parse_node) -> bool { - // No action, just a bracketing node. - node_stack_.Push(parse_node); - return true; -} - -auto SemanticsParseTreeHandler::HandleReturnType(ParseTree::Node parse_node) - -> bool { - // Propagate the type expression. - auto [type_parse_node, type_node_id] = node_stack_.PopForParseNodeAndNodeId(); - auto cast_node_id = ImplicitAsRequired(type_parse_node, type_node_id, - SemanticsNodeId::BuiltinTypeType); - node_stack_.Push(parse_node, cast_node_id); - return true; -} - -auto SemanticsParseTreeHandler::HandleSelfTypeIdentifier( - ParseTree::Node parse_node) -> bool { - emitter_->Emit(parse_node, SemanticsTodo, "HandleSelfTypeIdentifier"); - return false; -} - -auto SemanticsParseTreeHandler::HandleSelfValueIdentifier( - ParseTree::Node parse_node) -> bool { - emitter_->Emit(parse_node, SemanticsTodo, "HandleSelfValueIdentifier"); - return false; -} - -auto SemanticsParseTreeHandler::HandleStructComma( - ParseTree::Node /*parse_node*/) -> bool { - ParamOrArgComma( - /*for_args=*/parse_tree_->node_kind(node_stack_.PeekParseNode()) != - ParseNodeKind::StructFieldType); - return true; -} - -auto SemanticsParseTreeHandler::HandleStructFieldDesignator( - ParseTree::Node /*parse_node*/) -> bool { - // This leaves the designated name on top because the `.` isn't interesting. - CARBON_CHECK(parse_tree_->node_kind(node_stack_.PeekParseNode()) == - ParseNodeKind::DesignatedName); - return true; -} - -auto SemanticsParseTreeHandler::HandleStructFieldType( - ParseTree::Node parse_node) -> bool { - auto [type_node, type_id] = node_stack_.PopForParseNodeAndNodeId(); - SemanticsNodeId cast_type_id = - ImplicitAsRequired(type_node, type_id, SemanticsNodeId::BuiltinTypeType); - - auto [name_node, name_id] = - node_stack_.PopForParseNodeAndNameId(ParseNodeKind::DesignatedName); - - AddNode( - SemanticsNode::StructTypeField::Make(name_node, cast_type_id, name_id)); - node_stack_.Push(parse_node); - return true; -} - -auto SemanticsParseTreeHandler::HandleStructFieldUnknown( - ParseTree::Node parse_node) -> bool { - emitter_->Emit(parse_node, SemanticsTodo, "HandleStructFieldUnknown"); - return false; -} - -auto SemanticsParseTreeHandler::HandleStructFieldValue( - ParseTree::Node parse_node) -> bool { - auto [value_parse_node, value_node_id] = - node_stack_.PopForParseNodeAndNodeId(); - auto [_, name_id] = - node_stack_.PopForParseNodeAndNameId(ParseNodeKind::DesignatedName); - - // Store the name for the type. - auto type_block_id = args_type_info_stack_.PeekForAdd(); - semantics_->AddNode( - type_block_id, - SemanticsNode::StructTypeField::Make( - parse_node, semantics_->GetNode(value_node_id).type_id(), name_id)); - - // Push the value back on the stack as an argument. - node_stack_.Push(parse_node, value_node_id); - return true; -} - -auto SemanticsParseTreeHandler::HandleStructLiteral(ParseTree::Node parse_node) - -> bool { - auto [ir_id, refs_id] = ParamOrArgEnd( - /*for_args=*/true, ParseNodeKind::StructLiteralOrStructTypeLiteralStart); - - PopScope(); - node_stack_.PopAndDiscardSoloParseNode( - ParseNodeKind::StructLiteralOrStructTypeLiteralStart); - auto type_block_id = args_type_info_stack_.Pop(); - - // Special-case `{}`. - if (refs_id == SemanticsNodeBlockId::Empty) { - node_stack_.Push(parse_node, SemanticsNodeId::BuiltinEmptyStruct); - return true; - } - - // Construct a type for the literal. Each field is one node, so ir_id and - // refs_id match. - auto refs = semantics_->GetNodeBlock(refs_id); - auto type_id = AddNode(SemanticsNode::StructType::Make( - parse_node, type_block_id, type_block_id)); - - auto value_id = AddNode( - SemanticsNode::StructValue::Make(parse_node, type_id, ir_id, refs_id)); - node_stack_.Push(parse_node, value_id); - return true; -} - -auto SemanticsParseTreeHandler::HandleStructLiteralOrStructTypeLiteralStart( - ParseTree::Node parse_node) -> bool { - PushScope(); - node_stack_.Push(parse_node); - // At this point we aren't sure whether this will be a value or type literal, - // so we push onto args irrespective. It just won't be used for a type - // literal. - args_type_info_stack_.Push(); - ParamOrArgStart(); - return true; -} - -auto SemanticsParseTreeHandler::HandleStructTypeLiteral( - ParseTree::Node parse_node) -> bool { - auto [ir_id, refs_id] = ParamOrArgEnd( - /*for_args=*/false, ParseNodeKind::StructLiteralOrStructTypeLiteralStart); - - PopScope(); - node_stack_.PopAndDiscardSoloParseNode( - ParseNodeKind::StructLiteralOrStructTypeLiteralStart); - // This is only used for value literals. - args_type_info_stack_.Pop(); - - CARBON_CHECK(refs_id != SemanticsNodeBlockId::Empty) - << "{} is handled by StructLiteral."; - - auto type_id = - AddNode(SemanticsNode::StructType::Make(parse_node, ir_id, refs_id)); - node_stack_.Push(parse_node, type_id); - return true; -} - -auto SemanticsParseTreeHandler::HandleTemplate(ParseTree::Node parse_node) - -> bool { - emitter_->Emit(parse_node, SemanticsTodo, "HandleTemplate"); - return false; -} - -auto SemanticsParseTreeHandler::HandleTupleLiteral(ParseTree::Node parse_node) - -> bool { - emitter_->Emit(parse_node, SemanticsTodo, "HandleTupleLiteral"); - return false; -} - -auto SemanticsParseTreeHandler::HandleTupleLiteralComma( - ParseTree::Node parse_node) -> bool { - emitter_->Emit(parse_node, SemanticsTodo, "HandleTupleLiteralComma"); - return false; -} - -auto SemanticsParseTreeHandler::HandleVariableDeclaration( - ParseTree::Node parse_node) -> bool { - auto [last_parse_node, last_node_id] = node_stack_.PopForParseNodeAndNodeId(); - - if (parse_tree_->node_kind(last_parse_node) != - ParseNodeKind::PatternBinding) { - auto storage_id = - node_stack_.PopForNodeId(ParseNodeKind::VariableInitializer); - - auto binding = - node_stack_.PopForParseNodeAndNameId(ParseNodeKind::PatternBinding); - - // Restore the name now that the initializer is complete. - ReaddNameToLookup(binding.second, storage_id); - - auto cast_value_id = ImplicitAsRequired( - parse_node, last_node_id, semantics_->GetNode(storage_id).type_id()); - AddNode(SemanticsNode::Assign::Make( - parse_node, semantics_->GetNode(cast_value_id).type_id(), storage_id, - cast_value_id)); - } - - node_stack_.PopAndDiscardSoloParseNode(ParseNodeKind::VariableIntroducer); - node_stack_.Push(parse_node); - - return true; -} - -auto SemanticsParseTreeHandler::HandleVariableIntroducer( - ParseTree::Node parse_node) -> bool { - // No action, just a bracketing node. - node_stack_.Push(parse_node); - return true; -} - -auto SemanticsParseTreeHandler::HandleVariableInitializer( - ParseTree::Node parse_node) -> bool { - // Temporarily remove name lookup entries added by the `var`. These will be - // restored by `VariableDeclaration`. - - // Save the storage ID. - auto it = name_lookup_.find( - node_stack_.PeekForNameId(ParseNodeKind::PatternBinding)); - CARBON_CHECK(it != name_lookup_.end()); - CARBON_CHECK(!it->second.empty()); - auto storage_id = it->second.back(); - - // Pop the name from lookup. - if (it->second.size() == 1) { - // Erase names that no longer resolve. - name_lookup_.erase(it); - } else { - it->second.pop_back(); - } - - node_stack_.Push(parse_node, storage_id); - - return true; -} - -auto SemanticsParseTreeHandler::HandleWhileCondition(ParseTree::Node parse_node) - -> bool { - emitter_->Emit(parse_node, SemanticsTodo, "HandleWhileCondition"); - return false; -} - -auto SemanticsParseTreeHandler::HandleWhileConditionStart( - ParseTree::Node parse_node) -> bool { - emitter_->Emit(parse_node, SemanticsTodo, "HandleWhileConditionStart"); - return false; -} - -auto SemanticsParseTreeHandler::HandleWhileStatement(ParseTree::Node parse_node) - -> bool { - emitter_->Emit(parse_node, SemanticsTodo, "HandleWhileStatement"); - return false; -} - -} // namespace Carbon