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