Просмотр исходного кода

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.
Jon Ross-Perkins 3 лет назад
Родитель
Сommit
4c8fdf5124

+ 6 - 0
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
 // ============================================================================

+ 1 - 1
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_);

+ 1 - 1
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;
   };
 

+ 3 - 3
toolchain/lexer/tokenized_buffer.cpp

@@ -85,9 +85,9 @@ class TokenizedBuffer::Lexer {
 
   Lexer(TokenizedBuffer& buffer, DiagnosticConsumer& consumer)
       : buffer_(&buffer),
-        translator_(buffer, &current_column_),
+        translator_(&buffer, &current_column_),
         emitter_(translator_, consumer),
-        token_translator_(buffer, &current_column_),
+        token_translator_(&buffer, &current_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);
 }
 

+ 6 - 6
toolchain/lexer/tokenized_buffer.h

@@ -253,16 +253,16 @@ class TokenizedBuffer {
   class TokenLocationTranslator
       : public DiagnosticLocationTranslator<Internal::TokenizedBufferToken> {
    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<const char*> {
    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_;

+ 1 - 1
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.

+ 1 - 0
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",

+ 1 - 3
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:]

+ 10 - 0
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)

+ 33 - 14
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<int32_t>(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";
 

+ 39 - 0
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.

+ 53 - 5
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<SemanticsNodeId, SemanticsNodeId> {
@@ -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());

+ 1 - 0
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)
 

+ 28 - 3
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));
   }
 }
 

+ 5 - 0
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_;
 

+ 4 - 2
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: },

+ 4 - 2
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";

+ 4 - 2
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";

+ 8 - 6
toolchain/semantics/testdata/return/binary_op.carbon → 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: }
 

+ 37 - 0
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;
+}

+ 42 - 0
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;
+}

+ 6 - 4
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: }
 

+ 4 - 2
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";