-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Type completion of facet types is separate from Identifying #6385
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
|
@josh11b This comes from discussion with Richard on Friday, and we think may extend (ha ha) a little on what was proposed in https://github.com/carbon-language/carbon-lang/blob/trunk/proposals/p5168.md#proposed-rules since those rules do not mention extend. Our discussion notes are here and I will be following up to introduce a |
|
This needs a rebase to get tests happy, which I will get to later. |
c7e3481 to
eafaeb1
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.
This PR is fine, but it seems like we should update the official rules with a new proposal.
I had tried to get rid of the recursion onto the facet types in require decls, as we check them for completeness where they are written. However when the interface is generic, the facet type can contain generic bindings in it, and those may have been OK as symbolics, but produce an error with a given specific. We have tests for these included in the PR, which removing the recursion broke. So I have re-introduced the recursion (via worklist) on the specific facet type in an
We only need to do this step of forming specifics and recursing into the |
483b33d to
90e932d
Compare
|
We had a test with |
|
The last commit now avoids a UAF which is properly addressed by #6399. I will revert that commit when the fix lands. |
6b368f8 to
7aa776d
Compare
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.
PTAL
| // Requries B identified. | ||
| impl () as B; |
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.
Do we have a test for
constraint B;
impl () as B;somewhere? (Which should be an error because B is not identified.)
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.
We do here:
| impl C as X; |
But I have now moved tests around to consolidate things better in impl/incomplete.carbon.
toolchain/check/type_completion.cpp
Outdated
| for (auto extends : facet_type_info.extend_constraints) { | ||
| auto interface_id = extends.interface_id; | ||
| const auto& interface = context.interfaces().Get(interface_id); | ||
| if (!interface.is_complete()) { | ||
| if (diagnoser) { | ||
| auto builder = diagnoser(); | ||
| NoteIncompleteInterface(context, interface_id, builder); | ||
| builder.Emit(); | ||
| } | ||
| return false; | ||
| } | ||
| if (interface.generic_id.has_value()) { | ||
| specifics.push_back( | ||
| {extends.specific_id, interface.require_impls_block_id}); | ||
| } | ||
| } | ||
|
|
||
| for (auto extends : facet_type_info.extend_named_constraints) { | ||
| auto named_constraint_id = extends.named_constraint_id; | ||
| const auto& constraint = | ||
| context.named_constraints().Get(named_constraint_id); | ||
| if (!constraint.is_complete()) { | ||
| if (diagnoser) { | ||
| auto builder = diagnoser(); | ||
| NoteIncompleteNamedConstraint(context, named_constraint_id, builder); | ||
| builder.Emit(); | ||
| } | ||
| return false; | ||
| } | ||
| if (constraint.generic_id.has_value()) { | ||
| specifics.push_back( | ||
| {extends.specific_id, constraint.require_impls_block_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.
Would be nice if we could find some way to avoid this duplication, maybe add a TODO?
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 not sure. This is emblematic of the duplication between interfaces and named constraints across the toolchain. Since they have different types, with different field names for ids, etc, we end up with a lot of very similar code. I have tried elsewhere to use field pointers and templates but it gets really unreadable really quickly. I don't have any great ideas for how I would address that TODO right now.
toolchain/check/type_completion.cpp
Outdated
| auto requires_specifics = | ||
| llvm::ArrayRef(specifics).drop_front(specifics_done); | ||
| for (auto [specific_id, requires_block_id] : requires_specifics) { | ||
| for (auto require_impls_id : | ||
| context.require_impls_blocks().Get(requires_block_id)) { | ||
| const auto& require = context.require_impls().Get(require_impls_id); | ||
| if (require.extend_self) { | ||
| auto require_specific_id = MakeCopyOfSpecificAndAppendSelf( | ||
| context, loc_id, specific_id, require.generic_id, | ||
| GetRequireImplsSpecificSelf(context, require)); | ||
| // Construct the specific facet type. If None is returned, it means an | ||
| // error was diagnosed. | ||
| auto facet_type_id = GetFacetTypeInSpecific( | ||
| context, require_specific_id, require.facet_type_inst_id); | ||
| if (!facet_type_id.has_value()) { | ||
| return false; | ||
| } | ||
| work.push_back(facet_type_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 think we may be able to avoid this:
In handle_require.cpp, move the RequireCompleteType call for an extend require impls declaration later, to after we finish the generic for the RequireImplsDecl.
That way, if the enclosing context is itself generic, then we'll create a require_complete_type instruction in the generic for the interface / named constraint, which will cause the ResolveSpecificDefinition below to recursively require the extended type to be complete. (That's how we get recursive completeness requirements for extend in classes.)
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.
Trivially making this change did not work:
CHECK failure at toolchain/sem_ir/generic.cpp:96: specific_ir.generics().GetSelfSpecific( specific_ir.specifics().Get(specific_id).generic_id) == specific_id: Queried generic_inst_in_decl0 in specific5800002B for {kind: NamedConstraintDecl, arg0: constraint58000000, arg1: inst_block5800001B, type: type(inst58000041)} before it was resolved.
This looks like when I tried to resolve an inner specific facet type before resolving the containing constraint's specific. I think this strategy maybe just works for constraint->require but does not work for constraint->require->constraint->require as there's nothing crossing that middle require->constraint boundary.
I can try understand this a bit better tomorrow.
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.
Ok I found one issue.
ResolveSpecificDeclForInst() in eval was not resolving specific decl blocks for named constraints in a facet type after applying a specific.
But fixing that turned up a crash in import instead (that I can reproduce on interfaces too without that fix).
For the record this crash was fixed by Jon in #6409.
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.
Alright, this is done. I apologize cuz I have rebased this a few times to make it work since HEAD had fixes I needed, and mixing commits with merges makes for real confusion (for me?).
6a5962d to
beb7d87
Compare
This reverts commit 8c5e573.
beb7d87 to
e1fc972
Compare
toolchain/check/facet_type.cpp
Outdated
| context.TODO(loc_id, | ||
| "declaration of impl as incomplete interface with a rewrite " | ||
| "constraint"); |
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 am quite convinced this case should no longer ever happen so I changed it into a CHECK with a TODO to remove the parameter. I tried to organize the type completion of the facet type in impl as a bit better since it occurs in two different places with some loose coordination right now, but it's very tricky. I will try again elsewhere.
Identifying a facet type is an operation on a pair of (self type, facet type). It substitutes that self in as the
Selfof any require declarations in order to form the set of (self type, SpecificInterface) pairs that constitute the requirements of the IdentifiedFacetType. Currently we don't pass around any self type, and assume all require declarations are written againstSelfbut this will change in the future.By contrast, type completion is done in the abstract and does not form specifics for the require declarations. The purpose of type completion is to enumerate the scopes where name lookup can occur and ensure they are completed.
With this change, type completion is:
extendscopes are complete since name lookup can find symbols in them.We add some test cases that demonstrate consistency between a resolving the specific of a generic class, and a generic interface/constraint, both used in a type position. In all cases, an invalid specific is not materialized for the type completion when the specific's arguments are used in a non-extend context. But they specific is materialized and checked for type completion when in an extend context (extend impl or extend require).
Type completion itself does not need to recurse into named constraints or interfaces as the
extend requiredeclarations require the type to be complete immediately, just as forextend implin a class.We had a test (
fail_incomplete_where.carbon) withimpl as J where .Self impls KandJis incomplete, which used to be diagnosed but no longer is, because we don't require non-extend interfaces to be complete in type completion, nor in identification. The test was trying to test the presence of rewrite constraints though, which it didn't even use. So we remove the diagnostic that we can't hit anymore and replaced it with a TODO, and add a test that should reach that TODO once qualified rewrite constraints work.