mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 22:02:23 +01:00
Introduces `Context` and `SoftContext` messages, which can be introduced
through a `ContextBuilder`:
- The `Context` messages come before the diagnostic in the output.
- The first `Context` message steals the diagnostic level from the main
diagnostic, and turns the main diagnostic into a Note attached to the
context.
- A `SoftContext` message works similarly, but if it's preceeded by a
`Context` or `SoftContext` message, then it is dropped. This can be used
as a default/backup scope when nothing more interesting is provided up
the stack, such as in `TryEvalBlockForSpecific`.
The `ContextBuilder` is provided to a callback through
`Diagnostics::ContextScope`, an RAII type `AnnotationScope` but for
context messages.
This allows a high level operation to provide a context message like
"failed to identify facet type {0}" which will then be used as the error
if a diagnostic is produced during identification, with the latter
diagnostic attached as a note to explain why the contextual operation
failed.
In particular, this allows monomorphization errors (such as an array
bound being negative) to be attached to a higher lever operation instead
of being top-level diagnostics themselves, with the monomorphization
site being a note. This inverts the source code locations that appear in
the diagnostic, so that the top-level diagnostic points to the "user
code" which causes the monomorphization.
This is presented as an alternative strategy to #6753, which plumbed
diagnoser callbacks around to achieve the same goals.
We replace the diagnoser callbacks in type completion and operators with
ContextScope callbacks instead, which now provide better diagnostics for
monomorphization errors. Other callers to MakeSpecific do not yet have
ContextScopes introduced in order to turn monomorphization errors into
more interesting diagnostics.
526 lines
21 KiB
C++
526 lines
21 KiB
C++
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
|
// Exceptions. See /LICENSE for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
|
|
#include <utility>
|
|
|
|
#include "toolchain/base/kind_switch.h"
|
|
#include "toolchain/check/context.h"
|
|
#include "toolchain/check/convert.h"
|
|
#include "toolchain/check/facet_type.h"
|
|
#include "toolchain/check/handle.h"
|
|
#include "toolchain/check/inst.h"
|
|
#include "toolchain/check/interface.h"
|
|
#include "toolchain/check/name_lookup.h"
|
|
#include "toolchain/check/pattern.h"
|
|
#include "toolchain/check/return.h"
|
|
#include "toolchain/check/type.h"
|
|
#include "toolchain/check/type_completion.h"
|
|
#include "toolchain/check/unused.h"
|
|
#include "toolchain/diagnostics/format_providers.h"
|
|
#include "toolchain/parse/node_ids.h"
|
|
#include "toolchain/sem_ir/ids.h"
|
|
#include "toolchain/sem_ir/inst.h"
|
|
#include "toolchain/sem_ir/pattern.h"
|
|
#include "toolchain/sem_ir/typed_insts.h"
|
|
|
|
namespace Carbon::Check {
|
|
|
|
auto HandleParseNode(Context& context, Parse::UnderscoreNameId node_id)
|
|
-> bool {
|
|
context.node_stack().Push(node_id, SemIR::NameId::Underscore);
|
|
return true;
|
|
}
|
|
|
|
// Returns the `InstKind` corresponding to the pattern's `NodeKind`.
|
|
static auto GetPatternInstKind(Parse::NodeKind node_kind, bool is_ref)
|
|
-> SemIR::InstKind {
|
|
switch (node_kind) {
|
|
case Parse::NodeKind::CompileTimeBindingPattern:
|
|
return SemIR::InstKind::SymbolicBindingPattern;
|
|
case Parse::NodeKind::LetBindingPattern:
|
|
return is_ref ? SemIR::InstKind::RefBindingPattern
|
|
: SemIR::InstKind::ValueBindingPattern;
|
|
case Parse::NodeKind::VarBindingPattern:
|
|
return SemIR::InstKind::RefBindingPattern;
|
|
default:
|
|
CARBON_FATAL("Unexpected node kind: {0}", node_kind);
|
|
}
|
|
}
|
|
|
|
// Returns true if a parameter is valid in the given `introducer_kind`.
|
|
static auto IsValidParamForIntroducer(Context& context, Parse::NodeId node_id,
|
|
SemIR::NameId name_id,
|
|
Lex::TokenKind introducer_kind,
|
|
bool is_generic) -> bool {
|
|
switch (introducer_kind) {
|
|
case Lex::TokenKind::Fn: {
|
|
if (context.full_pattern_stack().CurrentKind() ==
|
|
FullPatternStack::Kind::ImplicitParamList &&
|
|
!(is_generic || name_id == SemIR::NameId::SelfValue)) {
|
|
CARBON_DIAGNOSTIC(
|
|
ImplictParamMustBeConstant, Error,
|
|
"implicit parameters of functions must be constant or `self`");
|
|
context.emitter().Emit(node_id, ImplictParamMustBeConstant);
|
|
return false;
|
|
}
|
|
// Parameters can have incomplete types in a function declaration, but not
|
|
// in a function definition. We don't know which kind we have here, so
|
|
// don't validate it.
|
|
return true;
|
|
}
|
|
case Lex::TokenKind::Choice:
|
|
if (context.scope_stack().PeekInstId().has_value()) {
|
|
// We are building a pattern for a choice alternative, not the
|
|
// choice type itself.
|
|
|
|
// Implicit param lists are prevented during parse.
|
|
CARBON_CHECK(context.full_pattern_stack().CurrentKind() !=
|
|
FullPatternStack::Kind::ImplicitParamList,
|
|
"choice alternative with implicit parameters");
|
|
// Don't fall through to the `Class` logic for choice alternatives.
|
|
return true;
|
|
}
|
|
[[fallthrough]];
|
|
case Lex::TokenKind::Class:
|
|
case Lex::TokenKind::Impl:
|
|
case Lex::TokenKind::Interface: {
|
|
if (name_id == SemIR::NameId::SelfValue) {
|
|
CARBON_DIAGNOSTIC(SelfParameterNotAllowed, Error,
|
|
"`self` parameter only allowed on functions");
|
|
context.emitter().Emit(node_id, SelfParameterNotAllowed);
|
|
return false;
|
|
}
|
|
if (!is_generic) {
|
|
CARBON_DIAGNOSTIC(GenericParamMustBeConstant, Error,
|
|
"parameters of generic types must be constant");
|
|
context.emitter().Emit(node_id, GenericParamMustBeConstant);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
default:
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// TODO: make this function shorter by factoring pieces out.
|
|
static auto HandleAnyBindingPattern(Context& context, Parse::NodeId node_id,
|
|
Parse::NodeKind node_kind,
|
|
bool is_unused = false) -> bool {
|
|
// TODO: split this into smaller, more focused functions.
|
|
auto [type_node, parsed_type_id] = context.node_stack().PopExprWithNodeId();
|
|
auto [cast_type_inst_id, cast_type_id] =
|
|
ExprAsType(context, type_node, parsed_type_id);
|
|
|
|
SemIR::ExprRegionId type_expr_region_id =
|
|
EndSubpatternAsExpr(context, cast_type_inst_id);
|
|
|
|
// The name in a generic binding may be wrapped in `template`.
|
|
bool is_generic = node_kind == Parse::NodeKind::CompileTimeBindingPattern;
|
|
bool is_template =
|
|
context.node_stack()
|
|
.PopAndDiscardSoloNodeIdIf<Parse::NodeKind::TemplateBindingName>();
|
|
// A non-generic template binding is diagnosed by the parser.
|
|
is_template &= is_generic;
|
|
|
|
// The name in a runtime binding may be wrapped in `ref`.
|
|
bool is_ref =
|
|
context.node_stack()
|
|
.PopAndDiscardSoloNodeIdIf<Parse::NodeKind::RefBindingName>();
|
|
|
|
SemIR::InstKind pattern_inst_kind = GetPatternInstKind(node_kind, is_ref);
|
|
|
|
auto [name_node, name_id] = context.node_stack().PopNameWithNodeId();
|
|
|
|
const DeclIntroducerState& introducer =
|
|
context.decl_introducer_state_stack().innermost();
|
|
|
|
auto make_binding_pattern = [&]() -> SemIR::InstId {
|
|
// TODO: Eventually the name will need to support associations with other
|
|
// scopes, but right now we don't support qualified names here.
|
|
auto binding = AddBindingPattern(context, name_node, name_id, cast_type_id,
|
|
type_expr_region_id, pattern_inst_kind,
|
|
is_template, is_unused);
|
|
|
|
// TODO: If `is_generic`, then `binding.bind_id is a SymbolicBinding. Subst
|
|
// the `.Self` of type `type` in the `cast_type_id` type (a `FacetType`)
|
|
// with the `binding.bind_id` itself, and build a new pattern with that.
|
|
// This is kind of cyclical. So we need to reuse the EntityNameId, which
|
|
// will also reuse the CompileTimeBinding for the new SymbolicBinding.
|
|
|
|
if (name_id != SemIR::NameId::Underscore) {
|
|
// Add name to lookup immediately, so it can be used in the rest of the
|
|
// enclosing pattern.
|
|
auto name_context =
|
|
context.decl_name_stack().MakeUnqualifiedName(name_node, name_id);
|
|
context.decl_name_stack().AddNameOrDiagnose(
|
|
name_context, binding.bind_id,
|
|
introducer.modifier_set.GetAccessKind());
|
|
context.full_pattern_stack().AddBindName(name_id);
|
|
}
|
|
|
|
return binding.pattern_id;
|
|
};
|
|
|
|
auto abstract_diagnostic_context = [&](auto& builder) {
|
|
CARBON_DIAGNOSTIC(AbstractTypeInVarPattern, Context,
|
|
"binding pattern has abstract type {0} in `var` "
|
|
"pattern",
|
|
SemIR::TypeId);
|
|
builder.Context(type_node, AbstractTypeInVarPattern, cast_type_id);
|
|
};
|
|
|
|
// A `self` binding can only appear in an implicit parameter list.
|
|
if (name_id == SemIR::NameId::SelfValue &&
|
|
!context.node_stack().PeekIs(Parse::NodeKind::ImplicitParamListStart)) {
|
|
CARBON_DIAGNOSTIC(
|
|
SelfOutsideImplicitParamList, Error,
|
|
"`self` can only be declared in an implicit parameter list");
|
|
context.emitter().Emit(node_id, SelfOutsideImplicitParamList);
|
|
}
|
|
|
|
if (node_kind == Parse::NodeKind::CompileTimeBindingPattern &&
|
|
introducer.kind == Lex::TokenKind::Let) {
|
|
// TODO: We should re-evaluate the contents of the eval block in a
|
|
// synthesized specific to form these values, in order to propagate the
|
|
// values.
|
|
return context.TODO(node_id,
|
|
"local `let :!` bindings are currently unsupported");
|
|
}
|
|
|
|
// Allocate an instruction of the appropriate kind, linked to the name for
|
|
// error locations.
|
|
switch (context.full_pattern_stack().CurrentKind()) {
|
|
case FullPatternStack::Kind::ImplicitParamList:
|
|
case FullPatternStack::Kind::ExplicitParamList: {
|
|
if (!IsValidParamForIntroducer(context, node_id, name_id, introducer.kind,
|
|
is_generic)) {
|
|
if (name_id != SemIR::NameId::Underscore) {
|
|
AddNameToLookup(context, name_id, SemIR::ErrorInst::InstId);
|
|
}
|
|
// Replace the parameter with `ErrorInst` so that we don't try
|
|
// constructing a generic based on it.
|
|
context.node_stack().Push(node_id, SemIR::ErrorInst::InstId);
|
|
break;
|
|
}
|
|
|
|
// Using `AsConcreteType` here causes `fn F[var self: Self]();`
|
|
// to fail since `Self` is an incomplete type.
|
|
if (node_kind == Parse::NodeKind::VarBindingPattern) {
|
|
auto [unqualified_type_id, qualifiers] =
|
|
context.types().GetUnqualifiedTypeAndQualifiers(cast_type_id);
|
|
if ((qualifiers & SemIR::TypeQualifiers::Partial) !=
|
|
SemIR::TypeQualifiers::Partial &&
|
|
context.types().Is<SemIR::ClassType>(unqualified_type_id)) {
|
|
auto class_type =
|
|
context.types().GetAs<SemIR::ClassType>(unqualified_type_id);
|
|
auto& class_info = context.classes().Get(class_type.class_id);
|
|
if (class_info.inheritance_kind ==
|
|
SemIR::Class::InheritanceKind::Abstract) {
|
|
Diagnostics::ContextScope scope(&context.emitter(),
|
|
abstract_diagnostic_context);
|
|
DiagnoseAbstractClass(context, class_type.class_id,
|
|
/*direct_use=*/true);
|
|
cast_type_id = SemIR::ErrorInst::TypeId;
|
|
}
|
|
}
|
|
}
|
|
|
|
auto result_inst_id = make_binding_pattern();
|
|
|
|
// A binding pattern in a function signature is a `Call` parameter
|
|
// unless it's nested inside a `var` pattern (because then the
|
|
// enclosing `var` pattern is), or it's a compile-time binding pattern
|
|
// (because then it's not passed to the `Call` inst).
|
|
if (node_kind == Parse::NodeKind::LetBindingPattern) {
|
|
auto type_id = context.insts().GetAttachedType(result_inst_id);
|
|
if (is_ref) {
|
|
result_inst_id = AddPatternInst<SemIR::RefParamPattern>(
|
|
context, node_id,
|
|
{.type_id = type_id,
|
|
.subpattern_id = result_inst_id,
|
|
.index = context.full_pattern_stack().NextCallParamIndex()});
|
|
} else {
|
|
result_inst_id = AddPatternInst<SemIR::ValueParamPattern>(
|
|
context, node_id,
|
|
{.type_id = type_id,
|
|
.subpattern_id = result_inst_id,
|
|
.index = context.full_pattern_stack().NextCallParamIndex()});
|
|
}
|
|
}
|
|
context.node_stack().Push(node_id, result_inst_id);
|
|
break;
|
|
}
|
|
|
|
case FullPatternStack::Kind::NameBindingDecl: {
|
|
auto incomplete_diagnostic_context = [&](auto& builder) {
|
|
CARBON_DIAGNOSTIC(IncompleteTypeInBindingDecl, Context,
|
|
"binding pattern has incomplete type {0} in name "
|
|
"binding declaration",
|
|
InstIdAsType);
|
|
builder.Context(type_node, IncompleteTypeInBindingDecl,
|
|
cast_type_inst_id);
|
|
};
|
|
if (node_kind == Parse::NodeKind::VarBindingPattern) {
|
|
if (!RequireConcreteType(context, cast_type_id, type_node,
|
|
incomplete_diagnostic_context,
|
|
abstract_diagnostic_context)) {
|
|
cast_type_id = SemIR::ErrorInst::TypeId;
|
|
}
|
|
} else {
|
|
if (!RequireCompleteType(context, cast_type_id, type_node,
|
|
incomplete_diagnostic_context)) {
|
|
cast_type_id = SemIR::ErrorInst::TypeId;
|
|
}
|
|
}
|
|
|
|
auto binding_pattern_id = make_binding_pattern();
|
|
if (node_kind == Parse::NodeKind::VarBindingPattern) {
|
|
CARBON_CHECK(!is_generic);
|
|
|
|
if (introducer.modifier_set.HasAnyOf(KeywordModifierSet::Returned)) {
|
|
// TODO: Should we check this for the `var` as a whole, rather than
|
|
// for the name binding?
|
|
auto bind_id = context.bind_name_map()
|
|
.Lookup(binding_pattern_id)
|
|
.value()
|
|
.bind_name_id;
|
|
RegisterReturnedVar(context,
|
|
introducer.modifier_node_id(ModifierOrder::Decl),
|
|
type_node, cast_type_id, bind_id, name_id);
|
|
}
|
|
}
|
|
context.node_stack().Push(node_id, binding_pattern_id);
|
|
break;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
auto HandleParseNode(Context& context, Parse::LetBindingPatternId node_id)
|
|
-> bool {
|
|
return HandleAnyBindingPattern(context, node_id,
|
|
Parse::NodeKind::LetBindingPattern);
|
|
}
|
|
|
|
auto HandleParseNode(Context& context, Parse::VarBindingPatternId node_id)
|
|
-> bool {
|
|
return HandleAnyBindingPattern(context, node_id,
|
|
Parse::NodeKind::VarBindingPattern);
|
|
}
|
|
|
|
auto HandleParseNode(Context& context, Parse::FormBindingPatternId node_id)
|
|
-> bool {
|
|
return context.TODO(node_id, "Implement :? support");
|
|
}
|
|
|
|
auto HandleParseNode(Context& context,
|
|
Parse::CompileTimeBindingPatternStartId /*node_id*/)
|
|
-> bool {
|
|
// Make a scope to contain the `.Self` facet value for use in the type of the
|
|
// compile time binding. This is popped when handling the
|
|
// CompileTimeBindingPatternId.
|
|
context.scope_stack().PushForSameRegion();
|
|
|
|
// The `.Self` must have a type of `FacetType`, so that it gets wrapped in
|
|
// `FacetAccessType` when used in a type position, such as in `U:! I(.Self)`.
|
|
// This allows substitution with other facet values without requiring an
|
|
// additional `FacetAccessType` to be inserted.
|
|
auto type_id = GetEmptyFacetType(context);
|
|
|
|
MakePeriodSelfFacetValue(context, type_id);
|
|
return true;
|
|
}
|
|
|
|
auto HandleParseNode(Context& context,
|
|
Parse::CompileTimeBindingPatternId node_id) -> bool {
|
|
// Pop the `.Self` facet value name introduced by the
|
|
// CompileTimeBindingPatternStart.
|
|
context.scope_stack().Pop(/*check_unused=*/true);
|
|
|
|
auto node_kind = Parse::NodeKind::CompileTimeBindingPattern;
|
|
const DeclIntroducerState& introducer =
|
|
context.decl_introducer_state_stack().innermost();
|
|
if (introducer.kind == Lex::TokenKind::Let) {
|
|
// Disallow `let` outside of function and interface definitions.
|
|
// TODO: Find a less brittle way of doing this. A `scope_inst_id` of `None`
|
|
// can represent a block scope, but is also used for other kinds of scopes
|
|
// that aren't necessarily part of a function decl.
|
|
// We don't need to check if the scope is an interface here as this is
|
|
// already caught in the parse phase by the separated associated constant
|
|
// logic.
|
|
auto scope_inst_id = context.scope_stack().PeekInstId();
|
|
if (scope_inst_id.has_value()) {
|
|
auto scope_inst = context.insts().Get(scope_inst_id);
|
|
if (!scope_inst.Is<SemIR::FunctionDecl>()) {
|
|
context.TODO(
|
|
node_id,
|
|
"`let` compile time binding outside function or interface");
|
|
node_kind = Parse::NodeKind::LetBindingPattern;
|
|
}
|
|
}
|
|
}
|
|
|
|
return HandleAnyBindingPattern(context, node_id, node_kind);
|
|
}
|
|
|
|
auto HandleParseNode(Context& context,
|
|
Parse::AssociatedConstantNameAndTypeId node_id) -> bool {
|
|
auto [type_node, parsed_type_id] = context.node_stack().PopExprWithNodeId();
|
|
auto [cast_type_inst_id, cast_type_id] =
|
|
ExprAsType(context, type_node, parsed_type_id);
|
|
|
|
EndSubpatternAsExpr(context, cast_type_inst_id);
|
|
|
|
auto [name_node, name_id] = context.node_stack().PopNameWithNodeId();
|
|
|
|
if (name_id == SemIR::NameId::Underscore) {
|
|
// The action item here may be to document this as not allowed, and
|
|
// add a proper diagnostic.
|
|
context.TODO(node_id, "_ used as associated constant name");
|
|
}
|
|
|
|
SemIR::AssociatedConstantDecl assoc_const_decl = {
|
|
.type_id = cast_type_id,
|
|
.assoc_const_id = SemIR::AssociatedConstantId::None,
|
|
.decl_block_id = SemIR::InstBlockId::None};
|
|
auto decl_id =
|
|
AddPlaceholderInstInNoBlock(context, node_id, assoc_const_decl);
|
|
assoc_const_decl.assoc_const_id = context.associated_constants().Add(
|
|
{.name_id = name_id,
|
|
.parent_scope_id = context.scope_stack().PeekNameScopeId(),
|
|
.decl_id = decl_id,
|
|
.default_value_id = SemIR::InstId::None});
|
|
ReplaceInstBeforeConstantUse(context, decl_id, assoc_const_decl);
|
|
|
|
context.node_stack().Push(node_id, decl_id);
|
|
return true;
|
|
}
|
|
|
|
auto HandleParseNode(Context& context, Parse::FieldNameAndTypeId node_id)
|
|
-> bool {
|
|
auto [type_node, parsed_type_id] = context.node_stack().PopExprWithNodeId();
|
|
auto [cast_type_inst_id, cast_type_id] =
|
|
ExprAsType(context, type_node, parsed_type_id);
|
|
auto [name_node, name_id] = context.node_stack().PopNameWithNodeId();
|
|
|
|
auto parent_class_decl =
|
|
context.scope_stack().TryGetCurrentScopeAs<SemIR::ClassDecl>();
|
|
CARBON_CHECK(parent_class_decl);
|
|
if (!RequireConcreteType(
|
|
context, cast_type_id, type_node,
|
|
[&](auto& builder) {
|
|
CARBON_DIAGNOSTIC(IncompleteTypeInFieldDecl, Context,
|
|
"field has incomplete type {0}", SemIR::TypeId);
|
|
builder.Context(type_node, IncompleteTypeInFieldDecl, cast_type_id);
|
|
},
|
|
[&](auto& builder) {
|
|
CARBON_DIAGNOSTIC(AbstractTypeInFieldDecl, Context,
|
|
"field has abstract type {0}", SemIR::TypeId);
|
|
builder.Context(type_node, AbstractTypeInFieldDecl, cast_type_id);
|
|
})) {
|
|
cast_type_id = SemIR::ErrorInst::TypeId;
|
|
}
|
|
if (cast_type_id == SemIR::ErrorInst::TypeId) {
|
|
cast_type_inst_id = SemIR::ErrorInst::TypeInstId;
|
|
}
|
|
auto& class_info = context.classes().Get(parent_class_decl->class_id);
|
|
auto field_type_id = GetUnboundElementType(
|
|
context, context.types().GetTypeInstId(class_info.self_type_id),
|
|
cast_type_inst_id);
|
|
auto field_id =
|
|
AddInst<SemIR::FieldDecl>(context, node_id,
|
|
{.type_id = field_type_id,
|
|
.name_id = name_id,
|
|
.index = SemIR::ElementIndex::None});
|
|
context.field_decls_stack().AppendToTop(field_id);
|
|
|
|
auto name_context =
|
|
context.decl_name_stack().MakeUnqualifiedName(node_id, name_id);
|
|
context.decl_name_stack().AddNameOrDiagnose(
|
|
name_context, field_id,
|
|
context.decl_introducer_state_stack()
|
|
.innermost()
|
|
.modifier_set.GetAccessKind());
|
|
return true;
|
|
}
|
|
|
|
auto HandleParseNode(Context& context, Parse::RefBindingNameId node_id)
|
|
-> bool {
|
|
context.node_stack().Push(node_id);
|
|
return true;
|
|
}
|
|
|
|
auto HandleParseNode(Context& context, Parse::TemplateBindingNameId node_id)
|
|
-> bool {
|
|
context.node_stack().Push(node_id);
|
|
return true;
|
|
}
|
|
|
|
// Within a pattern with an unused modifier, sets the is_unused on all
|
|
// entity names and also returns whether any names were found. The result
|
|
// is needed to emit a diagnostic when the unused modifier is
|
|
// unnecessary.
|
|
static auto MarkPatternUnused(Context& context, SemIR::InstId inst_id) -> bool {
|
|
bool found_name = false;
|
|
llvm::SmallVector<SemIR::InstId> worklist;
|
|
worklist.push_back(inst_id);
|
|
while (!worklist.empty()) {
|
|
auto current_inst_id = worklist.pop_back_val();
|
|
auto inst = context.insts().Get(current_inst_id);
|
|
CARBON_KIND_SWITCH(inst) {
|
|
case SemIR::OutParamPattern::Kind:
|
|
case SemIR::RefParamPattern::Kind:
|
|
case SemIR::ValueParamPattern::Kind:
|
|
case SemIR::VarParamPattern::Kind: {
|
|
auto param = inst.As<SemIR::AnyParamPattern>();
|
|
worklist.push_back(param.subpattern_id);
|
|
break;
|
|
}
|
|
case SemIR::RefBindingPattern::Kind:
|
|
case SemIR::SymbolicBindingPattern::Kind:
|
|
case SemIR::ValueBindingPattern::Kind: {
|
|
auto bind = inst.As<SemIR::AnyBindingPattern>();
|
|
auto& name = context.entity_names().Get(bind.entity_name_id);
|
|
name.is_unused = true;
|
|
// We treat `_` as not marking the pattern as unused for the purpose of
|
|
// deciding whether to issue a warning for `unused` on a pattern that
|
|
// doesn't contain any bindings. `_` is implicitly unused, so marking it
|
|
// `unused` is redundant but harmless.
|
|
if (name.name_id != SemIR::NameId::Underscore) {
|
|
found_name = true;
|
|
}
|
|
break;
|
|
}
|
|
case CARBON_KIND(SemIR::TuplePattern tuple): {
|
|
for (auto elem_id : context.inst_blocks().Get(tuple.elements_id)) {
|
|
worklist.push_back(elem_id);
|
|
}
|
|
break;
|
|
}
|
|
case CARBON_KIND(SemIR::VarPattern var): {
|
|
worklist.push_back(var.subpattern_id);
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
return found_name;
|
|
}
|
|
|
|
auto HandleParseNode(Context& context, Parse::UnusedPatternId node_id) -> bool {
|
|
auto [child_node, child_inst_id] =
|
|
context.node_stack().PopPatternWithNodeId();
|
|
if (!MarkPatternUnused(context, child_inst_id)) {
|
|
CARBON_DIAGNOSTIC(UnusedPatternNoBindings, Warning,
|
|
"`unused` modifier on pattern without bindings");
|
|
context.emitter().Emit(node_id, UnusedPatternNoBindings);
|
|
}
|
|
context.node_stack().Push(node_id, child_inst_id);
|
|
return true;
|
|
}
|
|
|
|
} // namespace Carbon::Check
|