mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-05 22:02:55 +01:00
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:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user