mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-05 22:02:55 +01:00
Diagnose explicit Self in extend in the parse node handler (Refactor Impl construction 1/7) (#6465)
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 `Self` in an `extend impl` declaration. We add tests that we don't double-diagnose the Self type when it's already an error, and make the behaviour of `extend require` match that of `extend 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.
This commit is contained in:
@@ -14,6 +14,7 @@
|
||||
#include "toolchain/check/inst.h"
|
||||
#include "toolchain/check/modifiers.h"
|
||||
#include "toolchain/check/name_lookup.h"
|
||||
#include "toolchain/check/name_scope.h"
|
||||
#include "toolchain/check/pattern_match.h"
|
||||
#include "toolchain/check/type.h"
|
||||
#include "toolchain/check/type_completion.h"
|
||||
@@ -24,6 +25,16 @@
|
||||
|
||||
namespace Carbon::Check {
|
||||
|
||||
// Returns the implicit `Self` type for an `impl` when it's in a `class`
|
||||
// declaration.
|
||||
//
|
||||
// TODO: Mixin scopes also have a default `Self` type.
|
||||
static auto GetImplDefaultSelfType(Context& context,
|
||||
const ClassScope& class_scope)
|
||||
-> SemIR::TypeId {
|
||||
return context.classes().Get(class_scope.class_decl.class_id).self_type_id;
|
||||
}
|
||||
|
||||
auto HandleParseNode(Context& context, Parse::ImplIntroducerId node_id)
|
||||
-> bool {
|
||||
// This might be a generic impl.
|
||||
@@ -56,14 +67,45 @@ auto HandleParseNode(Context& context, Parse::ForallId /*node_id*/) -> bool {
|
||||
|
||||
auto HandleParseNode(Context& context, Parse::ImplTypeAsId node_id) -> bool {
|
||||
auto [self_node, self_id] = context.node_stack().PopExprWithNodeId();
|
||||
auto self_type_inst_id = ExprAsType(context, self_node, self_id).inst_id;
|
||||
context.node_stack().Push(node_id, self_type_inst_id);
|
||||
auto self_type = ExprAsType(context, self_node, self_id);
|
||||
|
||||
const auto& introducer = context.decl_introducer_state_stack().innermost();
|
||||
if (introducer.modifier_set.HasAnyOf(KeywordModifierSet::Extend)) {
|
||||
// TODO: Also handle the parent scope being a mixin.
|
||||
if (auto class_scope = TryAsClassScope(
|
||||
context, context.decl_name_stack().PeekParentScopeId())) {
|
||||
// If we're not inside a class at all, that will be diagnosed against the
|
||||
// `extend` elsewhere.
|
||||
auto extend_node = introducer.modifier_node_id(ModifierOrder::Extend);
|
||||
CARBON_DIAGNOSTIC(ExtendImplSelfAs, Error,
|
||||
"cannot `extend` an `impl` with an explicit self type");
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Introduce `Self`. Note that we add this name lexically rather than adding
|
||||
// to the `NameScopeId` of the `impl`, because this happens before we enter
|
||||
// the `impl` scope or even identify which `impl` we're declaring.
|
||||
// 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);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -71,8 +113,9 @@ auto HandleParseNode(Context& context, Parse::ImplDefaultSelfAsId node_id)
|
||||
-> bool {
|
||||
auto self_inst_id = SemIR::TypeInstId::None;
|
||||
|
||||
if (auto self_type_id = GetImplDefaultSelfType(context);
|
||||
self_type_id.has_value()) {
|
||||
if (auto class_scope = TryAsClassScope(
|
||||
context, context.decl_name_stack().PeekParentScopeId())) {
|
||||
auto self_type_id = GetImplDefaultSelfType(context, *class_scope);
|
||||
// Build the implicit access to the enclosing `Self`.
|
||||
// TODO: Consider calling `HandleNameAsExpr` to build this implicit `Self`
|
||||
// expression. We've already done the work to check that the enclosing
|
||||
|
||||
Reference in New Issue
Block a user