Skip to content

Commit 3d39ab6

Browse files
authored
Wrap lookup result in a new ScopeLookupResult (#4831)
Benefits: * Provide a proper API for accessing lookup information. * Make assumptions on whether the result is poisoned or not and how we can use `InstId` explicit. * Allow safely reusing the `InstId` value for pointing to the poisoning entity for poisoned results (in a future PR). * Consolidate `LookupNameInExactScopeResult`, `std::pair<SemIR::InstId, bool>` and part of `LookupResult`. Part of #4622.
1 parent 5f888e1 commit 3d39ab6

File tree

14 files changed

+384
-245
lines changed

14 files changed

+384
-245
lines changed

toolchain/check/context.cpp

Lines changed: 44 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -317,7 +317,7 @@ auto Context::AddNameToLookup(SemIR::NameId name_id, SemIR::InstId target_id,
317317
auto Context::LookupNameInDecl(SemIR::LocId loc_id, SemIR::NameId name_id,
318318
SemIR::NameScopeId scope_id,
319319
ScopeIndex scope_index)
320-
-> std::pair<SemIR::InstId, bool> {
320+
-> SemIR::ScopeLookupResult {
321321
if (!scope_id.has_value()) {
322322
// Look for a name in the specified scope or a scope nested within it only.
323323
// There are two cases where the name would be in an outer scope:
@@ -353,8 +353,9 @@ auto Context::LookupNameInDecl(SemIR::LocId loc_id, SemIR::NameId name_id,
353353
//
354354
// In this case, the class C is not a redeclaration of its parameter, but
355355
// we find the parameter in order to diagnose a redeclaration error.
356-
return {scope_stack().LookupInLexicalScopesWithin(name_id, scope_index),
357-
false};
356+
return SemIR::ScopeLookupResult::MakeWrappedLookupResult(
357+
scope_stack().LookupInLexicalScopesWithin(name_id, scope_index),
358+
SemIR::AccessKind::Public);
358359
} else {
359360
// We do not look into `extend`ed scopes here. A qualified name in a
360361
// declaration must specify the exact scope in which the name was originally
@@ -365,10 +366,9 @@ auto Context::LookupNameInDecl(SemIR::LocId loc_id, SemIR::NameId name_id,
365366
//
366367
// // Error, no `F` in `B`.
367368
// fn B.F() {}
368-
auto result = LookupNameInExactScope(loc_id, name_id, scope_id,
369-
name_scopes().Get(scope_id),
370-
/*is_being_declared=*/true);
371-
return {result.inst_id, result.is_poisoned};
369+
return LookupNameInExactScope(loc_id, name_id, scope_id,
370+
name_scopes().Get(scope_id),
371+
/*is_being_declared=*/true);
372372
}
373373
}
374374

@@ -390,7 +390,7 @@ auto Context::LookupUnqualifiedName(Parse::NodeId node_id,
390390
LookupScope{.name_scope_id = lookup_scope_id,
391391
.specific_id = specific_id},
392392
/*required=*/false);
393-
non_lexical_result.inst_id.has_value()) {
393+
non_lexical_result.scope_result.is_found()) {
394394
return non_lexical_result;
395395
}
396396
}
@@ -400,14 +400,16 @@ auto Context::LookupUnqualifiedName(Parse::NodeId node_id,
400400
"`{0}` used before initialization", SemIR::NameId);
401401
emitter_->Emit(node_id, UsedBeforeInitialization, name_id);
402402
return {.specific_id = SemIR::SpecificId::None,
403-
.inst_id = SemIR::ErrorInst::SingletonInstId};
403+
.scope_result = SemIR::ScopeLookupResult::MakeError()};
404404
}
405405

406406
if (lexical_result.has_value()) {
407407
// A lexical scope never needs an associated specific. If there's a
408408
// lexically enclosing generic, then it also encloses the point of use of
409409
// the name.
410-
return {.specific_id = SemIR::SpecificId::None, .inst_id = lexical_result};
410+
return {.specific_id = SemIR::SpecificId::None,
411+
.scope_result = SemIR::ScopeLookupResult::MakeFound(
412+
lexical_result, SemIR::AccessKind::Public)};
411413
}
412414

413415
// We didn't find anything at all.
@@ -416,32 +418,32 @@ auto Context::LookupUnqualifiedName(Parse::NodeId node_id,
416418
}
417419

418420
return {.specific_id = SemIR::SpecificId::None,
419-
.inst_id = SemIR::ErrorInst::SingletonInstId};
421+
.scope_result = SemIR::ScopeLookupResult::MakeError()};
420422
}
421423

422424
auto Context::LookupNameInExactScope(SemIRLoc loc, SemIR::NameId name_id,
423425
SemIR::NameScopeId scope_id,
424426
SemIR::NameScope& scope,
425427
bool is_being_declared)
426-
-> LookupNameInExactScopeResult {
428+
-> SemIR::ScopeLookupResult {
427429
if (auto entry_id = is_being_declared ? scope.Lookup(name_id)
428430
: scope.LookupOrPoison(name_id)) {
429-
auto entry = scope.GetEntry(*entry_id);
430-
if (!entry.is_poisoned) {
431-
LoadImportRef(*this, entry.inst_id);
432-
} else if (is_being_declared) {
433-
entry.inst_id = SemIR::InstId::None;
431+
auto lookup_result = scope.GetEntry(*entry_id).result;
432+
if (!lookup_result.is_poisoned()) {
433+
LoadImportRef(*this, lookup_result.target_inst_id());
434+
return lookup_result;
434435
}
435-
return {entry.inst_id, entry.access_kind, entry.is_poisoned};
436+
return SemIR::ScopeLookupResult::MakePoisoned();
436437
}
437438

438439
if (!scope.import_ir_scopes().empty()) {
439440
// TODO: Enforce other access modifiers for imports.
440-
return {ImportNameFromOtherPackage(*this, loc, scope_id,
441-
scope.import_ir_scopes(), name_id),
442-
SemIR::AccessKind::Public};
441+
return SemIR::ScopeLookupResult::MakeWrappedLookupResult(
442+
ImportNameFromOtherPackage(*this, loc, scope_id,
443+
scope.import_ir_scopes(), name_id),
444+
SemIR::AccessKind::Public);
443445
}
444-
return {SemIR::InstId::None, SemIR::AccessKind::Public};
446+
return SemIR::ScopeLookupResult::MakeNotFound();
445447
}
446448

447449
// Prints diagnostics on invalid qualified name access.
@@ -583,8 +585,9 @@ auto Context::LookupQualifiedName(SemIR::LocId loc_id, SemIR::NameId name_id,
583585
// TODO: Support reporting of multiple prohibited access.
584586
llvm::SmallVector<ProhibitedAccessInfo> prohibited_accesses;
585587

586-
LookupResult result = {.specific_id = SemIR::SpecificId::None,
587-
.inst_id = SemIR::InstId::None};
588+
LookupResult result = {
589+
.specific_id = SemIR::SpecificId::None,
590+
.scope_result = SemIR::ScopeLookupResult::MakeNotFound()};
588591
bool has_error = false;
589592
bool is_parent_access = false;
590593

@@ -598,8 +601,9 @@ auto Context::LookupQualifiedName(SemIR::LocId loc_id, SemIR::NameId name_id,
598601
auto& name_scope = name_scopes().Get(scope_id);
599602
has_error |= name_scope.has_error();
600603

601-
auto [scope_result_id, access_kind, is_poisoned] =
604+
const SemIR::ScopeLookupResult scope_result =
602605
LookupNameInExactScope(loc_id, name_id, scope_id, name_scope);
606+
SemIR::AccessKind access_kind = scope_result.access_kind();
603607

604608
auto is_access_prohibited =
605609
IsAccessProhibited(access_info, access_kind, is_parent_access);
@@ -608,13 +612,13 @@ auto Context::LookupQualifiedName(SemIR::LocId loc_id, SemIR::NameId name_id,
608612
// multiple prohibited accesses if we can't find a suitable lookup.
609613
if (is_access_prohibited) {
610614
prohibited_accesses.push_back({
611-
.scope_result_id = scope_result_id,
615+
.scope_result_id = scope_result.target_inst_id(),
612616
.access_kind = access_kind,
613617
.is_parent_access = is_parent_access,
614618
});
615619
}
616620

617-
if (!scope_result_id.has_value() || is_access_prohibited) {
621+
if (!scope_result.is_found() || is_access_prohibited) {
618622
// If nothing is found in this scope or if we encountered an invalid
619623
// access, look in its extended scopes.
620624
const auto& extended = name_scope.extended_scopes();
@@ -643,23 +647,22 @@ auto Context::LookupQualifiedName(SemIR::LocId loc_id, SemIR::NameId name_id,
643647
}
644648

645649
// If this is our second lookup result, diagnose an ambiguity.
646-
if (result.inst_id.has_value()) {
650+
if (result.scope_result.is_found()) {
647651
CARBON_DIAGNOSTIC(
648652
NameAmbiguousDueToExtend, Error,
649653
"ambiguous use of name `{0}` found in multiple extended scopes",
650654
SemIR::NameId);
651655
emitter_->Emit(loc_id, NameAmbiguousDueToExtend, name_id);
652656
// TODO: Add notes pointing to the scopes.
653657
return {.specific_id = SemIR::SpecificId::None,
654-
.inst_id = SemIR::ErrorInst::SingletonInstId};
658+
.scope_result = SemIR::ScopeLookupResult::MakeError()};
655659
}
656660

657-
result.inst_id = scope_result_id;
661+
result.scope_result = scope_result;
658662
result.specific_id = specific_id;
659-
result.is_poisoned = is_poisoned;
660663
}
661664

662-
if (required && !result.inst_id.has_value()) {
665+
if (required && !result.scope_result.is_found()) {
663666
if (!has_error) {
664667
if (prohibited_accesses.empty()) {
665668
DiagnoseMemberNameNotFound(loc_id, name_id, lookup_scopes);
@@ -677,9 +680,9 @@ auto Context::LookupQualifiedName(SemIR::LocId loc_id, SemIR::NameId name_id,
677680
}
678681
}
679682

683+
CARBON_CHECK(!result.scope_result.is_poisoned());
680684
return {.specific_id = SemIR::SpecificId::None,
681-
.inst_id = SemIR::ErrorInst::SingletonInstId,
682-
.is_poisoned = result.is_poisoned};
685+
.scope_result = SemIR::ScopeLookupResult::MakeError()};
683686
}
684687

685688
return result;
@@ -699,13 +702,13 @@ static auto GetCorePackage(Context& context, SemIRLoc loc, llvm::StringRef name)
699702
auto core_name_id = SemIR::NameId::ForIdentifier(core_ident_id);
700703

701704
// Look up `package.Core`.
702-
auto [core_inst_id, _, is_poisoned] = context.LookupNameInExactScope(
705+
auto core_scope_result = context.LookupNameInExactScope(
703706
loc, core_name_id, SemIR::NameScopeId::Package,
704707
context.name_scopes().Get(SemIR::NameScopeId::Package));
705-
if (core_inst_id.has_value()) {
708+
if (core_scope_result.is_found()) {
706709
// We expect it to be a namespace.
707-
if (auto namespace_inst =
708-
context.insts().TryGetAs<SemIR::Namespace>(core_inst_id)) {
710+
if (auto namespace_inst = context.insts().TryGetAs<SemIR::Namespace>(
711+
core_scope_result.target_inst_id())) {
709712
// TODO: Decide whether to allow the case where `Core` is not a package.
710713
return namespace_inst->name_scope_id;
711714
}
@@ -727,9 +730,9 @@ auto Context::LookupNameInCore(SemIRLoc loc, llvm::StringRef name)
727730
}
728731

729732
auto name_id = SemIR::NameId::ForIdentifier(identifiers().Add(name));
730-
auto [inst_id, _, is_poisoned] = LookupNameInExactScope(
733+
auto scope_result = LookupNameInExactScope(
731734
loc, name_id, core_package_id, name_scopes().Get(core_package_id));
732-
if (!inst_id.has_value()) {
735+
if (!scope_result.is_found()) {
733736
CARBON_DIAGNOSTIC(
734737
CoreNameNotFound, Error,
735738
"name `Core.{0}` implicitly referenced here, but not found",
@@ -739,7 +742,7 @@ auto Context::LookupNameInCore(SemIRLoc loc, llvm::StringRef name)
739742
}
740743

741744
// Look through import_refs and aliases.
742-
return constant_values().GetConstantInstId(inst_id);
745+
return constant_values().GetConstantInstId(scope_result.target_inst_id());
743746
}
744747

745748
template <typename BranchNode, typename... Args>

toolchain/check/context.h

Lines changed: 5 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,9 @@ struct LookupResult {
4545
// The specific in which the lookup result was found. `None` if the result
4646
// was not found in a specific.
4747
SemIR::SpecificId specific_id;
48-
// The declaration that was found by name lookup. `None` for poisoned items.
49-
// TODO: Make this point to the poisoning declaration.
50-
SemIR::InstId inst_id;
51-
// Whether the lookup found a poisoned name.
52-
bool is_poisoned = false;
48+
49+
// The result from the lookup in the scope.
50+
SemIR::ScopeLookupResult scope_result;
5351
};
5452

5553
// Information about an access.
@@ -73,17 +71,6 @@ class Context {
7371
using BuildDiagnosticFn =
7472
llvm::function_ref<auto()->Context::DiagnosticBuilder>;
7573

76-
struct LookupNameInExactScopeResult {
77-
// The matching entity if found, or `None` if poisoned or not found.
78-
SemIR::InstId inst_id;
79-
80-
// The access level required to use inst_id when it's not `None`.
81-
SemIR::AccessKind access_kind;
82-
83-
// Whether a poisoned entry was found.
84-
bool is_poisoned = false;
85-
};
86-
8774
// Stores references for work.
8875
explicit Context(DiagnosticEmitter* emitter,
8976
llvm::function_ref<const Parse::TreeAndSubtrees&()>
@@ -233,7 +220,7 @@ class Context {
233220
// TODO: For poisoned names, return the poisoning `InstId`.
234221
auto LookupNameInDecl(SemIR::LocId loc_id, SemIR::NameId name_id,
235222
SemIR::NameScopeId scope_id, ScopeIndex scope_index)
236-
-> std::pair<SemIR::InstId, bool>;
223+
-> SemIR::ScopeLookupResult;
237224

238225
// Performs an unqualified name lookup, returning the referenced `InstId`.
239226
auto LookupUnqualifiedName(Parse::NodeId node_id, SemIR::NameId name_id,
@@ -252,7 +239,7 @@ class Context {
252239
SemIR::NameScopeId scope_id,
253240
SemIR::NameScope& scope,
254241
bool is_being_declared = false)
255-
-> LookupNameInExactScopeResult;
242+
-> SemIR::ScopeLookupResult;
256243

257244
// Appends the lookup scopes corresponding to `base_const_id` to `*scopes`.
258245
// Returns `false` if not a scope. On invalid scopes, prints a diagnostic, but

toolchain/check/decl_name_stack.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -160,8 +160,8 @@ auto DeclNameStack::AddName(NameContext name_context, SemIR::InstId target_id,
160160
}
161161

162162
name_scope.AddRequired({.name_id = name_context.unresolved_name_id,
163-
.inst_id = target_id,
164-
.access_kind = access_kind});
163+
.result = SemIR::ScopeLookupResult::MakeFound(
164+
target_id, access_kind)});
165165
}
166166
break;
167167

@@ -262,20 +262,20 @@ auto DeclNameStack::ApplyAndLookupName(NameContext& name_context,
262262
}
263263

264264
// For identifier nodes, we need to perform a lookup on the identifier.
265-
auto [resolved_inst_id, is_poisoned] = context_->LookupNameInDecl(
265+
auto lookup_result = context_->LookupNameInDecl(
266266
name_context.loc_id, name_id, name_context.parent_scope_id,
267267
name_context.initial_scope_index);
268-
if (is_poisoned) {
268+
if (lookup_result.is_poisoned()) {
269269
name_context.unresolved_name_id = name_id;
270270
name_context.state = NameContext::State::Poisoned;
271-
} else if (!resolved_inst_id.has_value()) {
271+
} else if (!lookup_result.is_found()) {
272272
// Invalid indicates an unresolved name. Store it and return.
273273
name_context.unresolved_name_id = name_id;
274274
name_context.state = NameContext::State::Unresolved;
275275
} else {
276276
// Store the resolved instruction and continue for the target scope
277277
// update.
278-
name_context.resolved_inst_id = resolved_inst_id;
278+
name_context.resolved_inst_id = lookup_result.target_inst_id();
279279
name_context.state = NameContext::State::Resolved;
280280
}
281281
}

toolchain/check/handle_export.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -78,10 +78,11 @@ auto HandleParseNode(Context& context, Parse::ExportDeclId node_id) -> bool {
7878
// diagnostic and so that cross-package imports can find it easily.
7979
auto entity_name = context.entity_names().Get(import_ref->entity_name_id);
8080
auto& parent_scope = context.name_scopes().Get(entity_name.parent_scope_id);
81-
auto& scope_inst_id =
82-
parent_scope.GetEntry(*parent_scope.Lookup(entity_name.name_id)).inst_id;
83-
CARBON_CHECK(scope_inst_id == inst_id);
84-
scope_inst_id = export_id;
81+
auto& scope_result =
82+
parent_scope.GetEntry(*parent_scope.Lookup(entity_name.name_id)).result;
83+
CARBON_CHECK(scope_result.target_inst_id() == inst_id);
84+
scope_result = SemIR::ScopeLookupResult::MakeFound(
85+
export_id, scope_result.access_kind());
8586

8687
return true;
8788
}

toolchain/check/handle_name.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -103,24 +103,24 @@ static auto GetIdentifierAsName(Context& context, Parse::NodeId node_id)
103103
static auto HandleNameAsExpr(Context& context, Parse::NodeId node_id,
104104
SemIR::NameId name_id) -> SemIR::InstId {
105105
auto result = context.LookupUnqualifiedName(node_id, name_id);
106-
auto value = context.insts().Get(result.inst_id);
106+
SemIR::InstId inst_id = result.scope_result.target_inst_id();
107+
auto value = context.insts().Get(inst_id);
107108
auto type_id = SemIR::GetTypeInSpecific(context.sem_ir(), result.specific_id,
108109
value.type_id());
109110
CARBON_CHECK(type_id.has_value(), "Missing type for {0}", value);
110111

111112
// If the named entity has a constant value that depends on its specific,
112113
// store the specific too.
113114
if (result.specific_id.has_value() &&
114-
context.constant_values().Get(result.inst_id).is_symbolic()) {
115-
result.inst_id = context.AddInst<SemIR::SpecificConstant>(
115+
context.constant_values().Get(inst_id).is_symbolic()) {
116+
inst_id = context.AddInst<SemIR::SpecificConstant>(
116117
node_id, {.type_id = type_id,
117-
.inst_id = result.inst_id,
118+
.inst_id = inst_id,
118119
.specific_id = result.specific_id});
119120
}
120121

121122
return context.AddInst<SemIR::NameRef>(
122-
node_id,
123-
{.type_id = type_id, .name_id = name_id, .value_id = result.inst_id});
123+
node_id, {.type_id = type_id, .name_id = name_id, .value_id = inst_id});
124124
}
125125

126126
static auto HandleIdentifierName(Context& context,

toolchain/check/impl.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -416,12 +416,13 @@ auto FinishImplWitness(Context& context, SemIR::Impl& impl) -> void {
416416
CARBON_FATAL("Unexpected type: {0}", type_inst);
417417
}
418418
auto& fn = context.functions().Get(fn_type->function_id);
419-
auto [impl_decl_id, _, is_poisoned] = context.LookupNameInExactScope(
419+
auto lookup_result = context.LookupNameInExactScope(
420420
decl_id, fn.name_id, impl.scope_id, impl_scope);
421-
if (impl_decl_id.has_value()) {
422-
used_decl_ids.push_back(impl_decl_id);
421+
if (lookup_result.is_found()) {
422+
used_decl_ids.push_back(lookup_result.target_inst_id());
423423
witness_block[index] = CheckAssociatedFunctionImplementation(
424-
context, *fn_type, impl_decl_id, self_type_id, impl.witness_id);
424+
context, *fn_type, lookup_result.target_inst_id(), self_type_id,
425+
impl.witness_id);
425426
} else {
426427
CARBON_DIAGNOSTIC(
427428
ImplMissingFunction, Error,

0 commit comments

Comments
 (0)