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.
This commit is contained in:
Jon Ross-Perkins
2023-03-22 14:55:53 -07:00
committed by GitHub
parent e0c90767be
commit c615d76196
12 changed files with 95 additions and 27 deletions
@@ -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;
@@ -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
View File
@@ -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.
@@ -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
View File
@@ -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
View File
@@ -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.
@@ -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;
@@ -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);
}
@@ -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;
}
@@ -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;
}
@@ -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;
}
@@ -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;
}