Selaa lähdekoodia

Add import support for remaining decl types. (#3651)

I'd excluded these initially just because I was thinking towards copies,
but under the current model I'm trying to catch all the decl types just
for consistency. Note references will still be a TODO error
(LazyImportRef is already tested for this, it just didn't feel necessary
to add individual tests while I try to sort out behavior).

Fixes an oversight where declarations in an entity's scope were being
added to the list of exports.

Note I'm trimming some Import API arguments as now-unused.
Jon Ross-Perkins 2 vuotta sitten
vanhempi
sitoutus
f4a741903f

+ 2 - 2
toolchain/check/check.cpp

@@ -165,8 +165,7 @@ static auto InitPackageScopeAndImports(Context& context, UnitInfo& unit_info)
     bool error_in_import = self_import->second.has_load_error;
     for (const auto& import : self_import->second.imports) {
       const auto& import_sem_ir = **import.unit_info->unit->sem_ir;
-      Import(context, namespace_type_id, self_import->second.node,
-             import_sem_ir);
+      Import(context, namespace_type_id, import_sem_ir);
       error_in_import = error_in_import || import_sem_ir.name_scopes()
                                                .Get(SemIR::NameScopeId::Package)
                                                .has_error;
@@ -183,6 +182,7 @@ static auto InitPackageScopeAndImports(Context& context, UnitInfo& unit_info)
     // Push the scope; there are no names to add.
     context.PushScope(package_inst_id, SemIR::NameScopeId::Package);
   }
+  CARBON_CHECK(context.current_scope_index() == ScopeIndex::Package);
 
   for (auto& [package_id, package_imports] : unit_info.package_imports_map) {
     if (!package_id.is_valid()) {

+ 3 - 2
toolchain/check/context.h

@@ -547,8 +547,9 @@ class Context {
   llvm::SmallVector<std::pair<ScopeIndex, SemIR::NameScopeId>>
       non_lexical_scope_stack_;
 
-  // The index of the next scope that will be pushed onto scope_stack_.
-  ScopeIndex next_scope_index_ = ScopeIndex(0);
+  // The index of the next scope that will be pushed onto scope_stack_. The
+  // first is always the package scope.
+  ScopeIndex next_scope_index_ = ScopeIndex::Package;
 
   // The stack used for qualified declaration name construction.
   DeclNameStack decl_name_stack_;

+ 7 - 1
toolchain/check/decl_name_stack.cpp

@@ -83,7 +83,13 @@ auto DeclNameStack::LookupOrAddName(NameContext name_context,
                                      QualifiedDeclOutsideScopeEntity);
           }
         }
-        context_->AddExport(target_id);
+
+        // Exports are only tracked when the declaration is at the file-level
+        // scope. Otherwise, it's in some other entity, such as a class.
+        if (name_context.enclosing_scope == ScopeIndex::Package) {
+          context_->AddExport(target_id);
+        }
+
         auto [_, success] = name_scope.names.insert(
             {name_context.unresolved_name_id, target_id});
         CARBON_CHECK(success)

+ 16 - 9
toolchain/check/import.cpp

@@ -17,8 +17,7 @@ namespace Carbon::Check {
 
 // Returns name information for the entity, corresponding to IDs in the import
 // IR rather than the current IR. May return Invalid for a TODO.
-static auto GetImportName(Parse::NodeId parse_node, Context& context,
-                          const SemIR::File& import_sem_ir,
+static auto GetImportName(const SemIR::File& import_sem_ir,
                           SemIR::Inst import_inst)
     -> std::pair<SemIR::NameId, SemIR::NameScopeId> {
   switch (import_inst.kind()) {
@@ -28,12 +27,24 @@ static auto GetImportName(Parse::NodeId parse_node, Context& context,
       return {bind_name.name_id, bind_name.enclosing_scope_id};
     }
 
+    case SemIR::InstKind::ClassDecl: {
+      const auto& class_info = import_sem_ir.classes().Get(
+          import_inst.As<SemIR::ClassDecl>().class_id);
+      return {class_info.name_id, class_info.enclosing_scope_id};
+    }
+
     case SemIR::InstKind::FunctionDecl: {
       const auto& function = import_sem_ir.functions().Get(
           import_inst.As<SemIR::FunctionDecl>().function_id);
       return {function.name_id, function.enclosing_scope_id};
     }
 
+    case SemIR::InstKind::InterfaceDecl: {
+      const auto& interface = import_sem_ir.interfaces().Get(
+          import_inst.As<SemIR::InterfaceDecl>().interface_id);
+      return {interface.name_id, interface.enclosing_scope_id};
+    }
+
     case SemIR::InstKind::Namespace: {
       const auto& scope = import_sem_ir.name_scopes().Get(
           import_inst.As<SemIR::Namespace>().name_scope_id);
@@ -41,10 +52,7 @@ static auto GetImportName(Parse::NodeId parse_node, Context& context,
     }
 
     default:
-      context.TODO(parse_node, (llvm::Twine("Support GetImportName of ") +
-                                import_inst.kind().name())
-                                   .str());
-      return {SemIR::NameId::Invalid, SemIR::NameScopeId::Invalid};
+      CARBON_FATAL() << "Unsupported export kind: " << import_inst;
   }
 }
 
@@ -172,15 +180,14 @@ static auto CopyEnclosingNameScopesFromImportIR(
 }
 
 auto Import(Context& context, SemIR::TypeId namespace_type_id,
-            Parse::NodeId import_node, const SemIR::File& import_sem_ir)
-    -> void {
+            const SemIR::File& import_sem_ir) -> void {
   auto ir_id = context.cross_ref_irs().Add(&import_sem_ir);
 
   for (const auto import_inst_id :
        import_sem_ir.inst_blocks().Get(SemIR::InstBlockId::Exports)) {
     auto import_inst = import_sem_ir.insts().Get(import_inst_id);
     auto [import_name_id, import_enclosing_scope_id] =
-        GetImportName(import_node, context, import_sem_ir, import_inst);
+        GetImportName(import_sem_ir, import_inst);
     // TODO: This should only be invalid when GetImportName for an inst
     // isn't yet implemented. Long-term this should be removed.
     if (!import_name_id.is_valid()) {

+ 1 - 2
toolchain/check/import.h

@@ -12,8 +12,7 @@ namespace Carbon::Check {
 
 // Add imports to the root block.
 auto Import(Context& context, SemIR::TypeId namespace_type_id,
-            Parse::NodeId import_node, const SemIR::File& import_sem_ir)
-    -> void;
+            const SemIR::File& import_sem_ir) -> void;
 
 }  // namespace Carbon::Check
 

+ 4 - 0
toolchain/check/scope_index.h

@@ -18,9 +18,13 @@ namespace Carbon::Check {
 // `ScopeIndex` values are comparable. Lower `ScopeIndex` values correspond to
 // scopes entered earlier in the file.
 struct ScopeIndex : public IndexBase, public Printable<ScopeIndex> {
+  static const ScopeIndex Package;
+
   using IndexBase::IndexBase;
 };
 
+constexpr ScopeIndex ScopeIndex::Package = ScopeIndex(0);
+
 }  // namespace Carbon::Check
 
 #endif  // CARBON_TOOLCHAIN_CHECK_SCOPE_INDEX_H_

+ 64 - 0
toolchain/check/testdata/class/import.carbon

@@ -0,0 +1,64 @@
+// 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
+
+// --- a.carbon
+
+library "a" api;
+
+class Empty {
+}
+
+class ForwardDeclared;
+
+class ForwardDeclared {
+  fn F();
+}
+
+// --- b.carbon
+
+library "b" api;
+
+import library "a";
+
+// TODO: When ready, consider tests of basic import functionality.
+
+// CHECK:STDOUT: --- a.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %Empty: type = class_type @Empty [template]
+// CHECK:STDOUT:   %.1: type = struct_type {} [template]
+// CHECK:STDOUT:   %ForwardDeclared: type = class_type @ForwardDeclared [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace {.Empty = %Empty.decl, .ForwardDeclared = %ForwardDeclared.decl.loc7} [template]
+// CHECK:STDOUT:   %Empty.decl = class_decl @Empty, ()
+// CHECK:STDOUT:   %ForwardDeclared.decl.loc7 = class_decl @ForwardDeclared, ()
+// CHECK:STDOUT:   %ForwardDeclared.decl.loc9 = class_decl @ForwardDeclared, ()
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @Empty {
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @ForwardDeclared {
+// CHECK:STDOUT:   %F: <function> = fn_decl @F [template]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .F = %F
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F();
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- b.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace {.Empty = %lazy_import_ref.1, .ForwardDeclared = %lazy_import_ref.2} [template]
+// CHECK:STDOUT:   %lazy_import_ref.1 = lazy_import_ref ir1, inst+1
+// CHECK:STDOUT:   %lazy_import_ref.2 = lazy_import_ref ir1, inst+4
+// CHECK:STDOUT: }
+// CHECK:STDOUT:

+ 36 - 0
toolchain/check/testdata/function/declaration/import.carbon

@@ -0,0 +1,36 @@
+// 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
+
+// --- a.carbon
+
+library "a" api;
+
+fn F();
+
+// --- b.carbon
+
+library "b" api;
+
+import library "a";
+
+// TODO: When ready, consider tests of basic import functionality.
+
+// CHECK:STDOUT: --- a.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace {.F = %F} [template]
+// CHECK:STDOUT:   %F: <function> = fn_decl @F [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F();
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- b.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace {.F = %lazy_import_ref} [template]
+// CHECK:STDOUT:   %lazy_import_ref = lazy_import_ref ir1, inst+1
+// CHECK:STDOUT: }
+// CHECK:STDOUT:

+ 39 - 0
toolchain/check/testdata/function/definition/import.carbon

@@ -0,0 +1,39 @@
+// 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
+
+// --- a.carbon
+
+library "a" api;
+
+fn F() {}
+
+// --- b.carbon
+
+library "b" api;
+
+import library "a";
+
+// TODO: When ready, consider tests of basic import functionality.
+
+// CHECK:STDOUT: --- a.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace {.F = %F} [template]
+// CHECK:STDOUT:   %F: <function> = fn_decl @F [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- b.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace {.F = %lazy_import_ref} [template]
+// CHECK:STDOUT:   %lazy_import_ref = lazy_import_ref ir1, inst+1
+// CHECK:STDOUT: }
+// CHECK:STDOUT:

+ 58 - 0
toolchain/check/testdata/interface/import.carbon

@@ -0,0 +1,58 @@
+// 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
+
+// --- a.carbon
+
+library "a" api;
+
+interface Empty {
+}
+
+interface ForwardDeclared;
+
+interface ForwardDeclared {
+  fn F();
+}
+
+// --- b.carbon
+
+library "b" api;
+
+import library "a";
+
+// TODO: When ready, consider tests of basic import functionality.
+
+// CHECK:STDOUT: --- a.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace {.Empty = %Empty.decl, .ForwardDeclared = %ForwardDeclared.decl.loc7} [template]
+// CHECK:STDOUT:   %Empty.decl = interface_decl @Empty, ()
+// CHECK:STDOUT:   %ForwardDeclared.decl.loc7 = interface_decl @ForwardDeclared, ()
+// CHECK:STDOUT:   %ForwardDeclared.decl.loc9 = interface_decl @ForwardDeclared, ()
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @Empty {
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @ForwardDeclared {
+// CHECK:STDOUT:   %F: <function> = fn_decl @F [template]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .F = %F
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F();
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- b.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace {.Empty = %lazy_import_ref.1, .ForwardDeclared = %lazy_import_ref.2} [template]
+// CHECK:STDOUT:   %lazy_import_ref.1 = lazy_import_ref ir1, inst+1
+// CHECK:STDOUT:   %lazy_import_ref.2 = lazy_import_ref ir1, inst+2
+// CHECK:STDOUT: }
+// CHECK:STDOUT: