mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-05 14:51:03 +01:00
Avoid symbolic witnesses for .Self in an impl decl (#7564)
Point symbolic witnesses into `.Self` written inside an impl decl at the impl that is being declared. This is tricky because the impl does not yet exist. So we use a new instruction `ImplSelfWitness` which _will_ be replaced by the `ImplWitness` once it becomes available. The `ImplSelfWitness` acts like a symbolic witness, except it does not perform lookup, since we know which impl we will get a witness from. This prevents us from finding other impls when performing lookups into `.Self` in an impl decl, which produces incorrect/incoherent results.
This commit is contained in:
@@ -108,6 +108,10 @@ auto HandleParseNode(Context& context, Parse::ImplTypeAsId node_id) -> bool {
|
||||
// TODO: Revisit this once #3714 is resolved.
|
||||
AddNameToLookup(context, SemIR::NameId::SelfType, self_type.inst_id);
|
||||
context.node_stack().Push(node_id, self_type.inst_id);
|
||||
|
||||
// The value in here, if any, is populated by the `where` expression that
|
||||
// introduces a `.Self` in the impl's constraint facet type.
|
||||
context.declaring_impl_decls().push_back(SemIR::SpecificInterface::None);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -140,6 +144,10 @@ auto HandleParseNode(Context& context, Parse::ImplDefaultSelfAsId node_id)
|
||||
// There's no need to push `Self` into scope here, because we can find it in
|
||||
// the parent class scope.
|
||||
context.node_stack().Push(node_id, self_inst_id);
|
||||
|
||||
// The value in here, if any, is populated by the `where` expression that
|
||||
// introduces a `.Self` in the impl's constraint facet type.
|
||||
context.declaring_impl_decls().push_back(SemIR::SpecificInterface::None);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -197,7 +205,7 @@ static auto PopImplIntroducerAndParamsAsNameComponent(
|
||||
// also sets the `definition_id` on the Impl structure.
|
||||
static auto BuildImplDecl(Context& context, Parse::AnyImplDeclId node_id,
|
||||
bool has_definition)
|
||||
-> std::pair<SemIR::ImplId, SemIR::InstId> {
|
||||
-> std::tuple<SemIR::ImplId, SemIR::InstId, SemIR::TypeInstId> {
|
||||
auto [constraint_node, constraint_id] =
|
||||
context.node_stack().PopExprWithNodeId();
|
||||
auto [self_type_node, self_type_inst_id] =
|
||||
@@ -206,9 +214,10 @@ static auto BuildImplDecl(Context& context, Parse::AnyImplDeclId node_id,
|
||||
auto name = PopImplIntroducerAndParamsAsNameComponent(context, node_id);
|
||||
auto decl_block_id = context.inst_block_stack().Pop();
|
||||
|
||||
// Convert the constraint expression to a type.
|
||||
auto [constraint_type_inst_id, constraint_type_id] =
|
||||
ExprAsType(context, constraint_node, constraint_id);
|
||||
// Convert the constraint expression to a type. This contains all constraints,
|
||||
// including rewrites and other constrains on the RHS of `where`.
|
||||
auto full_constraint_type_inst_id =
|
||||
ExprAsType(context, constraint_node, constraint_id).inst_id;
|
||||
|
||||
// Process modifiers.
|
||||
// TODO: Should we somehow permit access specifiers on `impl`s?
|
||||
@@ -229,30 +238,35 @@ static auto BuildImplDecl(Context& context, Parse::AnyImplDeclId node_id,
|
||||
SemIR::ImplDecl{.impl_id = SemIR::ImplId::None,
|
||||
.decl_block_id = decl_block_id});
|
||||
|
||||
if (!CheckConstraintIsFacetType(context, node_id, constraint_type_inst_id)) {
|
||||
constraint_type_inst_id = SemIR::ErrorInst::TypeInstId;
|
||||
if (!CheckConstraintIsFacetType(context, node_id,
|
||||
full_constraint_type_inst_id)) {
|
||||
full_constraint_type_inst_id = SemIR::ErrorInst::TypeInstId;
|
||||
}
|
||||
|
||||
// The identified facet type will also replace `.Self` references in the
|
||||
// specific interface, but we want to store the full facet type not just the
|
||||
// identified one. So we have to replace `.Self` references explicitly here in
|
||||
// the constraint.
|
||||
//
|
||||
// We do this after `CheckConstraintIsFacetType()` which has ensured the
|
||||
// constraint is in fact a FacetType. We do this before identifying the facet
|
||||
// type in `CheckConstraintIsInterface()` so that the identified facet type is
|
||||
// for the substituted facet type instruction that will be stored in the Impl.
|
||||
// This ensures the impl bucket finds the identified facet type.
|
||||
constraint_type_inst_id = SubstPeriodSelfInFacetType(
|
||||
context, constraint_node, self_type_inst_id, constraint_type_inst_id);
|
||||
|
||||
// This requires that the facet type is identified, and returns the single
|
||||
// interface from the identified facet type. It returns None if an error was
|
||||
// diagnosed.
|
||||
auto specific_interface = CheckConstraintIsInterface(
|
||||
context, node_id, self_type_inst_id, constraint_type_inst_id);
|
||||
context, node_id, self_type_inst_id, full_constraint_type_inst_id);
|
||||
if (!specific_interface.interface_id.has_value()) {
|
||||
constraint_type_inst_id = SemIR::ErrorInst::TypeInstId;
|
||||
full_constraint_type_inst_id = SemIR::ErrorInst::TypeInstId;
|
||||
}
|
||||
|
||||
// Strip off anything on the RHS of `where`, as they are not part of the
|
||||
// constraint being implemented, they just represent requirements that must be
|
||||
// met when the impl is defined. This drops any `.Self` references from the
|
||||
// resulting impl's `constraint_id` as they don't make sense outside the scope
|
||||
// of the impl declaration.
|
||||
auto extend_constraint_type_inst_id = full_constraint_type_inst_id;
|
||||
if (auto where = context.insts().TryGetAs<SemIR::WhereExpr>(
|
||||
extend_constraint_type_inst_id)) {
|
||||
for (auto req_id : context.inst_blocks().Get(where->requirements_id)) {
|
||||
if (auto base = context.insts().TryGetAs<SemIR::RequirementBaseFacetType>(
|
||||
req_id)) {
|
||||
extend_constraint_type_inst_id = base->base_type_inst_id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The impl decl has a scope stack entry for the DeclNameStack, so we look at
|
||||
@@ -267,7 +281,7 @@ static auto BuildImplDecl(Context& context, Parse::AnyImplDeclId node_id,
|
||||
{.parent_scope_inst_id = parent_scope_inst_id,
|
||||
.is_final = is_final,
|
||||
.self_id = self_type_inst_id,
|
||||
.constraint_id = constraint_type_inst_id,
|
||||
.constraint_id = extend_constraint_type_inst_id,
|
||||
.interface = specific_interface}};
|
||||
if (has_definition) {
|
||||
impl.definition_id = impl_decl_id;
|
||||
@@ -366,7 +380,7 @@ static auto BuildImplDecl(Context& context, Parse::AnyImplDeclId node_id,
|
||||
// also must be part of the generic eval block by coming before
|
||||
// FinishGenericDecl().
|
||||
impl.witness_id = AddImplWitnessForDeclaration(
|
||||
context, node_id, impl,
|
||||
context, node_id, impl, full_constraint_type_inst_id,
|
||||
context.generics().GetSelfSpecific(impl.generic_id));
|
||||
impl.witness_block_id = context.inst_block_stack().Pop();
|
||||
|
||||
@@ -395,14 +409,15 @@ static auto BuildImplDecl(Context& context, Parse::AnyImplDeclId node_id,
|
||||
impl_decl.impl_id = impl_id;
|
||||
ReplaceInstBeforeConstantUse(context, impl_decl_id, impl_decl);
|
||||
|
||||
return {impl_id, impl_decl_id};
|
||||
return {impl_id, impl_decl_id, full_constraint_type_inst_id};
|
||||
}
|
||||
|
||||
auto HandleParseNode(Context& context, Parse::ImplDeclId node_id) -> bool {
|
||||
auto [impl_id, impl_decl_id] = BuildImplDecl(context, node_id, false);
|
||||
auto [impl_id, impl_decl_id, _] = BuildImplDecl(context, node_id, false);
|
||||
auto& impl = context.impls().Get(impl_id);
|
||||
|
||||
context.decl_name_stack().PopScope();
|
||||
context.declaring_impl_decls().pop_back();
|
||||
|
||||
// Impl definitions are required in the same file as the declaration. We skip
|
||||
// this requirement if we've already issued an invalid redeclaration error, or
|
||||
@@ -416,7 +431,8 @@ auto HandleParseNode(Context& context, Parse::ImplDeclId node_id) -> bool {
|
||||
|
||||
auto HandleParseNode(Context& context, Parse::ImplDefinitionStartId node_id)
|
||||
-> bool {
|
||||
auto [impl_id, impl_decl_id] = BuildImplDecl(context, node_id, true);
|
||||
auto [impl_id, impl_decl_id, full_constraint_id] =
|
||||
BuildImplDecl(context, node_id, true);
|
||||
auto& impl = context.impls().Get(impl_id);
|
||||
|
||||
impl.scope_id =
|
||||
@@ -430,9 +446,10 @@ auto HandleParseNode(Context& context, Parse::ImplDefinitionStartId node_id)
|
||||
context.generics().GetSelfSpecific(impl.generic_id));
|
||||
StartGenericDefinition(context, impl.generic_id);
|
||||
ImplWitnessStartDefinition(context, impl);
|
||||
CheckRequireDeclsSatisfied(context, node_id, impl);
|
||||
CheckRequireDeclsSatisfied(context, node_id, impl, full_constraint_id);
|
||||
context.inst_block_stack().Push();
|
||||
context.node_stack().Push(node_id, impl_id);
|
||||
context.declaring_impl_decls().pop_back();
|
||||
|
||||
// TODO: Handle the case where there's control flow in the impl body. For
|
||||
// example:
|
||||
|
||||
Reference in New Issue
Block a user