Procházet zdrojové kódy

Add stringification for nodes. (#2704)

This is used to make error messages more readable. It'll be particularly important as more complex types are added.
Jon Ross-Perkins před 3 roky
rodič
revize
c615d76196

+ 1 - 1
toolchain/lowering/testdata/basics/fail_in_semantics.carbon

@@ -6,5 +6,5 @@
 // AUTOUPDATE
 // RUN: %{not} %{carbon-run-lowering}
 
-// CHECK:STDERR: {{.*}}/toolchain/lowering/testdata/basics/fail_in_semantics.carbon:[[@LINE+1]]:17: Type mismatch: lhs is nodeIntegerType, rhs is nodeFloatingPointType
+// CHECK:STDERR: {{.*}}/toolchain/lowering/testdata/basics/fail_in_semantics.carbon:[[@LINE+1]]:17: Type mismatch: lhs is i32, rhs is f64
 var x: i32 = 1.0;

+ 8 - 0
toolchain/semantics/semantics_builtin_kind.cpp

@@ -12,4 +12,12 @@ CARBON_DEFINE_ENUM_CLASS_NAMES(SemanticsBuiltinKind) = {
 #include "toolchain/semantics/semantics_builtin_kind.def"
 };
 
+auto SemanticsBuiltinKind::label() -> llvm::StringRef {
+  static constexpr llvm::StringLiteral Labels[] = {
+#define CARBON_SEMANTICS_BUILTIN_KIND(Name, Type, Label) Label,
+#include "toolchain/semantics/semantics_builtin_kind.def"
+  };
+  return Labels[AsInt()];
+}
+
 }  // namespace Carbon

+ 10 - 10
toolchain/semantics/semantics_builtin_kind.def

@@ -11,7 +11,7 @@
 // - CARBON_SEMANTICS_BUILTIN_KIND_NAME(Name)
 //   Used as a fallback if other macros are missing. Used directly by Invalid
 //   only, which is defined last.
-//   - CARBON_SEMANTICS_BUILTIN_KIND(Name, Type)
+//   - CARBON_SEMANTICS_BUILTIN_KIND(Name, Type, Label)
 //     Defines a builtin kind with the associated type, which must also be
 //     builtin.
 //
@@ -38,12 +38,12 @@
 
 // Tracks expressions which are valid as types.
 // This has a deliberately self-referential type.
-CARBON_SEMANTICS_BUILTIN_KIND(TypeType, TypeType)
+CARBON_SEMANTICS_BUILTIN_KIND(TypeType, TypeType, "Type")
 
 // Used when a SemanticNode has an invalid type, which should then be ignored
 // for future type checking.
 // This has a deliberately self-referential type.
-CARBON_SEMANTICS_BUILTIN_KIND(InvalidType, InvalidType)
+CARBON_SEMANTICS_BUILTIN_KIND(InvalidType, InvalidType, "<unknown>")
 
 // -----------------------------------------------------------------------------
 // TODO: Below types are all placeholders. While the above may last, the below
@@ -52,19 +52,19 @@ CARBON_SEMANTICS_BUILTIN_KIND(InvalidType, InvalidType)
 // -----------------------------------------------------------------------------
 
 // The type of integer values and integer literals, currently always i32.
-CARBON_SEMANTICS_BUILTIN_KIND(IntegerType, TypeType)
+CARBON_SEMANTICS_BUILTIN_KIND(IntegerType, TypeType, "i32")
 
 // The type of floating point values and real literals, currently always f64.
-CARBON_SEMANTICS_BUILTIN_KIND(FloatingPointType, TypeType)
+CARBON_SEMANTICS_BUILTIN_KIND(FloatingPointType, TypeType, "f64")
 
 // The type of string values and String literals.
-CARBON_SEMANTICS_BUILTIN_KIND(StringType, TypeType)
+CARBON_SEMANTICS_BUILTIN_KIND(StringType, TypeType, "String")
 
-// The canonical empty tuple type, or `() as type`.
-CARBON_SEMANTICS_BUILTIN_KIND(EmptyTupleType, TypeType)
+// The canonical empty tuple type.
+CARBON_SEMANTICS_BUILTIN_KIND(EmptyTupleType, TypeType, "() as Type")
 
-// The canonical empty tuple, or `()`.
-CARBON_SEMANTICS_BUILTIN_KIND(EmptyTuple, EmptyTupleType)
+// The canonical empty tuple.
+CARBON_SEMANTICS_BUILTIN_KIND(EmptyTuple, EmptyTupleType, "()")
 
 // Keep invalid last, so that we can use values as array indices without needing
 // an invalid entry.

+ 2 - 0
toolchain/semantics/semantics_builtin_kind.h

@@ -23,6 +23,8 @@ class SemanticsBuiltinKind : public CARBON_ENUM_BASE(SemanticsBuiltinKind) {
   CARBON_ENUM_CONSTANT_DECLARATION(Name)
 #include "toolchain/semantics/semantics_builtin_kind.def"
 
+  auto label() -> llvm::StringRef;
+
   // The count of enum values excluding Invalid.
   //
   // Note that we *define* this as `constexpr` making it a true compile-time

+ 49 - 1
toolchain/semantics/semantics_ir.cpp

@@ -16,7 +16,7 @@ auto SemanticsIR::MakeBuiltinIR() -> SemanticsIR {
   SemanticsIR semantics(/*builtin_ir=*/nullptr);
   semantics.nodes_.reserve(SemanticsBuiltinKind::ValidCount);
 
-#define CARBON_SEMANTICS_BUILTIN_KIND(Name, Type)        \
+#define CARBON_SEMANTICS_BUILTIN_KIND(Name, Type, ...)   \
   semantics.nodes_.push_back(SemanticsNode::MakeBuiltin( \
       SemanticsBuiltinKind::Name, SemanticsNodeId::Builtin##Type));
 #include "toolchain/semantics/semantics_builtin_kind.def"
@@ -105,4 +105,52 @@ auto SemanticsIR::Print(llvm::raw_ostream& out, bool include_builtins) const
   out << "]\n";
 }
 
+auto SemanticsIR::StringifyNode(SemanticsNodeId node_id) -> std::string {
+  std::string str;
+  llvm::raw_string_ostream out(str);
+  StringifyNodeImpl(out, node_id);
+  return str;
+}
+
+auto SemanticsIR::StringifyNodeImpl(llvm::raw_ostream& out,
+                                    SemanticsNodeId node_id) -> void {
+  // Invalid node IDs will use the default invalid printing.
+  if (!node_id.is_valid()) {
+    out << node_id;
+    return;
+  }
+
+  // Builtins have designated labels.
+  if (node_id.index < SemanticsBuiltinKind::ValidCount) {
+    out << SemanticsBuiltinKind::FromInt(node_id.index).label();
+    return;
+  }
+
+  auto node = GetNode(node_id);
+  switch (node.kind()) {
+    case SemanticsNodeKind::Assign:
+    case SemanticsNodeKind::BinaryOperatorAdd:
+    case SemanticsNodeKind::BindName:
+    case SemanticsNodeKind::Builtin:
+    case SemanticsNodeKind::Call:
+    case SemanticsNodeKind::CodeBlock:
+    case SemanticsNodeKind::CrossReference:
+    case SemanticsNodeKind::FunctionDeclaration:
+    case SemanticsNodeKind::FunctionDefinition:
+    case SemanticsNodeKind::IntegerLiteral:
+    case SemanticsNodeKind::RealLiteral:
+    case SemanticsNodeKind::Return:
+    case SemanticsNodeKind::ReturnExpression:
+    case SemanticsNodeKind::StringLiteral:
+    case SemanticsNodeKind::VarStorage:
+      // We don't need to handle stringification for nodes that don't show up in
+      // errors, but make it clear what's going on so that it's clearer when
+      // stringification is needed.
+      out << "<cannot stringify " << node_id << ">";
+      return;
+    case SemanticsNodeKind::Invalid:
+      llvm_unreachable("SemanticsNodeKind::Invalid is never used.");
+  }
+}
+
 }  // namespace Carbon

+ 7 - 0
toolchain/semantics/semantics_ir.h

@@ -209,6 +209,13 @@ class SemanticsIR {
     return std::nullopt;
   }
 
+  // Produces a string version of a node.
+  auto StringifyNode(SemanticsNodeId node_id) -> std::string;
+
+  // Implements StringifyNode using streaming.
+  auto StringifyNodeImpl(llvm::raw_ostream& out, SemanticsNodeId node_id)
+      -> void;
+
   bool has_errors_ = false;
 
   // Storage for call objects.

+ 13 - 10
toolchain/semantics/semantics_parse_tree_handler.cpp

@@ -168,9 +168,10 @@ auto SemanticsParseTreeHandler::TryTypeConversion(ParseTree::Node parse_node,
   }
   // TODO: This should use type names instead of nodes.
   CARBON_DIAGNOSTIC(TypeMismatch, Error,
-                    "Type mismatch: lhs is {0}, rhs is {1}", SemanticsNodeId,
-                    SemanticsNodeId);
-  emitter_->Emit(parse_node, TypeMismatch, lhs_type, rhs_type);
+                    "Type mismatch: lhs is {0}, rhs is {1}", std::string,
+                    std::string);
+  emitter_->Emit(parse_node, TypeMismatch, semantics_->StringifyNode(lhs_type),
+                 semantics_->StringifyNode(rhs_type));
   return SemanticsNodeId::BuiltinInvalidType;
 }
 
@@ -217,10 +218,11 @@ auto SemanticsParseTreeHandler::TryTypeConversionOnArgs(
       CARBON_DIAGNOSTIC(
           CallArgTypeMismatch, Note,
           "Type mismatch: cannot convert argument {0} from {1} to {2}.", size_t,
-          SemanticsNodeId, SemanticsNodeId);
+          std::string, std::string);
       emitter_->Build(arg_parse_node, NoMatchingCall)
-          .Note(param_parse_node, CallArgTypeMismatch, i, arg_ref_type,
-                param_ref_type)
+          .Note(param_parse_node, CallArgTypeMismatch, i,
+                semantics_->StringifyNode(arg_ref_type),
+                semantics_->StringifyNode(param_ref_type))
           .Emit();
       return false;
     }
@@ -892,11 +894,12 @@ auto SemanticsParseTreeHandler::HandleReturnStatement(
         // TODO: Stringify types, add a note pointing at the return
         // type's parse node.
         CARBON_DIAGNOSTIC(ReturnStatementTypeMismatch, Error,
-                          "Cannot convert {0} to {1}.", SemanticsNodeId,
-                          SemanticsNodeId);
+                          "Cannot convert {0} to {1}.", std::string,
+                          std::string);
         emitter_
-            ->Build(parse_node, ReturnStatementTypeMismatch, arg_type,
-                    callable.return_type_id)
+            ->Build(parse_node, ReturnStatementTypeMismatch,
+                    semantics_->StringifyNode(arg_type),
+                    semantics_->StringifyNode(callable.return_type_id))
             .Emit();
       }
       arg_type = new_type;

+ 1 - 1
toolchain/semantics/testdata/function/call/fail_param_type.carbon

@@ -58,6 +58,6 @@ fn Run(a: i32) {}
 
 fn Main() {
   // CHECK:STDERR: {{.*}}/toolchain/semantics/testdata/function/call/fail_param_type.carbon:[[@LINE+2]]:6: No matching callable was found.
-  // CHECK:STDERR: {{.*}}/toolchain/semantics/testdata/function/call/fail_param_type.carbon:[[@LINE-4]]:1: Type mismatch: cannot convert argument 0 from nodeFloatingPointType to nodeIntegerType.
+  // CHECK:STDERR: {{.*}}/toolchain/semantics/testdata/function/call/fail_param_type.carbon:[[@LINE-4]]:1: Type mismatch: cannot convert argument 0 from f64 to i32.
   Run(1.0);
 }

+ 1 - 1
toolchain/semantics/testdata/operators/fail_type_mismatch.carbon

@@ -43,6 +43,6 @@
 // CHECK:STDOUT: ]
 
 fn Main() -> i32 {
-  // CHECK:STDERR: {{.*}}/toolchain/semantics/testdata/operators/fail_type_mismatch.carbon:[[@LINE+1]]:13: Type mismatch: lhs is nodeIntegerType, rhs is nodeFloatingPointType
+  // CHECK:STDERR: {{.*}}/toolchain/semantics/testdata/operators/fail_type_mismatch.carbon:[[@LINE+1]]:13: Type mismatch: lhs is i32, rhs is f64
   return 12 + 3.4;
 }

+ 1 - 1
toolchain/semantics/testdata/operators/fail_type_mismatch_once.carbon

@@ -50,6 +50,6 @@
 fn Main() -> i32 {
   // 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: lhs is nodeIntegerType, rhs is nodeFloatingPointType
+  // CHECK:STDERR: {{.*}}/toolchain/semantics/testdata/operators/fail_type_mismatch_once.carbon:[[@LINE+1]]:13: Type mismatch: lhs is i32, rhs is f64
   return 12 + 3.4 + 12;
 }

+ 1 - 1
toolchain/semantics/testdata/return/fail_type_mismatch.carbon

@@ -38,6 +38,6 @@
 // CHECK:STDOUT: ]
 
 fn Main() -> i32 {
-  // CHECK:STDERR: {{.*}}/toolchain/semantics/testdata/return/fail_type_mismatch.carbon:[[@LINE+1]]:13: Cannot convert nodeFloatingPointType to nodeIntegerType.
+  // CHECK:STDERR: {{.*}}/toolchain/semantics/testdata/return/fail_type_mismatch.carbon:[[@LINE+1]]:13: Cannot convert f64 to i32.
   return 1.0;
 }

+ 1 - 1
toolchain/semantics/testdata/var/fail_init_type_mismatch.carbon

@@ -43,6 +43,6 @@
 // CHECK:STDOUT: ]
 
 fn Main() {
-  // CHECK:STDERR: {{.*}}/toolchain/semantics/testdata/var/fail_init_type_mismatch.carbon:[[@LINE+1]]:19: Type mismatch: lhs is nodeIntegerType, rhs is nodeFloatingPointType
+  // CHECK:STDERR: {{.*}}/toolchain/semantics/testdata/var/fail_init_type_mismatch.carbon:[[@LINE+1]]:19: Type mismatch: lhs is i32, rhs is f64
   var x: i32 = 1.0;
 }