-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Refactor Impl construction to clarify and reduce extraneous work #6420
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: trunk
Are you sure you want to change the base?
Conversation
|
This is based on #6385, the first commit is |
Rebased to trunk. |
c713931 to
181c03c
Compare
josh11b
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry for the delay in reviewing.
This PR seems a bit large, it ended up being pretty difficult to sort through all the changes. It would have helped a lot to break this into smaller changes.
toolchain/check/name_scope.cpp
Outdated
| if (!class_decl) { | ||
| return std::nullopt; | ||
| } | ||
| // TODO: This is also valid in a mixin. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This comment no longer makes sense in this new context. It previously was in a "processing an extend impl" function, where extend impl would make sense in both a class and mixin scope.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reworded
| [&](const SemIR::FacetTypeInfo::RewriteConstraint& rewrite) { | ||
| auto access = context.insts().GetAs<SemIR::ImplWitnessAccess>( | ||
| GetImplWitnessAccessWithoutSubstitution(context, rewrite.lhs_id)); | ||
| return WitnessQueryMatchesInterface(context, access.witness_id, | ||
| interface_to_witness); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comment using words to describe what is going on here? Maybe something like:
| [&](const SemIR::FacetTypeInfo::RewriteConstraint& rewrite) { | |
| auto access = context.insts().GetAs<SemIR::ImplWitnessAccess>( | |
| GetImplWitnessAccessWithoutSubstitution(context, rewrite.lhs_id)); | |
| return WitnessQueryMatchesInterface(context, access.witness_id, | |
| interface_to_witness); | |
| // A rewrite constraint requires `interface_to_witness` to have a witness | |
| // table if its left-hand side names a member of that interface. | |
| [&](const SemIR::FacetTypeInfo::RewriteConstraint& rewrite) { | |
| auto access = context.insts().GetAs<SemIR::ImplWitnessAccess>( | |
| GetImplWitnessAccessWithoutSubstitution(context, rewrite.lhs_id)); | |
| return WitnessQueryMatchesInterface(context, access.witness_id, | |
| interface_to_witness); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added this:
// An iterator over the rewrite_constraints where the LHS of the rewrite names
// a member of the `interface_to_witness`. This filters out rewrites into
// other interfaces, as they do not set values in the witness table.
toolchain/check/handle_impl.cpp
Outdated
| static auto GetImplDefaultSelfType(Context& context, | ||
| const ClassScope& class_scope) | ||
| -> SemIR::TypeId { | ||
| // TODO: This is also valid in a mixin. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This comment no longer makes sense in the context of a class scope. It was previously in a "maybe class scope" context.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reworded
| // TODO: Revisit this once #3714 is resolved. | ||
| AddNameToLookup(context, SemIR::NameId::SelfType, self_type_inst_id); | ||
| AddNameToLookup(context, SemIR::NameId::SelfType, self_type.inst_id); | ||
| context.node_stack().Push(node_id, self_type.inst_id); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see where this is popped.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's popped in BuildImplDecl. The Push here is not new, it was on L60 before, but I moved it to be the last thing in the function since that is how they normally appear and helps me follow the push/pops.
toolchain/check/impl.cpp
Outdated
| } | ||
|
|
||
| if (is_definition && impl.witness_id != SemIR::ErrorInst::InstId) { | ||
| if (!RequireCompleteFacetTypeForImplDefinition( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm surprised to see this new call to RequireCompleteFacetTypeForImplDefinition. It still seems to be called from ImplWitnessStartDefinition.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Argh yeah that was how this whole PR started was to combine things, and I got lost (sorry it was hard to review, it was also hard to write and took me 3 tries...).
Now at the end of this PR I finally do understand exactly what was needed to get to a single RequireCompleteType instruction. It must be part of the definition, because in the case of a redeclaration, all generic decls must match, so we can't insert the RequireCompleteType instruction into the definition's declaration. So I have moved it appropriately into only ImplWitnessStartDefinition.
The result is that the declaration always makes a placeholder witness table if there's no rewrites, which gets resized in the definition. This eliminates a whole bunch of plumbing of is_definition around, further simplifying logic.
The tests churn a bunch with instruction orders changing, but we can see the RequireCompleteType inst moves to be consistently in the impl's definition.
danakj
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, PTAL
toolchain/check/name_scope.cpp
Outdated
| if (!class_decl) { | ||
| return std::nullopt; | ||
| } | ||
| // TODO: This is also valid in a mixin. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reworded
| [&](const SemIR::FacetTypeInfo::RewriteConstraint& rewrite) { | ||
| auto access = context.insts().GetAs<SemIR::ImplWitnessAccess>( | ||
| GetImplWitnessAccessWithoutSubstitution(context, rewrite.lhs_id)); | ||
| return WitnessQueryMatchesInterface(context, access.witness_id, | ||
| interface_to_witness); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added this:
// An iterator over the rewrite_constraints where the LHS of the rewrite names
// a member of the `interface_to_witness`. This filters out rewrites into
// other interfaces, as they do not set values in the witness table.
toolchain/check/handle_impl.cpp
Outdated
| static auto GetImplDefaultSelfType(Context& context, | ||
| const ClassScope& class_scope) | ||
| -> SemIR::TypeId { | ||
| // TODO: This is also valid in a mixin. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reworded
| // TODO: Revisit this once #3714 is resolved. | ||
| AddNameToLookup(context, SemIR::NameId::SelfType, self_type_inst_id); | ||
| AddNameToLookup(context, SemIR::NameId::SelfType, self_type.inst_id); | ||
| context.node_stack().Push(node_id, self_type.inst_id); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's popped in BuildImplDecl. The Push here is not new, it was on L60 before, but I moved it to be the last thing in the function since that is how they normally appear and helps me follow the push/pops.
toolchain/check/impl.cpp
Outdated
| } | ||
|
|
||
| if (is_definition && impl.witness_id != SemIR::ErrorInst::InstId) { | ||
| if (!RequireCompleteFacetTypeForImplDefinition( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Argh yeah that was how this whole PR started was to combine things, and I got lost (sorry it was hard to review, it was also hard to write and took me 3 tries...).
Now at the end of this PR I finally do understand exactly what was needed to get to a single RequireCompleteType instruction. It must be part of the definition, because in the case of a redeclaration, all generic decls must match, so we can't insert the RequireCompleteType instruction into the definition's declaration. So I have moved it appropriately into only ImplWitnessStartDefinition.
The result is that the declaration always makes a placeholder witness table if there's no rewrites, which gets resized in the definition. This eliminates a whole bunch of plumbing of is_definition around, further simplifying logic.
The tests churn a bunch with instruction orders changing, but we can see the RequireCompleteType inst moves to be consistently in the impl's definition.
We were requiring the facet type to be complete in two different places, but this was not necessary. Now we only require it to be complete in a single place when checking the definition. If it was required earlier for a rewrite constraint, it would have been already checked and diagnosed in member lookup for the rewrite.
In the process reorganize and rename code to help clarify the relationships and behaviour between handle_impl.cpp and impl.cpp.
Move the diagnosing of
extend impl T asto the handler of theT, similar to where it's diagnosed forrequiredecls. This helps simplify the code, avoiding a bunch of looking through parse nodes for specific types. Make them both handle errors in the self type in a similar manner, avoiding a second diagnostic.