mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-05 22:02:55 +01:00
Resolve cycles in .Self replacement in nested designators (#7183)
A nested designator like `.(X.X1).(Y.Y1)` results in nested ImplWitnessAccess instructions, which can produce cycles in the toolchain easily when replacing `.Self`. First, when constructing a facet type like `V:! Z where .Z1 impls (Y where .Y1 = U)` we substitute replace `.Self` in the nested facet type, and in this case we replace `.Self` with `.Z1` which contains a `.Self` of its own. This was coming from us being lazy about replacing `.Self` in an `impl as` declaration, such as `impl C as Z where .Z1 = .Self`. The self type is known there, so we can more eagerly replace `.Self` as we do in a `require impls` declaration. Then the replacement for `.Self` never comes with a `.Self` that needs to also be replaced. Any resulting `.Self` would always be the top-level one. Second, when evaluating ImplWitnessAccess, we were replacing .Self in the LHS of rewrite constraints, but the `.Self` may itself have a type that contains rewrite constraints. If one of those rewrite constraints has nested ImplWitnessAccess instructions, we evaluate the new ImplWitnessAccess, which again finds rewrite constraints to replace `.Self` in, and we repeat forever. For this one we just stop replacing .Self in the LHS of rewrite constraints. Since they are always against .Self, we can always look in the access facet's type for a value. While fixing ImplWitness access, also correct the lookup to search through the types of nested ImplWitnessAccess instructions to find a rewrite value, since it may find it at any level up to the eventual `.Self`. --------- Co-authored-by: Richard Smith <richard@metafoo.co.uk>
This commit is contained in:
co-authored by
Richard Smith
parent
798b177fc0
commit
6326bbdbe1
@@ -17,6 +17,7 @@
|
||||
#include "toolchain/check/name_lookup.h"
|
||||
#include "toolchain/check/name_scope.h"
|
||||
#include "toolchain/check/pattern_match.h"
|
||||
#include "toolchain/check/period_self.h"
|
||||
#include "toolchain/check/type.h"
|
||||
#include "toolchain/check/type_completion.h"
|
||||
#include "toolchain/parse/node_ids.h"
|
||||
@@ -228,10 +229,31 @@ static auto BuildImplDecl(Context& context, Parse::AnyImplDeclId node_id)
|
||||
SemIR::ImplDecl{.impl_id = SemIR::ImplId::None,
|
||||
.decl_block_id = decl_block_id});
|
||||
|
||||
// This requires that the facet type is identified. It returns None if an
|
||||
// error was diagnosed.
|
||||
if (!CheckConstraintIsFacetType(context, node_id, constraint_type_inst_id)) {
|
||||
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, impl_decl_id, self_type_inst_id, constraint_type_inst_id);
|
||||
context, node_id, self_type_inst_id, constraint_type_inst_id);
|
||||
if (!specific_interface.interface_id.has_value()) {
|
||||
constraint_type_inst_id = SemIR::ErrorInst::TypeInstId;
|
||||
}
|
||||
|
||||
// The impl decl has a scope stack entry for the DeclNameStack, so we look at
|
||||
// the parent scope of that.
|
||||
@@ -256,8 +278,7 @@ static auto BuildImplDecl(Context& context, Parse::AnyImplDeclId node_id)
|
||||
context.types().GetTypeIdForTypeInstId(impl.self_id) ==
|
||||
SemIR::ErrorInst::TypeId ||
|
||||
context.types().GetTypeIdForTypeInstId(impl.constraint_id) ==
|
||||
SemIR::ErrorInst::TypeId ||
|
||||
!impl.interface.interface_id.has_value();
|
||||
SemIR::ErrorInst::TypeId;
|
||||
|
||||
CARBON_KIND_SWITCH(FindImplId(context, impl)) {
|
||||
case CARBON_KIND(RedeclaredImpl redeclared_impl): {
|
||||
|
||||
Reference in New Issue
Block a user