diff --git a/toolchain/check/check_unit.cpp b/toolchain/check/check_unit.cpp index 3d3e8d4bd8d64..54a5e5f9e8d76 100644 --- a/toolchain/check/check_unit.cpp +++ b/toolchain/check/check_unit.cpp @@ -402,7 +402,10 @@ auto CheckUnit::CheckRequiredDefinitions() -> void { CARBON_FATAL("TODO: Support interfaces in DiagnoseMissingDefinitions"); } case CARBON_KIND(SemIR::SpecificFunction specific_function): { - if (!ResolveSpecificDefinition(context_, + // TODO: Track a location for the use. In general we may want to track a + // list of enclosing locations if this was used from a generic. + SemIRLoc use_loc = decl_inst_id; + if (!ResolveSpecificDefinition(context_, use_loc, specific_function.specific_id)) { CARBON_DIAGNOSTIC(MissingGenericFunctionDefinition, Error, "use of undefined generic function"); diff --git a/toolchain/check/context.cpp b/toolchain/check/context.cpp index 5bb666cd0e401..adab63ebe771d 100644 --- a/toolchain/check/context.cpp +++ b/toolchain/check/context.cpp @@ -228,47 +228,21 @@ auto Context::DiagnoseNameNotFound(SemIRLoc loc, SemIR::NameId name_id) emitter_->Emit(loc, NameNotFound, name_id); } -// Given an instruction associated with a scope and a `SpecificId` for that -// scope, returns an instruction that describes the specific scope. -static auto GetInstForSpecificScope(Context& context, SemIR::InstId inst_id, - SemIR::SpecificId specific_id) - -> SemIR::InstId { - if (!specific_id.is_valid()) { - return inst_id; - } - auto inst = context.insts().Get(inst_id); - CARBON_KIND_SWITCH(inst) { - case CARBON_KIND(SemIR::ClassDecl class_decl): { - return context.types().GetInstId( - context.GetClassType(class_decl.class_id, specific_id)); - } - case CARBON_KIND(SemIR::InterfaceDecl interface_decl): { - return context.types().GetInstId( - context.GetInterfaceType(interface_decl.interface_id, specific_id)); - } - default: { - // Don't know how to form a specific for this generic scope. - // TODO: Handle more cases. - return SemIR::InstId::Invalid; - } - } -} - auto Context::DiagnoseMemberNameNotFound( SemIRLoc loc, SemIR::NameId name_id, llvm::ArrayRef lookup_scopes) -> void { if (lookup_scopes.size() == 1 && lookup_scopes.front().name_scope_id.is_valid()) { - const auto& scope = name_scopes().Get(lookup_scopes.front().name_scope_id); - if (auto specific_inst_id = GetInstForSpecificScope( - *this, scope.inst_id(), lookup_scopes.front().specific_id); - specific_inst_id.is_valid()) { - CARBON_DIAGNOSTIC(MemberNameNotFoundInScope, Error, - "member name `{0}` not found in {1}", SemIR::NameId, - InstIdAsType); - emitter_->Emit(loc, MemberNameNotFoundInScope, name_id, specific_inst_id); - return; - } + auto specific_id = lookup_scopes.front().specific_id; + auto scope_inst_id = + specific_id.is_valid() + ? GetInstForSpecific(*this, specific_id) + : name_scopes().Get(lookup_scopes.front().name_scope_id).inst_id(); + CARBON_DIAGNOSTIC(MemberNameNotFoundInScope, Error, + "member name `{0}` not found in {1}", SemIR::NameId, + InstIdAsType); + emitter_->Emit(loc, MemberNameNotFoundInScope, name_id, scope_inst_id); + return; } CARBON_DIAGNOSTIC(MemberNameNotFound, Error, "member name `{0}` not found", @@ -895,8 +869,9 @@ namespace { // complete. class TypeCompleter { public: - TypeCompleter(Context& context, Context::BuildDiagnosticFn diagnoser) - : context_(context), diagnoser_(diagnoser) {} + TypeCompleter(Context& context, SemIRLoc loc, + Context::BuildDiagnosticFn diagnoser) + : context_(context), loc_(loc), diagnoser_(diagnoser) {} // Attempts to complete the given type. Returns true if it is now complete, // false if it could not be completed. @@ -1009,7 +984,7 @@ class TypeCompleter { return false; } if (inst.specific_id.is_valid()) { - ResolveSpecificDefinition(context_, inst.specific_id); + ResolveSpecificDefinition(context_, loc_, inst.specific_id); } if (auto adapted_type_id = class_info.GetAdaptedType(context_.sem_ir(), inst.specific_id); @@ -1281,19 +1256,21 @@ class TypeCompleter { Context& context_; llvm::SmallVector work_list_; + SemIRLoc loc_; Context::BuildDiagnosticFn diagnoser_; }; } // namespace auto Context::TryToCompleteType(SemIR::TypeId type_id) -> bool { - return TypeCompleter(*this, nullptr).Complete(type_id); + // TODO: We need a location here in case we need to instantiate a class type. + return TypeCompleter(*this, SemIR::LocId::Invalid, nullptr).Complete(type_id); } auto Context::RequireCompleteType(SemIR::TypeId type_id, SemIR::LocId loc_id, BuildDiagnosticFn diagnoser) -> bool { CARBON_CHECK(diagnoser); - if (!TypeCompleter(*this, diagnoser).Complete(type_id)) { + if (!TypeCompleter(*this, loc_id, diagnoser).Complete(type_id)) { return false; } @@ -1358,7 +1335,7 @@ auto Context::RequireDefinedType(SemIR::TypeId type_id, SemIR::LocId loc_id, } if (interface.specific_id.is_valid()) { - ResolveSpecificDefinition(*this, interface.specific_id); + ResolveSpecificDefinition(*this, loc_id, interface.specific_id); } } // TODO: Finish facet type resolution. @@ -1443,7 +1420,7 @@ auto Context::GetSingletonType(SemIR::InstId singleton_id) -> SemIR::TypeId { auto Context::GetClassType(SemIR::ClassId class_id, SemIR::SpecificId specific_id) -> SemIR::TypeId { - return GetCompleteTypeImpl(*this, class_id, specific_id); + return GetTypeImpl(*this, class_id, specific_id); } auto Context::GetFunctionType(SemIR::FunctionId fn_id, diff --git a/toolchain/check/decl_name_stack.cpp b/toolchain/check/decl_name_stack.cpp index c8b795e6243c8..ae98f100ebfc0 100644 --- a/toolchain/check/decl_name_stack.cpp +++ b/toolchain/check/decl_name_stack.cpp @@ -115,7 +115,8 @@ auto DeclNameStack::Restore(SuspendedName sus) -> void { // Reattempt to resolve the definition of the specific. The generic might // have been defined after we suspended this scope. if (suspended_scope.entry.specific_id.is_valid()) { - ResolveSpecificDefinition(*context_, suspended_scope.entry.specific_id); + ResolveSpecificDefinition(*context_, sus.name_context.loc_id, + suspended_scope.entry.specific_id); } context_->scope_stack().Restore(std::move(suspended_scope)); @@ -192,7 +193,7 @@ auto DeclNameStack::LookupOrAddName(NameContext name_context, // `fn Class(T:! type).F(n: i32)` we will push the scope for `Class(T:! type)` // between the scope containing the declaration of `T` and the scope // containing the declaration of `n`. -static auto PushNameQualifierScope(Context& context, +static auto PushNameQualifierScope(Context& context, SemIRLoc loc, SemIR::InstId scope_inst_id, SemIR::NameScopeId scope_id, SemIR::SpecificId specific_id, @@ -203,7 +204,7 @@ static auto PushNameQualifierScope(Context& context, // When declaring a member of a generic, resolve the self specific. if (specific_id.is_valid()) { - ResolveSpecificDefinition(context, specific_id); + ResolveSpecificDefinition(context, loc, specific_id); } context.scope_stack().Push(scope_inst_id, scope_id, specific_id, has_error); @@ -229,8 +230,8 @@ auto DeclNameStack::ApplyNameQualifier(const NameComponent& name) -> void { // Resolve the qualifier as a scope and enter the new scope. auto [scope_id, specific_id] = ResolveAsScope(name_context, name); if (scope_id.is_valid()) { - PushNameQualifierScope(*context_, name_context.resolved_inst_id, scope_id, - specific_id, + PushNameQualifierScope(*context_, name_context.loc_id, + name_context.resolved_inst_id, scope_id, specific_id, context_->name_scopes().Get(scope_id).has_error()); name_context.parent_scope_id = scope_id; } else { diff --git a/toolchain/check/deduce.cpp b/toolchain/check/deduce.cpp index c4cfe8e8f016a..0ee531c9bcdc9 100644 --- a/toolchain/check/deduce.cpp +++ b/toolchain/check/deduce.cpp @@ -505,7 +505,7 @@ auto DeductionContext::MakeSpecific() -> SemIR::SpecificId { // TODO: Convert the deduced values to the types of the bindings. return Check::MakeSpecific( - context(), generic_id_, + context(), loc_id_, generic_id_, context().inst_blocks().AddCanonical(result_arg_ids_)); } diff --git a/toolchain/check/eval.cpp b/toolchain/check/eval.cpp index e195dca3d27b1..b70ac1d0dcd9b 100644 --- a/toolchain/check/eval.cpp +++ b/toolchain/check/eval.cpp @@ -31,13 +31,32 @@ struct SpecificEvalInfo { class EvalContext { public: explicit EvalContext( - Context& context, + Context& context, SemIRLoc fallback_loc, SemIR::SpecificId specific_id = SemIR::SpecificId::Invalid, std::optional specific_eval_info = std::nullopt) : context_(context), + fallback_loc_(fallback_loc), specific_id_(specific_id), specific_eval_info_(specific_eval_info) {} + // Gets the location to use for diagnostics if a better location is + // unavailable. + // TODO: This is also sometimes unavailable. + auto fallback_loc() const -> SemIRLoc { return fallback_loc_; } + + // Returns a location to use to point at an instruction in a diagnostic, given + // a list of instructions that might have an attached location. This is the + // location of the first instruction in the list that has a location if there + // is one, and otherwise the fallback location. + auto GetDiagnosticLoc(llvm::ArrayRef inst_ids) -> SemIRLoc { + for (auto inst_id : inst_ids) { + if (inst_id.is_valid() && context_.insts().GetLocId(inst_id).is_valid()) { + return inst_id; + } + } + return fallback_loc_; + } + // Gets the value of the specified compile-time binding in this context. // Returns `Invalid` if the value is not fixed in this context. auto GetCompileTimeBindValue(SemIR::CompileTimeBindIndex bind_index) @@ -161,6 +180,8 @@ class EvalContext { private: // The type-checking context in which we're performing evaluation. Context& context_; + // The location to use for diagnostics when a better location isn't available. + SemIRLoc fallback_loc_; // The specific that we are evaluating within. SemIR::SpecificId specific_id_; // If we are currently evaluating an eval block for `specific_id_`, @@ -419,7 +440,8 @@ static auto GetConstantValue(EvalContext& eval_context, if (args_id == specific.args_id) { return specific_id; } - return MakeSpecific(eval_context.context(), specific.generic_id, args_id); + return MakeSpecific(eval_context.context(), eval_context.fallback_loc(), + specific.generic_id, args_id); } // Like `GetConstantValue` but does a `FacetTypeId` -> `FacetTypeInfo` @@ -602,7 +624,7 @@ static auto PerformArrayIndex(EvalContext& eval_context, SemIR::ArrayIndex inst) "array index `{0}` is past the end of type {1}", TypedInt, SemIR::TypeId); eval_context.emitter().Emit( - inst.index_id, ArrayIndexOutOfBounds, + eval_context.GetDiagnosticLoc(inst.index_id), ArrayIndexOutOfBounds, {.type = index->type_id, .value = index_val}, aggregate_type_id); return SemIR::ErrorInst::SingletonConstantId; } @@ -1336,7 +1358,7 @@ static auto TryEvalInstInContext(EvalContext& eval_context, CARBON_DIAGNOSTIC(ArrayBoundNegative, Error, "array bound of {0} is negative", TypedInt); eval_context.emitter().Emit( - bound_id, ArrayBoundNegative, + eval_context.GetDiagnosticLoc(bound_id), ArrayBoundNegative, {.type = int_bound->type_id, .value = bound_val}); return false; } @@ -1344,7 +1366,7 @@ static auto TryEvalInstInContext(EvalContext& eval_context, CARBON_DIAGNOSTIC(ArrayBoundTooLarge, Error, "array bound of {0} is too large", TypedInt); eval_context.emitter().Emit( - bound_id, ArrayBoundTooLarge, + eval_context.GetDiagnosticLoc(bound_id), ArrayBoundTooLarge, {.type = int_bound->type_id, .value = bound_val}); return false; } @@ -1393,7 +1415,8 @@ static auto TryEvalInstInContext(EvalContext& eval_context, [&](SemIR::IntType result) { return ValidateIntType( eval_context.context(), - inst_id.is_valid() ? inst_id : int_type.bit_width_id, result); + eval_context.GetDiagnosticLoc({inst_id, int_type.bit_width_id}), + result); }, &SemIR::IntType::bit_width_id); } @@ -1405,7 +1428,9 @@ static auto TryEvalInstInContext(EvalContext& eval_context, eval_context, inst, [&](SemIR::FloatType result) { return ValidateFloatType(eval_context.context(), - float_type.bit_width_id, result); + eval_context.GetDiagnosticLoc( + {inst_id, float_type.bit_width_id}), + result); }, &SemIR::FloatType::bit_width_id); } @@ -1568,7 +1593,8 @@ static auto TryEvalInstInContext(EvalContext& eval_context, } case CARBON_KIND(SemIR::Call call): { - return MakeConstantForCall(eval_context, inst_id, call); + return MakeConstantForCall(eval_context, + eval_context.GetDiagnosticLoc(inst_id), call); } // TODO: These need special handling. @@ -1785,7 +1811,8 @@ static auto TryEvalInstInContext(EvalContext& eval_context, "{0} evaluates to incomplete type {1}", SemIR::TypeId, SemIR::TypeId); return eval_context.emitter().Build( - inst_id, IncompleteTypeInMonomorphization, + eval_context.GetDiagnosticLoc(inst_id), + IncompleteTypeInMonomorphization, require_complete.complete_type_id, complete_type_id); }); if (complete_type_id == SemIR::ErrorInst::SingletonTypeId) { @@ -1842,11 +1869,12 @@ static auto TryEvalInstInContext(EvalContext& eval_context, auto TryEvalInst(Context& context, SemIR::InstId inst_id, SemIR::Inst inst) -> SemIR::ConstantId { - EvalContext eval_context(context); + EvalContext eval_context(context, inst_id); return TryEvalInstInContext(eval_context, inst_id, inst); } -auto TryEvalBlockForSpecific(Context& context, SemIR::SpecificId specific_id, +auto TryEvalBlockForSpecific(Context& context, SemIRLoc loc, + SemIR::SpecificId specific_id, SemIR::GenericInstIndex::Region region) -> SemIR::InstBlockId { auto generic_id = context.specifics().Get(specific_id).generic_id; @@ -1856,20 +1884,27 @@ auto TryEvalBlockForSpecific(Context& context, SemIR::SpecificId specific_id, llvm::SmallVector result; result.resize(eval_block.size(), SemIR::InstId::Invalid); - EvalContext eval_context(context, specific_id, + EvalContext eval_context(context, loc, specific_id, SpecificEvalInfo{ .region = region, .values = result, }); + DiagnosticAnnotationScope annotate_diagnostics( + &context.emitter(), [&](auto& builder) { + CARBON_DIAGNOSTIC(ResolvingSpecificHere, Note, "in {0} used here", + InstIdAsType); + if (loc.is_inst_id && !loc.inst_id.is_valid()) { + return; + } + builder.Note(loc, ResolvingSpecificHere, + GetInstForSpecific(context, specific_id)); + }); + for (auto [i, inst_id] : llvm::enumerate(eval_block)) { auto const_id = TryEvalInstInContext(eval_context, inst_id, context.insts().Get(inst_id)); result[i] = context.constant_values().GetInstId(const_id); - - // TODO: If this becomes possible through monomorphization failure, produce - // a diagnostic and put `SemIR::ErrorInst::SingletonInstId` in the table - // entry. CARBON_CHECK(result[i].is_valid()); } diff --git a/toolchain/check/eval.h b/toolchain/check/eval.h index d56a41ba4ff7c..5c444969eddbe 100644 --- a/toolchain/check/eval.h +++ b/toolchain/check/eval.h @@ -20,7 +20,8 @@ auto TryEvalInst(Context& context, SemIR::InstId inst_id, SemIR::Inst inst) // Evaluates the eval block for a region of a specific. Produces a block // containing the evaluated constant values of the instructions in the eval // block. -auto TryEvalBlockForSpecific(Context& context, SemIR::SpecificId specific_id, +auto TryEvalBlockForSpecific(Context& context, SemIRLoc loc, + SemIR::SpecificId specific_id, SemIR::GenericInstIndex::Region region) -> SemIR::InstBlockId; diff --git a/toolchain/check/generic.cpp b/toolchain/check/generic.cpp index 7f2c297385174..ca37cc70d5cc8 100644 --- a/toolchain/check/generic.cpp +++ b/toolchain/check/generic.cpp @@ -5,11 +5,13 @@ #include "toolchain/check/generic.h" #include "common/map.h" +#include "toolchain/base/kind_switch.h" #include "toolchain/check/eval.h" #include "toolchain/check/generic_region_stack.h" #include "toolchain/check/subst.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/inst.h" +#include "toolchain/sem_ir/typed_insts.h" namespace Carbon::Check { @@ -341,7 +343,7 @@ auto FinishGenericDecl(Context& context, SemIR::InstId decl_id) context.generic_region_stack().Pop(); context.generics().Get(generic_id).decl_block_id = decl_block_id; - auto self_specific_id = MakeSelfSpecific(context, generic_id); + auto self_specific_id = MakeSelfSpecific(context, decl_id, generic_id); context.generics().Get(generic_id).self_specific_id = self_specific_id; return generic_id; } @@ -371,15 +373,16 @@ auto FinishGenericDefinition(Context& context, SemIR::GenericId generic_id) context.generic_region_stack().Pop(); } -auto MakeSpecific(Context& context, SemIR::GenericId generic_id, +auto MakeSpecific(Context& context, SemIRLoc loc, SemIR::GenericId generic_id, SemIR::InstBlockId args_id) -> SemIR::SpecificId { auto specific_id = context.specifics().GetOrAdd(generic_id, args_id); // If this is the first time we've formed this specific, evaluate its decl // block to form information about the specific. if (!context.specifics().Get(specific_id).decl_block_id.is_valid()) { - auto decl_block_id = TryEvalBlockForSpecific( - context, specific_id, SemIR::GenericInstIndex::Region::Declaration); + auto decl_block_id = + TryEvalBlockForSpecific(context, loc, specific_id, + SemIR::GenericInstIndex::Region::Declaration); // Note that TryEvalBlockForSpecific may reallocate the list of specifics, // so re-lookup the specific here. context.specifics().Get(specific_id).decl_block_id = decl_block_id; @@ -388,8 +391,8 @@ auto MakeSpecific(Context& context, SemIR::GenericId generic_id, return specific_id; } -auto MakeSelfSpecific(Context& context, SemIR::GenericId generic_id) - -> SemIR::SpecificId { +auto MakeSelfSpecific(Context& context, SemIRLoc loc, + SemIR::GenericId generic_id) -> SemIR::SpecificId { if (!generic_id.is_valid()) { return SemIR::SpecificId::Invalid; } @@ -409,11 +412,11 @@ auto MakeSelfSpecific(Context& context, SemIR::GenericId generic_id) // TODO: This could be made more efficient. We don't need to perform // substitution here; we know we want identity mappings for all constants and // types. We could also consider not storing the mapping at all in this case. - return MakeSpecific(context, generic_id, args_id); + return MakeSpecific(context, loc, generic_id, args_id); } -auto ResolveSpecificDefinition(Context& context, SemIR::SpecificId specific_id) - -> bool { +auto ResolveSpecificDefinition(Context& context, SemIRLoc loc, + SemIR::SpecificId specific_id) -> bool { auto& specific = context.specifics().Get(specific_id); auto generic_id = specific.generic_id; CARBON_CHECK(generic_id.is_valid(), "Specific with no generic ID"); @@ -426,7 +429,7 @@ auto ResolveSpecificDefinition(Context& context, SemIR::SpecificId specific_id) return false; } auto definition_block_id = TryEvalBlockForSpecific( - context, specific_id, SemIR::GenericInstIndex::Region::Definition); + context, loc, specific_id, SemIR::GenericInstIndex::Region::Definition); // Note that TryEvalBlockForSpecific may reallocate the list of specifics, // so re-lookup the specific here. context.specifics().Get(specific_id).definition_block_id = @@ -435,4 +438,34 @@ auto ResolveSpecificDefinition(Context& context, SemIR::SpecificId specific_id) return true; } +auto GetInstForSpecific(Context& context, SemIR::SpecificId specific_id) + -> SemIR::InstId { + CARBON_CHECK(specific_id.is_valid()); + const auto& specific = context.specifics().Get(specific_id); + const auto& generic = context.generics().Get(specific.generic_id); + auto decl = context.insts().Get(generic.decl_id); + CARBON_KIND_SWITCH(decl) { + case CARBON_KIND(SemIR::ClassDecl class_decl): { + return context.types().GetInstId( + context.GetClassType(class_decl.class_id, specific_id)); + } + case CARBON_KIND(SemIR::InterfaceDecl interface_decl): { + return context.types().GetInstId( + context.GetInterfaceType(interface_decl.interface_id, specific_id)); + } + case SemIR::FunctionDecl::Kind: { + return context.constant_values().GetInstId( + TryEvalInst(context, SemIR::InstId::Invalid, + SemIR::SpecificFunction{ + .type_id = context.GetSingletonType( + SemIR::SpecificFunctionType::SingletonInstId), + .callee_id = generic.decl_id, + .specific_id = specific_id})); + } + default: { + CARBON_FATAL("Unknown kind for generic declaration {0}", decl); + } + } +} + } // namespace Carbon::Check diff --git a/toolchain/check/generic.h b/toolchain/check/generic.h index f9b45d00dbb4c..9813ae405c97d 100644 --- a/toolchain/check/generic.h +++ b/toolchain/check/generic.h @@ -49,29 +49,34 @@ auto RebuildGenericEvalBlock(Context& context, SemIR::GenericId generic_id, // declaration, but not the definition, of the generic. // // `args_id` should be a canonical instruction block referring to constants. -auto MakeSpecific(Context& context, SemIR::GenericId generic_id, +auto MakeSpecific(Context& context, SemIRLoc loc, SemIR::GenericId generic_id, SemIR::InstBlockId args_id) -> SemIR::SpecificId; // Builds a new specific if the given generic is valid. Otherwise returns an // invalid specific. -inline auto MakeSpecificIfGeneric(Context& context, SemIR::GenericId generic_id, +inline auto MakeSpecificIfGeneric(Context& context, SemIRLoc loc, + SemIR::GenericId generic_id, SemIR::InstBlockId args_id) -> SemIR::SpecificId { - return generic_id.is_valid() ? MakeSpecific(context, generic_id, args_id) + return generic_id.is_valid() ? MakeSpecific(context, loc, generic_id, args_id) : SemIR::SpecificId::Invalid; } // Builds the specific that describes how the generic should refer to itself. // For example, for a generic `G(T:! type)`, this is the specific `G(T)`. For an // invalid `generic_id`, returns an invalid specific ID. -auto MakeSelfSpecific(Context& context, SemIR::GenericId generic_id) - -> SemIR::SpecificId; +auto MakeSelfSpecific(Context& context, SemIRLoc loc, + SemIR::GenericId generic_id) -> SemIR::SpecificId; // Attempts to resolve the definition of the given specific, by evaluating the // eval block of the corresponding generic and storing a corresponding value // block in the specific. Returns false if a definition is not available. -auto ResolveSpecificDefinition(Context& context, SemIR::SpecificId specific_id) - -> bool; +auto ResolveSpecificDefinition(Context& context, SemIRLoc loc, + SemIR::SpecificId specific_id) -> bool; + +// Returns an instruction describing the entity named by the given specific. +auto GetInstForSpecific(Context& context, SemIR::SpecificId specific_id) + -> SemIR::InstId; } // namespace Carbon::Check diff --git a/toolchain/check/impl.cpp b/toolchain/check/impl.cpp index 75a8a504e3eac..7ca75fceb9577 100644 --- a/toolchain/check/impl.cpp +++ b/toolchain/check/impl.cpp @@ -33,7 +33,7 @@ static auto NoteAssociatedFunction(Context& context, // Gets the self specific of a generic declaration that is an interface member, // given a specific for an enclosing generic, plus a type to use as `Self`. static auto GetSelfSpecificForInterfaceMemberWithSelfType( - Context& context, SemIR::SpecificId enclosing_specific_id, + Context& context, SemIRLoc loc, SemIR::SpecificId enclosing_specific_id, SemIR::GenericId generic_id, SemIR::TypeId self_type_id) -> SemIR::SpecificId { const auto& generic = context.generics().Get(generic_id); @@ -84,7 +84,7 @@ static auto GetSelfSpecificForInterfaceMemberWithSelfType( } auto args_id = context.inst_blocks().AddCanonical(arg_ids); - return MakeSpecific(context, generic_id, args_id); + return MakeSpecific(context, loc, generic_id, args_id); } // Checks that `impl_function_id` is a valid implementation of the function @@ -115,7 +115,7 @@ static auto CheckAssociatedFunctionImplementation( // parameters. auto interface_function_specific_id = GetSelfSpecificForInterfaceMemberWithSelfType( - context, interface_function_type.specific_id, + context, impl_decl_id, interface_function_type.specific_id, context.functions() .Get(interface_function_type.function_id) .generic_id, diff --git a/toolchain/check/impl_lookup.cpp b/toolchain/check/impl_lookup.cpp index 53bc43686033f..197ea33ae3b15 100644 --- a/toolchain/check/impl_lookup.cpp +++ b/toolchain/check/impl_lookup.cpp @@ -160,7 +160,7 @@ auto LookupInterfaceWitness(Context& context, SemIR::LocId loc_id, if (specific_id.is_valid()) { // We need a definition of the specific `impl` so we can access its // witness. - ResolveSpecificDefinition(context, specific_id); + ResolveSpecificDefinition(context, loc_id, specific_id); } return context.constant_values().GetInstId( SemIR::GetConstantValueInSpecific(context.sem_ir(), specific_id, diff --git a/toolchain/check/import_ref.cpp b/toolchain/check/import_ref.cpp index b01b91d9b4493..33aa2d2728062 100644 --- a/toolchain/check/import_ref.cpp +++ b/toolchain/check/import_ref.cpp @@ -2714,8 +2714,9 @@ static auto FinishPendingGeneric(ImportRefResolver& resolver, SemIR::GenericInstIndex::Region::Declaration); resolver.local_generics().Get(pending.local_id).decl_block_id = decl_block_id; - auto self_specific_id = - MakeSelfSpecific(resolver.local_context(), pending.local_id); + auto local_decl_id = resolver.local_generics().Get(pending.local_id).decl_id; + auto self_specific_id = MakeSelfSpecific(resolver.local_context(), + local_decl_id, pending.local_id); resolver.local_generics().Get(pending.local_id).self_specific_id = self_specific_id; resolver.AddPendingSpecific({.import_id = import_generic.self_specific_id, diff --git a/toolchain/check/subst.cpp b/toolchain/check/subst.cpp index 61ca7d94b366d..5e96504885031 100644 --- a/toolchain/check/subst.cpp +++ b/toolchain/check/subst.cpp @@ -190,7 +190,9 @@ static auto PopOperand(Context& context, Worklist& worklist, SemIR::IdKind kind, auto args_id = PopOperand(context, worklist, SemIR::IdKind::For, specific.args_id.index); - return MakeSpecific(context, specific.generic_id, + // TODO: Provide a location here. + SemIRLoc loc = SemIR::InstId::Invalid; + return MakeSpecific(context, loc, specific.generic_id, SemIR::InstBlockId(args_id)) .index; } diff --git a/toolchain/check/testdata/function/generic/resolve_used.carbon b/toolchain/check/testdata/function/generic/resolve_used.carbon index 51b5a86e01e5e..b8ecc92ee0b71 100644 --- a/toolchain/check/testdata/function/generic/resolve_used.carbon +++ b/toolchain/check/testdata/function/generic/resolve_used.carbon @@ -24,6 +24,9 @@ fn ErrorIfNIsZero(N:! Core.IntLiteral()) { } fn CallNegative() { + // CHECK:STDERR: fail_todo_call_monomorphization_error.carbon:[[@LINE+3]]:3: note: in `ErrorIfNIsZero(0)` used here [ResolvingSpecificHere] + // CHECK:STDERR: ErrorIfNIsZero(0); + // CHECK:STDERR: ^~~~~~~~~~~~~~ ErrorIfNIsZero(0); } diff --git a/toolchain/check/testdata/generic/complete_type.carbon b/toolchain/check/testdata/generic/complete_type.carbon index 15cc795cfc947..b4b8da4eb211c 100644 --- a/toolchain/check/testdata/generic/complete_type.carbon +++ b/toolchain/check/testdata/generic/complete_type.carbon @@ -15,20 +15,23 @@ library "[[@TEST_NAME]]"; class B; class A(T:! type) { - // CHECK:STDERR: fail_incomplete_in_class.carbon:[[@LINE+7]]:10: error: `T` evaluates to incomplete type `B` [IncompleteTypeInMonomorphization] + // CHECK:STDERR: fail_incomplete_in_class.carbon:[[@LINE+6]]:10: error: `T` evaluates to incomplete type `B` [IncompleteTypeInMonomorphization] // CHECK:STDERR: var v: T; // CHECK:STDERR: ^ // CHECK:STDERR: fail_incomplete_in_class.carbon:[[@LINE-6]]:1: note: class was forward declared here [ClassForwardDeclaredHere] // CHECK:STDERR: class B; // CHECK:STDERR: ^~~~~~~~ - // CHECK:STDERR: var v: T; } +// CHECK:STDERR: fail_incomplete_in_class.carbon:[[@LINE+11]]:6: note: in `A(B)` used here [ResolvingSpecificHere] +// CHECK:STDERR: fn F(x: A(B)) {} +// CHECK:STDERR: ^~~~~~~ +// CHECK:STDERR: // CHECK:STDERR: fail_incomplete_in_class.carbon:[[@LINE+7]]:6: error: parameter has incomplete type `A(B)` in function definition [IncompleteTypeInFunctionParam] // CHECK:STDERR: fn F(x: A(B)) {} // CHECK:STDERR: ^~~~~~~ -// CHECK:STDERR: fail_incomplete_in_class.carbon:[[@LINE-16]]:1: note: class was forward declared here [ClassForwardDeclaredHere] +// CHECK:STDERR: fail_incomplete_in_class.carbon:[[@LINE-19]]:1: note: class was forward declared here [ClassForwardDeclaredHere] // CHECK:STDERR: class B; // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: @@ -67,6 +70,9 @@ fn F(T:! type) { var v: T; } +// CHECK:STDERR: fail_incomplete_in_function_at_eof.carbon:[[@LINE+3]]:10: note: in `F(B)` used here [ResolvingSpecificHere] +// CHECK:STDERR: fn G() { F(B); } +// CHECK:STDERR: ^ fn G() { F(B); } // CHECK:STDOUT: --- fail_incomplete_in_class.carbon @@ -125,7 +131,7 @@ fn G() { F(B); } // CHECK:STDOUT: %x.param: %A.2 = value_param runtime_param0 // CHECK:STDOUT: %x: %A.2 = bind_name x, %x.param // CHECK:STDOUT: } -// CHECK:STDOUT: %B.decl.loc26: type = class_decl @B [template = constants.%B] {} {} +// CHECK:STDOUT: %B.decl.loc29: type = class_decl @B [template = constants.%B] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @B { @@ -145,17 +151,17 @@ fn G() { F(B); } // CHECK:STDOUT: %A: type = class_type @A, @A(%T.loc6_9.2) [symbolic = %A (constants.%A.1)] // CHECK:STDOUT: %A.elem: type = unbound_element_type @A.%A (%A.1), @A.%T.loc6_9.2 (%T) [symbolic = %A.elem (constants.%A.elem.1)] // CHECK:STDOUT: %struct_type.v: type = struct_type {.v: @A.%T.loc6_9.2 (%T)} [symbolic = %struct_type.v (constants.%struct_type.v.1)] -// CHECK:STDOUT: %complete_type.loc15_1.2: = complete_type_witness @A.%struct_type.v (%struct_type.v.1) [symbolic = %complete_type.loc15_1.2 (constants.%complete_type.1)] +// CHECK:STDOUT: %complete_type.loc14_1.2: = complete_type_witness @A.%struct_type.v (%struct_type.v.1) [symbolic = %complete_type.loc14_1.2 (constants.%complete_type.1)] // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc6_9.1 [symbolic = %T.loc6_9.2 (constants.%T)] -// CHECK:STDOUT: %.loc14: @A.%A.elem (%A.elem.1) = field_decl v, element0 [template] -// CHECK:STDOUT: %complete_type.loc15_1.1: = complete_type_witness %struct_type.v.1 [symbolic = %complete_type.loc15_1.2 (constants.%complete_type.1)] +// CHECK:STDOUT: %.loc13: @A.%A.elem (%A.elem.1) = field_decl v, element0 [template] +// CHECK:STDOUT: %complete_type.loc14_1.1: = complete_type_witness %struct_type.v.1 [symbolic = %complete_type.loc14_1.2 (constants.%complete_type.1)] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%A.1 -// CHECK:STDOUT: .v = %.loc14 -// CHECK:STDOUT: complete_type_witness = %complete_type.loc15_1.1 +// CHECK:STDOUT: .v = %.loc13 +// CHECK:STDOUT: complete_type_witness = %complete_type.loc14_1.1 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: @@ -183,7 +189,7 @@ fn G() { F(B); } // CHECK:STDOUT: %A => constants.%A.2 // CHECK:STDOUT: %A.elem => constants.%A.elem.2 // CHECK:STDOUT: %struct_type.v => constants.%struct_type.v.2 -// CHECK:STDOUT: %complete_type.loc15_1.2 => constants.%complete_type.2 +// CHECK:STDOUT: %complete_type.loc14_1.2 => constants.%complete_type.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- incomplete_in_function.carbon diff --git a/toolchain/diagnostics/diagnostic_kind.def b/toolchain/diagnostics/diagnostic_kind.def index 738d43113d839..ceb0df8691386 100644 --- a/toolchain/diagnostics/diagnostic_kind.def +++ b/toolchain/diagnostics/diagnostic_kind.def @@ -141,6 +141,7 @@ CARBON_DIAGNOSTIC_KIND(SemanticsTodo) // Location context. CARBON_DIAGNOSTIC_KIND(InImport) +CARBON_DIAGNOSTIC_KIND(ResolvingSpecificHere) // Package/import checking diagnostics. CARBON_DIAGNOSTIC_KIND(IncorrectExtension) diff --git a/toolchain/sem_ir/stringify_type.cpp b/toolchain/sem_ir/stringify_type.cpp index 7a83f22913fdc..ca13382b65f91 100644 --- a/toolchain/sem_ir/stringify_type.cpp +++ b/toolchain/sem_ir/stringify_type.cpp @@ -403,6 +403,16 @@ auto StringifyTypeExpr(const SemIR::File& sem_ir, InstId outer_inst_id) step_stack.PushTypeId(inst.pointee_id); break; } + case CARBON_KIND(SpecificFunction inst): { + auto callee = SemIR::GetCalleeFunction(sem_ir, inst.callee_id); + if (callee.function_id.is_valid()) { + step_stack.PushEntityName(sem_ir.functions().Get(callee.function_id), + inst.specific_id); + } else { + step_stack.PushString(""); + } + break; + } case CARBON_KIND(StructType inst): { auto fields = sem_ir.struct_type_fields().Get(inst.fields_id); if (fields.empty()) { @@ -500,7 +510,6 @@ auto StringifyTypeExpr(const SemIR::File& sem_ir, InstId outer_inst_id) case ReturnSlot::Kind: case ReturnSlotPattern::Kind: case SpecificConstant::Kind: - case SpecificFunction::Kind: case SpliceBlock::Kind: case StringLiteral::Kind: case StructAccess::Kind: