mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-05 22:02:55 +01:00
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:
co-authored by
Jon Ross-Perkins
parent
3ab02b98ee
commit
c26241cd03
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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_
|
||||
|
||||
@@ -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
@@ -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:
|
||||
Reference in New Issue
Block a user