mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-05 22:02:55 +01:00
When performing name lookup, determine the generic instance within which the lookup result was found. (#4118)
Require types into which qualified lookup is performed to be completely defined. Eventually this will trigger substitution into the definition for generic types.
This commit is contained in:
+67
-22
@@ -263,7 +263,7 @@ auto Context::LookupNameInDecl(SemIR::LocId loc_id, SemIR::NameId name_id,
|
||||
}
|
||||
|
||||
auto Context::LookupUnqualifiedName(Parse::NodeId node_id,
|
||||
SemIR::NameId name_id) -> SemIR::InstId {
|
||||
SemIR::NameId name_id) -> LookupResult {
|
||||
// TODO: Check for shadowed lookup results.
|
||||
|
||||
// Find the results from ancestor lexical scopes. These will be combined with
|
||||
@@ -272,22 +272,33 @@ auto Context::LookupUnqualifiedName(Parse::NodeId node_id,
|
||||
scope_stack().LookupInLexicalScopes(name_id);
|
||||
|
||||
// Walk the non-lexical scopes and perform lookups into each of them.
|
||||
for (auto [index, name_scope_id] : llvm::reverse(non_lexical_scopes)) {
|
||||
if (auto non_lexical_result =
|
||||
LookupQualifiedName(node_id, name_id, name_scope_id,
|
||||
/*required=*/false);
|
||||
non_lexical_result.is_valid()) {
|
||||
for (auto [index, lookup_scope_id] : llvm::reverse(non_lexical_scopes)) {
|
||||
// Enclosing non-lexical scopes cannot correspond to an instance of a
|
||||
// generic, so it's always OK to pass an invalid generic instance here.
|
||||
// Note that the lookup result might still be found in an extended scope, so
|
||||
// it can be in a generic instance.
|
||||
if (auto non_lexical_result = LookupQualifiedName(
|
||||
node_id, name_id,
|
||||
{.name_scope_id = lookup_scope_id,
|
||||
.instance_id = SemIR::GenericInstanceId::Invalid},
|
||||
/*required=*/false);
|
||||
non_lexical_result.inst_id.is_valid()) {
|
||||
return non_lexical_result;
|
||||
}
|
||||
}
|
||||
|
||||
if (lexical_result.is_valid()) {
|
||||
return lexical_result;
|
||||
// A lexical scope never needs an associated generic instance. If there's a
|
||||
// lexically enclosing generic, then it also encloses the point of use of
|
||||
// the name.
|
||||
return {.instance_id = SemIR::GenericInstanceId::Invalid,
|
||||
.inst_id = lexical_result};
|
||||
}
|
||||
|
||||
// We didn't find anything at all.
|
||||
DiagnoseNameNotFound(node_id, name_id);
|
||||
return SemIR::InstId::BuiltinError;
|
||||
return {.instance_id = SemIR::GenericInstanceId::Invalid,
|
||||
.inst_id = SemIR::InstId::BuiltinError};
|
||||
}
|
||||
|
||||
// Handles lookup through the import_ir_scopes for LookupNameInExactScope.
|
||||
@@ -391,15 +402,16 @@ auto Context::LookupNameInExactScope(SemIRLoc loc, SemIR::NameId name_id,
|
||||
}
|
||||
|
||||
auto Context::LookupQualifiedName(Parse::NodeId node_id, SemIR::NameId name_id,
|
||||
SemIR::NameScopeId scope_id, bool required)
|
||||
-> SemIR::InstId {
|
||||
llvm::SmallVector<SemIR::NameScopeId> scope_ids = {scope_id};
|
||||
auto result_id = SemIR::InstId::Invalid;
|
||||
LookupScope scope, bool required)
|
||||
-> LookupResult {
|
||||
llvm::SmallVector<LookupScope> scopes = {scope};
|
||||
LookupResult result = {.instance_id = SemIR::GenericInstanceId::Invalid,
|
||||
.inst_id = SemIR::InstId::Invalid};
|
||||
bool has_error = false;
|
||||
|
||||
// Walk this scope and, if nothing is found here, the scopes it extends.
|
||||
while (!scope_ids.empty()) {
|
||||
auto scope_id = scope_ids.pop_back_val();
|
||||
while (!scopes.empty()) {
|
||||
auto [scope_id, instance_id] = scopes.pop_back_val();
|
||||
const auto& scope = name_scopes().Get(scope_id);
|
||||
has_error |= scope.has_error;
|
||||
|
||||
@@ -407,13 +419,19 @@ auto Context::LookupQualifiedName(Parse::NodeId node_id, SemIR::NameId name_id,
|
||||
LookupNameInExactScope(node_id, name_id, scope_id, scope);
|
||||
if (!scope_result_id.is_valid()) {
|
||||
// Nothing found in this scope: also look in its extended scopes.
|
||||
auto extended = llvm::reverse(scope.extended_scopes);
|
||||
scope_ids.append(extended.begin(), extended.end());
|
||||
auto extended = scope.extended_scopes;
|
||||
scopes.reserve(scopes.size() + extended.size());
|
||||
for (auto extended_id : llvm::reverse(extended)) {
|
||||
// TODO: Track a constant describing the extended scope, and substitute
|
||||
// into it to determine its corresponding generic instance.
|
||||
scopes.push_back({.name_scope_id = extended_id,
|
||||
.instance_id = SemIR::GenericInstanceId::Invalid});
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// If this is our second lookup result, diagnose an ambiguity.
|
||||
if (result_id.is_valid()) {
|
||||
if (result.inst_id.is_valid()) {
|
||||
// TODO: This is currently not reachable because the only scope that can
|
||||
// extend is a class scope, and it can only extend a single base class.
|
||||
// Add test coverage once this is possible.
|
||||
@@ -423,20 +441,23 @@ auto Context::LookupQualifiedName(Parse::NodeId node_id, SemIR::NameId name_id,
|
||||
SemIR::NameId);
|
||||
emitter_->Emit(node_id, NameAmbiguousDueToExtend, name_id);
|
||||
// TODO: Add notes pointing to the scopes.
|
||||
return SemIR::InstId::BuiltinError;
|
||||
return {.instance_id = SemIR::GenericInstanceId::Invalid,
|
||||
.inst_id = SemIR::InstId::BuiltinError};
|
||||
}
|
||||
|
||||
result_id = scope_result_id;
|
||||
result.inst_id = scope_result_id;
|
||||
result.instance_id = instance_id;
|
||||
}
|
||||
|
||||
if (required && !result_id.is_valid()) {
|
||||
if (required && !result.inst_id.is_valid()) {
|
||||
if (!has_error) {
|
||||
DiagnoseNameNotFound(node_id, name_id);
|
||||
}
|
||||
return SemIR::InstId::BuiltinError;
|
||||
return {.instance_id = SemIR::GenericInstanceId::Invalid,
|
||||
.inst_id = SemIR::InstId::BuiltinError};
|
||||
}
|
||||
|
||||
return result_id;
|
||||
return result;
|
||||
}
|
||||
|
||||
// Returns the scope of the Core package, or Invalid if it's not found.
|
||||
@@ -779,6 +800,7 @@ class TypeCompleter {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
// TODO: Trigger generic resolution here for a generic class.
|
||||
Push(class_info.object_repr_id);
|
||||
break;
|
||||
}
|
||||
@@ -1042,6 +1064,29 @@ auto Context::TryToCompleteType(
|
||||
return TypeCompleter(*this, diagnoser).Complete(type_id);
|
||||
}
|
||||
|
||||
auto Context::TryToDefineType(
|
||||
SemIR::TypeId type_id,
|
||||
std::optional<llvm::function_ref<auto()->DiagnosticBuilder>> diagnoser)
|
||||
-> bool {
|
||||
if (!TryToCompleteType(type_id, diagnoser)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (auto interface = types().TryGetAs<SemIR::InterfaceType>(type_id)) {
|
||||
auto interface_id = interface->interface_id;
|
||||
if (!interfaces().Get(interface_id).is_defined()) {
|
||||
auto builder = (*diagnoser)();
|
||||
NoteUndefinedInterface(interface_id, builder);
|
||||
builder.Emit();
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: Trigger generic resolution here for a generic instance.
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto Context::GetTypeIdForTypeConstant(SemIR::ConstantId constant_id)
|
||||
-> SemIR::TypeId {
|
||||
CARBON_CHECK(constant_id.is_constant())
|
||||
|
||||
Reference in New Issue
Block a user