-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Diagnose explicit Self in extend in the parse node handler (Refactor Impl construction 1/7)
#6465
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?
Diagnose explicit Self in extend in the parse node handler (Refactor Impl construction 1/7)
#6465
Conversation
zygoloid
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.
Some thoughts but apart from the "existant" -> "existent" spelling fix, these can all be deferred -- I appreciate this is part of a big refactor and it might be easier to handle these comments at the end of that, if they still apply at all.
| // --- fail_extend_require_with_nonexistant_type.carbon | ||
| library "[[@TEST_NAME]]"; | ||
|
|
||
| interface Y {} | ||
|
|
||
| interface Z { | ||
| // CHECK:STDERR: fail_extend_require_with_nonexistant_type.carbon:[[@LINE+4]]:18: error: name `nonexistant` not found [NameNotFound] | ||
| // CHECK:STDERR: extend require nonexistant impls Y; | ||
| // CHECK:STDERR: ^~~~~~~~~~~ | ||
| // CHECK:STDERR: | ||
| extend require nonexistant impls Y; | ||
| } | ||
|
|
||
| // --- fail_extend_require_with_nonexistant_type_pointer.carbon | ||
| library "[[@TEST_NAME]]"; | ||
|
|
||
| interface Y {} | ||
|
|
||
| interface Z { | ||
| // CHECK:STDERR: fail_extend_require_with_nonexistant_type_pointer.carbon:[[@LINE+4]]:18: error: name `nonexistant` not found [NameNotFound] | ||
| // CHECK:STDERR: extend require nonexistant* impls Y; | ||
| // CHECK:STDERR: ^~~~~~~~~~~ | ||
| // CHECK:STDERR: | ||
| extend require nonexistant* impls Y; | ||
| } |
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.
| // --- fail_extend_require_with_nonexistant_type.carbon | |
| library "[[@TEST_NAME]]"; | |
| interface Y {} | |
| interface Z { | |
| // CHECK:STDERR: fail_extend_require_with_nonexistant_type.carbon:[[@LINE+4]]:18: error: name `nonexistant` not found [NameNotFound] | |
| // CHECK:STDERR: extend require nonexistant impls Y; | |
| // CHECK:STDERR: ^~~~~~~~~~~ | |
| // CHECK:STDERR: | |
| extend require nonexistant impls Y; | |
| } | |
| // --- fail_extend_require_with_nonexistant_type_pointer.carbon | |
| library "[[@TEST_NAME]]"; | |
| interface Y {} | |
| interface Z { | |
| // CHECK:STDERR: fail_extend_require_with_nonexistant_type_pointer.carbon:[[@LINE+4]]:18: error: name `nonexistant` not found [NameNotFound] | |
| // CHECK:STDERR: extend require nonexistant* impls Y; | |
| // CHECK:STDERR: ^~~~~~~~~~~ | |
| // CHECK:STDERR: | |
| extend require nonexistant* impls Y; | |
| } | |
| // --- fail_extend_require_with_nonexistent_type.carbon | |
| library "[[@TEST_NAME]]"; | |
| interface Y {} | |
| interface Z { | |
| // CHECK:STDERR: fail_extend_require_with_nonexistant_type.carbon:[[@LINE+4]]:18: error: name `nonexistent` not found [NameNotFound] | |
| // CHECK:STDERR: extend require nonexistent impls Y; | |
| // CHECK:STDERR: ^~~~~~~~~~~ | |
| // CHECK:STDERR: | |
| extend require nonexistent impls Y; | |
| } | |
| // --- fail_extend_require_with_nonexistent_type_pointer.carbon | |
| library "[[@TEST_NAME]]"; | |
| interface Y {} | |
| interface Z { | |
| // CHECK:STDERR: fail_extend_require_with_nonexistent_type_pointer.carbon:[[@LINE+4]]:18: error: name `nonexistent` not found [NameNotFound] | |
| // CHECK:STDERR: extend require nonexistent* impls Y; | |
| // CHECK:STDERR: ^~~~~~~~~~~ | |
| // CHECK:STDERR: | |
| extend require nonexistent* impls Y; | |
| } |
| auto class_scope = | ||
| TryAsClassScope(context, context.decl_name_stack().PeekParentScopeId()); | ||
| if (class_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.
| auto class_scope = | |
| TryAsClassScope(context, context.decl_name_stack().PeekParentScopeId()); | |
| if (class_scope) { | |
| if (auto class_scope = | |
| TryAsClassScope(context, context.decl_name_stack().PeekParentScopeId())) { |
| auto diag = context.emitter().Build(extend_node, ExtendImplSelfAs); | ||
|
|
||
| if (self_type.type_id == GetImplDefaultSelfType(context, *class_scope)) { | ||
| // If the explicit self type is the default, suggest removing it with a | ||
| // diagnostic, but continue as if no error occurred since the self-type | ||
| // is semantically valid. | ||
| CARBON_DIAGNOSTIC(ExtendImplSelfAsDefault, Note, | ||
| "remove the explicit `Self` type here"); | ||
| diag.Note(self_node, ExtendImplSelfAsDefault); | ||
| if (self_type.type_id != SemIR::ErrorInst::TypeId) { | ||
| diag.Emit(); | ||
| } | ||
| } else if (self_type.type_id != SemIR::ErrorInst::TypeId) { | ||
| // Otherwise, the self-type is an error. | ||
| diag.Emit(); | ||
| class_scope->name_scope->set_has_error(); | ||
| self_type.inst_id = SemIR::ErrorInst::TypeInstId; | ||
| } | ||
| } |
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 should also call name_scope->set_has_error() if there's an error in the self type itself. So maybe:
| auto diag = context.emitter().Build(extend_node, ExtendImplSelfAs); | |
| if (self_type.type_id == GetImplDefaultSelfType(context, *class_scope)) { | |
| // If the explicit self type is the default, suggest removing it with a | |
| // diagnostic, but continue as if no error occurred since the self-type | |
| // is semantically valid. | |
| CARBON_DIAGNOSTIC(ExtendImplSelfAsDefault, Note, | |
| "remove the explicit `Self` type here"); | |
| diag.Note(self_node, ExtendImplSelfAsDefault); | |
| if (self_type.type_id != SemIR::ErrorInst::TypeId) { | |
| diag.Emit(); | |
| } | |
| } else if (self_type.type_id != SemIR::ErrorInst::TypeId) { | |
| // Otherwise, the self-type is an error. | |
| diag.Emit(); | |
| class_scope->name_scope->set_has_error(); | |
| self_type.inst_id = SemIR::ErrorInst::TypeInstId; | |
| } | |
| } | |
| auto diag = | |
| self_type.type_id == SemIR::ErrorInst::TypeId | |
| ? context.emitter().BuildSuppressed() | |
| : context.emitter().Build(extend_node, ExtendImplSelfAs); | |
| if (self_type.type_id == GetImplDefaultSelfType(context, *class_scope)) { | |
| // If the explicit self type is the default, suggest removing it with a | |
| // diagnostic, but continue as if no error occurred since the self-type | |
| // is semantically valid. | |
| CARBON_DIAGNOSTIC(ExtendImplSelfAsDefault, Note, | |
| "remove the explicit `Self` type here"); | |
| diag.Note(self_node, ExtendImplSelfAsDefault); | |
| } else { | |
| // Otherwise, the self-type is an error. | |
| class_scope->name_scope->set_has_error(); | |
| self_type.inst_id = SemIR::ErrorInst::TypeInstId; | |
| } | |
| } | |
| diag.Emit(); |
| auto class_scope = | ||
| TryAsClassScope(context, context.decl_name_stack().PeekParentScopeId()); | ||
| if (class_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.
| auto class_scope = | |
| TryAsClassScope(context, context.decl_name_stack().PeekParentScopeId()); | |
| if (class_scope) { | |
| if (auto class_scope = | |
| TryAsClassScope(context, context.decl_name_stack().PeekParentScopeId())) { |
| } | ||
| self_type.inst_id = SemIR::ErrorInst::TypeInstId; |
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.
Should we set the scope to be invalid here too?
| // TODO: If the explicit self-type matches a lookup of NameId::SelfType, | ||
| // add a note to the diagnostic: "remove the explicit `Self` type here", | ||
| // and continue without an ErrorInst. See ExtendImplSelfAsDefault. | ||
| context.emitter().Emit(self_node_id, RequireImplsExtendWithExplicitSelf); |
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 seems a bit inconsistent to be diagnosing this here but diagnosing the class case when handling the ImplTypeAs node.
We encode the state of looking that the parent scope is a Class into a type so that we can avoid extra lookups. Then use that in refactoring where diagnostics are generated for an explicit
Selfin anextend impldeclaration.We add tests that we don't double-diagnose the Self type when it's already an error, and make the behaviour of
extend requirematch that ofextend impl as.This avoids some fragile/complex parse-node lookups (such as
context.parse_tree_and_subtrees().ExtractAs<Parse::ImplTypeAs>) by using the parse node at the point where we are handling it instead of much later.This is part of #6420 which is being split up into a chain of smaller PRs.