From 4c8fdf5124038cbf56a338cb4e279e98770073f6 Mon Sep 17 00:00:00 2001 From: Jon Ross-Perkins Date: Thu, 17 Nov 2022 13:36:40 -0800 Subject: [PATCH] Start drafting out semantic type checking. (#2406) This is a first pass at what semantic type checking might look like. Types propagate along nodes, we use an InvalidType object when there's an error, and once there's an InvalidType we stop doing so much type checking. This adds some RealLiteral handling in order to get type mismatches. I'm cautious about creating some real value for SemanticsIR (since the tokenized buffer version is a bit constrained), so I'm not doing that yet. But I will probably need to in order to maintain SemanticsIR having hermetic copies of its data, without a parse tree dependency. --- toolchain/diagnostics/diagnostic_registry.def | 6 ++ toolchain/driver/driver.cpp | 2 +- toolchain/lexer/numeric_literal.h | 2 +- toolchain/lexer/tokenized_buffer.cpp | 6 +- toolchain/lexer/tokenized_buffer.h | 12 ++-- toolchain/parser/parse_tree.cpp | 2 +- toolchain/semantics/BUILD | 1 + toolchain/semantics/lit_autoupdate.py | 4 +- .../semantics/semantics_builtin_kind.def | 10 ++++ toolchain/semantics/semantics_ir.cpp | 47 ++++++++++----- toolchain/semantics/semantics_ir.h | 39 +++++++++++++ toolchain/semantics/semantics_node.h | 58 +++++++++++++++++-- toolchain/semantics/semantics_node_kind.def | 1 + .../semantics_parse_tree_handler.cpp | 31 +++++++++- .../semantics/semantics_parse_tree_handler.h | 5 ++ .../semantics/testdata/basics/empty.carbon | 6 +- .../semantics/testdata/function/basic.carbon | 6 +- .../semantics/testdata/function/order.carbon | 6 +- .../{return => operators}/binary_op.carbon | 14 +++-- .../testdata/operators/type_mismatch.carbon | 37 ++++++++++++ .../operators/type_mismatch_once.carbon | 42 ++++++++++++++ .../semantics/testdata/return/literal.carbon | 10 ++-- .../semantics/testdata/return/trivial.carbon | 6 +- 23 files changed, 298 insertions(+), 55 deletions(-) rename toolchain/semantics/testdata/{return => operators}/binary_op.carbon (64%) create mode 100644 toolchain/semantics/testdata/operators/type_mismatch.carbon create mode 100644 toolchain/semantics/testdata/operators/type_mismatch_once.carbon diff --git a/toolchain/diagnostics/diagnostic_registry.def b/toolchain/diagnostics/diagnostic_registry.def index 14103ce4e379..3a209faacff9 100644 --- a/toolchain/diagnostics/diagnostic_registry.def +++ b/toolchain/diagnostics/diagnostic_registry.def @@ -78,6 +78,12 @@ CARBON_DIAGNOSTIC_KIND(ExpectedSemiToEndPackageDirective) // For-specific diagnostics CARBON_DIAGNOSTIC_KIND(ExpectedIn) +// ============================================================================ +// Semantics diagnostics +// ============================================================================ + +CARBON_DIAGNOSTIC_KIND(TypeMismatch) + // ============================================================================ // Other diagnostics // ============================================================================ diff --git a/toolchain/driver/driver.cpp b/toolchain/driver/driver.cpp index 68728a749083..dcfd9efe9d8a 100644 --- a/toolchain/driver/driver.cpp +++ b/toolchain/driver/driver.cpp @@ -175,7 +175,7 @@ auto Driver::RunDumpSubcommand(DiagnosticConsumer& consumer, const SemanticsIR builtin_ir = SemanticsIR::MakeBuiltinIR(); const SemanticsIR semantics_ir = SemanticsIR::MakeFromParseTree( - builtin_ir, tokenized_source, parse_tree, vlog_stream_); + builtin_ir, tokenized_source, parse_tree, consumer, vlog_stream_); if (dump_mode == DumpMode::SemanticsIR) { consumer.Flush(); semantics_ir.Print(output_stream_); diff --git a/toolchain/lexer/numeric_literal.h b/toolchain/lexer/numeric_literal.h index 52fb693b86d5..e34f81a9d664 100644 --- a/toolchain/lexer/numeric_literal.h +++ b/toolchain/lexer/numeric_literal.h @@ -32,7 +32,7 @@ class LexedNumericLiteral { Radix radix; // The mantissa, represented as a variable-width unsigned integer. llvm::APInt mantissa; - // The exponent, represented as a variable-width signed integer.. + // The exponent, represented as a variable-width signed integer. llvm::APInt exponent; }; diff --git a/toolchain/lexer/tokenized_buffer.cpp b/toolchain/lexer/tokenized_buffer.cpp index 876bd8187108..582a416e7ba5 100644 --- a/toolchain/lexer/tokenized_buffer.cpp +++ b/toolchain/lexer/tokenized_buffer.cpp @@ -85,9 +85,9 @@ class TokenizedBuffer::Lexer { Lexer(TokenizedBuffer& buffer, DiagnosticConsumer& consumer) : buffer_(&buffer), - translator_(buffer, ¤t_column_), + translator_(&buffer, ¤t_column_), emitter_(translator_, consumer), - token_translator_(buffer, ¤t_column_), + token_translator_(&buffer, ¤t_column_), token_emitter_(token_translator_, consumer), current_line_(buffer.AddLine({0, 0, 0})), current_line_info_(&buffer.GetLineInfo(current_line_)) {} @@ -944,7 +944,7 @@ auto TokenizedBuffer::TokenLocationTranslator::GetLocation(Token token) // Find the corresponding file location. // TODO: Should we somehow indicate in the diagnostic location if this token // is a recovery token that doesn't correspond to the original source? - return SourceBufferLocationTranslator(*buffer_, last_line_lexed_to_column_) + return SourceBufferLocationTranslator(buffer_, last_line_lexed_to_column_) .GetLocation(token_start); } diff --git a/toolchain/lexer/tokenized_buffer.h b/toolchain/lexer/tokenized_buffer.h index 70164535af51..4b1968517d81 100644 --- a/toolchain/lexer/tokenized_buffer.h +++ b/toolchain/lexer/tokenized_buffer.h @@ -253,16 +253,16 @@ class TokenizedBuffer { class TokenLocationTranslator : public DiagnosticLocationTranslator { public: - explicit TokenLocationTranslator(TokenizedBuffer& buffer, + explicit TokenLocationTranslator(const TokenizedBuffer* buffer, int* last_line_lexed_to_column) - : buffer_(&buffer), + : buffer_(buffer), last_line_lexed_to_column_(last_line_lexed_to_column) {} // Map the given token into a diagnostic location. auto GetLocation(Token token) -> DiagnosticLocation override; private: - TokenizedBuffer* buffer_; + const TokenizedBuffer* buffer_; // Passed to SourceBufferLocationTranslator. int* last_line_lexed_to_column_; }; @@ -377,9 +377,9 @@ class TokenizedBuffer { class SourceBufferLocationTranslator : public DiagnosticLocationTranslator { public: - explicit SourceBufferLocationTranslator(TokenizedBuffer& buffer, + explicit SourceBufferLocationTranslator(const TokenizedBuffer* buffer, int* last_line_lexed_to_column) - : buffer_(&buffer), + : buffer_(buffer), last_line_lexed_to_column_(last_line_lexed_to_column) {} // Map the given position within the source buffer into a diagnostic @@ -387,7 +387,7 @@ class TokenizedBuffer { auto GetLocation(const char* loc) -> DiagnosticLocation override; private: - TokenizedBuffer* buffer_; + const TokenizedBuffer* buffer_; // The last lexed column, for determining whether the last line should be // checked for unlexed newlines. May be null after lexing is complete. int* last_line_lexed_to_column_; diff --git a/toolchain/parser/parse_tree.cpp b/toolchain/parser/parse_tree.cpp index 83067011bf7c..a0f7bf48f07a 100644 --- a/toolchain/parser/parse_tree.cpp +++ b/toolchain/parser/parse_tree.cpp @@ -23,7 +23,7 @@ namespace Carbon { auto ParseTree::Parse(TokenizedBuffer& tokens, DiagnosticConsumer& consumer) -> ParseTree { TokenizedBuffer::TokenLocationTranslator translator( - tokens, /*last_line_lexed_to_column=*/nullptr); + &tokens, /*last_line_lexed_to_column=*/nullptr); TokenDiagnosticEmitter emitter(translator, consumer); // Delegate to the parser. diff --git a/toolchain/semantics/BUILD b/toolchain/semantics/BUILD index d6934b9b72a8..28f1fcfd48e6 100644 --- a/toolchain/semantics/BUILD +++ b/toolchain/semantics/BUILD @@ -44,6 +44,7 @@ cc_library( "//common:check", "//common:ostream", "//common:vlog", + "//toolchain/lexer:numeric_literal", "//toolchain/lexer:token_kind", "//toolchain/lexer:tokenized_buffer", "//toolchain/parser:parse_node_kind", diff --git a/toolchain/semantics/lit_autoupdate.py b/toolchain/semantics/lit_autoupdate.py index 51bd4078a745..3f18f6bf449b 100755 --- a/toolchain/semantics/lit_autoupdate.py +++ b/toolchain/semantics/lit_autoupdate.py @@ -26,9 +26,7 @@ def main() -> None: "--tool=carbon", "--autoupdate_arg=dump", "--autoupdate_arg=semantics-ir", - # TODO: This should eventually have lines in output, but it doesn't - # right now. - "--line_number_pattern=UNUSED", + r"--line_number_pattern=(?<=\.carbon:)(\d+)(?=(?:\D|$))", "--lit_run=%{carbon-run-semantics}", "--testdata=toolchain/semantics/testdata", ] + sys.argv[1:] diff --git a/toolchain/semantics/semantics_builtin_kind.def b/toolchain/semantics/semantics_builtin_kind.def index 7db2db32c579..b5b1402ba8c1 100644 --- a/toolchain/semantics/semantics_builtin_kind.def +++ b/toolchain/semantics/semantics_builtin_kind.def @@ -13,9 +13,19 @@ #error "Must define the x-macro to use this file." #endif +// Tracks expressions which are valid as types. CARBON_SEMANTICS_BUILTIN_KIND(TypeType) + +// Used when a SemanticNode has an invalid type, which should then be ignored +// for future type checking. +CARBON_SEMANTICS_BUILTIN_KIND(InvalidType) + +// The type of an integer literal. CARBON_SEMANTICS_BUILTIN_KIND(IntegerLiteralType) +// The type of a real literal. +CARBON_SEMANTICS_BUILTIN_KIND(RealLiteralType) + // Keep invalid last, so that we can use values as array indices without needing // an invalid entry. CARBON_SEMANTICS_BUILTIN_KIND(Invalid) diff --git a/toolchain/semantics/semantics_ir.cpp b/toolchain/semantics/semantics_ir.cpp index b70db517b6ee..369c3768b5ca 100644 --- a/toolchain/semantics/semantics_ir.cpp +++ b/toolchain/semantics/semantics_ir.cpp @@ -20,23 +20,36 @@ auto SemanticsIR::MakeBuiltinIR() -> SemanticsIR { semantics.cross_references_.resize_for_overwrite( SemanticsBuiltinKind::ValidCount); - // TODO: The type-type should be replaced with a constraint. It will probably - // remain a builtin, just reimplemented as equivalent to `constraint type {}`. - auto builtin_type_type = semantics.AddNode( + constexpr int32_t TypeOfTypeType = 0; + auto type_type = semantics.AddNode( block_id, SemanticsNode::MakeBuiltin(SemanticsBuiltinKind::TypeType(), - SemanticsNodeId(0))); + SemanticsNodeId(TypeOfTypeType))); semantics.cross_references_[SemanticsBuiltinKind::TypeType().AsInt()] = - SemanticsCrossReference(BuiltinIR, block_id, builtin_type_type); - CARBON_CHECK(builtin_type_type.id == 0) + SemanticsCrossReference(BuiltinIR, block_id, type_type); + CARBON_CHECK(type_type.id == TypeOfTypeType) << "TypeType's type must be self-referential."; - auto builtin_int32 = semantics.AddNode( - block_id, - SemanticsNode::MakeBuiltin(SemanticsBuiltinKind::IntegerLiteralType(), - builtin_type_type)); + constexpr int32_t TypeOfInvalidType = 1; + auto invalid_type = semantics.AddNode( + block_id, SemanticsNode::MakeBuiltin(SemanticsBuiltinKind::InvalidType(), + SemanticsNodeId(TypeOfInvalidType))); + semantics.cross_references_[SemanticsBuiltinKind::InvalidType().AsInt()] = + SemanticsCrossReference(BuiltinIR, block_id, invalid_type); + CARBON_CHECK(invalid_type.id == TypeOfInvalidType) + << "InvalidType's type must be self-referential."; + + auto integer_literal_type = semantics.AddNode( + block_id, SemanticsNode::MakeBuiltin( + SemanticsBuiltinKind::IntegerLiteralType(), type_type)); semantics .cross_references_[SemanticsBuiltinKind::IntegerLiteralType().AsInt()] = - SemanticsCrossReference(BuiltinIR, block_id, builtin_int32); + SemanticsCrossReference(BuiltinIR, block_id, integer_literal_type); + + auto real_literal_type = semantics.AddNode( + block_id, SemanticsNode::MakeBuiltin( + SemanticsBuiltinKind::RealLiteralType(), type_type)); + semantics.cross_references_[SemanticsBuiltinKind::RealLiteralType().AsInt()] = + SemanticsCrossReference(BuiltinIR, block_id, real_literal_type); CARBON_CHECK(semantics.node_blocks_.size() == 1) << "BuildBuiltins should only produce 1 block, actual: " @@ -47,10 +60,16 @@ auto SemanticsIR::MakeBuiltinIR() -> SemanticsIR { auto SemanticsIR::MakeFromParseTree(const SemanticsIR& builtin_ir, const TokenizedBuffer& tokens, const ParseTree& parse_tree, + DiagnosticConsumer& consumer, llvm::raw_ostream* vlog_stream) -> SemanticsIR { SemanticsIR semantics(builtin_ir); - SemanticsParseTreeHandler(tokens, parse_tree, semantics, vlog_stream).Build(); + + TokenizedBuffer::TokenLocationTranslator translator( + &tokens, /*last_line_lexed_to_column=*/nullptr); + TokenDiagnosticEmitter emitter(translator, consumer); + SemanticsParseTreeHandler(tokens, emitter, parse_tree, semantics, vlog_stream) + .Build(); return semantics; } @@ -62,8 +81,8 @@ auto SemanticsIR::Print(llvm::raw_ostream& out) const -> void { out << "cross_references = {\n"; for (int32_t i = 0; i < static_cast(cross_references_.size()); ++i) { out.indent(Indent); - out << SemanticsNodeId::MakeCrossReference(i) << " = \"" - << cross_references_[i] << "\";\n"; + out << SemanticsNodeId::MakeCrossReference(i) << " = " + << cross_references_[i] << ";\n"; } out << "},\n"; diff --git a/toolchain/semantics/semantics_ir.h b/toolchain/semantics/semantics_ir.h index 3ce1fce388d3..86276577a48f 100644 --- a/toolchain/semantics/semantics_ir.h +++ b/toolchain/semantics/semantics_ir.h @@ -7,6 +7,7 @@ #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/SmallVector.h" +#include "toolchain/lexer/numeric_literal.h" #include "toolchain/parser/parse_tree.h" #include "toolchain/semantics/semantics_node.h" @@ -21,6 +22,15 @@ struct SemanticsCrossReferenceIRId { SemanticsCrossReferenceIRId() : id(-1) {} constexpr explicit SemanticsCrossReferenceIRId(int32_t id) : id(id) {} + friend auto operator==(SemanticsCrossReferenceIRId lhs, + SemanticsCrossReferenceIRId rhs) -> bool { + return lhs.id == rhs.id; + } + friend auto operator!=(SemanticsCrossReferenceIRId lhs, + SemanticsCrossReferenceIRId rhs) -> bool { + return lhs.id != rhs.id; + } + auto Print(llvm::raw_ostream& out) const -> void { out << "ir" << id; } int32_t id; @@ -53,6 +63,7 @@ class SemanticsIR { static auto MakeFromParseTree(const SemanticsIR& builtin_ir, const TokenizedBuffer& tokens, const ParseTree& parse_tree, + DiagnosticConsumer& consumer, llvm::raw_ostream* vlog_stream) -> SemanticsIR; // Prints the full IR. @@ -61,6 +72,10 @@ 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. @@ -68,6 +83,30 @@ class SemanticsIR { : cross_reference_irs_({&builtins, this}), cross_references_(builtins.cross_references_) {} + auto GetType(SemanticsNodeBlockId block_id, SemanticsNodeId node_id) + -> SemanticsNodeId { + if (node_id.is_cross_reference()) { + auto ref = cross_references_[node_id.GetAsCrossReference()]; + auto type = cross_reference_irs_[ref.ir.id] + ->node_blocks_[ref.node_block.id][ref.node.id] + .type(); + if (type.is_cross_reference() || + (ref.ir == ThisIR && ref.node_block == block_id)) { + return type; + } else { + // TODO: If the type is a local reference within a block other than the + // present one, we don't really want to add a cross reference at this + // point. Does this mean types should be required to be cross + // references? And maybe always with a presence in the current IR's + // cross-references, so that equality is straightforward even though + // resolving the actual type is a two-step process? + CARBON_FATAL() << "Need to think more about this case"; + } + } else { + return node_blocks_[block_id.id][node_id.id].type(); + } + } + // Adds an identifier, returning an ID to reference it. // TODO: Deduplicate strings. // TODO: Probably make generic for all strings, including literals. diff --git a/toolchain/semantics/semantics_node.h b/toolchain/semantics/semantics_node.h index 222ae2777637..677aeb520f8c 100644 --- a/toolchain/semantics/semantics_node.h +++ b/toolchain/semantics/semantics_node.h @@ -42,6 +42,13 @@ struct SemanticsNodeId { return id & ~CrossReferenceBit; } + friend auto operator==(SemanticsNodeId lhs, SemanticsNodeId rhs) -> bool { + return lhs.id == rhs.id; + } + friend auto operator!=(SemanticsNodeId lhs, SemanticsNodeId rhs) -> bool { + return lhs.id != rhs.id; + } + auto Print(llvm::raw_ostream& out) const -> void { if (is_cross_reference()) { out << "node_xref" << GetAsCrossReference(); @@ -58,6 +65,15 @@ struct SemanticsIdentifierId { SemanticsIdentifierId() : id(-1) {} explicit SemanticsIdentifierId(int32_t id) : id(id) {} + friend auto operator==(SemanticsIdentifierId lhs, SemanticsIdentifierId rhs) + -> bool { + return lhs.id == rhs.id; + } + friend auto operator!=(SemanticsIdentifierId lhs, SemanticsIdentifierId rhs) + -> bool { + return lhs.id != rhs.id; + } + auto Print(llvm::raw_ostream& out) const -> void { out << "ident" << id; } int32_t id; @@ -68,6 +84,15 @@ struct SemanticsIntegerLiteralId { SemanticsIntegerLiteralId() : id(-1) {} explicit SemanticsIntegerLiteralId(int32_t id) : id(id) {} + friend auto operator==(SemanticsIntegerLiteralId lhs, + SemanticsIntegerLiteralId rhs) -> bool { + return lhs.id == rhs.id; + } + friend auto operator!=(SemanticsIntegerLiteralId lhs, + SemanticsIntegerLiteralId rhs) -> bool { + return lhs.id != rhs.id; + } + auto Print(llvm::raw_ostream& out) const -> void { out << "int" << id; } int32_t id; @@ -78,6 +103,15 @@ struct SemanticsNodeBlockId { SemanticsNodeBlockId() : id(-1) {} explicit SemanticsNodeBlockId(int32_t id) : id(id) {} + friend auto operator==(SemanticsNodeBlockId lhs, SemanticsNodeBlockId rhs) + -> bool { + return lhs.id == rhs.id; + } + friend auto operator!=(SemanticsNodeBlockId lhs, SemanticsNodeBlockId rhs) + -> bool { + return lhs.id != rhs.id; + } + auto Print(llvm::raw_ostream& out) const -> void { out << "block" << id; } int32_t id; @@ -91,10 +125,10 @@ class SemanticsNode { auto GetAsInvalid() const -> NoArgs { CARBON_FATAL() << "Invalid access"; } static auto MakeBinaryOperatorAdd(ParseTree::Node parse_node, - SemanticsNodeId lhs, SemanticsNodeId rhs) - -> SemanticsNode { + SemanticsNodeId type, SemanticsNodeId lhs, + SemanticsNodeId rhs) -> SemanticsNode { return SemanticsNode(parse_node, SemanticsNodeKind::BinaryOperatorAdd(), - SemanticsNodeId(), lhs.id, rhs.id); + type, lhs.id, rhs.id); } auto GetAsBinaryOperatorAdd() const -> std::pair { @@ -173,7 +207,20 @@ class SemanticsNode { return SemanticsIntegerLiteralId(arg0_); } + static auto MakeRealLiteral(ParseTree::Node parse_node) -> SemanticsNode { + return SemanticsNode(parse_node, SemanticsNodeKind::RealLiteral(), + SemanticsNodeId::MakeBuiltinReference( + SemanticsBuiltinKind::RealLiteralType())); + } + auto GetAsRealLiteral() const -> NoArgs { + CARBON_CHECK(kind_ == SemanticsNodeKind::RealLiteral()); + return {}; + } + static auto MakeReturn(ParseTree::Node parse_node) -> SemanticsNode { + // The actual type is `()`. However, code dealing with `return;` should + // understand the type without checking, so it's not necessary but could be + // specified if needed. return SemanticsNode(parse_node, SemanticsNodeKind::Return(), SemanticsNodeId()); } @@ -183,9 +230,10 @@ class SemanticsNode { } static auto MakeReturnExpression(ParseTree::Node parse_node, - SemanticsNodeId expr) -> SemanticsNode { + SemanticsNodeId type, SemanticsNodeId expr) + -> SemanticsNode { return SemanticsNode(parse_node, SemanticsNodeKind::ReturnExpression(), - SemanticsNodeId(), expr.id); + type, expr.id); } auto GetAsReturnExpression() const -> SemanticsNodeId { CARBON_CHECK(kind_ == SemanticsNodeKind::ReturnExpression()); diff --git a/toolchain/semantics/semantics_node_kind.def b/toolchain/semantics/semantics_node_kind.def index 896b9762799c..86a37b969b3e 100644 --- a/toolchain/semantics/semantics_node_kind.def +++ b/toolchain/semantics/semantics_node_kind.def @@ -22,6 +22,7 @@ CARBON_SEMANTICS_NODE_KIND(CodeBlock) CARBON_SEMANTICS_NODE_KIND(FunctionDeclaration) CARBON_SEMANTICS_NODE_KIND(FunctionDefinition) CARBON_SEMANTICS_NODE_KIND(IntegerLiteral) +CARBON_SEMANTICS_NODE_KIND(RealLiteral) CARBON_SEMANTICS_NODE_KIND(Return) CARBON_SEMANTICS_NODE_KIND(ReturnExpression) diff --git a/toolchain/semantics/semantics_parse_tree_handler.cpp b/toolchain/semantics/semantics_parse_tree_handler.cpp index a380d8fac4ce..e5afb79f1afa 100644 --- a/toolchain/semantics/semantics_parse_tree_handler.cpp +++ b/toolchain/semantics/semantics_parse_tree_handler.cpp @@ -9,6 +9,7 @@ #include "toolchain/lexer/token_kind.h" #include "toolchain/lexer/tokenized_buffer.h" #include "toolchain/parser/parse_node_kind.h" +#include "toolchain/semantics/semantics_builtin_kind.h" #include "toolchain/semantics/semantics_node.h" namespace Carbon { @@ -219,12 +220,29 @@ auto SemanticsParseTreeHandler::HandleInfixOperator(ParseTree::Node parse_node) auto rhs_id = PopWithResult(); auto lhs_id = PopWithResult(); + auto block = node_block_stack_.back(); + auto lhs_type = semantics_->GetType(block, lhs_id); + auto rhs_type = semantics_->GetType(block, rhs_id); + SemanticsNodeId result_type = lhs_type; + // TODO: This should attempt a type conversion, but there's not enough + // implemented to do that right now. + if (lhs_type != rhs_type) { + auto invalid_type = SemanticsNodeId::MakeBuiltinReference( + SemanticsBuiltinKind::InvalidType()); + if (lhs_type != invalid_type && rhs_type != invalid_type) { + // TODO: This is a poor diagnostic, and should be expanded. + CARBON_DIAGNOSTIC(TypeMismatch, Error, "Type mismatch"); + emitter_->Emit(parse_tree_->node_token(parse_node), TypeMismatch); + } + result_type = invalid_type; + } + // 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(): - Push(parse_node, - SemanticsNode::MakeBinaryOperatorAdd(parse_node, lhs_id, rhs_id)); + Push(parse_node, SemanticsNode::MakeBinaryOperatorAdd( + parse_node, result_type, lhs_id, rhs_id)); break; default: CARBON_FATAL() << "Unrecognized token kind: " << token_kind.Name(); @@ -241,6 +259,11 @@ auto SemanticsParseTreeHandler::HandleLiteral(ParseTree::Node parse_node) Push(parse_node, SemanticsNode::MakeIntegerLiteral(parse_node, id)); break; } + case TokenKind::RealLiteral(): { + // TODO: Add storage of the Real literal. + Push(parse_node, SemanticsNode::MakeRealLiteral(parse_node)); + break; + } default: CARBON_FATAL() << "Unhandled kind: " << token_kind.Name(); } @@ -264,8 +287,10 @@ auto SemanticsParseTreeHandler::HandleReturnStatement( Push(parse_node, SemanticsNode::MakeReturn(parse_node)); } else { auto arg = PopWithResult(); + auto arg_type = semantics_->GetType(node_block_stack_.back(), arg); Pop(ParseNodeKind::ReturnStatementStart()); - Push(parse_node, SemanticsNode::MakeReturnExpression(parse_node, arg)); + 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 74a85f02f582..b49083bfeaa7 100644 --- a/toolchain/semantics/semantics_parse_tree_handler.h +++ b/toolchain/semantics/semantics_parse_tree_handler.h @@ -16,10 +16,12 @@ class SemanticsParseTreeHandler { public: // Stores references for work. explicit SemanticsParseTreeHandler(const TokenizedBuffer& tokens, + TokenDiagnosticEmitter& 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) {} @@ -75,6 +77,9 @@ class SemanticsParseTreeHandler { // Tokens for getting data on literals. const TokenizedBuffer* tokens_; + // Handles diagnostics. + TokenDiagnosticEmitter* emitter_; + // The file's parse tree. const ParseTree* parse_tree_; diff --git a/toolchain/semantics/testdata/basics/empty.carbon b/toolchain/semantics/testdata/basics/empty.carbon index aed964adfc55..e70c818d0d85 100644 --- a/toolchain/semantics/testdata/basics/empty.carbon +++ b/toolchain/semantics/testdata/basics/empty.carbon @@ -6,8 +6,10 @@ // 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_xref0 = xref(ir0, block0, node0); +// CHECK:STDOUT: node_xref1 = xref(ir0, block0, node1); +// CHECK:STDOUT: node_xref2 = xref(ir0, block0, node2); +// CHECK:STDOUT: node_xref3 = xref(ir0, block0, node3); // CHECK:STDOUT: }, // CHECK:STDOUT: identifiers = { // CHECK:STDOUT: }, diff --git a/toolchain/semantics/testdata/function/basic.carbon b/toolchain/semantics/testdata/function/basic.carbon index 6d7cd85795a6..b1dcca36db35 100644 --- a/toolchain/semantics/testdata/function/basic.carbon +++ b/toolchain/semantics/testdata/function/basic.carbon @@ -6,8 +6,10 @@ // 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_xref0 = xref(ir0, block0, node0); +// CHECK:STDOUT: node_xref1 = xref(ir0, block0, node1); +// CHECK:STDOUT: node_xref2 = xref(ir0, block0, node2); +// CHECK:STDOUT: node_xref3 = xref(ir0, block0, node3); // CHECK:STDOUT: }, // CHECK:STDOUT: identifiers = { // CHECK:STDOUT: ident0 = "Foo"; diff --git a/toolchain/semantics/testdata/function/order.carbon b/toolchain/semantics/testdata/function/order.carbon index 5dea70875fba..ea30f23a3384 100644 --- a/toolchain/semantics/testdata/function/order.carbon +++ b/toolchain/semantics/testdata/function/order.carbon @@ -6,8 +6,10 @@ // 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_xref0 = xref(ir0, block0, node0); +// CHECK:STDOUT: node_xref1 = xref(ir0, block0, node1); +// CHECK:STDOUT: node_xref2 = xref(ir0, block0, node2); +// CHECK:STDOUT: node_xref3 = xref(ir0, block0, node3); // CHECK:STDOUT: }, // CHECK:STDOUT: identifiers = { // CHECK:STDOUT: ident0 = "Foo"; diff --git a/toolchain/semantics/testdata/return/binary_op.carbon b/toolchain/semantics/testdata/operators/binary_op.carbon similarity index 64% rename from toolchain/semantics/testdata/return/binary_op.carbon rename to toolchain/semantics/testdata/operators/binary_op.carbon index ea93800e9ea4..3d23d5e1ac93 100644 --- a/toolchain/semantics/testdata/return/binary_op.carbon +++ b/toolchain/semantics/testdata/operators/binary_op.carbon @@ -6,8 +6,10 @@ // 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_xref0 = xref(ir0, block0, node0); +// CHECK:STDOUT: node_xref1 = xref(ir0, block0, node1); +// CHECK:STDOUT: node_xref2 = xref(ir0, block0, node2); +// CHECK:STDOUT: node_xref3 = xref(ir0, block0, node3); // CHECK:STDOUT: }, // CHECK:STDOUT: identifiers = { // CHECK:STDOUT: ident0 = "Main"; @@ -23,10 +25,10 @@ // CHECK:STDOUT: node2 = FunctionDefinition(node0, block1); // CHECK:STDOUT: }, // CHECK:STDOUT: block1 = { -// CHECK:STDOUT: node0 = IntegerLiteral(int0): node_xref1; -// CHECK:STDOUT: node1 = IntegerLiteral(int1): node_xref1; -// CHECK:STDOUT: node2 = BinaryOperatorAdd(node0, node1); -// CHECK:STDOUT: node3 = ReturnExpression(node2); +// CHECK:STDOUT: node0 = IntegerLiteral(int0): node_xref2; +// CHECK:STDOUT: node1 = IntegerLiteral(int1): node_xref2; +// CHECK:STDOUT: node2 = BinaryOperatorAdd(node0, node1): node_xref2; +// CHECK:STDOUT: node3 = ReturnExpression(node2): node_xref2; // CHECK:STDOUT: }, // CHECK:STDOUT: } diff --git a/toolchain/semantics/testdata/operators/type_mismatch.carbon b/toolchain/semantics/testdata/operators/type_mismatch.carbon new file mode 100644 index 000000000000..95290b1cbd28 --- /dev/null +++ b/toolchain/semantics/testdata/operators/type_mismatch.carbon @@ -0,0 +1,37 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE +// RUN: %{carbon-run-semantics} +// CHECK:STDOUT: cross_reference_irs.size == 2, +// CHECK:STDOUT: cross_references = { +// CHECK:STDOUT: node_xref0 = xref(ir0, block0, node0); +// CHECK:STDOUT: node_xref1 = xref(ir0, block0, node1); +// CHECK:STDOUT: node_xref2 = xref(ir0, block0, node2); +// CHECK:STDOUT: node_xref3 = xref(ir0, block0, node3); +// CHECK:STDOUT: }, +// CHECK:STDOUT: identifiers = { +// CHECK:STDOUT: ident0 = "Main"; +// CHECK:STDOUT: }, +// CHECK:STDOUT: integer_literals = { +// CHECK:STDOUT: int0 = 12; +// CHECK:STDOUT: }, +// CHECK:STDOUT: node_blocks = { +// CHECK:STDOUT: block0 = { +// CHECK:STDOUT: node0 = FunctionDeclaration(); +// CHECK:STDOUT: node1 = BindName(ident0, node0); +// CHECK:STDOUT: node2 = FunctionDefinition(node0, block1); +// CHECK:STDOUT: }, +// CHECK:STDOUT: block1 = { +// CHECK:STDOUT: node0 = IntegerLiteral(int0): node_xref2; +// CHECK:STDOUT: node1 = RealLiteral(): node_xref3; +// CHECK:STDOUT: node2 = BinaryOperatorAdd(node0, node1): node_xref1; +// CHECK:STDOUT: node3 = ReturnExpression(node2): node_xref1; +// CHECK:STDOUT: }, +// CHECK:STDOUT: } + +fn Main() { + // CHECK:STDERR: {{.*}}/toolchain/semantics/testdata/operators/type_mismatch.carbon:[[@LINE+1]]:13: Type mismatch + return 12 + 3.4; +} diff --git a/toolchain/semantics/testdata/operators/type_mismatch_once.carbon b/toolchain/semantics/testdata/operators/type_mismatch_once.carbon new file mode 100644 index 000000000000..e5b33f93b6b3 --- /dev/null +++ b/toolchain/semantics/testdata/operators/type_mismatch_once.carbon @@ -0,0 +1,42 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE +// RUN: %{carbon-run-semantics} +// CHECK:STDOUT: cross_reference_irs.size == 2, +// CHECK:STDOUT: cross_references = { +// CHECK:STDOUT: node_xref0 = xref(ir0, block0, node0); +// CHECK:STDOUT: node_xref1 = xref(ir0, block0, node1); +// CHECK:STDOUT: node_xref2 = xref(ir0, block0, node2); +// CHECK:STDOUT: node_xref3 = xref(ir0, block0, node3); +// CHECK:STDOUT: }, +// CHECK:STDOUT: identifiers = { +// CHECK:STDOUT: ident0 = "Main"; +// CHECK:STDOUT: }, +// CHECK:STDOUT: integer_literals = { +// CHECK:STDOUT: int0 = 12; +// CHECK:STDOUT: int1 = 12; +// CHECK:STDOUT: }, +// CHECK:STDOUT: node_blocks = { +// CHECK:STDOUT: block0 = { +// CHECK:STDOUT: node0 = FunctionDeclaration(); +// CHECK:STDOUT: node1 = BindName(ident0, node0); +// CHECK:STDOUT: node2 = FunctionDefinition(node0, block1); +// CHECK:STDOUT: }, +// CHECK:STDOUT: block1 = { +// CHECK:STDOUT: node0 = IntegerLiteral(int0): node_xref2; +// CHECK:STDOUT: node1 = RealLiteral(): node_xref3; +// CHECK:STDOUT: node2 = BinaryOperatorAdd(node0, node1): node_xref1; +// CHECK:STDOUT: node3 = IntegerLiteral(int1): node_xref2; +// CHECK:STDOUT: node4 = BinaryOperatorAdd(node2, node3): node_xref1; +// CHECK:STDOUT: node5 = ReturnExpression(node4): node_xref1; +// CHECK:STDOUT: }, +// CHECK:STDOUT: } + +fn Main() { + // The following line has two mismatches, but after the first, it shouldn't + // keep erroring. + // CHECK:STDERR: {{.*}}/toolchain/semantics/testdata/operators/type_mismatch_once.carbon:[[@LINE+1]]:13: Type mismatch + return 12 + 3.4 + 12; +} diff --git a/toolchain/semantics/testdata/return/literal.carbon b/toolchain/semantics/testdata/return/literal.carbon index c2dac54a9978..fb88269c8f79 100644 --- a/toolchain/semantics/testdata/return/literal.carbon +++ b/toolchain/semantics/testdata/return/literal.carbon @@ -6,8 +6,10 @@ // 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_xref0 = xref(ir0, block0, node0); +// CHECK:STDOUT: node_xref1 = xref(ir0, block0, node1); +// CHECK:STDOUT: node_xref2 = xref(ir0, block0, node2); +// CHECK:STDOUT: node_xref3 = xref(ir0, block0, node3); // CHECK:STDOUT: }, // CHECK:STDOUT: identifiers = { // CHECK:STDOUT: ident0 = "Main"; @@ -22,8 +24,8 @@ // CHECK:STDOUT: node2 = FunctionDefinition(node0, block1); // CHECK:STDOUT: }, // CHECK:STDOUT: block1 = { -// CHECK:STDOUT: node0 = IntegerLiteral(int0): node_xref1; -// CHECK:STDOUT: node1 = ReturnExpression(node0); +// CHECK:STDOUT: node0 = IntegerLiteral(int0): node_xref2; +// CHECK:STDOUT: node1 = ReturnExpression(node0): node_xref2; // CHECK:STDOUT: }, // CHECK:STDOUT: } diff --git a/toolchain/semantics/testdata/return/trivial.carbon b/toolchain/semantics/testdata/return/trivial.carbon index 0928a9360cf2..6f87dcc4dee7 100644 --- a/toolchain/semantics/testdata/return/trivial.carbon +++ b/toolchain/semantics/testdata/return/trivial.carbon @@ -6,8 +6,10 @@ // 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_xref0 = xref(ir0, block0, node0); +// CHECK:STDOUT: node_xref1 = xref(ir0, block0, node1); +// CHECK:STDOUT: node_xref2 = xref(ir0, block0, node2); +// CHECK:STDOUT: node_xref3 = xref(ir0, block0, node3); // CHECK:STDOUT: }, // CHECK:STDOUT: identifiers = { // CHECK:STDOUT: ident0 = "Main";