From 9d234aa7e730096d7b2bfc7bf9d24c423f972f52 Mon Sep 17 00:00:00 2001 From: Jon Ross-Perkins Date: Tue, 20 Dec 2022 10:10:23 -0800 Subject: [PATCH] Start doing name lookup for references (#2472) When binding a name, add it to name lookup. On NameReference nodes, use name lookup. - Switches from "identifiers" to the more generic "strings". Not strictly necessary here, but it's the overall direction I think we've agreed upon and wanted to do it while building more support out. - Starts doing deduplication of strings. - On BindName, registers names with name lookup. - Does name lookup based on the deduplicated string. - Per discussion with zygoloid, design is intended to be constant-time lookup regardless of the number of parent scopes. - Adds scopes so that we can track names which will be deregistered from lookup. --- toolchain/diagnostics/diagnostic_registry.def | 3 + toolchain/semantics/semantics_ir.cpp | 14 +- toolchain/semantics/semantics_ir.h | 67 ++++++-- toolchain/semantics/semantics_node.h | 19 ++- .../semantics_parse_tree_handler.cpp | 143 ++++++++++++++---- .../semantics/semantics_parse_tree_handler.h | 72 ++++++++- .../semantics/testdata/basics/empty.carbon | 4 +- .../testdata/basics/empty_decl.carbon | 4 +- .../testdata/basics/fail_name_lookup.carbon | 33 ++++ .../semantics/testdata/basics/verbose.carbon | 2 +- .../semantics/testdata/function/basic.carbon | 9 +- .../semantics/testdata/function/order.carbon | 19 ++- .../testdata/operators/binary_op.carbon | 9 +- .../operators/fail_type_mismatch.carbon | 11 +- .../operators/fail_type_mismatch_once.carbon | 11 +- .../semantics/testdata/return/literal.carbon | 9 +- .../semantics/testdata/return/trivial.carbon | 9 +- toolchain/semantics/testdata/var/decl.carbon | 14 +- .../testdata/var/decl_with_init.carbon | 14 +- .../testdata/var/fail_duplicate_decl.carbon | 48 ++++++ .../var/fail_init_type_mismatch.carbon | 16 +- .../semantics/testdata/var/global_decl.carbon | 9 +- .../testdata/var/global_decl_with_init.carbon | 9 +- .../testdata/var/global_lookup.carbon | 36 +++++ .../var/global_lookup_in_scope.carbon | 46 ++++++ .../semantics/testdata/var/lookup.carbon | 40 +++++ .../var/todo_bad_init_with_self.carbon | 38 +++++ .../var/todo_bad_lookup_outside_scope.carbon | 41 +++++ 28 files changed, 615 insertions(+), 134 deletions(-) create mode 100644 toolchain/semantics/testdata/basics/fail_name_lookup.carbon create mode 100644 toolchain/semantics/testdata/var/fail_duplicate_decl.carbon create mode 100644 toolchain/semantics/testdata/var/global_lookup.carbon create mode 100644 toolchain/semantics/testdata/var/global_lookup_in_scope.carbon create mode 100644 toolchain/semantics/testdata/var/lookup.carbon create mode 100644 toolchain/semantics/testdata/var/todo_bad_init_with_self.carbon create mode 100644 toolchain/semantics/testdata/var/todo_bad_lookup_outside_scope.carbon diff --git a/toolchain/diagnostics/diagnostic_registry.def b/toolchain/diagnostics/diagnostic_registry.def index 4b4da600de50..75fca2a5947d 100644 --- a/toolchain/diagnostics/diagnostic_registry.def +++ b/toolchain/diagnostics/diagnostic_registry.def @@ -91,6 +91,9 @@ CARBON_DIAGNOSTIC_KIND(ExpectedDeducedParam) // ============================================================================ CARBON_DIAGNOSTIC_KIND(TypeMismatch) +CARBON_DIAGNOSTIC_KIND(NameNotFound) +CARBON_DIAGNOSTIC_KIND(NameRedefined) +CARBON_DIAGNOSTIC_KIND(PreviousDefinition) // ============================================================================ // Other diagnostics diff --git a/toolchain/semantics/semantics_ir.cpp b/toolchain/semantics/semantics_ir.cpp index 6d00f7f96f3c..9dedded1d3bf 100644 --- a/toolchain/semantics/semantics_ir.cpp +++ b/toolchain/semantics/semantics_ir.cpp @@ -87,13 +87,6 @@ auto SemanticsIR::Print(llvm::raw_ostream& out) const -> void { } out << "},\n"; - out << "identifiers = {\n"; - for (int32_t i = 0; i < static_cast(identifiers_.size()); ++i) { - out.indent(Indent); - out << SemanticsIdentifierId(i) << " = \"" << identifiers_[i] << "\";\n"; - } - out << "},\n"; - out << "integer_literals = {\n"; for (int32_t i = 0; i < static_cast(integer_literals_.size()); ++i) { out.indent(Indent); @@ -102,6 +95,13 @@ auto SemanticsIR::Print(llvm::raw_ostream& out) const -> void { } out << "},\n"; + out << "strings = {\n"; + for (int32_t i = 0; i < static_cast(strings_.size()); ++i) { + out.indent(Indent); + out << SemanticsStringId(i) << " = \"" << strings_[i] << "\";\n"; + } + out << "},\n"; + out << "node_blocks = {\n"; for (int32_t i = 0; i < static_cast(node_blocks_.size()); ++i) { out.indent(Indent); diff --git a/toolchain/semantics/semantics_ir.h b/toolchain/semantics/semantics_ir.h index 6195ac7438ea..0c369cbcc08e 100644 --- a/toolchain/semantics/semantics_ir.h +++ b/toolchain/semantics/semantics_ir.h @@ -5,9 +5,8 @@ #ifndef CARBON_TOOLCHAIN_SEMANTICS_SEMANTICS_IR_H_ #define CARBON_TOOLCHAIN_SEMANTICS_SEMANTICS_IR_H_ -#include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/SmallVector.h" -#include "toolchain/lexer/numeric_literal.h" +#include "llvm/ADT/StringMap.h" #include "toolchain/parser/parse_tree.h" #include "toolchain/semantics/semantics_node.h" @@ -43,6 +42,10 @@ struct SemanticsCrossReference { // Provides semantic analysis on a ParseTree. class SemanticsIR { public: + // As noted under cross_reference_irs_, the current IR must always be at + // index 1. This is a constant for that. + static constexpr auto ThisIR = SemanticsCrossReferenceIRId(1); + // Produces the builtins. static auto MakeBuiltinIR() -> SemanticsIR; @@ -62,10 +65,6 @@ class SemanticsIR { private: friend class SemanticsParseTreeHandler; - // As noted under cross_reference_irs_, the current IR must always be at - // index 1. This is a constant for that. - static constexpr auto ThisIR = SemanticsCrossReferenceIRId(1); - // For the builtin IR only. SemanticsIR() : SemanticsIR(*this) {} // For most IRs. @@ -73,6 +72,19 @@ class SemanticsIR { : cross_reference_irs_({&builtins, this}), cross_references_(builtins.cross_references_) {} + // Returns the requested node, resolving cross references. + auto GetNode(SemanticsNodeBlockId block_id, SemanticsNodeId node_id) + -> SemanticsNode { + if (node_id.is_cross_reference()) { + auto ref = cross_references_[node_id.GetAsCrossReference()]; + return cross_reference_irs_[ref.ir.index] + ->node_blocks_[ref.node_block.index][ref.node.index]; + } else { + return node_blocks_[block_id.index][node_id.index]; + } + } + + // Returns the type of the requested node, resolving cross references. auto GetType(SemanticsNodeBlockId block_id, SemanticsNodeId node_id) -> SemanticsNodeId { if (node_id.is_cross_reference()) { @@ -97,12 +109,12 @@ class SemanticsIR { } } - // Adds an identifier, returning an ID to reference it. - // TODO: Deduplicate strings. - // TODO: Probably make generic for all strings, including literals. - auto AddIdentifier(llvm::StringRef identifier) -> SemanticsIdentifierId { - SemanticsIdentifierId id(identifiers_.size()); - identifiers_.push_back(identifier); + // Adds a cross reference, returning an ID to reference it. + auto AddCrossReference(SemanticsCrossReference cross_reference) + -> SemanticsNodeId { + SemanticsNodeId id = + SemanticsNodeId::MakeCrossReference(cross_references_.size()); + cross_references_.push_back(cross_reference); return id; } @@ -131,6 +143,29 @@ class SemanticsIR { return node_id; } + // 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. + if (auto existing_id = GetString(str)) { + return *existing_id; + } + + // Allocate the string and store it in the map. + SemanticsStringId id(strings_.size()); + strings_.push_back(str); + CARBON_CHECK(string_to_id_.insert({str, id}).second); + return id; + } + + // Returns an ID for the string if it's previously been stored. + auto GetString(llvm::StringRef str) -> std::optional { + auto str_find = string_to_id_.find(str); + if (str_find != string_to_id_.end()) { + return str_find->second; + } + return std::nullopt; + } + bool has_errors_ = false; // Related IRs. There will always be at least 2 entries, the builtin IR (used @@ -145,12 +180,14 @@ class SemanticsIR { // builtins. llvm::SmallVector cross_references_; - // Storage for identifiers. - llvm::SmallVector identifiers_; - // Storage for integer literals. llvm::SmallVector integer_literals_; + // Storage for strings. strings_ provides a list of allocated strings, while + // string_to_id_ provides a mapping to identify strings. + llvm::StringMap string_to_id_; + llvm::SmallVector strings_; + // Storage for blocks within the IR. llvm::SmallVector> node_blocks_; }; diff --git a/toolchain/semantics/semantics_node.h b/toolchain/semantics/semantics_node.h index 0c3c2b53d9d2..f98b6855c0de 100644 --- a/toolchain/semantics/semantics_node.h +++ b/toolchain/semantics/semantics_node.h @@ -52,12 +52,6 @@ struct SemanticsNodeId : public IndexBase { } }; -// Type-safe storage of identifiers. -struct SemanticsIdentifierId : public IndexBase { - using IndexBase::IndexBase; - auto Print(llvm::raw_ostream& out) const -> void { out << "ident" << index; } -}; - // Type-safe storage of integer literals. struct SemanticsIntegerLiteralId : public IndexBase { using IndexBase::IndexBase; @@ -70,6 +64,12 @@ struct SemanticsNodeBlockId : public IndexBase { auto Print(llvm::raw_ostream& out) const -> void { out << "block" << index; } }; +// Type-safe storage of strings. +struct SemanticsStringId : public IndexBase { + using IndexBase::IndexBase; + auto Print(llvm::raw_ostream& out) const -> void { out << "str" << index; } +}; + // The standard structure for nodes. class SemanticsNode { public: @@ -101,15 +101,14 @@ class SemanticsNode { } static auto MakeBindName(ParseTree::Node parse_node, SemanticsNodeId type, - SemanticsIdentifierId name, SemanticsNodeId node) + SemanticsStringId name, SemanticsNodeId node) -> SemanticsNode { return SemanticsNode(parse_node, SemanticsNodeKind::BindName(), type, name.index, node.index); } - auto GetAsBindName() const - -> std::pair { + auto GetAsBindName() const -> std::pair { CARBON_CHECK(kind_ == SemanticsNodeKind::BindName()); - return {SemanticsIdentifierId(arg0_), SemanticsNodeId(arg1_)}; + return {SemanticsStringId(arg0_), SemanticsNodeId(arg1_)}; } static auto MakeBuiltin(SemanticsBuiltinKind builtin_kind, diff --git a/toolchain/semantics/semantics_parse_tree_handler.cpp b/toolchain/semantics/semantics_parse_tree_handler.cpp index 8c4592de9ba2..774f3fbe6ef3 100644 --- a/toolchain/semantics/semantics_parse_tree_handler.cpp +++ b/toolchain/semantics/semantics_parse_tree_handler.cpp @@ -65,6 +65,7 @@ auto SemanticsParseTreeHandler::Build() -> void { // Add a block for the ParseTree. node_block_stack_.push_back(semantics_->AddNodeBlock()); + PushScope(); for (auto parse_node : parse_tree_->postorder()) { switch (auto parse_kind = parse_tree_->node_kind(parse_node)) { @@ -76,12 +77,57 @@ auto SemanticsParseTreeHandler::Build() -> void { #include "toolchain/parser/parse_node_kind.def" } } + + node_block_stack_.pop_back(); + CARBON_CHECK(node_block_stack_.empty()) << node_block_stack_.size(); + + PopScope(); + CARBON_CHECK(name_lookup_.empty()) << name_lookup_.size(); + CARBON_CHECK(scope_stack_.empty()) << scope_stack_.size(); +} + +auto SemanticsParseTreeHandler::AddCrossReference(SemanticsNodeId node_id) + -> SemanticsNodeId { + CARBON_CHECK(!node_id.is_cross_reference()) + << "Should use the existing cross-reference. Might want to return this, " + "but lacking a use-case, it's treated as bad input."; + return semantics_->AddCrossReference(SemanticsCrossReference( + SemanticsIR::ThisIR, current_block_id(), node_id)); } auto SemanticsParseTreeHandler::AddNode(SemanticsNode node) -> SemanticsNodeId { - CARBON_VLOG() << "AddNode " << node_block_stack_.back() << ": " << node - << "\n"; - return semantics_->AddNode(node_block_stack_.back(), node); + CARBON_VLOG() << "AddNode " << current_block_id() << ": " << node << "\n"; + return semantics_->AddNode(current_block_id(), node); +} + +auto SemanticsParseTreeHandler::BindName(ParseTree::Node name_node, + SemanticsNodeId type_id, + SemanticsNodeId target_id) -> void { + 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); + + auto bind_id = AddNode( + SemanticsNode::MakeBindName(name_node, type_id, name_id, target_id)); + auto [it, inserted] = current_scope().names.insert(name_id); + if (inserted) { + name_lookup_[name_id].push_back(AddCrossReference(bind_id)); + } else { + CARBON_DIAGNOSTIC(NameRedefined, Error, "Redefining {0} in the same scope.", + llvm::StringRef); + emitter_->Emit(parse_tree_->node_token(name_node), NameRedefined, name_str); + + // TODO: This should be a note and sorted with the above diagnostic. + // But that depends on more diagnostic support we currently don't have. + auto prev_def_id = name_lookup_[name_id].back(); + auto prev_def = semantics_->GetNode(current_block_id(), prev_def_id); + CARBON_DIAGNOSTIC(PreviousDefinition, Error, + "Previous definition is here."); + emitter_->Emit(parse_tree_->node_token(prev_def.parse_node()), + PreviousDefinition); + } } auto SemanticsParseTreeHandler::Push(ParseTree::Node parse_node) -> void { @@ -166,12 +212,29 @@ auto SemanticsParseTreeHandler::PopWithResultIf(ParseNodeKind pop_parse_kind) return node_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::TryTypeConversion(ParseTree::Node parse_node, SemanticsNodeId lhs_id, SemanticsNodeId rhs_id, bool /*can_convert_lhs*/) -> SemanticsNodeId { - auto block = node_block_stack_.back(); + auto block = current_block_id(); auto lhs_type = semantics_->GetType(block, lhs_id); auto rhs_type = semantics_->GetType(block, rhs_id); // TODO: This should attempt a type conversion, but there's not enough @@ -181,23 +244,17 @@ auto SemanticsParseTreeHandler::TryTypeConversion(ParseTree::Node parse_node, SemanticsBuiltinKind::InvalidType()); if (lhs_type != invalid_type && rhs_type != invalid_type) { // TODO: This is a poor diagnostic, and should be expanded. - CARBON_DIAGNOSTIC(TypeMismatch, Error, "Type mismatch"); - emitter_->Emit(parse_tree_->node_token(parse_node), TypeMismatch); + CARBON_DIAGNOSTIC(TypeMismatch, Error, + "Type mismatch: lhs is {0}, rhs is {1}", + SemanticsNodeId, SemanticsNodeId); + emitter_->Emit(parse_tree_->node_token(parse_node), TypeMismatch, + lhs_type, rhs_type); } return invalid_type; } return lhs_type; } -auto SemanticsParseTreeHandler::AddIdentifier(ParseTree::Node decl_node) - -> SemanticsIdentifierId { - CARBON_CHECK(parse_tree_->node_kind(decl_node) == - ParseNodeKind::DeclaredName()) - << parse_tree_->node_kind(decl_node); - auto text = parse_tree_->GetNodeText(decl_node); - return semantics_->AddIdentifier(text); -} - auto SemanticsParseTreeHandler::HandleAddress(ParseTree::Node /*parse_node*/) -> void { CARBON_FATAL() << "TODO"; @@ -282,13 +339,17 @@ auto SemanticsParseTreeHandler::HandleEmptyDeclaration( } auto SemanticsParseTreeHandler::HandleExpressionStatement( - ParseTree::Node /*parse_node*/) -> void { - CARBON_FATAL() << "TODO"; + ParseTree::Node parse_node) -> void { + // Pop the expression without investigating its contents. + // TODO: This will probably eventually need to do some "do not discard" + // analysis. + PopWithResult(); + Push(parse_node); } auto SemanticsParseTreeHandler::HandleFileEnd(ParseTree::Node /*parse_node*/) -> void { - CARBON_CHECK(node_block_stack_.size() == 1) << node_block_stack_.size(); + // Do nothing, no need to balance this node. } auto SemanticsParseTreeHandler::HandleForHeader(ParseTree::Node /*parse_node*/) @@ -324,6 +385,7 @@ auto SemanticsParseTreeHandler::HandleFunctionDefinition( node_stack_.pop_back(); } Pop(ParseNodeKind::FunctionDefinitionStart()); + PopScope(); node_block_stack_.pop_back(); Push(parse_node); } @@ -332,18 +394,17 @@ auto SemanticsParseTreeHandler::HandleFunctionDefinitionStart( ParseTree::Node parse_node) -> void { Pop(ParseNodeKind::ParameterList()); auto name_node = node_stack_.back().parse_node; - auto name = AddIdentifier(name_node); node_stack_.pop_back(); auto fn_node = node_stack_.back().parse_node; Pop(ParseNodeKind::FunctionIntroducer()); auto decl_id = AddNode(SemanticsNode::MakeFunctionDeclaration(fn_node)); // TODO: Propagate the type of the function. - AddNode(SemanticsNode::MakeBindName(name_node, SemanticsNodeId::MakeInvalid(), - name, decl_id)); + BindName(name_node, SemanticsNodeId::MakeInvalid(), decl_id); auto block_id = semantics_->AddNodeBlock(); AddNode(SemanticsNode::MakeFunctionDefinition(parse_node, decl_id, block_id)); node_block_stack_.push_back(block_id); + PushScope(); Push(parse_node); } @@ -434,9 +495,33 @@ auto SemanticsParseTreeHandler::HandleLiteral(ParseTree::Node parse_node) } } -auto SemanticsParseTreeHandler::HandleNameReference( - ParseTree::Node /*parse_node*/) -> void { - CARBON_FATAL() << "TODO"; +auto SemanticsParseTreeHandler::HandleNameReference(ParseTree::Node parse_node) + -> void { + 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_tree_->node_token(parse_node), NameNotFound, name_str); + Push(parse_node, SemanticsNodeId::MakeBuiltinReference( + SemanticsBuiltinKind::InvalidType())); + }; + + auto name_id = semantics_->GetString(name_str); + if (!name_id) { + name_not_found(); + return; + } + + auto it = name_lookup_.find(*name_id); + if (it == name_lookup_.end()) { + name_not_found(); + return; + } + CARBON_CHECK(!it->second.empty()) << "Should have been erased: " << name_str; + + // TODO: Check for ambiguous lookups. + Push(parse_node, it->second.back()); } auto SemanticsParseTreeHandler::HandlePackageApi(ParseTree::Node /*parse_node*/) @@ -503,14 +588,12 @@ auto SemanticsParseTreeHandler::HandlePatternBinding(ParseTree::Node parse_node) // Get the name. auto name_node = node_stack_.pop_back_val().parse_node; - auto name_id = AddIdentifier(name_node); // Bind the name to storage. - AddNode(SemanticsNode::MakeBindName(name_node, type.result_id, name_id, - storage_id)); + BindName(name_node, type.result_id, storage_id); - // If this node's result is used, it'll be for the storage address, so provide - // that. + // If this node's result is used, it'll be for the storage address, so + // provide that. Push(parse_node, storage_id); } @@ -532,7 +615,7 @@ auto SemanticsParseTreeHandler::HandleReturnStatement( Push(parse_node, SemanticsNode::MakeReturn(parse_node)); } else { auto arg = PopWithResult(); - auto arg_type = semantics_->GetType(node_block_stack_.back(), arg); + auto arg_type = semantics_->GetType(current_block_id(), arg); Pop(ParseNodeKind::ReturnStatementStart()); Push(parse_node, SemanticsNode::MakeReturnExpression(parse_node, arg_type, arg)); diff --git a/toolchain/semantics/semantics_parse_tree_handler.h b/toolchain/semantics/semantics_parse_tree_handler.h index fbc222792deb..25e1824da7de 100644 --- a/toolchain/semantics/semantics_parse_tree_handler.h +++ b/toolchain/semantics/semantics_parse_tree_handler.h @@ -5,6 +5,9 @@ #ifndef CARBON_TOOLCHAIN_SEMANTICS_SEMANTICS_PARSE_TREE_HANDLER_H_ #define CARBON_TOOLCHAIN_SEMANTICS_SEMANTICS_PARSE_TREE_HANDLER_H_ +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/SmallVector.h" #include "toolchain/parser/parse_tree.h" #include "toolchain/semantics/semantics_ir.h" #include "toolchain/semantics/semantics_node.h" @@ -35,20 +38,52 @@ class SemanticsParseTreeHandler { // Prints the node_block_stack_ on stack dumps. class PrettyStackTraceNodeBlockStack; - struct TraversalStackEntry { + // Provides DenseMapInfo for SemanticsStringId. + struct SemanticsStringIdMapInfo { + static inline auto getEmptyKey() -> SemanticsStringId { + return SemanticsStringId(llvm::DenseMapInfo::getEmptyKey()); + } + static inline auto getTombstoneKey() -> SemanticsStringId { + return SemanticsStringId(llvm::DenseMapInfo::getTombstoneKey()); + } + + static auto getHashValue(const SemanticsStringId& val) -> unsigned { + return llvm::DenseMapInfo::getHashValue(val.index); + } + + static auto isEqual(const SemanticsStringId& lhs, + const SemanticsStringId& rhs) -> bool { + return lhs == rhs; + } + }; + + // An entry in node_stack_. + struct NodeStackEntry { ParseTree::Node parse_node; // The result_id may be invalid if there's no result. SemanticsNodeId result_id; }; - static_assert(sizeof(TraversalStackEntry) == 8, - "Unexpected TraversalStackEntry size"); + static_assert(sizeof(NodeStackEntry) == 8, "Unexpected NodeStackEntry size"); - // Adds an identifier for a DeclaredName node, returning its reference. - auto AddIdentifier(ParseTree::Node decl_node) -> SemanticsIdentifierId; + // An entry in scope_stack_. + struct ScopeStackEntry { + // Names which are registered with name_lookup_, and will need to be + // deregistered when the scope ends. + llvm::DenseSet names; + + // TODO: This likely needs to track things which need to be destructed. + }; + + // Adds a cross-reference for a node_id in the current block. + auto AddCrossReference(SemanticsNodeId node_id) -> SemanticsNodeId; // Adds a node to the current block, returning the produced ID. auto AddNode(SemanticsNode node) -> SemanticsNodeId; + // Binds a DeclaredName to a target node with the given type. + auto BindName(ParseTree::Node name_node, SemanticsNodeId type_id, + SemanticsNodeId target_id) -> void; + // Pushes a parse tree node onto the stack. Used when there is no IR generated // by the node. auto Push(ParseTree::Node parse_node) -> void; @@ -76,6 +111,12 @@ class SemanticsParseTreeHandler { auto PopWithResultIf(ParseNodeKind pop_parse_kind) -> std::optional; + // 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; + // Attempts a type conversion between arguments of the two arguments with // provided types, returning the result type. The result type will be invalid // for errors; this handles printing diagnostics. @@ -88,6 +129,12 @@ class SemanticsParseTreeHandler { auto Handle##Name(ParseTree::Node parse_node)->void; #include "toolchain/parser/parse_node_kind.def" + auto current_block_id() -> SemanticsNodeBlockId { + return node_block_stack_.back(); + } + + auto current_scope() -> ScopeStackEntry& { return scope_stack_.back(); } + // Tokens for getting data on literals. const TokenizedBuffer* tokens_; @@ -104,11 +151,24 @@ class SemanticsParseTreeHandler { llvm::raw_ostream* vlog_stream_; // The stack during Build. Will contain file-level parse nodes on return. - llvm::SmallVector node_stack_; + llvm::SmallVector node_stack_; // The stack of node blocks during build. Only updated on ParseTree nodes that // affect the stack. llvm::SmallVector node_block_stack_; + + // A stack for scope context. + llvm::SmallVector scope_stack_; + + // Maps identifiers to name lookup results. Values are a stack of name lookup + // results in the ancestor scopes. This offers constant-time lookup of names, + // regardless of how many scopes exist between the name declaration and + // reference. + // + // Names which no longer have lookup results are erased. + llvm::DenseMap, + SemanticsStringIdMapInfo> + name_lookup_; }; } // namespace Carbon diff --git a/toolchain/semantics/testdata/basics/empty.carbon b/toolchain/semantics/testdata/basics/empty.carbon index e70c818d0d85..7aeabdce219d 100644 --- a/toolchain/semantics/testdata/basics/empty.carbon +++ b/toolchain/semantics/testdata/basics/empty.carbon @@ -11,10 +11,10 @@ // CHECK:STDOUT: node_xref2 = xref(ir0, block0, node2); // CHECK:STDOUT: node_xref3 = xref(ir0, block0, node3); // CHECK:STDOUT: }, -// CHECK:STDOUT: identifiers = { -// CHECK:STDOUT: }, // CHECK:STDOUT: integer_literals = { // CHECK:STDOUT: }, +// CHECK:STDOUT: strings = { +// CHECK:STDOUT: }, // CHECK:STDOUT: node_blocks = { // CHECK:STDOUT: block0 = { // CHECK:STDOUT: }, diff --git a/toolchain/semantics/testdata/basics/empty_decl.carbon b/toolchain/semantics/testdata/basics/empty_decl.carbon index 392c8194518f..8f028feaafa7 100644 --- a/toolchain/semantics/testdata/basics/empty_decl.carbon +++ b/toolchain/semantics/testdata/basics/empty_decl.carbon @@ -11,10 +11,10 @@ // CHECK:STDOUT: node_xref2 = xref(ir0, block0, node2); // CHECK:STDOUT: node_xref3 = xref(ir0, block0, node3); // CHECK:STDOUT: }, -// CHECK:STDOUT: identifiers = { -// CHECK:STDOUT: }, // CHECK:STDOUT: integer_literals = { // CHECK:STDOUT: }, +// CHECK:STDOUT: strings = { +// CHECK:STDOUT: }, // CHECK:STDOUT: node_blocks = { // CHECK:STDOUT: block0 = { // CHECK:STDOUT: }, diff --git a/toolchain/semantics/testdata/basics/fail_name_lookup.carbon b/toolchain/semantics/testdata/basics/fail_name_lookup.carbon new file mode 100644 index 000000000000..66783581f45c --- /dev/null +++ b/toolchain/semantics/testdata/basics/fail_name_lookup.carbon @@ -0,0 +1,33 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE +// RUN: %{not} %{carbon-run-semantics} +// CHECK:STDOUT: cross_reference_irs.size == 2, +// CHECK:STDOUT: cross_references = { +// CHECK:STDOUT: node_xref0 = xref(ir0, block0, node0); +// CHECK:STDOUT: node_xref1 = xref(ir0, block0, node1); +// CHECK:STDOUT: node_xref2 = xref(ir0, block0, node2); +// CHECK:STDOUT: node_xref3 = xref(ir0, block0, node3); +// CHECK:STDOUT: node_xref4 = xref(ir1, block0, node1); +// CHECK:STDOUT: }, +// CHECK:STDOUT: integer_literals = { +// CHECK:STDOUT: }, +// CHECK:STDOUT: strings = { +// CHECK:STDOUT: str0 = "Main"; +// CHECK:STDOUT: }, +// CHECK:STDOUT: node_blocks = { +// CHECK:STDOUT: block0 = { +// CHECK:STDOUT: node0 = FunctionDeclaration(); +// CHECK:STDOUT: node1 = BindName(str0, node0); +// CHECK:STDOUT: node2 = FunctionDefinition(node0, block1); +// CHECK:STDOUT: }, +// CHECK:STDOUT: block1 = { +// CHECK:STDOUT: }, +// CHECK:STDOUT: } + +fn Main() { + // CHECK:STDERR: {{.*}}/toolchain/semantics/testdata/basics/fail_name_lookup.carbon:[[@LINE+1]]:3: Name x not found + x; +} diff --git a/toolchain/semantics/testdata/basics/verbose.carbon b/toolchain/semantics/testdata/basics/verbose.carbon index b24b6b54d33d..669b4784a991 100644 --- a/toolchain/semantics/testdata/basics/verbose.carbon +++ b/toolchain/semantics/testdata/basics/verbose.carbon @@ -7,7 +7,7 @@ // // Only checks a couple statements in order to minimize manual update churn. // CHECK:STDERR: Push 0: FunctionIntroducer -// CHECK:STDERR: AddNode block0: BindName(ident0, node0) +// CHECK:STDERR: AddNode block0: BindName(str0, node0) fn Foo() { return; diff --git a/toolchain/semantics/testdata/function/basic.carbon b/toolchain/semantics/testdata/function/basic.carbon index b1dcca36db35..dfc8638d1376 100644 --- a/toolchain/semantics/testdata/function/basic.carbon +++ b/toolchain/semantics/testdata/function/basic.carbon @@ -10,16 +10,17 @@ // CHECK:STDOUT: node_xref1 = xref(ir0, block0, node1); // CHECK:STDOUT: node_xref2 = xref(ir0, block0, node2); // CHECK:STDOUT: node_xref3 = xref(ir0, block0, node3); -// CHECK:STDOUT: }, -// CHECK:STDOUT: identifiers = { -// CHECK:STDOUT: ident0 = "Foo"; +// CHECK:STDOUT: node_xref4 = xref(ir1, block0, node1); // CHECK:STDOUT: }, // CHECK:STDOUT: integer_literals = { // CHECK:STDOUT: }, +// CHECK:STDOUT: strings = { +// CHECK:STDOUT: str0 = "Foo"; +// CHECK:STDOUT: }, // CHECK:STDOUT: node_blocks = { // CHECK:STDOUT: block0 = { // CHECK:STDOUT: node0 = FunctionDeclaration(); -// CHECK:STDOUT: node1 = BindName(ident0, node0); +// CHECK:STDOUT: node1 = BindName(str0, node0); // CHECK:STDOUT: node2 = FunctionDefinition(node0, block1); // CHECK:STDOUT: }, // CHECK:STDOUT: block1 = { diff --git a/toolchain/semantics/testdata/function/order.carbon b/toolchain/semantics/testdata/function/order.carbon index ea30f23a3384..7a1b04a027b2 100644 --- a/toolchain/semantics/testdata/function/order.carbon +++ b/toolchain/semantics/testdata/function/order.carbon @@ -10,24 +10,27 @@ // CHECK:STDOUT: node_xref1 = xref(ir0, block0, node1); // CHECK:STDOUT: node_xref2 = xref(ir0, block0, node2); // CHECK:STDOUT: node_xref3 = xref(ir0, block0, node3); -// CHECK:STDOUT: }, -// CHECK:STDOUT: identifiers = { -// CHECK:STDOUT: ident0 = "Foo"; -// CHECK:STDOUT: ident1 = "Bar"; -// CHECK:STDOUT: ident2 = "Baz"; +// CHECK:STDOUT: node_xref4 = xref(ir1, block0, node1); +// CHECK:STDOUT: node_xref5 = xref(ir1, block0, node4); +// CHECK:STDOUT: node_xref6 = xref(ir1, block0, node7); // CHECK:STDOUT: }, // CHECK:STDOUT: integer_literals = { // CHECK:STDOUT: }, +// CHECK:STDOUT: strings = { +// CHECK:STDOUT: str0 = "Foo"; +// CHECK:STDOUT: str1 = "Bar"; +// CHECK:STDOUT: str2 = "Baz"; +// CHECK:STDOUT: }, // CHECK:STDOUT: node_blocks = { // CHECK:STDOUT: block0 = { // CHECK:STDOUT: node0 = FunctionDeclaration(); -// CHECK:STDOUT: node1 = BindName(ident0, node0); +// CHECK:STDOUT: node1 = BindName(str0, node0); // CHECK:STDOUT: node2 = FunctionDefinition(node0, block1); // CHECK:STDOUT: node3 = FunctionDeclaration(); -// CHECK:STDOUT: node4 = BindName(ident1, node3); +// CHECK:STDOUT: node4 = BindName(str1, node3); // CHECK:STDOUT: node5 = FunctionDefinition(node3, block2); // CHECK:STDOUT: node6 = FunctionDeclaration(); -// CHECK:STDOUT: node7 = BindName(ident2, node6); +// CHECK:STDOUT: node7 = BindName(str2, node6); // CHECK:STDOUT: node8 = FunctionDefinition(node6, block3); // CHECK:STDOUT: }, // CHECK:STDOUT: block1 = { diff --git a/toolchain/semantics/testdata/operators/binary_op.carbon b/toolchain/semantics/testdata/operators/binary_op.carbon index 3d23d5e1ac93..088b1104cde3 100644 --- a/toolchain/semantics/testdata/operators/binary_op.carbon +++ b/toolchain/semantics/testdata/operators/binary_op.carbon @@ -10,18 +10,19 @@ // CHECK:STDOUT: node_xref1 = xref(ir0, block0, node1); // CHECK:STDOUT: node_xref2 = xref(ir0, block0, node2); // CHECK:STDOUT: node_xref3 = xref(ir0, block0, node3); -// CHECK:STDOUT: }, -// CHECK:STDOUT: identifiers = { -// CHECK:STDOUT: ident0 = "Main"; +// CHECK:STDOUT: node_xref4 = xref(ir1, block0, node1); // CHECK:STDOUT: }, // CHECK:STDOUT: integer_literals = { // CHECK:STDOUT: int0 = 12; // CHECK:STDOUT: int1 = 34; // CHECK:STDOUT: }, +// CHECK:STDOUT: strings = { +// CHECK:STDOUT: str0 = "Main"; +// CHECK:STDOUT: }, // CHECK:STDOUT: node_blocks = { // CHECK:STDOUT: block0 = { // CHECK:STDOUT: node0 = FunctionDeclaration(); -// CHECK:STDOUT: node1 = BindName(ident0, node0); +// CHECK:STDOUT: node1 = BindName(str0, node0); // CHECK:STDOUT: node2 = FunctionDefinition(node0, block1); // CHECK:STDOUT: }, // CHECK:STDOUT: block1 = { diff --git a/toolchain/semantics/testdata/operators/fail_type_mismatch.carbon b/toolchain/semantics/testdata/operators/fail_type_mismatch.carbon index b9af16e205d7..fee3d99b1b0f 100644 --- a/toolchain/semantics/testdata/operators/fail_type_mismatch.carbon +++ b/toolchain/semantics/testdata/operators/fail_type_mismatch.carbon @@ -10,17 +10,18 @@ // CHECK:STDOUT: node_xref1 = xref(ir0, block0, node1); // CHECK:STDOUT: node_xref2 = xref(ir0, block0, node2); // CHECK:STDOUT: node_xref3 = xref(ir0, block0, node3); -// CHECK:STDOUT: }, -// CHECK:STDOUT: identifiers = { -// CHECK:STDOUT: ident0 = "Main"; +// CHECK:STDOUT: node_xref4 = xref(ir1, block0, node1); // CHECK:STDOUT: }, // CHECK:STDOUT: integer_literals = { // CHECK:STDOUT: int0 = 12; // CHECK:STDOUT: }, +// CHECK:STDOUT: strings = { +// CHECK:STDOUT: str0 = "Main"; +// CHECK:STDOUT: }, // CHECK:STDOUT: node_blocks = { // CHECK:STDOUT: block0 = { // CHECK:STDOUT: node0 = FunctionDeclaration(); -// CHECK:STDOUT: node1 = BindName(ident0, node0); +// CHECK:STDOUT: node1 = BindName(str0, node0); // CHECK:STDOUT: node2 = FunctionDefinition(node0, block1); // CHECK:STDOUT: }, // CHECK:STDOUT: block1 = { @@ -32,6 +33,6 @@ // CHECK:STDOUT: } fn Main() { - // CHECK:STDERR: {{.*}}/toolchain/semantics/testdata/operators/fail_type_mismatch.carbon:[[@LINE+1]]:13: Type mismatch + // CHECK:STDERR: {{.*}}/toolchain/semantics/testdata/operators/fail_type_mismatch.carbon:[[@LINE+1]]:13: Type mismatch: lhs is node_xref2, rhs is node_xref3 return 12 + 3.4; } diff --git a/toolchain/semantics/testdata/operators/fail_type_mismatch_once.carbon b/toolchain/semantics/testdata/operators/fail_type_mismatch_once.carbon index 98d8375ab59e..0e42359964a1 100644 --- a/toolchain/semantics/testdata/operators/fail_type_mismatch_once.carbon +++ b/toolchain/semantics/testdata/operators/fail_type_mismatch_once.carbon @@ -10,18 +10,19 @@ // CHECK:STDOUT: node_xref1 = xref(ir0, block0, node1); // CHECK:STDOUT: node_xref2 = xref(ir0, block0, node2); // CHECK:STDOUT: node_xref3 = xref(ir0, block0, node3); -// CHECK:STDOUT: }, -// CHECK:STDOUT: identifiers = { -// CHECK:STDOUT: ident0 = "Main"; +// CHECK:STDOUT: node_xref4 = xref(ir1, block0, node1); // CHECK:STDOUT: }, // CHECK:STDOUT: integer_literals = { // CHECK:STDOUT: int0 = 12; // CHECK:STDOUT: int1 = 12; // CHECK:STDOUT: }, +// CHECK:STDOUT: strings = { +// CHECK:STDOUT: str0 = "Main"; +// CHECK:STDOUT: }, // CHECK:STDOUT: node_blocks = { // CHECK:STDOUT: block0 = { // CHECK:STDOUT: node0 = FunctionDeclaration(); -// CHECK:STDOUT: node1 = BindName(ident0, node0); +// CHECK:STDOUT: node1 = BindName(str0, node0); // CHECK:STDOUT: node2 = FunctionDefinition(node0, block1); // CHECK:STDOUT: }, // CHECK:STDOUT: block1 = { @@ -37,6 +38,6 @@ fn Main() { // The following line has two mismatches, but after the first, it shouldn't // keep erroring. - // CHECK:STDERR: {{.*}}/toolchain/semantics/testdata/operators/fail_type_mismatch_once.carbon:[[@LINE+1]]:13: Type mismatch + // CHECK:STDERR: {{.*}}/toolchain/semantics/testdata/operators/fail_type_mismatch_once.carbon:[[@LINE+1]]:13: Type mismatch: lhs is node_xref2, rhs is node_xref3 return 12 + 3.4 + 12; } diff --git a/toolchain/semantics/testdata/return/literal.carbon b/toolchain/semantics/testdata/return/literal.carbon index fb88269c8f79..c1ea28edb58e 100644 --- a/toolchain/semantics/testdata/return/literal.carbon +++ b/toolchain/semantics/testdata/return/literal.carbon @@ -10,17 +10,18 @@ // CHECK:STDOUT: node_xref1 = xref(ir0, block0, node1); // CHECK:STDOUT: node_xref2 = xref(ir0, block0, node2); // CHECK:STDOUT: node_xref3 = xref(ir0, block0, node3); -// CHECK:STDOUT: }, -// CHECK:STDOUT: identifiers = { -// CHECK:STDOUT: ident0 = "Main"; +// CHECK:STDOUT: node_xref4 = xref(ir1, block0, node1); // CHECK:STDOUT: }, // CHECK:STDOUT: integer_literals = { // CHECK:STDOUT: int0 = 0; // CHECK:STDOUT: }, +// CHECK:STDOUT: strings = { +// CHECK:STDOUT: str0 = "Main"; +// CHECK:STDOUT: }, // CHECK:STDOUT: node_blocks = { // CHECK:STDOUT: block0 = { // CHECK:STDOUT: node0 = FunctionDeclaration(); -// CHECK:STDOUT: node1 = BindName(ident0, node0); +// CHECK:STDOUT: node1 = BindName(str0, node0); // CHECK:STDOUT: node2 = FunctionDefinition(node0, block1); // CHECK:STDOUT: }, // CHECK:STDOUT: block1 = { diff --git a/toolchain/semantics/testdata/return/trivial.carbon b/toolchain/semantics/testdata/return/trivial.carbon index 6f87dcc4dee7..373236d32bd2 100644 --- a/toolchain/semantics/testdata/return/trivial.carbon +++ b/toolchain/semantics/testdata/return/trivial.carbon @@ -10,16 +10,17 @@ // CHECK:STDOUT: node_xref1 = xref(ir0, block0, node1); // CHECK:STDOUT: node_xref2 = xref(ir0, block0, node2); // CHECK:STDOUT: node_xref3 = xref(ir0, block0, node3); -// CHECK:STDOUT: }, -// CHECK:STDOUT: identifiers = { -// CHECK:STDOUT: ident0 = "Main"; +// CHECK:STDOUT: node_xref4 = xref(ir1, block0, node1); // CHECK:STDOUT: }, // CHECK:STDOUT: integer_literals = { // CHECK:STDOUT: }, +// CHECK:STDOUT: strings = { +// CHECK:STDOUT: str0 = "Main"; +// CHECK:STDOUT: }, // CHECK:STDOUT: node_blocks = { // CHECK:STDOUT: block0 = { // CHECK:STDOUT: node0 = FunctionDeclaration(); -// CHECK:STDOUT: node1 = BindName(ident0, node0); +// CHECK:STDOUT: node1 = BindName(str0, node0); // CHECK:STDOUT: node2 = FunctionDefinition(node0, block1); // CHECK:STDOUT: }, // CHECK:STDOUT: block1 = { diff --git a/toolchain/semantics/testdata/var/decl.carbon b/toolchain/semantics/testdata/var/decl.carbon index 020b59c0ce73..21101f4c014c 100644 --- a/toolchain/semantics/testdata/var/decl.carbon +++ b/toolchain/semantics/testdata/var/decl.carbon @@ -10,22 +10,24 @@ // CHECK:STDOUT: node_xref1 = xref(ir0, block0, node1); // CHECK:STDOUT: node_xref2 = xref(ir0, block0, node2); // CHECK:STDOUT: node_xref3 = xref(ir0, block0, node3); -// CHECK:STDOUT: }, -// CHECK:STDOUT: identifiers = { -// CHECK:STDOUT: ident0 = "Main"; -// CHECK:STDOUT: ident1 = "x"; +// CHECK:STDOUT: node_xref4 = xref(ir1, block0, node1); +// CHECK:STDOUT: node_xref5 = xref(ir1, block1, node1); // CHECK:STDOUT: }, // CHECK:STDOUT: integer_literals = { // CHECK:STDOUT: }, +// CHECK:STDOUT: strings = { +// CHECK:STDOUT: str0 = "Main"; +// CHECK:STDOUT: str1 = "x"; +// CHECK:STDOUT: }, // CHECK:STDOUT: node_blocks = { // CHECK:STDOUT: block0 = { // CHECK:STDOUT: node0 = FunctionDeclaration(); -// CHECK:STDOUT: node1 = BindName(ident0, node0); +// CHECK:STDOUT: node1 = BindName(str0, node0); // CHECK:STDOUT: node2 = FunctionDefinition(node0, block1); // CHECK:STDOUT: }, // CHECK:STDOUT: block1 = { // CHECK:STDOUT: node0 = VarStorage(): node_xref2; -// CHECK:STDOUT: node1 = BindName(ident1, node0): node_xref2; +// CHECK:STDOUT: node1 = BindName(str1, node0): node_xref2; // CHECK:STDOUT: }, // CHECK:STDOUT: } diff --git a/toolchain/semantics/testdata/var/decl_with_init.carbon b/toolchain/semantics/testdata/var/decl_with_init.carbon index c44551c6b87c..efab87126d14 100644 --- a/toolchain/semantics/testdata/var/decl_with_init.carbon +++ b/toolchain/semantics/testdata/var/decl_with_init.carbon @@ -10,23 +10,25 @@ // CHECK:STDOUT: node_xref1 = xref(ir0, block0, node1); // CHECK:STDOUT: node_xref2 = xref(ir0, block0, node2); // CHECK:STDOUT: node_xref3 = xref(ir0, block0, node3); -// CHECK:STDOUT: }, -// CHECK:STDOUT: identifiers = { -// CHECK:STDOUT: ident0 = "Main"; -// CHECK:STDOUT: ident1 = "x"; +// CHECK:STDOUT: node_xref4 = xref(ir1, block0, node1); +// CHECK:STDOUT: node_xref5 = xref(ir1, block1, node1); // CHECK:STDOUT: }, // CHECK:STDOUT: integer_literals = { // CHECK:STDOUT: int0 = 0; // CHECK:STDOUT: }, +// CHECK:STDOUT: strings = { +// CHECK:STDOUT: str0 = "Main"; +// CHECK:STDOUT: str1 = "x"; +// CHECK:STDOUT: }, // CHECK:STDOUT: node_blocks = { // CHECK:STDOUT: block0 = { // CHECK:STDOUT: node0 = FunctionDeclaration(); -// CHECK:STDOUT: node1 = BindName(ident0, node0); +// CHECK:STDOUT: node1 = BindName(str0, node0); // CHECK:STDOUT: node2 = FunctionDefinition(node0, block1); // CHECK:STDOUT: }, // CHECK:STDOUT: block1 = { // CHECK:STDOUT: node0 = VarStorage(): node_xref2; -// CHECK:STDOUT: node1 = BindName(ident1, node0): node_xref2; +// CHECK:STDOUT: node1 = BindName(str1, node0): node_xref2; // CHECK:STDOUT: node2 = IntegerLiteral(int0): node_xref2; // CHECK:STDOUT: node3 = Assign(node0, node2): node_xref2; // CHECK:STDOUT: }, diff --git a/toolchain/semantics/testdata/var/fail_duplicate_decl.carbon b/toolchain/semantics/testdata/var/fail_duplicate_decl.carbon new file mode 100644 index 000000000000..2a74d5cceb33 --- /dev/null +++ b/toolchain/semantics/testdata/var/fail_duplicate_decl.carbon @@ -0,0 +1,48 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE +// RUN: %{not} %{carbon-run-semantics} +// CHECK:STDOUT: cross_reference_irs.size == 2, +// CHECK:STDOUT: cross_references = { +// CHECK:STDOUT: node_xref0 = xref(ir0, block0, node0); +// CHECK:STDOUT: node_xref1 = xref(ir0, block0, node1); +// CHECK:STDOUT: node_xref2 = xref(ir0, block0, node2); +// CHECK:STDOUT: node_xref3 = xref(ir0, block0, node3); +// CHECK:STDOUT: node_xref4 = xref(ir1, block0, node1); +// CHECK:STDOUT: node_xref5 = xref(ir1, block1, node1); +// CHECK:STDOUT: }, +// CHECK:STDOUT: integer_literals = { +// CHECK:STDOUT: int0 = 0; +// CHECK:STDOUT: int1 = 0; +// CHECK:STDOUT: }, +// CHECK:STDOUT: strings = { +// CHECK:STDOUT: str0 = "Main"; +// CHECK:STDOUT: str1 = "x"; +// CHECK:STDOUT: }, +// CHECK:STDOUT: node_blocks = { +// CHECK:STDOUT: block0 = { +// CHECK:STDOUT: node0 = FunctionDeclaration(); +// CHECK:STDOUT: node1 = BindName(str0, node0); +// CHECK:STDOUT: node2 = FunctionDefinition(node0, block1); +// CHECK:STDOUT: }, +// CHECK:STDOUT: block1 = { +// CHECK:STDOUT: node0 = VarStorage(): node_xref2; +// CHECK:STDOUT: node1 = BindName(str1, node0): node_xref2; +// CHECK:STDOUT: node2 = IntegerLiteral(int0): node_xref2; +// CHECK:STDOUT: node3 = Assign(node0, node2): node_xref2; +// CHECK:STDOUT: node4 = VarStorage(): node_xref2; +// CHECK:STDOUT: node5 = BindName(str1, node4): node_xref2; +// CHECK:STDOUT: node6 = IntegerLiteral(int1): node_xref2; +// CHECK:STDOUT: node7 = Assign(node4, node6): node_xref2; +// CHECK:STDOUT: }, +// CHECK:STDOUT: } + + +fn Main() { + // CHECK:STDERR: {{.*}}/toolchain/semantics/testdata/var/fail_duplicate_decl.carbon:[[@LINE+1]]:7: Previous definition is here. + var x: i32 = 0; + // CHECK:STDERR: {{.*}}/toolchain/semantics/testdata/var/fail_duplicate_decl.carbon:[[@LINE+1]]:7: Redefining x in the same scope. + var x: i32 = 0; +} diff --git a/toolchain/semantics/testdata/var/fail_init_type_mismatch.carbon b/toolchain/semantics/testdata/var/fail_init_type_mismatch.carbon index a1865da743a7..ba2dce8c3973 100644 --- a/toolchain/semantics/testdata/var/fail_init_type_mismatch.carbon +++ b/toolchain/semantics/testdata/var/fail_init_type_mismatch.carbon @@ -10,28 +10,30 @@ // CHECK:STDOUT: node_xref1 = xref(ir0, block0, node1); // CHECK:STDOUT: node_xref2 = xref(ir0, block0, node2); // CHECK:STDOUT: node_xref3 = xref(ir0, block0, node3); -// CHECK:STDOUT: }, -// CHECK:STDOUT: identifiers = { -// CHECK:STDOUT: ident0 = "Main"; -// CHECK:STDOUT: ident1 = "x"; +// CHECK:STDOUT: node_xref4 = xref(ir1, block0, node1); +// CHECK:STDOUT: node_xref5 = xref(ir1, block1, node1); // CHECK:STDOUT: }, // CHECK:STDOUT: integer_literals = { // CHECK:STDOUT: }, +// CHECK:STDOUT: strings = { +// CHECK:STDOUT: str0 = "Main"; +// CHECK:STDOUT: str1 = "x"; +// CHECK:STDOUT: }, // CHECK:STDOUT: node_blocks = { // CHECK:STDOUT: block0 = { // CHECK:STDOUT: node0 = FunctionDeclaration(); -// CHECK:STDOUT: node1 = BindName(ident0, node0); +// CHECK:STDOUT: node1 = BindName(str0, node0); // CHECK:STDOUT: node2 = FunctionDefinition(node0, block1); // CHECK:STDOUT: }, // CHECK:STDOUT: block1 = { // CHECK:STDOUT: node0 = VarStorage(): node_xref2; -// CHECK:STDOUT: node1 = BindName(ident1, node0): node_xref2; +// CHECK:STDOUT: node1 = BindName(str1, node0): node_xref2; // CHECK:STDOUT: node2 = RealLiteral(): node_xref3; // CHECK:STDOUT: node3 = Assign(node0, node2): node_xref1; // CHECK:STDOUT: }, // CHECK:STDOUT: } fn Main() { - // CHECK:STDERR: {{.*}}/toolchain/semantics/testdata/var/fail_init_type_mismatch.carbon:[[@LINE+1]]:19: Type mismatch + // CHECK:STDERR: {{.*}}/toolchain/semantics/testdata/var/fail_init_type_mismatch.carbon:[[@LINE+1]]:19: Type mismatch: lhs is node_xref2, rhs is node_xref3 var x: i32 = 1.0; } diff --git a/toolchain/semantics/testdata/var/global_decl.carbon b/toolchain/semantics/testdata/var/global_decl.carbon index 66ab5284e205..1bbc94a292a7 100644 --- a/toolchain/semantics/testdata/var/global_decl.carbon +++ b/toolchain/semantics/testdata/var/global_decl.carbon @@ -10,16 +10,17 @@ // CHECK:STDOUT: node_xref1 = xref(ir0, block0, node1); // CHECK:STDOUT: node_xref2 = xref(ir0, block0, node2); // CHECK:STDOUT: node_xref3 = xref(ir0, block0, node3); -// CHECK:STDOUT: }, -// CHECK:STDOUT: identifiers = { -// CHECK:STDOUT: ident0 = "x"; +// CHECK:STDOUT: node_xref4 = xref(ir1, block0, node1); // CHECK:STDOUT: }, // CHECK:STDOUT: integer_literals = { // CHECK:STDOUT: }, +// CHECK:STDOUT: strings = { +// CHECK:STDOUT: str0 = "x"; +// CHECK:STDOUT: }, // CHECK:STDOUT: node_blocks = { // CHECK:STDOUT: block0 = { // CHECK:STDOUT: node0 = VarStorage(): node_xref2; -// CHECK:STDOUT: node1 = BindName(ident0, node0): node_xref2; +// CHECK:STDOUT: node1 = BindName(str0, node0): node_xref2; // CHECK:STDOUT: }, // CHECK:STDOUT: } diff --git a/toolchain/semantics/testdata/var/global_decl_with_init.carbon b/toolchain/semantics/testdata/var/global_decl_with_init.carbon index 8fbbb75aedbe..4b374b5b8042 100644 --- a/toolchain/semantics/testdata/var/global_decl_with_init.carbon +++ b/toolchain/semantics/testdata/var/global_decl_with_init.carbon @@ -10,17 +10,18 @@ // CHECK:STDOUT: node_xref1 = xref(ir0, block0, node1); // CHECK:STDOUT: node_xref2 = xref(ir0, block0, node2); // CHECK:STDOUT: node_xref3 = xref(ir0, block0, node3); -// CHECK:STDOUT: }, -// CHECK:STDOUT: identifiers = { -// CHECK:STDOUT: ident0 = "x"; +// CHECK:STDOUT: node_xref4 = xref(ir1, block0, node1); // CHECK:STDOUT: }, // CHECK:STDOUT: integer_literals = { // CHECK:STDOUT: int0 = 0; // CHECK:STDOUT: }, +// CHECK:STDOUT: strings = { +// CHECK:STDOUT: str0 = "x"; +// CHECK:STDOUT: }, // CHECK:STDOUT: node_blocks = { // CHECK:STDOUT: block0 = { // CHECK:STDOUT: node0 = VarStorage(): node_xref2; -// CHECK:STDOUT: node1 = BindName(ident0, node0): node_xref2; +// CHECK:STDOUT: node1 = BindName(str0, node0): node_xref2; // CHECK:STDOUT: node2 = IntegerLiteral(int0): node_xref2; // CHECK:STDOUT: node3 = Assign(node0, node2): node_xref2; // CHECK:STDOUT: }, diff --git a/toolchain/semantics/testdata/var/global_lookup.carbon b/toolchain/semantics/testdata/var/global_lookup.carbon new file mode 100644 index 000000000000..17cfe5a09696 --- /dev/null +++ b/toolchain/semantics/testdata/var/global_lookup.carbon @@ -0,0 +1,36 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE +// RUN: %{carbon-run-semantics} +// CHECK:STDOUT: cross_reference_irs.size == 2, +// CHECK:STDOUT: cross_references = { +// CHECK:STDOUT: node_xref0 = xref(ir0, block0, node0); +// CHECK:STDOUT: node_xref1 = xref(ir0, block0, node1); +// CHECK:STDOUT: node_xref2 = xref(ir0, block0, node2); +// CHECK:STDOUT: node_xref3 = xref(ir0, block0, node3); +// CHECK:STDOUT: node_xref4 = xref(ir1, block0, node1); +// CHECK:STDOUT: node_xref5 = xref(ir1, block0, node5); +// CHECK:STDOUT: }, +// CHECK:STDOUT: integer_literals = { +// CHECK:STDOUT: int0 = 0; +// CHECK:STDOUT: }, +// CHECK:STDOUT: strings = { +// CHECK:STDOUT: str0 = "x"; +// CHECK:STDOUT: str1 = "y"; +// CHECK:STDOUT: }, +// CHECK:STDOUT: node_blocks = { +// CHECK:STDOUT: block0 = { +// CHECK:STDOUT: node0 = VarStorage(): node_xref2; +// CHECK:STDOUT: node1 = BindName(str0, node0): node_xref2; +// CHECK:STDOUT: node2 = IntegerLiteral(int0): node_xref2; +// CHECK:STDOUT: node3 = Assign(node0, node2): node_xref2; +// CHECK:STDOUT: node4 = VarStorage(): node_xref2; +// CHECK:STDOUT: node5 = BindName(str1, node4): node_xref2; +// CHECK:STDOUT: node6 = Assign(node4, node_xref4): node_xref2; +// CHECK:STDOUT: }, +// CHECK:STDOUT: } + +var x: i32 = 0; +var y: i32 = x; diff --git a/toolchain/semantics/testdata/var/global_lookup_in_scope.carbon b/toolchain/semantics/testdata/var/global_lookup_in_scope.carbon new file mode 100644 index 000000000000..8d2bd2ab4542 --- /dev/null +++ b/toolchain/semantics/testdata/var/global_lookup_in_scope.carbon @@ -0,0 +1,46 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE +// RUN: %{carbon-run-semantics} +// CHECK:STDOUT: cross_reference_irs.size == 2, +// CHECK:STDOUT: cross_references = { +// CHECK:STDOUT: node_xref0 = xref(ir0, block0, node0); +// CHECK:STDOUT: node_xref1 = xref(ir0, block0, node1); +// CHECK:STDOUT: node_xref2 = xref(ir0, block0, node2); +// CHECK:STDOUT: node_xref3 = xref(ir0, block0, node3); +// CHECK:STDOUT: node_xref4 = xref(ir1, block0, node1); +// CHECK:STDOUT: node_xref5 = xref(ir1, block0, node5); +// CHECK:STDOUT: node_xref6 = xref(ir1, block1, node1); +// CHECK:STDOUT: }, +// CHECK:STDOUT: integer_literals = { +// CHECK:STDOUT: int0 = 0; +// CHECK:STDOUT: }, +// CHECK:STDOUT: strings = { +// CHECK:STDOUT: str0 = "x"; +// CHECK:STDOUT: str1 = "Main"; +// CHECK:STDOUT: str2 = "y"; +// CHECK:STDOUT: }, +// CHECK:STDOUT: node_blocks = { +// CHECK:STDOUT: block0 = { +// CHECK:STDOUT: node0 = VarStorage(): node_xref2; +// CHECK:STDOUT: node1 = BindName(str0, node0): node_xref2; +// CHECK:STDOUT: node2 = IntegerLiteral(int0): node_xref2; +// CHECK:STDOUT: node3 = Assign(node0, node2): node_xref2; +// CHECK:STDOUT: node4 = FunctionDeclaration(); +// CHECK:STDOUT: node5 = BindName(str1, node4); +// CHECK:STDOUT: node6 = FunctionDefinition(node4, block1); +// CHECK:STDOUT: }, +// CHECK:STDOUT: block1 = { +// CHECK:STDOUT: node0 = VarStorage(): node_xref2; +// CHECK:STDOUT: node1 = BindName(str2, node0): node_xref2; +// CHECK:STDOUT: node2 = Assign(node0, node_xref4): node_xref2; +// CHECK:STDOUT: }, +// CHECK:STDOUT: } + +var x: i32 = 0; + +fn Main() { + var y: i32 = x; +} diff --git a/toolchain/semantics/testdata/var/lookup.carbon b/toolchain/semantics/testdata/var/lookup.carbon new file mode 100644 index 000000000000..7409038856ac --- /dev/null +++ b/toolchain/semantics/testdata/var/lookup.carbon @@ -0,0 +1,40 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE +// RUN: %{carbon-run-semantics} +// CHECK:STDOUT: cross_reference_irs.size == 2, +// CHECK:STDOUT: cross_references = { +// CHECK:STDOUT: node_xref0 = xref(ir0, block0, node0); +// CHECK:STDOUT: node_xref1 = xref(ir0, block0, node1); +// CHECK:STDOUT: node_xref2 = xref(ir0, block0, node2); +// CHECK:STDOUT: node_xref3 = xref(ir0, block0, node3); +// CHECK:STDOUT: node_xref4 = xref(ir1, block0, node1); +// CHECK:STDOUT: node_xref5 = xref(ir1, block1, node1); +// CHECK:STDOUT: }, +// CHECK:STDOUT: integer_literals = { +// CHECK:STDOUT: int0 = 0; +// CHECK:STDOUT: }, +// CHECK:STDOUT: strings = { +// CHECK:STDOUT: str0 = "Main"; +// CHECK:STDOUT: str1 = "x"; +// CHECK:STDOUT: }, +// CHECK:STDOUT: node_blocks = { +// CHECK:STDOUT: block0 = { +// CHECK:STDOUT: node0 = FunctionDeclaration(); +// CHECK:STDOUT: node1 = BindName(str0, node0); +// CHECK:STDOUT: node2 = FunctionDefinition(node0, block1); +// CHECK:STDOUT: }, +// CHECK:STDOUT: block1 = { +// CHECK:STDOUT: node0 = VarStorage(): node_xref2; +// CHECK:STDOUT: node1 = BindName(str1, node0): node_xref2; +// CHECK:STDOUT: node2 = IntegerLiteral(int0): node_xref2; +// CHECK:STDOUT: node3 = Assign(node0, node2): node_xref2; +// CHECK:STDOUT: }, +// CHECK:STDOUT: } + +fn Main() { + var x: i32 = 0; + x; +} diff --git a/toolchain/semantics/testdata/var/todo_bad_init_with_self.carbon b/toolchain/semantics/testdata/var/todo_bad_init_with_self.carbon new file mode 100644 index 000000000000..0a9a0f18b21b --- /dev/null +++ b/toolchain/semantics/testdata/var/todo_bad_init_with_self.carbon @@ -0,0 +1,38 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE +// RUN: %{carbon-run-semantics} +// CHECK:STDOUT: cross_reference_irs.size == 2, +// CHECK:STDOUT: cross_references = { +// CHECK:STDOUT: node_xref0 = xref(ir0, block0, node0); +// CHECK:STDOUT: node_xref1 = xref(ir0, block0, node1); +// CHECK:STDOUT: node_xref2 = xref(ir0, block0, node2); +// CHECK:STDOUT: node_xref3 = xref(ir0, block0, node3); +// CHECK:STDOUT: node_xref4 = xref(ir1, block0, node1); +// CHECK:STDOUT: node_xref5 = xref(ir1, block1, node1); +// CHECK:STDOUT: }, +// CHECK:STDOUT: integer_literals = { +// CHECK:STDOUT: }, +// CHECK:STDOUT: strings = { +// CHECK:STDOUT: str0 = "Main"; +// CHECK:STDOUT: str1 = "x"; +// CHECK:STDOUT: }, +// CHECK:STDOUT: node_blocks = { +// CHECK:STDOUT: block0 = { +// CHECK:STDOUT: node0 = FunctionDeclaration(); +// CHECK:STDOUT: node1 = BindName(str0, node0); +// CHECK:STDOUT: node2 = FunctionDefinition(node0, block1); +// CHECK:STDOUT: }, +// CHECK:STDOUT: block1 = { +// CHECK:STDOUT: node0 = VarStorage(): node_xref2; +// CHECK:STDOUT: node1 = BindName(str1, node0): node_xref2; +// CHECK:STDOUT: node2 = Assign(node0, node_xref5): node_xref2; +// CHECK:STDOUT: }, +// CHECK:STDOUT: } + +fn Main() { + // TODO: This should fail with `x`... uninitialized? Not yet declared? + var x: i32 = x; +} diff --git a/toolchain/semantics/testdata/var/todo_bad_lookup_outside_scope.carbon b/toolchain/semantics/testdata/var/todo_bad_lookup_outside_scope.carbon new file mode 100644 index 000000000000..c91319b04a1b --- /dev/null +++ b/toolchain/semantics/testdata/var/todo_bad_lookup_outside_scope.carbon @@ -0,0 +1,41 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE +// RUN: %{carbon-run-semantics} +// CHECK:STDOUT: cross_reference_irs.size == 2, +// CHECK:STDOUT: cross_references = { +// CHECK:STDOUT: node_xref0 = xref(ir0, block0, node0); +// CHECK:STDOUT: node_xref1 = xref(ir0, block0, node1); +// CHECK:STDOUT: node_xref2 = xref(ir0, block0, node2); +// CHECK:STDOUT: node_xref3 = xref(ir0, block0, node3); +// CHECK:STDOUT: node_xref4 = xref(ir1, block0, node1); +// CHECK:STDOUT: node_xref5 = xref(ir1, block1, node1); +// CHECK:STDOUT: }, +// CHECK:STDOUT: integer_literals = { +// CHECK:STDOUT: }, +// CHECK:STDOUT: strings = { +// CHECK:STDOUT: str0 = "Main"; +// CHECK:STDOUT: str1 = "x"; +// CHECK:STDOUT: }, +// CHECK:STDOUT: node_blocks = { +// CHECK:STDOUT: block0 = { +// CHECK:STDOUT: node0 = FunctionDeclaration(); +// CHECK:STDOUT: node1 = BindName(str0, node0); +// CHECK:STDOUT: node2 = FunctionDefinition(node0, block1); +// CHECK:STDOUT: }, +// CHECK:STDOUT: block1 = { +// CHECK:STDOUT: node0 = VarStorage(): node_xref2; +// CHECK:STDOUT: node1 = BindName(str1, node0): node_xref2; +// CHECK:STDOUT: }, +// CHECK:STDOUT: } + +fn Main() { + var x: i32; +} + +// TODO: This is a crash because the type of `x` is incorrect. That needs to be +// fixed, but it crashes due to cross-reference handling which I intend to +// rewrite, so leaving this as a TODO. +// var y: i32 = x;