diff --git a/toolchain/lowering/testdata/basics/fail_in_semantics.carbon b/toolchain/lowering/testdata/basics/fail_in_semantics.carbon index 7413efe4a1f1..840d56920858 100644 --- a/toolchain/lowering/testdata/basics/fail_in_semantics.carbon +++ b/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; diff --git a/toolchain/semantics/semantics_builtin_kind.cpp b/toolchain/semantics/semantics_builtin_kind.cpp index 100015b7359e..73c8a09a5913 100644 --- a/toolchain/semantics/semantics_builtin_kind.cpp +++ b/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 diff --git a/toolchain/semantics/semantics_builtin_kind.def b/toolchain/semantics/semantics_builtin_kind.def index 2389576a0c2f..036f1c7c29fd 100644 --- a/toolchain/semantics/semantics_builtin_kind.def +++ b/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, "") // ----------------------------------------------------------------------------- // 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. diff --git a/toolchain/semantics/semantics_builtin_kind.h b/toolchain/semantics/semantics_builtin_kind.h index 0ccba6fd5c9d..d84fbfdb2dfe 100644 --- a/toolchain/semantics/semantics_builtin_kind.h +++ b/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 diff --git a/toolchain/semantics/semantics_ir.cpp b/toolchain/semantics/semantics_ir.cpp index 035542b92b36..d537a23e06a1 100644 --- a/toolchain/semantics/semantics_ir.cpp +++ b/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 << ""; + return; + case SemanticsNodeKind::Invalid: + llvm_unreachable("SemanticsNodeKind::Invalid is never used."); + } +} + } // namespace Carbon diff --git a/toolchain/semantics/semantics_ir.h b/toolchain/semantics/semantics_ir.h index 3bf40dfd672d..9fc48d3890db 100644 --- a/toolchain/semantics/semantics_ir.h +++ b/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. diff --git a/toolchain/semantics/semantics_parse_tree_handler.cpp b/toolchain/semantics/semantics_parse_tree_handler.cpp index 4eadff743ccf..5ba7a318e723 100644 --- a/toolchain/semantics/semantics_parse_tree_handler.cpp +++ b/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; diff --git a/toolchain/semantics/testdata/function/call/fail_param_type.carbon b/toolchain/semantics/testdata/function/call/fail_param_type.carbon index 24584b3d7de9..119243483251 100644 --- a/toolchain/semantics/testdata/function/call/fail_param_type.carbon +++ b/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); } diff --git a/toolchain/semantics/testdata/operators/fail_type_mismatch.carbon b/toolchain/semantics/testdata/operators/fail_type_mismatch.carbon index 68fc5940e27c..26539a2814b7 100644 --- a/toolchain/semantics/testdata/operators/fail_type_mismatch.carbon +++ b/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; } diff --git a/toolchain/semantics/testdata/operators/fail_type_mismatch_once.carbon b/toolchain/semantics/testdata/operators/fail_type_mismatch_once.carbon index 8e991d309acc..84fb140ce7ae 100644 --- a/toolchain/semantics/testdata/operators/fail_type_mismatch_once.carbon +++ b/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; } diff --git a/toolchain/semantics/testdata/return/fail_type_mismatch.carbon b/toolchain/semantics/testdata/return/fail_type_mismatch.carbon index 6d8b81a9606c..26891caddde1 100644 --- a/toolchain/semantics/testdata/return/fail_type_mismatch.carbon +++ b/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; } diff --git a/toolchain/semantics/testdata/var/fail_init_type_mismatch.carbon b/toolchain/semantics/testdata/var/fail_init_type_mismatch.carbon index c4d1dbab5a55..2e31f5a1d640 100644 --- a/toolchain/semantics/testdata/var/fail_init_type_mismatch.carbon +++ b/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; }