Skip to content

Commit 8567e02

Browse files
authored
Refactor handling of cross-package imports. (#3783)
This revamps the support for cross-package imports, making them look more like a namespace. The planned model is mentioned on [#toolchain](https://discord.com/channels/655572317891461132/655578254970716160/1217586076022210670). This does not implement name lookup into the new namespace structure. A few key changes in this PR (it's a little sprawling) are: - Moves logic for adding package imports from context.* to import.* - Remove SemIR::Import, which was the prior model. This is instead now a SemIR::Namespace with the NameScope getting a new import_ir_scopes field. - Allow SemIR::Namespace to use Parse::ImportDirectiveId in addition to the prior Parse::NamespaceId - The import_ir_scopes field includes a NameScopeId so that as we traverse to child namespaces, we can directly perform name lookup in the other IR. - is_closed_import now tracks whether a namespace comes from a different package. This has a diagnostic implemented in decl_name_stack.
1 parent 39462d4 commit 8567e02

22 files changed

+487
-169
lines changed

toolchain/check/check.cpp

Lines changed: 29 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -114,12 +114,13 @@ struct UnitInfo {
114114
struct PackageImports {
115115
// Use the constructor so that the SmallVector is only constructed
116116
// as-needed.
117-
explicit PackageImports(Parse::NodeId node) : node(node) {}
117+
explicit PackageImports(Parse::ImportDirectiveId node_id)
118+
: node_id(node_id) {}
118119

119120
// The first `import` directive in the file, which declared the package's
120121
// identifier (even if the import failed). Used for associating diagnostics
121122
// not specific to a single import.
122-
Parse::NodeId node;
123+
Parse::ImportDirectiveId node_id;
123124
// Whether there's an import that failed to load.
124125
bool has_load_error = false;
125126
// The list of valid imports.
@@ -181,10 +182,11 @@ static auto InitPackageScopeAndImports(Context& context, UnitInfo& unit_info)
181182
bool error_in_import = self_import->second.has_load_error;
182183
for (const auto& import : self_import->second.imports) {
183184
const auto& import_sem_ir = **import.unit_info->unit->sem_ir;
184-
Import(context, namespace_type_id, import_sem_ir);
185-
error_in_import = error_in_import || import_sem_ir.name_scopes()
186-
.Get(SemIR::NameScopeId::Package)
187-
.has_error;
185+
ImportLibraryFromCurrentPackage(context, namespace_type_id,
186+
import_sem_ir);
187+
error_in_import |= import_sem_ir.name_scopes()
188+
.Get(SemIR::NameScopeId::Package)
189+
.has_error;
188190
}
189191

190192
// If an import of the current package caused an error for the imported
@@ -210,8 +212,9 @@ static auto InitPackageScopeAndImports(Context& context, UnitInfo& unit_info)
210212
for (auto import : package_imports.imports) {
211213
sem_irs.push_back(&**import.unit_info->unit->sem_ir);
212214
}
213-
context.AddPackageImports(package_imports.node, package_id, sem_irs,
214-
package_imports.has_load_error);
215+
ImportLibrariesFromOtherPackage(context, namespace_type_id,
216+
package_imports.node_id, package_id,
217+
sem_irs, package_imports.has_load_error);
215218
}
216219

217220
context.import_ir_constant_values().resize(
@@ -338,12 +341,12 @@ static auto TrackImport(
338341
if (explicit_import_map) {
339342
// Diagnose redundant imports.
340343
if (auto [insert_it, success] =
341-
explicit_import_map->insert({import_key, import.node});
344+
explicit_import_map->insert({import_key, import.node_id});
342345
!success) {
343346
CARBON_DIAGNOSTIC(RepeatedImport, Error,
344347
"Library imported more than once.");
345348
CARBON_DIAGNOSTIC(FirstImported, Note, "First import here.");
346-
unit_info.emitter.Build(import.node, RepeatedImport)
349+
unit_info.emitter.Build(import.node_id, RepeatedImport)
347350
.Note(insert_it->second, FirstImported)
348351
.Emit();
349352
return;
@@ -377,7 +380,7 @@ static auto TrackImport(
377380
CARBON_DIAGNOSTIC(ImportSelf, Error, "File cannot import itself.");
378381
bool is_impl =
379382
!packaging || packaging->api_or_impl == Parse::Tree::ApiOrImpl::Impl;
380-
unit_info.emitter.Emit(import.node,
383+
unit_info.emitter.Emit(import.node_id,
381384
is_impl ? ExplicitImportApi : ImportSelf);
382385
return;
383386
}
@@ -388,7 +391,7 @@ static auto TrackImport(
388391
is_import_default_library) {
389392
CARBON_DIAGNOSTIC(ImportMainDefaultLibrary, Error,
390393
"Cannot import `Main//default`.");
391-
unit_info.emitter.Emit(import.node, ImportMainDefaultLibrary);
394+
unit_info.emitter.Emit(import.node_id, ImportMainDefaultLibrary);
392395

393396
return;
394397
}
@@ -400,15 +403,15 @@ static auto TrackImport(
400403
CARBON_DIAGNOSTIC(
401404
ImportCurrentPackageByName, Error,
402405
"Imports from the current package must omit the package name.");
403-
unit_info.emitter.Emit(import.node, ImportCurrentPackageByName);
406+
unit_info.emitter.Emit(import.node_id, ImportCurrentPackageByName);
404407
return;
405408
}
406409

407410
// Diagnose explicit imports from `Main`.
408411
if (is_explicit_main) {
409412
CARBON_DIAGNOSTIC(ImportMainPackage, Error,
410413
"Cannot import `Main` from other packages.");
411-
unit_info.emitter.Emit(import.node, ImportMainPackage);
414+
unit_info.emitter.Emit(import.node_id, ImportMainPackage);
412415
return;
413416
}
414417
}
@@ -420,9 +423,9 @@ static auto TrackImport(
420423
}
421424

422425
// Get the package imports.
423-
auto package_imports_it =
424-
unit_info.package_imports_map.try_emplace(import.package_id, import.node)
425-
.first;
426+
auto package_imports_it = unit_info.package_imports_map
427+
.try_emplace(import.package_id, import.node_id)
428+
.first;
426429

427430
if (auto api = api_map.find(import_key); api != api_map.end()) {
428431
// Add references between the file and imported api.
@@ -435,8 +438,9 @@ static auto TrackImport(
435438
CARBON_DIAGNOSTIC(LibraryApiNotFound, Error,
436439
"Corresponding API not found.");
437440
CARBON_DIAGNOSTIC(ImportNotFound, Error, "Imported API not found.");
438-
unit_info.emitter.Emit(
439-
import.node, explicit_import_map ? ImportNotFound : LibraryApiNotFound);
441+
unit_info.emitter.Emit(import.node_id, explicit_import_map
442+
? ImportNotFound
443+
: LibraryApiNotFound);
440444
}
441445
}
442446

@@ -463,9 +467,9 @@ static auto BuildApiMapAndDiagnosePackaging(
463467
"`Main//default` must omit `package` directive.");
464468
CARBON_DIAGNOSTIC(ExplicitMainLibrary, Error,
465469
"Use `library` directive in `Main` package libraries.");
466-
unit_info.emitter.Emit(packaging->names.node, import_key.second.empty()
467-
? ExplicitMainPackage
468-
: ExplicitMainLibrary);
470+
unit_info.emitter.Emit(packaging->names.node_id,
471+
import_key.second.empty() ? ExplicitMainPackage
472+
: ExplicitMainLibrary);
469473
continue;
470474
}
471475

@@ -485,7 +489,7 @@ static auto BuildApiMapAndDiagnosePackaging(
485489
CARBON_DIAGNOSTIC(DuplicateLibraryApi, Error,
486490
"Library's API previously provided by `{0}`.",
487491
std::string);
488-
unit_info.emitter.Emit(packaging->names.node, DuplicateLibraryApi,
492+
unit_info.emitter.Emit(packaging->names.node_id, DuplicateLibraryApi,
489493
prev_filename.str());
490494
} else {
491495
CARBON_DIAGNOSTIC(DuplicateMainApi, Error,
@@ -512,7 +516,7 @@ static auto BuildApiMapAndDiagnosePackaging(
512516
"File extension of `{0}` required for `{1}`.",
513517
llvm::StringLiteral, Lex::TokenKind);
514518
auto diag = unit_info.emitter.Build(
515-
packaging ? packaging->names.node : Parse::NodeId::Invalid,
519+
packaging ? packaging->names.node_id : Parse::NodeId::Invalid,
516520
IncorrectExtension, want_ext,
517521
is_impl ? Lex::TokenKind::Impl : Lex::TokenKind::Api);
518522
if (is_api_with_impl_ext) {
@@ -608,7 +612,7 @@ auto CheckParseTrees(const SemIR::File& builtin_ir,
608612
CARBON_DIAGNOSTIC(ImportCycleDetected, Error,
609613
"Import cannot be used due to a cycle. Cycle "
610614
"must be fixed to import.");
611-
unit_info.emitter.Emit(import_it->names.node,
615+
unit_info.emitter.Emit(import_it->names.node_id,
612616
ImportCycleDetected);
613617
// Make this look the same as an import which wasn't found.
614618
package_imports.has_load_error = true;

toolchain/check/context.cpp

Lines changed: 4 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -133,14 +133,14 @@ auto Context::ReplaceInstBeforeConstantUse(
133133
constant_values().Set(inst_id, const_id);
134134
}
135135

136-
auto Context::DiagnoseDuplicateName(SemIR::InstId dup_def_id,
137-
SemIR::InstId prev_def_id) -> void {
136+
auto Context::DiagnoseDuplicateName(SemIRLocation dup_def,
137+
SemIRLocation prev_def) -> void {
138138
CARBON_DIAGNOSTIC(NameDeclDuplicate, Error,
139139
"Duplicate name being declared in the same scope.");
140140
CARBON_DIAGNOSTIC(NameDeclPrevious, Note,
141141
"Name is previously declared here.");
142-
emitter_->Build(dup_def_id, NameDeclDuplicate)
143-
.Note(prev_def_id, NameDeclPrevious)
142+
emitter_->Build(dup_def, NameDeclDuplicate)
143+
.Note(prev_def, NameDeclPrevious)
144144
.Emit();
145145
}
146146

@@ -182,43 +182,6 @@ auto Context::NoteUndefinedInterface(SemIR::InterfaceId interface_id,
182182
}
183183
}
184184

185-
auto Context::AddPackageImports(Parse::NodeId import_node,
186-
IdentifierId package_id,
187-
llvm::ArrayRef<const SemIR::File*> sem_irs,
188-
bool has_load_error) -> void {
189-
CARBON_CHECK(has_load_error || !sem_irs.empty())
190-
<< "There should be either a load error or at least one IR.";
191-
192-
auto name_id = SemIR::NameId::ForIdentifier(package_id);
193-
194-
SemIR::ImportIRId first_id(import_irs().size());
195-
for (const auto* sem_ir : sem_irs) {
196-
import_irs().Add(sem_ir);
197-
}
198-
if (has_load_error) {
199-
import_irs().Add(nullptr);
200-
}
201-
SemIR::ImportIRId last_id(import_irs().size() - 1);
202-
203-
auto type_id = GetBuiltinType(SemIR::BuiltinKind::NamespaceType);
204-
auto inst_id =
205-
AddInst({import_node, SemIR::Import{.type_id = type_id,
206-
.first_import_ir_id = first_id,
207-
.last_import_ir_id = last_id}});
208-
209-
// Add the import to lookup. Should always succeed because imports will be
210-
// uniquely named.
211-
AddNameToLookup(name_id, inst_id);
212-
// Add a name for formatted output. This isn't used in name lookup in order
213-
// to reduce indirection, but it's separate from the Import because it
214-
// otherwise fits in an Inst.
215-
auto bind_name_id = bind_names().Add(
216-
{.name_id = name_id, .enclosing_scope_id = SemIR::NameScopeId::Package});
217-
AddInst({import_node, SemIR::BindName{.type_id = type_id,
218-
.bind_name_id = bind_name_id,
219-
.value_id = inst_id}});
220-
}
221-
222185
auto Context::AddNameToLookup(SemIR::NameId name_id, SemIR::InstId target_id)
223186
-> void {
224187
if (auto existing = scope_stack().LookupOrAddName(name_id, target_id);
@@ -837,7 +800,6 @@ class TypeCompleter {
837800
case SemIR::FieldDecl::Kind:
838801
case SemIR::FunctionDecl::Kind:
839802
case SemIR::ImplDecl::Kind:
840-
case SemIR::Import::Kind:
841803
case SemIR::ImportRefUnused::Kind:
842804
case SemIR::InitializeFrom::Kind:
843805
case SemIR::InterfaceDecl::Kind:

toolchain/check/context.h

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -106,12 +106,6 @@ class Context {
106106
sem_ir().insts().SetNodeId(inst_id, node_id);
107107
}
108108

109-
// Adds a package's imports to name lookup, with all libraries together.
110-
// sem_irs will all be non-null; has_load_error must be used for any errors.
111-
auto AddPackageImports(Parse::NodeId import_node, IdentifierId package_id,
112-
llvm::ArrayRef<const SemIR::File*> sem_irs,
113-
bool has_load_error) -> void;
114-
115109
// Adds a name to name lookup. Prints a diagnostic for name conflicts.
116110
auto AddNameToLookup(SemIR::NameId name_id, SemIR::InstId target_id) -> void;
117111

@@ -138,8 +132,8 @@ class Context {
138132
-> SemIR::InstId;
139133

140134
// Prints a diagnostic for a duplicate name.
141-
auto DiagnoseDuplicateName(SemIR::InstId dup_def_id,
142-
SemIR::InstId prev_def_id) -> void;
135+
auto DiagnoseDuplicateName(SemIRLocation dup_def, SemIRLocation prev_def)
136+
-> void;
143137

144138
// Prints a diagnostic for a missing name.
145139
auto DiagnoseNameNotFound(Parse::NodeId node_id, SemIR::NameId name_id)

toolchain/check/decl_name_stack.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,19 @@ auto DeclNameStack::UpdateScopeIfNeeded(NameContext& name_context,
214214
auto scope_id = resolved_inst.As<SemIR::Namespace>().name_scope_id;
215215
name_context.state = NameContext::State::Resolved;
216216
name_context.target_scope_id = scope_id;
217+
auto& scope = context_->name_scopes().Get(scope_id);
218+
if (scope.is_closed_import) {
219+
CARBON_DIAGNOSTIC(QualifiedDeclOutsidePackage, Error,
220+
"Imported packages cannot be used for declarations.");
221+
CARBON_DIAGNOSTIC(QualifiedDeclOutsidePackageSource, Note,
222+
"Package imported here.");
223+
context_->emitter()
224+
.Build(name_context.node_id, QualifiedDeclOutsidePackage)
225+
.Note(scope.inst_id, QualifiedDeclOutsidePackageSource)
226+
.Emit();
227+
// Only error once per package.
228+
scope.is_closed_import = false;
229+
}
217230
if (!is_unqualified) {
218231
PushNameQualifierScope(*context_, name_context.resolved_inst_id,
219232
scope_id,

toolchain/check/eval.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -506,7 +506,6 @@ auto TryEvalInst(Context& context, SemIR::InstId inst_id, SemIR::Inst inst)
506506
case SemIR::BranchIf::Kind:
507507
case SemIR::BranchWithArg::Kind:
508508
case SemIR::ImplDecl::Kind:
509-
case SemIR::Import::Kind:
510509
case SemIR::Param::Kind:
511510
case SemIR::ReturnExpr::Kind:
512511
case SemIR::Return::Kind:

0 commit comments

Comments
 (0)