Import all impls in imported IRs. (#3865)

This allows impl lookup to find such impls.

Eventually we'll want to do this more lazily, and filter to the relevant
subset of `impl`s needed for a query. But for simplicity, for now just
import all `impl`s.

---------

Co-authored-by: Jon Ross-Perkins <jperkins@google.com>
This commit is contained in:
Richard Smith
2024-04-05 18:47:55 +00:00
committed by GitHub
co-authored by Jon Ross-Perkins
parent 3ab02b98ee
commit c26241cd03
5 changed files with 238 additions and 5 deletions
+5
View File
@@ -16,6 +16,7 @@
#include "toolchain/check/function.h"
#include "toolchain/check/handle.h"
#include "toolchain/check/import.h"
#include "toolchain/check/import_ref.h"
#include "toolchain/diagnostics/diagnostic.h"
#include "toolchain/diagnostics/diagnostic_emitter.h"
#include "toolchain/lex/token_kind.h"
@@ -755,6 +756,10 @@ static auto CheckParseTree(
InitPackageScopeAndImports(context, unit_info);
// Import all impls declared in imports.
// TODO: Do this selectively when we see an impl query.
ImportImpls(context);
if (!ProcessNodeIds(context, vlog_stream, unit_info.err_tracker)) {
context.sem_ir().set_has_errors(true);
return;
+73 -1
View File
@@ -301,7 +301,11 @@ class ImportRefResolver {
if (!inst_id.is_valid()) {
// Map scopes that aren't associated with an instruction to invalid
// scopes. For now, such scopes aren't used, and we don't have a good way
// to rmmap them.
// to remap them.
return SemIR::NameScopeId::Invalid;
}
if (import_ir_.insts().Is<SemIR::ImplDecl>(inst_id)) {
// TODO: Import the scope for an `impl` definition.
return SemIR::NameScopeId::Invalid;
}
auto const_id = GetLocalConstantId(inst_id);
@@ -409,6 +413,9 @@ class ImportRefResolver {
case SemIR::InstKind::InterfaceDecl:
return TryResolveTypedInst(inst.As<SemIR::InterfaceDecl>(), const_id);
case SemIR::InstKind::InterfaceWitness:
return TryResolveTypedInst(inst.As<SemIR::InterfaceWitness>());
case SemIR::InstKind::InterfaceType:
return TryResolveTypedInst(inst.As<SemIR::InterfaceType>());
@@ -834,6 +841,31 @@ class ImportRefResolver {
return {interface_const_id};
}
auto TryResolveTypedInst(SemIR::InterfaceWitness inst) -> ResolveResult {
auto initial_work = work_stack_.size();
llvm::SmallVector<SemIR::InstId> elements;
auto import_elements = import_ir_.inst_blocks().Get(inst.elements_id);
elements.reserve(import_elements.size());
for (auto import_elem_id : import_elements) {
if (auto const_id = GetLocalConstantId(import_elem_id);
const_id.is_valid()) {
elements.push_back(const_id.inst_id());
}
}
if (HasNewWork(initial_work)) {
return ResolveResult::Retry();
}
CARBON_CHECK(elements.size() == import_elements.size())
<< "Failed to import an element without adding new work.";
auto elements_id = context_.inst_blocks().Add(elements);
return {TryEvalInst(
context_, SemIR::InstId::Invalid,
SemIR::InterfaceWitness{
context_.GetBuiltinType(SemIR::BuiltinKind::WitnessType),
elements_id})};
}
auto TryResolveTypedInst(SemIR::PointerType inst) -> ResolveResult {
auto initial_work = work_stack_.size();
CARBON_CHECK(inst.type_id == SemIR::TypeId::TypeType);
@@ -987,4 +1019,44 @@ auto LoadImportRef(Context& context, SemIR::InstId inst_id, SemIRLoc loc)
}
}
// Imports the impl `import_impl_id` from the imported IR `import_ir`.
static auto ImportImpl(Context& context, SemIR::ImportIRId import_ir_id,
const SemIR::File& import_ir,
SemIR::ImplId import_impl_id) -> void {
// Resolve the imported impl to a local impl ID.
ImportRefResolver resolver(context, import_ir_id);
const auto& import_impl = import_ir.impls().Get(import_impl_id);
auto self_id = resolver.ResolveType(import_impl.self_id);
auto constraint_id = resolver.ResolveType(import_impl.constraint_id);
// Import the definition if the impl is defined.
// TODO: Do we need to check for multiple definitions?
auto impl_id = context.impls().LookupOrAdd(self_id, constraint_id);
if (import_impl.is_defined()) {
// TODO: Create a scope for the `impl` if necessary.
// TODO: Consider importing the definition_id.
auto& impl = context.impls().Get(impl_id);
impl.witness_id = context.AddImportRef(
{.ir_id = import_ir_id, .inst_id = import_impl.witness_id});
}
}
// TODO: This doesn't belong in this file. Consider moving the import resolver
// and this file elsewhere.
auto ImportImpls(Context& context) -> void {
for (auto [import_index, import_ir] :
llvm::enumerate(context.import_irs().array_ref())) {
if (!import_ir.sem_ir) {
continue;
}
auto import_ir_id = SemIR::ImportIRId(import_index);
for (auto impl_index : llvm::seq(import_ir.sem_ir->impls().size())) {
auto impl_id = SemIR::ImplId(impl_index);
ImportImpl(context, import_ir_id, *import_ir.sem_ir, impl_id);
}
}
}
} // namespace Carbon::Check
+3
View File
@@ -15,6 +15,9 @@ namespace Carbon::Check {
auto LoadImportRef(Context& context, SemIR::InstId inst_id, SemIRLoc loc)
-> void;
// Load all impls declared in IRs imported into this context.
auto ImportImpls(Context& context) -> void;
} // namespace Carbon::Check
#endif // CARBON_TOOLCHAIN_CHECK_IMPORT_REF_H_
+9 -4
View File
@@ -5,6 +5,7 @@
#include "llvm/ADT/STLExtras.h"
#include "toolchain/check/context.h"
#include "toolchain/check/convert.h"
#include "toolchain/check/import_ref.h"
#include "toolchain/check/subst.h"
#include "toolchain/diagnostics/diagnostic_emitter.h"
#include "toolchain/sem_ir/inst.h"
@@ -120,7 +121,7 @@ static auto ScopeNeedsImplLookup(Context& context,
// Given a type and an interface, searches for an impl that describes how that
// type implements that interface, and returns the corresponding witness.
// Returns an invalid InstId if no matching impl is found.
static auto LookupInterfaceWitness(Context& context,
static auto LookupInterfaceWitness(Context& context, SemIRLoc loc,
SemIR::ConstantId type_const_id,
SemIR::InterfaceId interface_id)
-> SemIR::InstId {
@@ -141,7 +142,11 @@ static auto LookupInterfaceWitness(Context& context,
if (interface_type->interface_id != interface_id) {
continue;
}
// TODO: Diagnose if the impl isn't defined yet?
if (!impl.witness_id.is_valid()) {
// TODO: Diagnose if the impl isn't defined yet?
return SemIR::InstId::Invalid;
}
LoadImportRef(context, impl.witness_id, loc);
return impl.witness_id;
}
return SemIR::InstId::Invalid;
@@ -154,8 +159,8 @@ static auto PerformImplLookup(Context& context, Parse::NodeId node_id,
SemIR::AssociatedEntityType assoc_type,
SemIR::InstId member_id) -> SemIR::InstId {
auto& interface = context.interfaces().Get(assoc_type.interface_id);
auto witness_id =
LookupInterfaceWitness(context, type_const_id, assoc_type.interface_id);
auto witness_id = LookupInterfaceWitness(context, node_id, type_const_id,
assoc_type.interface_id);
if (!witness_id.is_valid()) {
CARBON_DIAGNOSTIC(MissingImplInMemberAccess, Error,
"Cannot access member of interface {0} in type {1} "
+148
View File
@@ -0,0 +1,148 @@
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
// AUTOUPDATE
// --- impl.carbon
package Impl api;
interface HasF {
fn F();
}
class C {}
impl C as HasF {
fn F() {}
}
// --- use.carbon
import Impl;
fn G(c: Impl.C) {
c.(Impl.HasF.F)();
}
// CHECK:STDOUT: --- impl.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: %.1: type = interface_type @HasF [template]
// CHECK:STDOUT: %.2: type = assoc_entity_type @HasF, <function> [template]
// CHECK:STDOUT: %.3: <associated <function> in HasF> = assoc_entity element0, @HasF.%F [template]
// CHECK:STDOUT: %C: type = class_type @C [template]
// CHECK:STDOUT: %.4: type = struct_type {} [template]
// CHECK:STDOUT: %.5: <witness> = interface_witness (@impl.%F) [template]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {
// CHECK:STDOUT: package: <namespace> = namespace [template] {
// CHECK:STDOUT: .HasF = %HasF.decl
// CHECK:STDOUT: .C = %C.decl
// CHECK:STDOUT: }
// CHECK:STDOUT: %HasF.decl: type = interface_decl @HasF [template = constants.%.1] {}
// CHECK:STDOUT: %C.decl: type = class_decl @C [template = constants.%C] {}
// CHECK:STDOUT: impl_decl @impl {
// CHECK:STDOUT: %C.ref: type = name_ref C, %C.decl [template = constants.%C]
// CHECK:STDOUT: %HasF.ref: type = name_ref HasF, %HasF.decl [template = constants.%.1]
// CHECK:STDOUT: }
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: interface @HasF {
// CHECK:STDOUT: %Self: HasF = bind_symbolic_name Self [symbolic]
// CHECK:STDOUT: %F: <function> = fn_decl @F.1 [template] {}
// CHECK:STDOUT: %.loc5: <associated <function> in HasF> = assoc_entity element0, %F [template = constants.%.3]
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .Self = %Self
// CHECK:STDOUT: .F = %.loc5
// CHECK:STDOUT: witness = (%F)
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: impl @impl: C as HasF {
// CHECK:STDOUT: %F: <function> = fn_decl @F.2 [template] {}
// CHECK:STDOUT: %.1: <witness> = interface_witness (%F) [template = constants.%.5]
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .F = %F
// CHECK:STDOUT: witness = %.1
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: class @C {
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .Self = constants.%C
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @F.1();
// CHECK:STDOUT:
// CHECK:STDOUT: fn @F.2() {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: --- use.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: %C: type = class_type @C [template]
// CHECK:STDOUT: %.1: type = struct_type {} [template]
// CHECK:STDOUT: %.2: type = interface_type @HasF [template]
// CHECK:STDOUT: %.3: type = tuple_type () [template]
// CHECK:STDOUT: %.4: type = ptr_type {} [template]
// CHECK:STDOUT: %.5: type = assoc_entity_type @HasF, <function> [template]
// CHECK:STDOUT: %.6: <associated <function> in HasF> = assoc_entity element0, file.%import_ref.8 [template]
// CHECK:STDOUT: %.7: <witness> = interface_witness (<unexpected instref inst+31>) [template]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {
// CHECK:STDOUT: package: <namespace> = namespace [template] {
// CHECK:STDOUT: .Impl = %Impl
// CHECK:STDOUT: .G = %G
// CHECK:STDOUT: }
// CHECK:STDOUT: %Impl: <namespace> = namespace [template] {}
// CHECK:STDOUT: %C.decl: invalid = class_decl @C [template = constants.%C] {}
// CHECK:STDOUT: %import_ref.1 = import_ref ir2, inst+9, unloaded
// CHECK:STDOUT: %HasF.decl: invalid = interface_decl @HasF [template = constants.%.2] {}
// CHECK:STDOUT: %import_ref.2: <associated <function> in HasF> = import_ref ir2, inst+6, loc_20 [template = constants.%.6]
// CHECK:STDOUT: %import_ref.3 = import_ref ir2, inst+3, unloaded
// CHECK:STDOUT: %import_ref.4 = import_ref ir2, inst+4, unloaded
// CHECK:STDOUT: %import_ref.5: <witness> = import_ref ir2, inst+15, loc_22 [template = constants.%.7]
// CHECK:STDOUT: %import_ref.6: type = import_ref ir2, inst+8, loc_10 [template = constants.%C]
// CHECK:STDOUT: %G: <function> = fn_decl @G [template] {
// CHECK:STDOUT: %Impl.ref: <namespace> = name_ref Impl, %Impl [template = %Impl]
// CHECK:STDOUT: %C.ref: type = name_ref C, %import_ref.6 [template = constants.%C]
// CHECK:STDOUT: %c.loc4_6.1: C = param c
// CHECK:STDOUT: @G.%c: C = bind_name c, %c.loc4_6.1
// CHECK:STDOUT: }
// CHECK:STDOUT: %import_ref.7: type = import_ref ir2, inst+1, loc_18 [template = constants.%.2]
// CHECK:STDOUT: %import_ref.8 = import_ref ir2, inst+4, unloaded
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: interface @HasF {
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .F = file.%import_ref.2
// CHECK:STDOUT: .Self = file.%import_ref.3
// CHECK:STDOUT: witness = (file.%import_ref.4)
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: impl @impl: C as HasF;
// CHECK:STDOUT:
// CHECK:STDOUT: class @C {
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .Self = file.%import_ref.1
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @G(%c: C) {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %c.ref: C = name_ref c, %c
// CHECK:STDOUT: %Impl.ref: <namespace> = name_ref Impl, file.%Impl [template = file.%Impl]
// CHECK:STDOUT: %HasF.ref: type = name_ref HasF, file.%import_ref.7 [template = constants.%.2]
// CHECK:STDOUT: %F.ref: <associated <function> in HasF> = name_ref F, file.%import_ref.2 [template = constants.%.6]
// CHECK:STDOUT: %.1: <function> = interface_witness_access file.%import_ref.5, element0 [template = <unexpected instref inst+31>]
// CHECK:STDOUT: %.loc5: init () = call %.1()
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @F();
// CHECK:STDOUT: