diff --git a/toolchain/check/check.cpp b/toolchain/check/check.cpp index 323a3580d6c44..a70f5583f1b63 100644 --- a/toolchain/check/check.cpp +++ b/toolchain/check/check.cpp @@ -74,6 +74,19 @@ class SemIRLocationTranslator } } + auto TranslateArg(DiagnosticTypeTranslation translation, llvm::Any arg) const + -> llvm::Any override { + switch (translation) { + case DiagnosticTypeTranslation::TypeId: { + auto type_id = llvm::any_cast(arg); + return sem_ir_->StringifyType(type_id); + } + default: + return DiagnosticLocationTranslator::TranslateArg( + translation, arg); + } + } + private: auto GetLocationInFile(const SemIR::File* sem_ir, Parse::NodeLocation node_location) const diff --git a/toolchain/check/convert.cpp b/toolchain/check/convert.cpp index 51cfa41ea9ab8..2fe952e2eb5c7 100644 --- a/toolchain/check/convert.cpp +++ b/toolchain/check/convert.cpp @@ -485,11 +485,10 @@ static auto ConvertStructToStructOrClass(Context& context, CARBON_DIAGNOSTIC(StructInitMissingFieldInConversion, Error, "Cannot convert from struct type `{0}` to `{1}`: " "missing field `{2}` in source type.", - std::string, std::string, std::string); + SemIR::TypeId, SemIR::TypeId, std::string); context.emitter().Emit( value_parse_node, StructInitMissingFieldInConversion, - sem_ir.StringifyType(value.type_id()), - sem_ir.StringifyType(target.type_id), + value.type_id(), target.type_id, sem_ir.names().GetFormatted(dest_field.name_id).str()); } return SemIR::InstId::BuiltinError; @@ -554,9 +553,9 @@ static auto ConvertStructToClass(Context& context, SemIR::StructType src_type, CARBON_DIAGNOSTIC(ConstructionOfAbstractClass, Error, "Cannot construct instance of abstract class. " "Consider using `partial {0}` instead.", - std::string); + SemIR::TypeId); context.emitter().Emit(value_id, ConstructionOfAbstractClass, - context.sem_ir().StringifyType(target.type_id)); + target.type_id); return SemIR::InstId::BuiltinError; } if (class_info.object_repr_id == SemIR::TypeId::Error) { @@ -865,9 +864,8 @@ static auto PerformCopy(Context& context, SemIR::InstId expr_id) // TODO: We don't yet have rules for whether and when a class type is // copyable, or how to perform the copy. CARBON_DIAGNOSTIC(CopyOfUncopyableType, Error, - "Cannot copy value of type `{0}`.", std::string); - context.emitter().Emit(expr_id, CopyOfUncopyableType, - context.sem_ir().StringifyType(type_id)); + "Cannot copy value of type `{0}`.", SemIR::TypeId); + context.emitter().Emit(expr_id, CopyOfUncopyableType, type_id); return SemIR::InstId::BuiltinError; } @@ -897,19 +895,20 @@ auto Convert(Context& context, Parse::NodeId parse_node, SemIR::InstId expr_id, if (!context.TryToCompleteType(target.type_id, [&] { CARBON_DIAGNOSTIC(IncompleteTypeInInit, Error, "Initialization of incomplete type `{0}`.", - std::string); + SemIR::TypeId); CARBON_DIAGNOSTIC(IncompleteTypeInValueConversion, Error, "Forming value of incomplete type `{0}`.", - std::string); + SemIR::TypeId); CARBON_DIAGNOSTIC(IncompleteTypeInConversion, Error, - "Invalid use of incomplete type `{0}`.", std::string); - return context.emitter().Build( - parse_node, - target.is_initializer() ? IncompleteTypeInInit - : target.kind == ConversionTarget::Value - ? IncompleteTypeInValueConversion - : IncompleteTypeInConversion, - context.sem_ir().StringifyType(target.type_id)); + "Invalid use of incomplete type `{0}`.", + SemIR::TypeId); + return context.emitter().Build(parse_node, + target.is_initializer() + ? IncompleteTypeInInit + : target.kind == ConversionTarget::Value + ? IncompleteTypeInValueConversion + : IncompleteTypeInConversion, + target.type_id); })) { return SemIR::InstId::BuiltinError; } @@ -927,17 +926,16 @@ auto Convert(Context& context, Parse::NodeId parse_node, SemIR::InstId expr_id, if (expr.type_id() != target.type_id) { CARBON_DIAGNOSTIC(ImplicitAsConversionFailure, Error, "Cannot implicitly convert from `{0}` to `{1}`.", - std::string, std::string); + SemIR::TypeId, SemIR::TypeId); CARBON_DIAGNOSTIC(ExplicitAsConversionFailure, Error, "Cannot convert from `{0}` to `{1}` with `as`.", - std::string, std::string); + SemIR::TypeId, SemIR::TypeId); context.emitter() .Build(parse_node, target.kind == ConversionTarget::ExplicitAs ? ExplicitAsConversionFailure : ImplicitAsConversionFailure, - sem_ir.StringifyType(expr.type_id()), - sem_ir.StringifyType(target.type_id)) + expr.type_id(), target.type_id) .Emit(); return SemIR::InstId::BuiltinError; } diff --git a/toolchain/check/decl_name_stack.cpp b/toolchain/check/decl_name_stack.cpp index 8f831f79eb815..359462d8d4d99 100644 --- a/toolchain/check/decl_name_stack.cpp +++ b/toolchain/check/decl_name_stack.cpp @@ -231,11 +231,10 @@ auto DeclNameStack::TryResolveQualifier(NameContext& name_context, .TryAs()) { CARBON_DIAGNOSTIC(QualifiedDeclInIncompleteClassScope, Error, "Cannot declare a member of incomplete class `{0}`.", - std::string); + SemIR::TypeId); auto builder = context_->emitter().Build( name_context.parse_node, QualifiedDeclInIncompleteClassScope, - context_->sem_ir().StringifyType( - context_->classes().Get(class_decl->class_id).self_type_id)); + context_->classes().Get(class_decl->class_id).self_type_id); context_->NoteIncompleteClass(class_decl->class_id, builder); builder.Emit(); } else { diff --git a/toolchain/check/eval.cpp b/toolchain/check/eval.cpp index 5316205db2d7d..58f68a5bd14ea 100644 --- a/toolchain/check/eval.cpp +++ b/toolchain/check/eval.cpp @@ -257,11 +257,10 @@ static auto PerformAggregateIndex(Context& context, SemIR::Inst inst) context.ints().Get(bound->int_id).ule(index_val.getZExtValue())) { CARBON_DIAGNOSTIC(ArrayIndexOutOfBounds, Error, "Array index `{0}` is past the end of type `{1}`.", - llvm::APSInt, std::string); - context.emitter().Emit( - index_inst.index_id, ArrayIndexOutOfBounds, - llvm::APSInt(index_val, /*isUnsigned=*/true), - context.sem_ir().StringifyType(aggregate_type_id)); + llvm::APSInt, SemIR::TypeId); + context.emitter().Emit(index_inst.index_id, ArrayIndexOutOfBounds, + llvm::APSInt(index_val, /*isUnsigned=*/true), + aggregate_type_id); return SemIR::ConstantId::Error; } } diff --git a/toolchain/check/handle_binding_pattern.cpp b/toolchain/check/handle_binding_pattern.cpp index 89263e644ca64..64db79d3a9838 100644 --- a/toolchain/check/handle_binding_pattern.cpp +++ b/toolchain/check/handle_binding_pattern.cpp @@ -70,12 +70,12 @@ auto HandleAnyBindingPattern(Context& context, Parse::NodeId parse_node, cast_type_id = context.AsCompleteType(cast_type_id, [&] { CARBON_DIAGNOSTIC(IncompleteTypeInVarDecl, Error, "{0} has incomplete type `{1}`.", llvm::StringLiteral, - std::string); - return context.emitter().Build( - type_node, IncompleteTypeInVarDecl, - enclosing_class_decl ? llvm::StringLiteral("Field") - : llvm::StringLiteral("Variable"), - context.sem_ir().StringifyType(cast_type_id)); + SemIR::TypeId); + return context.emitter().Build(type_node, IncompleteTypeInVarDecl, + enclosing_class_decl + ? llvm::StringLiteral("Field") + : llvm::StringLiteral("Variable"), + cast_type_id); }); if (enclosing_class_decl) { CARBON_CHECK(context_parse_node_kind == @@ -142,10 +142,9 @@ auto HandleAnyBindingPattern(Context& context, Parse::NodeId parse_node, cast_type_id = context.AsCompleteType(cast_type_id, [&] { CARBON_DIAGNOSTIC(IncompleteTypeInLetDecl, Error, "`let` binding has incomplete type `{0}`.", - std::string); - return context.emitter().Build( - type_node, IncompleteTypeInLetDecl, - context.sem_ir().StringifyType(cast_type_id)); + SemIR::TypeId); + return context.emitter().Build(type_node, IncompleteTypeInLetDecl, + cast_type_id); }); // Create the instruction, but don't add it to a block until after we've // formed its initializer. diff --git a/toolchain/check/handle_call_expr.cpp b/toolchain/check/handle_call_expr.cpp index c7c7dc8265727..b01d6c997dfa7 100644 --- a/toolchain/check/handle_call_expr.cpp +++ b/toolchain/check/handle_call_expr.cpp @@ -38,9 +38,9 @@ auto HandleCallExpr(Context& context, Parse::CallExprId parse_node) -> bool { auto callee_type_id = context.insts().Get(callee_id).type_id(); if (callee_type_id != SemIR::TypeId::Error) { CARBON_DIAGNOSTIC(CallToNonCallable, Error, - "Value of type `{0}` is not callable.", std::string); + "Value of type `{0}` is not callable.", SemIR::TypeId); context.emitter().Emit(call_expr_parse_node, CallToNonCallable, - context.sem_ir().StringifyType(callee_type_id)); + callee_type_id); } context.node_stack().Push(parse_node, SemIR::InstId::BuiltinError); return true; diff --git a/toolchain/check/handle_class.cpp b/toolchain/check/handle_class.cpp index ddc6270cb6bf4..6c79a413f8014 100644 --- a/toolchain/check/handle_class.cpp +++ b/toolchain/check/handle_class.cpp @@ -208,9 +208,8 @@ static auto DiagnoseBaseIsFinal(Context& context, Parse::NodeId parse_node, CARBON_DIAGNOSTIC(BaseIsFinal, Error, "Deriving from final type `{0}`. Base type must be an " "`abstract` or `base` class.", - std::string); - context.emitter().Emit(parse_node, BaseIsFinal, - context.sem_ir().StringifyType(base_type_id)); + SemIR::TypeId); + context.emitter().Emit(parse_node, BaseIsFinal, base_type_id); } // Checks that the specified base type is valid. @@ -219,10 +218,9 @@ static auto CheckBaseType(Context& context, Parse::NodeId parse_node, auto base_type_id = ExprAsType(context, parse_node, base_expr_id); base_type_id = context.AsCompleteType(base_type_id, [&] { CARBON_DIAGNOSTIC(IncompleteTypeInBaseDecl, Error, - "Base `{0}` is an incomplete type.", std::string); - return context.emitter().Build( - parse_node, IncompleteTypeInBaseDecl, - context.sem_ir().StringifyType(base_type_id)); + "Base `{0}` is an incomplete type.", SemIR::TypeId); + return context.emitter().Build(parse_node, IncompleteTypeInBaseDecl, + base_type_id); }); if (base_type_id == SemIR::TypeId::Error) { diff --git a/toolchain/check/handle_function.cpp b/toolchain/check/handle_function.cpp index ac01bbc825219..e3e6c23c11b79 100644 --- a/toolchain/check/handle_function.cpp +++ b/toolchain/check/handle_function.cpp @@ -78,10 +78,11 @@ static auto BuildFunctionDecl(Context& context, return_type_id = context.AsCompleteType(return_type_id, [&] { CARBON_DIAGNOSTIC(IncompleteTypeInFunctionReturnType, Error, - "Function returns incomplete type `{0}`.", std::string); - return context.emitter().Build( - return_node_and_id->first, IncompleteTypeInFunctionReturnType, - context.sem_ir().StringifyType(return_type_id)); + "Function returns incomplete type `{0}`.", + SemIR::TypeId); + return context.emitter().Build(return_node_and_id->first, + IncompleteTypeInFunctionReturnType, + return_type_id); }); if (!SemIR::GetInitRepr(context.sem_ir(), return_type_id) @@ -241,10 +242,9 @@ auto HandleFunctionDefinitionStart(Context& context, CARBON_DIAGNOSTIC( IncompleteTypeInFunctionParam, Error, "Parameter has incomplete type `{0}` in function definition.", - std::string); - return context.emitter().Build( - param_id, IncompleteTypeInFunctionParam, - context.sem_ir().StringifyType(param.type_id())); + SemIR::TypeId); + return context.emitter().Build(param_id, IncompleteTypeInFunctionParam, + param.type_id()); }); } diff --git a/toolchain/check/handle_impl.cpp b/toolchain/check/handle_impl.cpp index 462507e08eb45..7108c4187e6df 100644 --- a/toolchain/check/handle_impl.cpp +++ b/toolchain/check/handle_impl.cpp @@ -115,10 +115,9 @@ static auto ExtendImpl(Context& context, Parse::AnyImplDeclId parse_node, CARBON_DIAGNOSTIC( ExtendUndefinedInterface, Error, "`extend impl` requires a definition for interface `{0}`.", - std::string); - auto diag = - context.emitter().Build(parse_node, ExtendUndefinedInterface, - context.sem_ir().StringifyType(constraint_id)); + SemIR::TypeId); + auto diag = context.emitter().Build(parse_node, ExtendUndefinedInterface, + constraint_id); context.NoteUndefinedInterface(interface_type->interface_id, diag); diag.Emit(); enclosing_scope.has_error = true; @@ -198,14 +197,13 @@ auto HandleImplDefinitionStart(Context& context, if (impl_info.definition_id.is_valid()) { CARBON_DIAGNOSTIC(ImplRedefinition, Error, - "Redefinition of `impl {0} as {1}`.", std::string, - std::string); + "Redefinition of `impl {0} as {1}`.", SemIR::TypeId, + SemIR::TypeId); CARBON_DIAGNOSTIC(ImplPreviousDefinition, Note, "Previous definition was here."); context.emitter() - .Build(parse_node, ImplRedefinition, - context.sem_ir().StringifyType(impl_info.self_id), - context.sem_ir().StringifyType(impl_info.constraint_id)) + .Build(parse_node, ImplRedefinition, impl_info.self_id, + impl_info.constraint_id) .Note(impl_info.definition_id, ImplPreviousDefinition) .Emit(); } else { diff --git a/toolchain/check/handle_index.cpp b/toolchain/check/handle_index.cpp index 6ef4d9e92b359..8ea66f911fd27 100644 --- a/toolchain/check/handle_index.cpp +++ b/toolchain/check/handle_index.cpp @@ -26,11 +26,10 @@ static auto ValidateTupleIndex(Context& context, Parse::NodeId parse_node, CARBON_DIAGNOSTIC( TupleIndexOutOfBounds, Error, "Tuple element index `{0}` is past the end of type `{1}`.", - llvm::APSInt, std::string); - context.emitter().Emit( - parse_node, TupleIndexOutOfBounds, - llvm::APSInt(index_val, /*isUnsigned=*/true), - context.sem_ir().StringifyType(operand_inst.type_id())); + llvm::APSInt, SemIR::TypeId); + context.emitter().Emit(parse_node, TupleIndexOutOfBounds, + llvm::APSInt(index_val, /*isUnsigned=*/true), + operand_inst.type_id()); return nullptr; } return &index_val; @@ -110,9 +109,9 @@ auto HandleIndexExpr(Context& context, Parse::IndexExprId parse_node) -> bool { default: { if (operand_type_id != SemIR::TypeId::Error) { CARBON_DIAGNOSTIC(TypeNotIndexable, Error, - "Type `{0}` does not support indexing.", std::string); - context.emitter().Emit(parse_node, TypeNotIndexable, - context.sem_ir().StringifyType(operand_type_id)); + "Type `{0}` does not support indexing.", + SemIR::TypeId); + context.emitter().Emit(parse_node, TypeNotIndexable, operand_type_id); } context.node_stack().Push(parse_node, SemIR::InstId::BuiltinError); return true; diff --git a/toolchain/check/handle_name.cpp b/toolchain/check/handle_name.cpp index 0cb26d2bb6911..6da5d9965a1d8 100644 --- a/toolchain/check/handle_name.cpp +++ b/toolchain/check/handle_name.cpp @@ -121,10 +121,9 @@ auto HandleMemberAccessExpr(Context& context, if (!context.TryToCompleteType(base_type_id, [&] { CARBON_DIAGNOSTIC(IncompleteTypeInMemberAccess, Error, "Member access into object of incomplete type `{0}`.", - std::string); - return context.emitter().Build( - base_id, IncompleteTypeInMemberAccess, - context.sem_ir().StringifyType(base_type_id)); + SemIR::TypeId); + return context.emitter().Build(base_id, IncompleteTypeInMemberAccess, + base_type_id); })) { context.node_stack().Push(parse_node, SemIR::InstId::BuiltinError); return true; @@ -223,10 +222,10 @@ auto HandleMemberAccessExpr(Context& context, } } CARBON_DIAGNOSTIC(QualifiedExprNameNotFound, Error, - "Type `{0}` does not have a member `{1}`.", std::string, - std::string); + "Type `{0}` does not have a member `{1}`.", + SemIR::TypeId, std::string); context.emitter().Emit(parse_node, QualifiedExprNameNotFound, - context.sem_ir().StringifyType(base_type_id), + base_type_id, context.names().GetFormatted(name_id).str()); break; } @@ -237,9 +236,9 @@ auto HandleMemberAccessExpr(Context& context, if (base_type_id != SemIR::TypeId::Error) { CARBON_DIAGNOSTIC(QualifiedExprUnsupported, Error, "Type `{0}` does not support qualified expressions.", - std::string); + SemIR::TypeId); context.emitter().Emit(parse_node, QualifiedExprUnsupported, - context.sem_ir().StringifyType(base_type_id)); + base_type_id); } break; } diff --git a/toolchain/check/handle_operator.cpp b/toolchain/check/handle_operator.cpp index fcb930470dc3e..90b64a78df0bf 100644 --- a/toolchain/check/handle_operator.cpp +++ b/toolchain/check/handle_operator.cpp @@ -296,10 +296,9 @@ auto HandlePrefixOperatorStar(Context& context, } else if (type_id != SemIR::TypeId::Error) { CARBON_DIAGNOSTIC(DerefOfNonPointer, Error, "Cannot dereference operand of non-pointer type `{0}`.", - std::string); - auto builder = - context.emitter().Build(TokenOnly(parse_node), DerefOfNonPointer, - context.sem_ir().StringifyType(type_id)); + SemIR::TypeId); + auto builder = context.emitter().Build(TokenOnly(parse_node), + DerefOfNonPointer, type_id); // TODO: Check for any facet here, rather than only a type. if (type_id == SemIR::TypeId::TypeType) { CARBON_DIAGNOSTIC( diff --git a/toolchain/check/return.cpp b/toolchain/check/return.cpp index cd49c47d8f765..6664287d7e71a 100644 --- a/toolchain/check/return.cpp +++ b/toolchain/check/return.cpp @@ -35,14 +35,13 @@ static auto NoteNoReturnTypeProvided(Context::DiagnosticBuilder& diag, } // Produces a note describing the return type of the given function. -static auto NoteReturnType(Context& context, Context::DiagnosticBuilder& diag, +static auto NoteReturnType(Context::DiagnosticBuilder& diag, const SemIR::Function& function) { CARBON_DIAGNOSTIC(ReturnTypeHereNote, Note, - "Return type of function is `{0}`.", std::string); + "Return type of function is `{0}`.", SemIR::TypeId); // TODO: This is using the location of the `fn` keyword. Find the location of // the return type. - diag.Note(function.decl_id, ReturnTypeHereNote, - context.sem_ir().StringifyType(function.return_type_id)); + diag.Note(function.decl_id, ReturnTypeHereNote, function.return_type_id); } // Produces a note pointing at the currently in scope `returned var`. @@ -73,11 +72,10 @@ auto CheckReturnedVar(Context& context, Parse::NodeId returned_node, CARBON_DIAGNOSTIC(ReturnedVarWrongType, Error, "Type `{0}` of `returned var` does not match " "return type of enclosing function.", - std::string); + SemIR::TypeId); auto diag = - context.emitter().Build(type_node, ReturnedVarWrongType, - context.sem_ir().StringifyType(type_id)); - NoteReturnType(context, diag, function); + context.emitter().Build(type_node, ReturnedVarWrongType, type_id); + NoteReturnType(diag, function); diag.Emit(); return SemIR::InstId::BuiltinError; } @@ -110,7 +108,7 @@ auto BuildReturnWithNoExpr(Context& context, CARBON_DIAGNOSTIC(ReturnStatementMissingExpr, Error, "Missing return value."); auto diag = context.emitter().Build(parse_node, ReturnStatementMissingExpr); - NoteReturnType(context, diag, function); + NoteReturnType(diag, function); diag.Emit(); } diff --git a/toolchain/diagnostics/diagnostic_emitter.h b/toolchain/diagnostics/diagnostic_emitter.h index e121d92bc15cb..7a084e768081d 100644 --- a/toolchain/diagnostics/diagnostic_emitter.h +++ b/toolchain/diagnostics/diagnostic_emitter.h @@ -136,6 +136,14 @@ class DiagnosticConsumer { virtual auto Flush() -> void {} }; +// Known diagnostic type translations. These are enumerated because `llvm::Any` +// doesn't expose the contained type; instead, we infer it from a given +// diagnostic. +enum class DiagnosticTypeTranslation : int8_t { + None, + TypeId, +}; + // An interface that can translate some representation of a location into a // diagnostic location. // @@ -147,10 +155,46 @@ class DiagnosticLocationTranslator { virtual ~DiagnosticLocationTranslator() = default; virtual auto GetLocation(LocationT loc) -> DiagnosticLocation = 0; + + // Translates arg types as needed. Not all uses support translation, so the + // default simply errors. + virtual auto TranslateArg(DiagnosticTypeTranslation translation, + llvm::Any /*arg*/) const -> llvm::Any { + CARBON_FATAL() << "Unexpected call to TranslateArg: " + << static_cast(translation); + } +}; + +template +struct DiagnosticTypeInfo { + using StorageType = StorageTypeT; + static constexpr DiagnosticTypeTranslation Translation = TranslationV; }; namespace Internal { +// Determines whether there's a DiagnosticType member on Arg. +template +concept HasDiagnosticType = + requires { std::type_identity(); }; + +// The default implementation with no translation. +template +struct DiagnosticTypeForArg { + using StorageType = Arg; + static constexpr DiagnosticTypeTranslation Translation = + DiagnosticTypeTranslation::None; +}; + +// Exposes a custom translation for an argument type. +template + requires HasDiagnosticType +struct DiagnosticTypeForArg { + using StorageType = Arg::DiagnosticType::StorageType; + static constexpr DiagnosticTypeTranslation Translation = + Arg::DiagnosticType::Translation; +}; + // Use the DIAGNOSTIC macro to instantiate this. // This stores static information about a diagnostic category. template @@ -184,16 +228,18 @@ struct DiagnosticBase { inline auto FormatFnImpl(const DiagnosticMessage& message, std::index_sequence /*indices*/) const -> std::string { - assert(message.format_args.size() == sizeof...(Args)); - return llvm::formatv(message.format.data(), - llvm::any_cast(message.format_args[N])...); + CARBON_CHECK(message.format_args.size() == sizeof...(Args)); + return llvm::formatv( + message.format.data(), + llvm::any_cast::StorageType>( + message.format_args[N])...); } }; // Disable type deduction based on `args`; the type of `diagnostic_base` // determines the diagnostic's parameter types. template -using NoTypeDeduction = std::common_type_t; +using NoTypeDeduction = std::type_identity_t; } // namespace Internal @@ -232,8 +278,9 @@ class DiagnosticEmitter { Internal::NoTypeDeduction... args) -> DiagnosticBuilder& { CARBON_CHECK(diagnostic_base.Level == DiagnosticLevel::Note) << static_cast(diagnostic_base.Level); - diagnostic_.notes.push_back(MakeMessage( - emitter_, location, diagnostic_base, {llvm::Any(args)...})); + diagnostic_.notes.push_back( + MakeMessage(emitter_, location, diagnostic_base, + {emitter_->MakeAny(args)...})); return *this; } @@ -296,7 +343,7 @@ class DiagnosticEmitter { auto Emit(LocationT location, const Internal::DiagnosticBase& diagnostic_base, Internal::NoTypeDeduction... args) -> void { - DiagnosticBuilder(this, location, diagnostic_base, {llvm::Any(args)...}) + DiagnosticBuilder(this, location, diagnostic_base, {MakeAny(args)...}) .Emit(); } @@ -311,7 +358,7 @@ class DiagnosticEmitter { const Internal::DiagnosticBase& diagnostic_base, Internal::NoTypeDeduction... args) -> DiagnosticBuilder { return DiagnosticBuilder(this, location, diagnostic_base, - {llvm::Any(args)...}); + {MakeAny(args)...}); } private: @@ -340,6 +387,19 @@ class DiagnosticEmitter { DiagnosticEmitter* emitter_; }; + // Converts an argument to llvm::Any for storage, handling input to storage + // type translation when needed. + template + auto MakeAny(Arg arg) -> llvm::Any { + if constexpr (Internal::DiagnosticTypeForArg::Translation == + DiagnosticTypeTranslation::None) { + return arg; + } else { + return translator_->TranslateArg( + Internal::DiagnosticTypeForArg::Translation, arg); + } + } + template friend class DiagnosticAnnotationScope; diff --git a/toolchain/sem_ir/BUILD b/toolchain/sem_ir/BUILD index af7371d8d2577..b8450b1b4964b 100644 --- a/toolchain/sem_ir/BUILD +++ b/toolchain/sem_ir/BUILD @@ -22,6 +22,7 @@ cc_library( "//common:ostream", "//toolchain/base:index_base", "//toolchain/base:value_store", + "//toolchain/diagnostics:diagnostic_emitter", "//toolchain/sem_ir:builtin_kind", ], ) diff --git a/toolchain/sem_ir/ids.h b/toolchain/sem_ir/ids.h index 44b5320769f98..5f5b56b8f525d 100644 --- a/toolchain/sem_ir/ids.h +++ b/toolchain/sem_ir/ids.h @@ -9,6 +9,7 @@ #include "common/ostream.h" #include "toolchain/base/index_base.h" #include "toolchain/base/value_store.h" +#include "toolchain/diagnostics/diagnostic_emitter.h" #include "toolchain/sem_ir/builtin_kind.h" namespace Carbon::SemIR { @@ -416,6 +417,9 @@ constexpr InstBlockId InstBlockId::GlobalInit = InstBlockId(2); // The ID of a type. struct TypeId : public IdBase, public Printable { using ValueType = TypeInfo; + // StringifyType() is used for diagnostics. + using DiagnosticType = + DiagnosticTypeInfo; // The builtin TypeType. static const TypeId TypeType;