Skip to content

Conversation

@danakj
Copy link
Contributor

@danakj danakj commented Nov 21, 2025

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 as to the handler of the T, similar to where it's diagnosed for require decls. 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.

@danakj danakj requested a review from a team as a code owner November 21, 2025 18:32
@danakj danakj requested review from josh11b and removed request for a team November 21, 2025 18:32
@danakj
Copy link
Contributor Author

danakj commented Nov 21, 2025

This is based on #6385, the first commit is less-completing-impl-as.

@danakj
Copy link
Contributor Author

danakj commented Nov 24, 2025

This is based on #6385, the first commit is less-completing-impl-as.

Rebased to trunk.

@danakj danakj force-pushed the less-completing-impl-as branch from c713931 to 181c03c Compare November 24, 2025 15:23
@danakj danakj requested a review from zygoloid November 26, 2025 16:30
Copy link
Contributor

@josh11b josh11b left a 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.

if (!class_decl) {
return std::nullopt;
}
// TODO: This is also valid in a mixin.
Copy link
Contributor

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reworded

Comment on lines +89 to +93
[&](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);
Copy link
Contributor

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:

Suggested change
[&](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);

Copy link
Contributor Author

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.

static auto GetImplDefaultSelfType(Context& context,
const ClassScope& class_scope)
-> SemIR::TypeId {
// TODO: This is also valid in a mixin.
Copy link
Contributor

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.

Copy link
Contributor Author

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);
Copy link
Contributor

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.

Copy link
Contributor Author

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.

}

if (is_definition && impl.witness_id != SemIR::ErrorInst::InstId) {
if (!RequireCompleteFacetTypeForImplDefinition(
Copy link
Contributor

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.

Copy link
Contributor Author

@danakj danakj Nov 26, 2025

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.

Copy link
Contributor Author

@danakj danakj left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, PTAL

if (!class_decl) {
return std::nullopt;
}
// TODO: This is also valid in a mixin.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reworded

Comment on lines +89 to +93
[&](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);
Copy link
Contributor Author

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.

static auto GetImplDefaultSelfType(Context& context,
const ClassScope& class_scope)
-> SemIR::TypeId {
// TODO: This is also valid in a mixin.
Copy link
Contributor Author

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);
Copy link
Contributor Author

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.

}

if (is_definition && impl.witness_id != SemIR::ErrorInst::InstId) {
if (!RequireCompleteFacetTypeForImplDefinition(
Copy link
Contributor Author

@danakj danakj Nov 26, 2025

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 danakj requested a review from josh11b November 26, 2025 23:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants