Add check support for struct patterns (#7876)

Implements check and pattern matching support for struct patterns.

This PR implements complete patterns, and leaves `_` field handling for
future work.
This commit is contained in:
oli-ej authored and GitHub committed 2026-10-30 21:03:16 +00:00
1 parent 3ee012b86a
commit c6b54b57e2
23 files changed
+1764 -110

No files matched your search

+2
View File
@@ -73,6 +73,7 @@ cc_library(
"require_impls_stack.cpp",
"return.cpp",
"scope_stack.cpp",
"struct.cpp",
"subst.cpp",
"thunk.cpp",
"type.cpp",
@@ -147,6 +148,7 @@ cc_library(
"return.h",
"scope_index.h",
"scope_stack.h",
"struct.h",
"subst.h",
"thunk.h",
"type.h",
+2 -1
View File
@@ -518,7 +518,8 @@ class Context {
// - The associated entries witness table, while parsing an interface.
InstBlockStack args_type_info_stack_;
// The stack of StructTypeFields for in-progress StructTypeLiterals.
// The stack of StructTypeFields for in-progress StructTypeLiterals,
// StructLiteral values, and StructPatterns.
ArrayStack<SemIR::StructTypeField> struct_type_fields_stack_;
// The stack of FieldDecls for in-progress Class definitions.
+54 -4
View File
@@ -42,9 +42,11 @@ static auto GetLeafBindingPatternInstKind(Parse::NodeKind node_kind,
case Parse::NodeKind::CompileTimeBindingPattern:
return SemIR::InstKind::SymbolicBindingPattern;
case Parse::NodeKind::LetBindingPattern:
case Parse::NodeKind::StructPatternFieldLetBindingPattern:
return is_ref ? SemIR::InstKind::RefBindingPattern
: SemIR::InstKind::ValueBindingPattern;
case Parse::NodeKind::VarBindingPattern:
case Parse::NodeKind::StructPatternFieldVarBindingPattern:
return SemIR::InstKind::RefBindingPattern;
default:
CARBON_FATAL("Unexpected node kind: {0}", node_kind);
@@ -421,7 +423,9 @@ static auto HandleAnyBindingPattern(Context& context, Parse::NodeId node_id,
case FullPatternStack::Kind::ExplicitParamList: {
bool is_deduced = context.full_pattern_stack().CurrentKind() ==
FullPatternStack::Kind::ImplicitParamList;
bool is_var = node_kind == Parse::NodeKind::VarBindingPattern;
bool is_var =
node_kind == Parse::NodeKind::VarBindingPattern ||
node_kind == Parse::NodeKind::StructPatternFieldVarBindingPattern;
if (!IsValidParamForIntroducer(context, node_id, name_id, introducer.kind,
is_generic, is_deduced, is_var)) {
if (name_id != SemIR::NameId::Underscore) {
@@ -435,7 +439,7 @@ static auto HandleAnyBindingPattern(Context& context, Parse::NodeId node_id,
// Using `AsConcreteType` here causes `fn F[var self: Self]();`
// to fail since `Self` is an incomplete type.
if (node_kind == Parse::NodeKind::VarBindingPattern) {
if (is_var) {
auto [unqualified_type_id, qualifiers] =
context.types().GetUnqualifiedTypeAndQualifiers(
type_expr.type_component_id);
@@ -463,6 +467,7 @@ static auto HandleAnyBindingPattern(Context& context, Parse::NodeId node_id,
// enclosing `var` pattern is), or it's a compile-time binding pattern
// (because then it's not passed to the `Call` inst).
case Parse::NodeKind::LetBindingPattern:
case Parse::NodeKind::StructPatternFieldLetBindingPattern:
case Parse::NodeKind::FormBindingPattern: {
auto param_pattern_id = SemIR::InstId::None;
auto pattern_type_id =
@@ -497,6 +502,7 @@ static auto HandleAnyBindingPattern(Context& context, Parse::NodeId node_id,
break;
}
case Parse::NodeKind::VarBindingPattern:
case Parse::NodeKind::StructPatternFieldVarBindingPattern:
result_inst_id = make_binding_pattern(SemIR::RefBindingPattern::Kind);
break;
case Parse::NodeKind::CompileTimeBindingPattern:
@@ -506,6 +512,20 @@ static auto HandleAnyBindingPattern(Context& context, Parse::NodeId node_id,
default:
CARBON_FATAL("Unexpected node kind {0}", node_kind);
}
if (node_kind == Parse::NodeKind::StructPatternFieldLetBindingPattern ||
node_kind == Parse::NodeKind::StructPatternFieldVarBindingPattern) {
auto type_inst_id =
context.types().GetTypeInstId(type_expr.type_component_id);
context.struct_type_fields_stack().AppendToTop(
{.name_id = name_id, .type_inst_id = type_inst_id});
// Put the name node back on the stack for inspection when handling the
// struct pattern.
context.node_stack().Push(name_node, name_id);
}
context.node_stack().Push(node_id, result_inst_id);
break;
}
@@ -524,7 +544,8 @@ static auto HandleAnyBindingPattern(Context& context, Parse::NodeId node_id,
builder.Attach(type_expr.node_id, IncompleteTypeInBindingDecl,
type_expr.inst_id);
};
if (node_kind == Parse::NodeKind::VarBindingPattern) {
if (node_kind == Parse::NodeKind::VarBindingPattern ||
node_kind == Parse::NodeKind::StructPatternFieldVarBindingPattern) {
if (!RequireConcreteType(
context, type_expr.type_component_id, type_expr.node_id,
incomplete_diagnostic_context, abstract_diagnostic_context)) {
@@ -540,7 +561,8 @@ static auto HandleAnyBindingPattern(Context& context, Parse::NodeId node_id,
auto binding_pattern_id = make_binding_pattern(
GetLeafBindingPatternInstKind(node_kind, is_ref));
if (node_kind == Parse::NodeKind::VarBindingPattern) {
if (node_kind == Parse::NodeKind::VarBindingPattern ||
node_kind == Parse::NodeKind::StructPatternFieldVarBindingPattern) {
CARBON_CHECK(!is_generic);
if (introducer.modifier_set.HasAnyOf(KeywordModifierSet::Returned)) {
@@ -555,6 +577,18 @@ static auto HandleAnyBindingPattern(Context& context, Parse::NodeId node_id,
type_expr.node_id, type_expr.type_component_id, bind_id, name_id);
}
}
if (node_kind == Parse::NodeKind::StructPatternFieldLetBindingPattern ||
node_kind == Parse::NodeKind::StructPatternFieldVarBindingPattern) {
auto type_inst_id =
context.types().GetTypeInstId(type_expr.type_component_id);
context.struct_type_fields_stack().AppendToTop(
{.name_id = name_id, .type_inst_id = type_inst_id});
// Put the name node back on the stack for inspection when handling the
// struct pattern.
context.node_stack().Push(name_node, name_id);
}
context.node_stack().Push(node_id, binding_pattern_id);
break;
}
@@ -579,6 +613,22 @@ auto HandleParseNode(Context& context, Parse::LetBindingPatternId node_id)
Parse::NodeKind::BindingPatternTypeStart);
}
auto HandleParseNode(Context& context,
Parse::StructPatternFieldLetBindingPatternId node_id)
-> bool {
return HandleAnyBindingPattern(
context, node_id, Parse::NodeKind::StructPatternFieldLetBindingPattern,
Parse::NodeKind::BindingPatternTypeStart);
}
auto HandleParseNode(Context& context,
Parse::StructPatternFieldVarBindingPatternId node_id)
-> bool {
return HandleAnyBindingPattern(
context, node_id, Parse::NodeKind::StructPatternFieldVarBindingPattern,
Parse::NodeKind::BindingPatternTypeStart);
}
auto HandleParseNode(Context& context, Parse::SelfBindingPatternId node_id)
-> bool {
// A `self` binding with an omitted type behaves like `self: Self`. There is
+99 -3
View File
@@ -9,8 +9,10 @@
#include "toolchain/check/handle.h"
#include "toolchain/check/inst.h"
#include "toolchain/check/pattern.h"
#include "toolchain/check/struct.h"
#include "toolchain/check/type.h"
#include "toolchain/diagnostics/emitter.h"
#include "toolchain/diagnostics/format_providers.h"
namespace Carbon::Check {
@@ -39,7 +41,12 @@ auto HandleParseNode(Context& context, Parse::TuplePatternStartId node_id)
auto HandleParseNode(Context& context, Parse::StructPatternStartId node_id)
-> bool {
return context.TODO(node_id, "struct pattern start");
EndEmptyExprRegionForPattern(context);
context.node_stack().Push(node_id);
context.struct_type_fields_stack().PushArray();
context.param_and_arg_refs_stack().Push();
BeginExprRegionForPattern(context);
return true;
}
auto HandleParseNode(Context& context, Parse::ExplicitParamListStartId node_id)
@@ -137,12 +144,101 @@ auto HandleParseNode(Context& context, Parse::TuplePatternId node_id) -> bool {
}
auto HandleParseNode(Context& context, Parse::StructPatternId node_id) -> bool {
return context.TODO(node_id, "struct pattern");
if (context.node_stack().PeekIs(Parse::NodeKind::StructPatternStart)) {
// End the pending region started by a trailing comma, or the opening
// delimiter of an empty list.
EndEmptyExprRegionForPattern(context);
} else {
// End the pending region for the last pattern in the list.
EndExprRegionForPattern(context, context.node_stack());
}
// TODO: Reject StructPatterns in classes during Parse, or treat patterns in a
// class scope as patterns and generate the FieldDecl insts in a later
// pattern-matching step.
if (context.scope_stack().TryGetCurrentScopeAs<SemIR::ClassDecl>()) {
bool is_var = context.full_pattern_stack().IsCurrentKindClassScopeVarDecl();
CARBON_DIAGNOSTIC(
FieldWithStructPattern, Error,
"found struct pattern in class member {0:var|let} declaration",
Diagnostics::BoolAsSelect);
context.emitter().Emit(node_id, FieldWithStructPattern, is_var);
return false;
}
if (context.node_stack().PeekIs(Parse::NodeKind::UnderscoreName)) {
return context.TODO(node_id, "Struct pattern underscore field");
}
if (!context.node_stack().PeekIs(Parse::NodeCategory::MemberName)) {
// Remove the last parameter from the node stack before collecting names.
context.param_and_arg_refs_stack().EndNoPop(
Parse::NodeKind::StructPatternStart);
}
auto fields = context.struct_type_fields_stack().PeekArray();
llvm::SmallVector<Parse::NodeId> field_name_nodes =
PopStructFieldNameNodes(context, fields.size());
auto refs_id = context.param_and_arg_refs_stack().EndAndPop(
Parse::NodeKind::StructPatternStart);
context.node_stack()
.PopAndDiscardSoloNodeId<Parse::NodeKind::StructPatternStart>();
if (DiagnoseDuplicateNames(context, field_name_nodes, fields,
StructKind::StructPattern)) {
context.node_stack().Push(node_id, SemIR::ErrorInst::InstId);
} else {
auto type_id = GetPatternType(
context,
GetStructType(context,
context.struct_type_fields().AddCanonical(fields)));
context.node_stack().Push(
node_id,
AddInst<SemIR::StructPattern>(
context, node_id, {.type_id = type_id, .elements_id = refs_id}));
}
context.struct_type_fields_stack().PopArray();
BeginExprRegionForPattern(context);
return true;
}
auto HandleParseNode(Context& context,
Parse::StructPatternDesignatedFieldId node_id) -> bool {
return context.TODO(node_id, "struct pattern field");
EndExprRegionForPattern(context, context.node_stack());
auto pattern_id = context.node_stack().PopPattern();
auto pattern_type_id = context.insts().Get(pattern_id).type_id();
auto name_id = context.node_stack().Peek<Parse::NodeCategory::MemberName>();
// NOTE: This check is required for the case when the struct pattern is found
// in a class field, so the node_stack contains FieldDecls instead of
// Patterns. See the TODO in the HandleParseNode function for the
// StructPatternId.
if (context.sem_ir().types().TryGetAs<SemIR::PatternType>(pattern_type_id)) {
auto type_id = ExtractScrutineeType(context.sem_ir(), pattern_type_id);
auto type_inst = context.types().GetTypeInstId(type_id);
context.struct_type_fields_stack().AppendToTop(
{.name_id = name_id, .type_inst_id = type_inst});
context.node_stack().Push(node_id, pattern_id);
} else {
context.struct_type_fields_stack().AppendToTop(
{.name_id = name_id, .type_inst_id = SemIR::ErrorInst::TypeInstId});
context.node_stack().Push(node_id, pattern_id);
}
// Start a new pending `ExprRegion`, to maintain the invariant that one is
// pending at the end of handling for a pattern.
BeginExprRegionForPattern(context);
return true;
}
auto HandleParseNode(Context& context, Parse::PatternListCommaId /*node_id*/)
+5 -46
View File
@@ -2,11 +2,11 @@
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#include "common/map.h"
#include "toolchain/check/context.h"
#include "toolchain/check/convert.h"
#include "toolchain/check/handle.h"
#include "toolchain/check/inst.h"
#include "toolchain/check/struct.h"
#include "toolchain/check/type.h"
#include "toolchain/check/unused.h"
#include "toolchain/diagnostics/format_providers.h"
@@ -77,47 +77,6 @@ auto HandleParseNode(Context& context,
return true;
}
// Diagnoses and returns true if there's a duplicate name.
static auto DiagnoseDuplicateNames(
Context& context, llvm::ArrayRef<Parse::NodeId> field_name_nodes,
llvm::ArrayRef<SemIR::StructTypeField> fields, bool is_struct_type_literal)
-> bool {
Map<SemIR::NameId, Parse::NodeId> names;
for (auto [field_name_node, field] :
llvm::zip_equal(field_name_nodes, fields)) {
auto result = names.Insert(field.name_id, field_name_node);
if (!result.is_inserted()) {
CARBON_DIAGNOSTIC(StructNameDuplicate, Error,
"duplicated field name `{1}` in "
"{0:struct type literal|struct literal}",
Diagnostics::BoolAsSelect, SemIR::NameId);
CARBON_DIAGNOSTIC_LABEL(StructNamePrevious, Info,
"field with the same name here");
context.emitter()
.Build(result.value(), StructNameDuplicate, is_struct_type_literal,
field.name_id)
.Attach(field_name_node, StructNamePrevious)
.Emit();
return true;
}
}
return false;
}
// Pops the names of each field from the stack. These will have been left while
// handling struct fields.
static auto PopFieldNameNodes(Context& context, size_t field_count)
-> llvm::SmallVector<Parse::NodeId> {
llvm::SmallVector<Parse::NodeId> nodes;
nodes.reserve(field_count);
for ([[maybe_unused]] auto i : llvm::seq(field_count)) {
auto [name_node, _] =
context.node_stack().PopWithNodeId<Parse::NodeCategory::MemberName>();
nodes.push_back(name_node);
}
return nodes;
}
auto HandleParseNode(Context& context, Parse::StructLiteralId node_id) -> bool {
if (!context.node_stack().PeekIs(Parse::NodeCategory::MemberName)) {
// Remove the last parameter from the node stack before collecting names.
@@ -127,7 +86,7 @@ auto HandleParseNode(Context& context, Parse::StructLiteralId node_id) -> bool {
auto fields = context.struct_type_fields_stack().PeekArray();
llvm::SmallVector<Parse::NodeId> field_name_nodes =
PopFieldNameNodes(context, fields.size());
PopStructFieldNameNodes(context, fields.size());
auto elements_id = context.param_and_arg_refs_stack().EndAndPop(
Parse::NodeKind::StructLiteralStart);
@@ -137,7 +96,7 @@ auto HandleParseNode(Context& context, Parse::StructLiteralId node_id) -> bool {
.PopAndDiscardSoloNodeId<Parse::NodeKind::StructLiteralStart>();
if (DiagnoseDuplicateNames(context, field_name_nodes, fields,
/*is_struct_type_literal=*/false)) {
StructKind::StructLiteral)) {
context.node_stack().Push(node_id, SemIR::ErrorInst::InstId);
} else {
auto type_id = GetStructType(
@@ -156,14 +115,14 @@ auto HandleParseNode(Context& context, Parse::StructTypeLiteralId node_id)
-> bool {
auto fields = context.struct_type_fields_stack().PeekArray();
llvm::SmallVector<Parse::NodeId> field_name_nodes =
PopFieldNameNodes(context, fields.size());
PopStructFieldNameNodes(context, fields.size());
context.scope_stack().Pop(/*check_unused=*/true);
context.node_stack()
.PopAndDiscardSoloNodeId<Parse::NodeKind::StructTypeLiteralStart>();
if (DiagnoseDuplicateNames(context, field_name_nodes, fields,
/*is_struct_type_literal=*/true)) {
StructKind::StructTypeLiteral)) {
context.node_stack().Push(node_id, SemIR::ErrorInst::InstId);
} else {
auto fields_id = context.struct_type_fields().AddCanonical(fields);
+1 -1
View File
@@ -422,6 +422,7 @@ class NodeStack {
case Parse::NodeKind::ShortCircuitOperandAnd:
case Parse::NodeKind::ShortCircuitOperandOr:
case Parse::NodeKind::StructLiteralField:
case Parse::NodeKind::StructPatternDesignatedField:
return Id::KindFor<SemIR::InstId>();
case Parse::NodeKind::ExplicitParamList:
case Parse::NodeKind::IfCondition:
@@ -477,7 +478,6 @@ class NodeStack {
case Parse::NodeKind::ReturnStatementStart:
case Parse::NodeKind::StructLiteralStart:
case Parse::NodeKind::StructPatternStart:
case Parse::NodeKind::StructPatternDesignatedField:
case Parse::NodeKind::StructTypeLiteralField:
case Parse::NodeKind::StructTypeLiteralStart:
case Parse::NodeKind::TemplateBindingName:
+112
View File
@@ -23,6 +23,7 @@
#include "toolchain/check/type.h"
#include "toolchain/diagnostics/format_providers.h"
#include "toolchain/sem_ir/expr_info.h"
#include "toolchain/sem_ir/generic.h"
#include "toolchain/sem_ir/ids.h"
#include "toolchain/sem_ir/inst_kind.h"
#include "toolchain/sem_ir/pattern.h"
@@ -200,6 +201,8 @@ class MatchContext {
SemIR::InstId scrutinee_id, WorkItem entry) -> void;
auto DoPreWork(State state, SemIR::TuplePattern tuple_pattern,
SemIR::InstId scrutinee_id, WorkItem entry) -> void;
auto DoPreWork(State state, SemIR::StructPattern struct_pattern,
SemIR::InstId scrutinee_id, WorkItem entry) -> void;
auto DoPreWork(State state, SemIR::SpliceInst splice,
SemIR::InstId scrutinee_id, WorkItem entry) -> void;
auto DoPreWork(State state, SemIR::SpecificConstant specific_constant,
@@ -223,6 +226,8 @@ class MatchContext {
WorkItem entry) -> void;
auto DoPostWork(State state, SemIR::TuplePattern tuple_pattern,
WorkItem entry) -> void;
auto DoPostWork(State state, SemIR::StructPattern struct_pattern,
WorkItem entry) -> void;
auto DoPostWork(State state, SemIR::SpecificConstant specific_constant,
WorkItem entry) -> void;
auto DoPostWork(State state, SemIR::ImportRefLoaded import_ref,
@@ -819,6 +824,105 @@ auto MatchContext::DoPostWork(State /*state*/,
results_stack_.AppendToTop(tuple_value_id);
}
auto MatchContext::DoPreWork(State state, SemIR::StructPattern struct_pattern,
SemIR::InstId scrutinee_id, WorkItem entry)
-> void {
if (struct_pattern.type_id == SemIR::ErrorInst::TypeId) {
return;
}
auto subpattern_ids = context_.inst_blocks().Get(struct_pattern.elements_id);
if (need_subpattern_results()) {
results_stack_.PushArray();
AddAsPostWork(entry);
}
if (!scrutinee_id.has_value()) {
CARBON_CHECK(std::holds_alternative<CalleeState*>(state) ||
std::holds_alternative<ThunkState*>(state));
for (auto subpattern_id : llvm::reverse(subpattern_ids)) {
AddWork({.pattern_id = subpattern_id,
.work = PreWork{.scrutinee_id = SemIR::InstId::None},
.allow_unmarked_ref = entry.allow_unmarked_ref});
}
return;
}
// TODO: Handle `_` fields in the pattern struct. If the final field is `_`,
// then number of fields in the expected type will be less than, or equal to,
// the number of fields in the scrutinee. Unmatched fields in the scrutinee
// should be discarded.
auto expected_type_id = GetScrutineeTypeInSpecific(context_, entry.pattern_id,
specific_id_stack_.back());
// TODO: This conversion is required to convert a StructLiteral into a
// StructValue, because a StructLiteral scrutinee will produce a
// StructAccess inst with a `Mixed` expression category, which can fail when
// performing type conversions on fields (E.g., let {.a = b: i32} = {.a = 1}
// needs to convert the IntLiteral `1` to an i32). If we can compute the
// category of a StructAccess, where the struct being accessed as a `Mixed`
// category, then we should be able to remove this conversion.
scrutinee_id = ConvertToValueOrRefOfType(
context_, SemIR::LocId(entry.pattern_id), scrutinee_id, expected_type_id);
if (scrutinee_id == SemIR::ErrorInst::InstId) {
return;
}
auto scrutinee = context_.insts().GetWithLocId(scrutinee_id);
auto pattern_struct_type_inst =
context_.types().GetAs<SemIR::StructType>(expected_type_id);
auto pattern_struct_fields =
context_.struct_type_fields().Get(pattern_struct_type_inst.fields_id);
auto scrutinee_struct_type_inst = context_.insts().GetAs<SemIR::StructType>(
context_.types().GetTypeInstId(scrutinee.inst.type_id()));
auto scrutinee_struct_fields =
context_.struct_type_fields().Get(scrutinee_struct_type_inst.fields_id);
llvm::SmallVector<SemIR::InstId> field_access_insts;
field_access_insts.reserve(subpattern_ids.size());
// TODO: This loop is O(m*n) for the number of fields in the
// pattern/scrutinee. The nested looping should be removed once struct field
// lookup is improved (see related TODO in PerformActionHelper in
// member_access.cpp)
for (auto field : pattern_struct_fields) {
for (auto [i, s_field] : llvm::enumerate(scrutinee_struct_fields)) {
if (field.name_id == s_field.name_id) {
field_access_insts.push_back(GetOrAddInst<SemIR::StructAccess>(
context_, scrutinee.loc_id,
{.type_id =
context_.types().GetTypeIdForTypeInstId(s_field.type_inst_id),
.struct_id = scrutinee_id,
.index = SemIR::ElementIndex(i)}));
break;
}
}
}
for (auto [subpattern_id, subscrutinee_id] :
llvm::reverse(llvm::zip_equal(subpattern_ids, field_access_insts))) {
AddWork({.pattern_id = subpattern_id,
.work = PreWork{.scrutinee_id = subscrutinee_id},
.allow_unmarked_ref = entry.allow_unmarked_ref});
}
}
auto MatchContext::DoPostWork(State /*state*/,
SemIR::StructPattern /*struct_pattern*/,
WorkItem entry) -> void {
auto elements_id = context_.inst_blocks().Add(results_stack_.PeekArray());
results_stack_.PopArray();
auto struct_value_id = AddInst<SemIR::StructValue>(
context_, SemIR::LocId(entry.pattern_id),
{.type_id = GetScrutineeTypeInSpecific(context_, entry.pattern_id,
specific_id_stack_.back()),
.elements_id = elements_id});
results_stack_.AppendToTop(struct_value_id);
}
// TODO: There is a cycle through pattern matching with an action.
// NOLINTNEXTLINE(misc-no-recursion)
auto MatchContext::DoPreWork(State state, SemIR::SpliceInst /*splice*/,
@@ -1019,6 +1123,10 @@ auto MatchContext::Dispatch(State state, WorkItem entry) -> void {
DoPreWork(state, tuple_pattern, work.scrutinee_id, entry);
break;
}
case CARBON_KIND(SemIR::StructPattern struct_pattern): {
DoPreWork(state, struct_pattern, work.scrutinee_id, entry);
break;
}
case CARBON_KIND(SemIR::SpliceInst splice_inst): {
DoPreWork(state, splice_inst, work.scrutinee_id, entry);
break;
@@ -1067,6 +1175,10 @@ auto MatchContext::Dispatch(State state, WorkItem entry) -> void {
DoPostWork(state, tuple_pattern, entry);
break;
}
case CARBON_KIND(SemIR::StructPattern struct_pattern): {
DoPostWork(state, struct_pattern, entry);
break;
}
case CARBON_KIND(SemIR::SpecificConstant specific_constant): {
DoPostWork(state, specific_constant, entry);
break;
+52
View File
@@ -0,0 +1,52 @@
// 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 "toolchain/check/struct.h"
#include "common/map.h"
#include "toolchain/check/context.h"
#include "toolchain/diagnostics/format_providers.h"
namespace Carbon::Check {
auto PopStructFieldNameNodes(Context& context, size_t field_count)
-> llvm::SmallVector<Parse::NodeId> {
llvm::SmallVector<Parse::NodeId> nodes;
nodes.reserve(field_count);
for ([[maybe_unused]] auto i : llvm::seq(field_count)) {
auto [name_node, _] =
context.node_stack().PopWithNodeId<Parse::NodeCategory::MemberName>();
nodes.push_back(name_node);
}
return nodes;
}
// Diagnoses and returns true if there's a duplicate name.
auto DiagnoseDuplicateNames(Context& context,
llvm::ArrayRef<Parse::NodeId> field_name_nodes,
llvm::ArrayRef<SemIR::StructTypeField> fields,
StructKind struct_kind_for_diagnostic) -> bool {
Map<SemIR::NameId, Parse::NodeId> names;
for (auto [field_name_node, field] :
llvm::zip_equal(field_name_nodes, fields)) {
auto result = names.Insert(field.name_id, field_name_node);
if (!result.is_inserted()) {
CARBON_DIAGNOSTIC(
StructNameDuplicate, Error,
"duplicated field name `{1}` in "
"{0:=0:struct literal|=1:struct type literal|=2:struct pattern}",
Diagnostics::IntAsSelect, SemIR::NameId);
CARBON_DIAGNOSTIC_LABEL(StructNamePrevious, Info,
"field with the same name here");
context.emitter()
.Build(result.value(), StructNameDuplicate,
static_cast<int>(struct_kind_for_diagnostic), field.name_id)
.Attach(field_name_node, StructNamePrevious)
.Emit();
return true;
}
}
return false;
}
} // namespace Carbon::Check
+36
View File
@@ -0,0 +1,36 @@
// 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
#ifndef CARBON_TOOLCHAIN_CHECK_STRUCT_H_
#define CARBON_TOOLCHAIN_CHECK_STRUCT_H_
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/SmallVector.h"
#include "toolchain/check/context.h"
#include "toolchain/parse/node_ids.h"
#include "toolchain/sem_ir/struct_type_field.h"
namespace Carbon::Check {
// Struct kinds, for diagnostics. Converted to an int for a format select.
enum class StructKind : uint8_t {
StructLiteral = 0,
StructTypeLiteral = 1,
StructPattern = 2
};
// Pops the names of each field from the stack. These will have been left while
// handling struct fields.
auto PopStructFieldNameNodes(Context& context, size_t field_count)
-> llvm::SmallVector<Parse::NodeId>;
// Diagnoses and returns true if there's a duplicate name.
auto DiagnoseDuplicateNames(Context& context,
llvm::ArrayRef<Parse::NodeId> field_name_nodes,
llvm::ArrayRef<SemIR::StructTypeField> fields,
StructKind struct_kind_for_diagnostic) -> bool;
} // namespace Carbon::Check
#endif // CARBON_TOOLCHAIN_CHECK_STRUCT_H_
File diff suppressed because it is too large. Load diff
+1
View File
@@ -303,6 +303,7 @@ CARBON_DIAGNOSTIC_KIND(ClassSpecificDeclOutsideClass)
CARBON_DIAGNOSTIC_KIND(ClassForwardDeclaredHere)
CARBON_DIAGNOSTIC_KIND(ClassIncompleteWithinDefinition)
CARBON_DIAGNOSTIC_KIND(FieldNamedUnderscore)
CARBON_DIAGNOSTIC_KIND(FieldWithStructPattern)
CARBON_DIAGNOSTIC_KIND(FieldWithTuplePattern)
CARBON_DIAGNOSTIC_KIND(FriendNotAllowedInScope)
CARBON_DIAGNOSTIC_KIND(FriendNotNameScope)
+6 -2
View File
@@ -281,7 +281,9 @@ static auto HandleBindingPatternFinish(Context& context, StateKind finish_kind)
auto node_kind = NodeKind::InvalidParse;
if (state.in_var_pattern) {
node_kind = NodeKind::VarBindingPattern;
node_kind = state.in_field_shorthand_pattern
? NodeKind::StructPatternFieldVarBindingPattern
: NodeKind::VarBindingPattern;
if (finish_kind != StateKind::BindingPatternFinishAsRegular) {
CARBON_DIAGNOSTIC(NonRegularBindingInVarDecl, Error,
"found {0:generic|`:?`} binding inside `var` pattern",
@@ -297,7 +299,9 @@ static auto HandleBindingPatternFinish(Context& context, StateKind finish_kind)
node_kind = NodeKind::CompileTimeBindingPattern;
break;
case StateKind::BindingPatternFinishAsRegular:
node_kind = NodeKind::LetBindingPattern;
node_kind = state.in_field_shorthand_pattern
? NodeKind::StructPatternFieldLetBindingPattern
: NodeKind::LetBindingPattern;
break;
case StateKind::BindingPatternFinishAsForm:
node_kind = NodeKind::FormBindingPattern;
+2
View File
@@ -220,6 +220,8 @@ CARBON_PARSE_NODE_KIND_PATTERN(ParenPattern)
CARBON_PARSE_NODE_KIND_PATTERN(TuplePattern)
CARBON_PARSE_NODE_KIND_PATTERN(StructPatternStart)
CARBON_PARSE_NODE_KIND_PATTERN(StructPatternFieldLetBindingPattern)
CARBON_PARSE_NODE_KIND_PATTERN(StructPatternFieldVarBindingPattern)
CARBON_PARSE_NODE_KIND_PATTERN(StructPatternDesignatedField)
CARBON_PARSE_NODE_KIND_PATTERN(StructPattern)
@@ -19,7 +19,7 @@ let {s};
// CHECK:STDOUT: │ │ │ ╭─IdentifierNameNotBeforeSignature 's'
// CHECK:STDOUT: │ │ │ ├─InvalidParse '}' has_error
// CHECK:STDOUT: │ │ │ ├─InvalidParse '}' has_error
// CHECK:STDOUT: │ │ ├─LetBindingPattern 's' has_error
// CHECK:STDOUT: │ │ ├─StructPatternFieldLetBindingPattern 's' has_error
// CHECK:STDOUT: │ ├─StructPattern '}' has_error
// CHECK:STDOUT: ├─LetDecl ';'
// CHECK:STDOUT: ├─FileEnd ''
+5 -5
View File
@@ -70,12 +70,12 @@ let {ref s: str, i: i32} = {.s = "hello ref shorthand", .i = 4};
// CHECK:STDOUT: │ │ │ ╭─IdentifierNameNotBeforeSignature 's'
// CHECK:STDOUT: │ │ │ ├─BindingPatternTypeStart ':'
// CHECK:STDOUT: │ │ │ ├─StringTypeLiteral 'str'
// CHECK:STDOUT: │ │ ├─LetBindingPattern ':'
// CHECK:STDOUT: │ │ ├─StructPatternFieldLetBindingPattern ':'
// CHECK:STDOUT: │ │ ├─PatternListComma ','
// CHECK:STDOUT: │ │ │ ╭─IdentifierNameNotBeforeSignature 'i'
// CHECK:STDOUT: │ │ │ ├─BindingPatternTypeStart ':'
// CHECK:STDOUT: │ │ │ ├─IntTypeLiteral 'i32'
// CHECK:STDOUT: │ │ ├─LetBindingPattern ':'
// CHECK:STDOUT: │ │ ├─StructPatternFieldLetBindingPattern ':'
// CHECK:STDOUT: │ ├─StructPattern '}'
// CHECK:STDOUT: │ ├─LetInitializer '='
// CHECK:STDOUT: │ │ ╭─StructLiteralStart '{'
@@ -107,7 +107,7 @@ let {ref s: str, i: i32} = {.s = "hello ref shorthand", .i = 4};
// CHECK:STDOUT: │ │ │ ╭─IdentifierNameNotBeforeSignature 'i'
// CHECK:STDOUT: │ │ │ ├─BindingPatternTypeStart ':'
// CHECK:STDOUT: │ │ │ ├─IntTypeLiteral 'i32'
// CHECK:STDOUT: │ │ ├─LetBindingPattern ':'
// CHECK:STDOUT: │ │ ├─StructPatternFieldLetBindingPattern ':'
// CHECK:STDOUT: │ ├─StructPattern '}'
// CHECK:STDOUT: │ ├─LetInitializer '='
// CHECK:STDOUT: │ │ ╭─StructLiteralStart '{'
@@ -168,12 +168,12 @@ let {ref s: str, i: i32} = {.s = "hello ref shorthand", .i = 4};
// CHECK:STDOUT: │ │ │ ╭─RefBindingName 'ref'
// CHECK:STDOUT: │ │ │ ├─BindingPatternTypeStart ':'
// CHECK:STDOUT: │ │ │ ├─StringTypeLiteral 'str'
// CHECK:STDOUT: │ │ ├─LetBindingPattern ':'
// CHECK:STDOUT: │ │ ├─StructPatternFieldLetBindingPattern ':'
// CHECK:STDOUT: │ │ ├─PatternListComma ','
// CHECK:STDOUT: │ │ │ ╭─IdentifierNameNotBeforeSignature 'i'
// CHECK:STDOUT: │ │ │ ├─BindingPatternTypeStart ':'
// CHECK:STDOUT: │ │ │ ├─IntTypeLiteral 'i32'
// CHECK:STDOUT: │ │ ├─LetBindingPattern ':'
// CHECK:STDOUT: │ │ ├─StructPatternFieldLetBindingPattern ':'
// CHECK:STDOUT: │ ├─StructPattern '}'
// CHECK:STDOUT: │ ├─LetInitializer '='
// CHECK:STDOUT: │ │ ╭─StructLiteralStart '{'
+32 -32
View File
@@ -370,12 +370,12 @@ let {var _: i32} = {};
// CHECK:STDOUT: │ │ │ ╭─IdentifierNameNotBeforeSignature 'x'
// CHECK:STDOUT: │ │ │ ├─BindingPatternTypeStart ':'
// CHECK:STDOUT: │ │ │ ├─IntTypeLiteral 'i32'
// CHECK:STDOUT: │ │ ├─LetBindingPattern ':'
// CHECK:STDOUT: │ │ ├─StructPatternFieldLetBindingPattern ':'
// CHECK:STDOUT: │ │ ├─PatternListComma ','
// CHECK:STDOUT: │ │ │ ╭─IdentifierNameNotBeforeSignature 'y'
// CHECK:STDOUT: │ │ │ ├─BindingPatternTypeStart ':'
// CHECK:STDOUT: │ │ │ ├─IntTypeLiteral 'i32'
// CHECK:STDOUT: │ │ ├─LetBindingPattern ':'
// CHECK:STDOUT: │ │ ├─StructPatternFieldLetBindingPattern ':'
// CHECK:STDOUT: │ ├─StructPattern '}'
// CHECK:STDOUT: │ ├─LetInitializer '='
// CHECK:STDOUT: │ │ ╭─StructLiteralStart '{'
@@ -391,12 +391,12 @@ let {var _: i32} = {};
// CHECK:STDOUT: │ │ │ ╭─RefBindingName 'ref'
// CHECK:STDOUT: │ │ │ ├─BindingPatternTypeStart ':'
// CHECK:STDOUT: │ │ │ ├─IntTypeLiteral 'i32'
// CHECK:STDOUT: │ │ ├─LetBindingPattern ':'
// CHECK:STDOUT: │ │ ├─StructPatternFieldLetBindingPattern ':'
// CHECK:STDOUT: │ │ ├─PatternListComma ','
// CHECK:STDOUT: │ │ │ ╭─IdentifierNameNotBeforeSignature 'y'
// CHECK:STDOUT: │ │ │ ├─BindingPatternTypeStart ':'
// CHECK:STDOUT: │ │ │ ├─IntTypeLiteral 'i32'
// CHECK:STDOUT: │ │ ├─LetBindingPattern ':'
// CHECK:STDOUT: │ │ ├─StructPatternFieldLetBindingPattern ':'
// CHECK:STDOUT: │ ├─StructPattern '}'
// CHECK:STDOUT: │ ├─LetInitializer '='
// CHECK:STDOUT: │ │ ╭─StructLiteralStart '{'
@@ -419,7 +419,7 @@ let {var _: i32} = {};
// CHECK:STDOUT: │ │ │ ╭─IdentifierNameNotBeforeSignature 'm'
// CHECK:STDOUT: │ │ │ ├─BindingPatternTypeStart ':'
// CHECK:STDOUT: │ │ │ ├─IntTypeLiteral 'i32'
// CHECK:STDOUT: │ │ ├─LetBindingPattern ':'
// CHECK:STDOUT: │ │ ├─StructPatternFieldLetBindingPattern ':'
// CHECK:STDOUT: │ ├─StructPattern '}'
// CHECK:STDOUT: │ ├─LetInitializer '='
// CHECK:STDOUT: │ │ ╭─StructLiteralStart '{'
@@ -434,7 +434,7 @@ let {var _: i32} = {};
// CHECK:STDOUT: │ │ │ ╭─IdentifierNameNotBeforeSignature 'x'
// CHECK:STDOUT: │ │ │ ├─BindingPatternTypeStart ':'
// CHECK:STDOUT: │ │ │ ├─IntTypeLiteral 'i32'
// CHECK:STDOUT: │ │ ├─LetBindingPattern ':'
// CHECK:STDOUT: │ │ ├─StructPatternFieldLetBindingPattern ':'
// CHECK:STDOUT: │ │ ├─PatternListComma ','
// CHECK:STDOUT: │ │ │ ╭─IdentifierNameNotBeforeSignature 'y'
// CHECK:STDOUT: │ │ │ ╭─StructFieldDesignator '.'
@@ -464,7 +464,7 @@ let {var _: i32} = {};
// CHECK:STDOUT: │ │ │ ╭─IdentifierNameNotBeforeSignature 'x'
// CHECK:STDOUT: │ │ │ ├─BindingPatternTypeStart ':'
// CHECK:STDOUT: │ │ │ ├─IntTypeLiteral 'i32'
// CHECK:STDOUT: │ │ ├─LetBindingPattern ':'
// CHECK:STDOUT: │ │ ├─StructPatternFieldLetBindingPattern ':'
// CHECK:STDOUT: │ │ ├─PatternListComma ','
// CHECK:STDOUT: │ │ │ ╭─IdentifierNameNotBeforeSignature 'y'
// CHECK:STDOUT: │ │ │ ╭─StructFieldDesignator '.'
@@ -472,12 +472,12 @@ let {var _: i32} = {};
// CHECK:STDOUT: │ │ │ │ │ ╭─IdentifierNameNotBeforeSignature 'a'
// CHECK:STDOUT: │ │ │ │ │ ├─BindingPatternTypeStart ':'
// CHECK:STDOUT: │ │ │ │ │ ├─IntTypeLiteral 'i32'
// CHECK:STDOUT: │ │ │ │ ├─LetBindingPattern ':'
// CHECK:STDOUT: │ │ │ │ ├─StructPatternFieldLetBindingPattern ':'
// CHECK:STDOUT: │ │ │ │ ├─PatternListComma ','
// CHECK:STDOUT: │ │ │ │ │ ╭─IdentifierNameNotBeforeSignature 'b'
// CHECK:STDOUT: │ │ │ │ │ ├─BindingPatternTypeStart ':'
// CHECK:STDOUT: │ │ │ │ │ ├─IntTypeLiteral 'i32'
// CHECK:STDOUT: │ │ │ │ ├─LetBindingPattern ':'
// CHECK:STDOUT: │ │ │ │ ├─StructPatternFieldLetBindingPattern ':'
// CHECK:STDOUT: │ │ │ ├─StructPattern '}'
// CHECK:STDOUT: │ │ ├─StructPatternDesignatedField '='
// CHECK:STDOUT: │ ├─StructPattern '}'
@@ -494,7 +494,7 @@ let {var _: i32} = {};
// CHECK:STDOUT: │ │ │ ╭─IdentifierNameNotBeforeSignature ',' has_error
// CHECK:STDOUT: │ │ │ ├─InvalidParse ',' has_error
// CHECK:STDOUT: │ │ │ ├─InvalidParse ',' has_error
// CHECK:STDOUT: │ │ ├─LetBindingPattern ',' has_error
// CHECK:STDOUT: │ │ ├─StructPatternFieldLetBindingPattern ',' has_error
// CHECK:STDOUT: │ │ ├─PatternListComma ','
// CHECK:STDOUT: │ ├─StructPattern '}' has_error
// CHECK:STDOUT: │ ├─LetInitializer '='
@@ -510,7 +510,7 @@ let {var _: i32} = {};
// CHECK:STDOUT: │ │ │ ╭─IdentifierNameNotBeforeSignature '=' has_error
// CHECK:STDOUT: │ │ │ ├─InvalidParse '=' has_error
// CHECK:STDOUT: │ │ │ ├─InvalidParse '=' has_error
// CHECK:STDOUT: │ │ │ ╭─LetBindingPattern '=' has_error
// CHECK:STDOUT: │ │ │ ╭─StructPatternFieldLetBindingPattern '=' has_error
// CHECK:STDOUT: │ │ │ ├─DefaultValueExprStart '='
// CHECK:STDOUT: │ │ │ ├─IdentifierNameExpr 'y'
// CHECK:STDOUT: │ │ ├─DefaultValuePattern '=' has_error
@@ -599,7 +599,7 @@ let {var _: i32} = {};
// CHECK:STDOUT: │ │ │ ╭─IdentifierNameNotBeforeSignature ',' has_error
// CHECK:STDOUT: │ │ │ ├─InvalidParse ',' has_error
// CHECK:STDOUT: │ │ │ ├─InvalidParse ',' has_error
// CHECK:STDOUT: │ │ ├─LetBindingPattern ',' has_error
// CHECK:STDOUT: │ │ ├─StructPatternFieldLetBindingPattern ',' has_error
// CHECK:STDOUT: │ │ ├─PatternListComma ','
// CHECK:STDOUT: │ ├─StructPattern '}' has_error
// CHECK:STDOUT: │ ├─LetInitializer '='
@@ -650,7 +650,7 @@ let {var _: i32} = {};
// CHECK:STDOUT: │ │ │ ╭─IdentifierNameNotBeforeSignature ':' has_error
// CHECK:STDOUT: │ │ │ ├─BindingPatternTypeStart ':' has_error
// CHECK:STDOUT: │ │ │ ├─IntTypeLiteral 'i32'
// CHECK:STDOUT: │ │ ├─LetBindingPattern ':' has_error
// CHECK:STDOUT: │ │ ├─StructPatternFieldLetBindingPattern ':' has_error
// CHECK:STDOUT: │ ├─StructPattern '}' has_error
// CHECK:STDOUT: │ ├─LetInitializer '='
// CHECK:STDOUT: │ │ ╭─StructLiteralStart '{'
@@ -665,7 +665,7 @@ let {var _: i32} = {};
// CHECK:STDOUT: │ │ │ ╭─IdentifierNameNotBeforeSignature 'x'
// CHECK:STDOUT: │ │ │ ├─BindingPatternTypeStart ':'
// CHECK:STDOUT: │ │ │ ├─InvalidParse '}' has_error
// CHECK:STDOUT: │ │ ├─LetBindingPattern ':' has_error
// CHECK:STDOUT: │ │ ├─StructPatternFieldLetBindingPattern ':' has_error
// CHECK:STDOUT: │ ├─StructPattern '}' has_error
// CHECK:STDOUT: │ ├─LetInitializer '='
// CHECK:STDOUT: │ │ ╭─StructLiteralStart '{'
@@ -680,7 +680,7 @@ let {var _: i32} = {};
// CHECK:STDOUT: │ │ │ ╭─IdentifierNameNotBeforeSignature 'x'
// CHECK:STDOUT: │ │ │ ├─BindingPatternTypeStart ':'
// CHECK:STDOUT: │ │ │ ├─IntTypeLiteral 'i32'
// CHECK:STDOUT: │ │ ├─LetBindingPattern ':'
// CHECK:STDOUT: │ │ ├─StructPatternFieldLetBindingPattern ':'
// CHECK:STDOUT: │ ├─StructPattern '}' has_error
// CHECK:STDOUT: │ ├─LetInitializer '='
// CHECK:STDOUT: │ │ ╭─StructLiteralStart '{'
@@ -785,7 +785,7 @@ let {var _: i32} = {};
// CHECK:STDOUT: │ │ │ ╭─IdentifierNameNotBeforeSignature 'foo'
// CHECK:STDOUT: │ │ │ ├─InvalidParse '.' has_error
// CHECK:STDOUT: │ │ │ ├─InvalidParse '.' has_error
// CHECK:STDOUT: │ │ ├─LetBindingPattern 'foo' has_error
// CHECK:STDOUT: │ │ ├─StructPatternFieldLetBindingPattern 'foo' has_error
// CHECK:STDOUT: │ ├─StructPattern '}' has_error
// CHECK:STDOUT: │ ├─LetInitializer '='
// CHECK:STDOUT: │ │ ╭─StructLiteralStart '{'
@@ -800,12 +800,12 @@ let {var _: i32} = {};
// CHECK:STDOUT: │ │ │ ╭─IdentifierNameNotBeforeSignature 'x'
// CHECK:STDOUT: │ │ │ ├─BindingPatternTypeStart ':'
// CHECK:STDOUT: │ │ │ ├─IntTypeLiteral 'i32'
// CHECK:STDOUT: │ │ ├─LetBindingPattern ':'
// CHECK:STDOUT: │ │ ├─StructPatternFieldLetBindingPattern ':'
// CHECK:STDOUT: │ │ ├─PatternListComma ','
// CHECK:STDOUT: │ │ │ ╭─IdentifierNameNotBeforeSignature 'y'
// CHECK:STDOUT: │ │ │ ├─BindingPatternTypeStart ':'
// CHECK:STDOUT: │ │ │ ├─IntTypeLiteral 'i32'
// CHECK:STDOUT: │ │ ├─LetBindingPattern ':'
// CHECK:STDOUT: │ │ ├─StructPatternFieldLetBindingPattern ':'
// CHECK:STDOUT: │ ├─StructPattern '}' has_error
// CHECK:STDOUT: │ ├─LetInitializer '='
// CHECK:STDOUT: │ │ ╭─StructLiteralStart '{'
@@ -820,12 +820,12 @@ let {var _: i32} = {};
// CHECK:STDOUT: │ │ │ ╭─IdentifierNameNotBeforeSignature 'x'
// CHECK:STDOUT: │ │ │ ├─BindingPatternTypeStart ':'
// CHECK:STDOUT: │ │ │ ├─IntTypeLiteral 'i32'
// CHECK:STDOUT: │ │ ├─LetBindingPattern ':'
// CHECK:STDOUT: │ │ ├─StructPatternFieldLetBindingPattern ':'
// CHECK:STDOUT: │ │ ├─PatternListComma ','
// CHECK:STDOUT: │ │ │ ╭─IdentifierNameNotBeforeSignature 'foo'
// CHECK:STDOUT: │ │ │ ├─InvalidParse 'y' has_error
// CHECK:STDOUT: │ │ │ ├─InvalidParse 'y' has_error
// CHECK:STDOUT: │ │ ├─LetBindingPattern 'foo' has_error
// CHECK:STDOUT: │ │ ├─StructPatternFieldLetBindingPattern 'foo' has_error
// CHECK:STDOUT: │ ├─StructPattern '}' has_error
// CHECK:STDOUT: │ ├─LetInitializer '='
// CHECK:STDOUT: │ │ ╭─StructLiteralStart '{'
@@ -840,7 +840,7 @@ let {var _: i32} = {};
// CHECK:STDOUT: │ │ │ ╭─IdentifierNameNotBeforeSignature '!' has_error
// CHECK:STDOUT: │ │ │ ├─InvalidParse '!' has_error
// CHECK:STDOUT: │ │ │ ├─InvalidParse '!' has_error
// CHECK:STDOUT: │ │ ├─LetBindingPattern '!' has_error
// CHECK:STDOUT: │ │ ├─StructPatternFieldLetBindingPattern '!' has_error
// CHECK:STDOUT: │ ├─StructPattern '}' has_error
// CHECK:STDOUT: │ ├─LetInitializer '='
// CHECK:STDOUT: │ │ ╭─StructLiteralStart '{'
@@ -917,7 +917,7 @@ let {var _: i32} = {};
// CHECK:STDOUT: │ │ │ ╭─IdentifierNameNotBeforeSignature '"foo"' has_error
// CHECK:STDOUT: │ │ │ ├─InvalidParse '"foo"' has_error
// CHECK:STDOUT: │ │ │ ├─InvalidParse '"foo"' has_error
// CHECK:STDOUT: │ │ ├─LetBindingPattern '"foo"' has_error
// CHECK:STDOUT: │ │ ├─StructPatternFieldLetBindingPattern '"foo"' has_error
// CHECK:STDOUT: │ │ ├─PatternListComma ','
// CHECK:STDOUT: │ │ │ ╭─IdentifierNameNotBeforeSignature 'x'
// CHECK:STDOUT: │ │ │ ╭─StructFieldDesignator '.'
@@ -941,13 +941,13 @@ let {var _: i32} = {};
// CHECK:STDOUT: │ │ │ ╭─IdentifierNameNotBeforeSignature 'x'
// CHECK:STDOUT: │ │ │ ├─BindingPatternTypeStart ':'
// CHECK:STDOUT: │ │ │ ├─IntTypeLiteral 'i32'
// CHECK:STDOUT: │ │ ├─VarBindingPattern ':'
// CHECK:STDOUT: │ │ ├─StructPatternFieldVarBindingPattern ':'
// CHECK:STDOUT: │ │ ├─PatternListComma ','
// CHECK:STDOUT: │ │ │ ╭─TuplePatternStart '('
// CHECK:STDOUT: │ │ │ │ ╭─IdentifierNameNotBeforeSignature 'y'
// CHECK:STDOUT: │ │ │ │ ├─BindingPatternTypeStart ':'
// CHECK:STDOUT: │ │ │ │ ├─IntTypeLiteral 'i32'
// CHECK:STDOUT: │ │ │ ├─VarBindingPattern ':'
// CHECK:STDOUT: │ │ │ ├─StructPatternFieldVarBindingPattern ':'
// CHECK:STDOUT: │ │ │ ├─PatternListComma ','
// CHECK:STDOUT: │ │ │ │ ╭─IdentifierNameNotBeforeSignature 'z'
// CHECK:STDOUT: │ │ │ │ ├─BindingPatternTypeStart ':'
@@ -969,13 +969,13 @@ let {var _: i32} = {};
// CHECK:STDOUT: │ │ │ ╭─IdentifierNameNotBeforeSignature 'x'
// CHECK:STDOUT: │ │ │ ├─BindingPatternTypeStart ':'
// CHECK:STDOUT: │ │ │ ├─IntTypeLiteral 'i32'
// CHECK:STDOUT: │ │ ├─LetBindingPattern ':'
// CHECK:STDOUT: │ │ ├─StructPatternFieldLetBindingPattern ':'
// CHECK:STDOUT: │ │ ├─PatternListComma ','
// CHECK:STDOUT: │ │ │ ╭─TuplePatternStart '('
// CHECK:STDOUT: │ │ │ │ ╭─IdentifierNameNotBeforeSignature 'y'
// CHECK:STDOUT: │ │ │ │ ├─BindingPatternTypeStart ':'
// CHECK:STDOUT: │ │ │ │ ├─IntTypeLiteral 'i32'
// CHECK:STDOUT: │ │ │ ├─VarBindingPattern ':'
// CHECK:STDOUT: │ │ │ ├─StructPatternFieldVarBindingPattern ':'
// CHECK:STDOUT: │ │ │ ├─PatternListComma ','
// CHECK:STDOUT: │ │ │ │ ╭─IdentifierNameNotBeforeSignature 'z'
// CHECK:STDOUT: │ │ │ │ ├─BindingPatternTypeStart ':'
@@ -997,18 +997,18 @@ let {var _: i32} = {};
// CHECK:STDOUT: │ │ │ ╭─IdentifierNameNotBeforeSignature 'x'
// CHECK:STDOUT: │ │ │ ├─BindingPatternTypeStart ':'
// CHECK:STDOUT: │ │ │ ├─IntTypeLiteral 'i32'
// CHECK:STDOUT: │ │ ├─LetBindingPattern ':'
// CHECK:STDOUT: │ │ ├─StructPatternFieldLetBindingPattern ':'
// CHECK:STDOUT: │ │ ├─PatternListComma ','
// CHECK:STDOUT: │ │ │ ╭─StructPatternStart '{'
// CHECK:STDOUT: │ │ │ │ ╭─IdentifierNameNotBeforeSignature 'y'
// CHECK:STDOUT: │ │ │ │ ├─BindingPatternTypeStart ':'
// CHECK:STDOUT: │ │ │ │ ├─IntTypeLiteral 'i32'
// CHECK:STDOUT: │ │ │ ├─LetBindingPattern ':'
// CHECK:STDOUT: │ │ │ ├─StructPatternFieldLetBindingPattern ':'
// CHECK:STDOUT: │ │ │ ├─PatternListComma ','
// CHECK:STDOUT: │ │ │ │ ╭─IdentifierNameNotBeforeSignature 'z'
// CHECK:STDOUT: │ │ │ │ ├─BindingPatternTypeStart ':'
// CHECK:STDOUT: │ │ │ │ ├─IntTypeLiteral 'i32'
// CHECK:STDOUT: │ │ │ ├─LetBindingPattern ':'
// CHECK:STDOUT: │ │ │ ├─StructPatternFieldLetBindingPattern ':'
// CHECK:STDOUT: │ │ ├─StructPattern '}'
// CHECK:STDOUT: │ ├─StructPattern '}' has_error
// CHECK:STDOUT: │ ├─LetInitializer '='
@@ -1024,7 +1024,7 @@ let {var _: i32} = {};
// CHECK:STDOUT: │ │ │ ╭─UnderscoreName '_'
// CHECK:STDOUT: │ │ │ ├─BindingPatternTypeStart ':' has_error
// CHECK:STDOUT: │ │ │ ├─IntTypeLiteral 'i32'
// CHECK:STDOUT: │ │ ├─VarBindingPattern ':' has_error
// CHECK:STDOUT: │ │ ├─StructPatternFieldVarBindingPattern ':' has_error
// CHECK:STDOUT: │ │ ╭─StructPattern '}' has_error
// CHECK:STDOUT: │ ├─VariablePattern 'var'
// CHECK:STDOUT: │ ├─VariableInitializer '='
@@ -1037,7 +1037,7 @@ let {var _: i32} = {};
// CHECK:STDOUT: │ │ │ ╭─RefBindingName 'ref' has_error
// CHECK:STDOUT: │ │ │ ├─BindingPatternTypeStart ':' has_error
// CHECK:STDOUT: │ │ │ ├─IntTypeLiteral 'i32'
// CHECK:STDOUT: │ │ ├─LetBindingPattern ':' has_error
// CHECK:STDOUT: │ │ ├─StructPatternFieldLetBindingPattern ':' has_error
// CHECK:STDOUT: │ ├─StructPattern '}' has_error
// CHECK:STDOUT: │ ├─LetInitializer '='
// CHECK:STDOUT: │ │ ╭─StructLiteralStart '{'
@@ -1048,7 +1048,7 @@ let {var _: i32} = {};
// CHECK:STDOUT: │ │ │ ╭─UnderscoreName '_'
// CHECK:STDOUT: │ │ │ ├─BindingPatternTypeStart ':' has_error
// CHECK:STDOUT: │ │ │ ├─IntTypeLiteral 'i32'
// CHECK:STDOUT: │ │ │ ╭─VarBindingPattern ':' has_error
// CHECK:STDOUT: │ │ │ ╭─StructPatternFieldVarBindingPattern ':' has_error
// CHECK:STDOUT: │ │ ├─VariablePattern 'var' has_error
// CHECK:STDOUT: │ ├─StructPattern '}' has_error
// CHECK:STDOUT: │ ├─LetInitializer '='
+8 -8
View File
@@ -316,7 +316,7 @@ var unused (unused y: i32) = (0,);
// CHECK:STDOUT: │ │ │ ╭─IdentifierNameNotBeforeSignature 'y'
// CHECK:STDOUT: │ │ │ ├─BindingPatternTypeStart ':'
// CHECK:STDOUT: │ │ │ ├─IntTypeLiteral 'i32'
// CHECK:STDOUT: │ │ │ ╭─VarBindingPattern ':'
// CHECK:STDOUT: │ │ │ ╭─StructPatternFieldVarBindingPattern ':'
// CHECK:STDOUT: │ │ │ ╭─UnusedPattern 'unused'
// CHECK:STDOUT: │ │ ├─VariablePattern 'var'
// CHECK:STDOUT: │ ├─StructPattern '}'
@@ -337,7 +337,7 @@ var unused (unused y: i32) = (0,);
// CHECK:STDOUT: │ │ │ ╭─IdentifierNameNotBeforeSignature 'y'
// CHECK:STDOUT: │ │ │ ├─BindingPatternTypeStart ':'
// CHECK:STDOUT: │ │ │ ├─IntTypeLiteral 'i32'
// CHECK:STDOUT: │ │ │ ╭─VarBindingPattern ':'
// CHECK:STDOUT: │ │ │ ╭─StructPatternFieldVarBindingPattern ':'
// CHECK:STDOUT: │ │ │ ╭─VariablePattern 'var'
// CHECK:STDOUT: │ │ ├─UnusedPattern 'unused'
// CHECK:STDOUT: │ ├─StructPattern '}'
@@ -350,12 +350,12 @@ var unused (unused y: i32) = (0,);
// CHECK:STDOUT: │ │ │ ╭─IdentifierNameNotBeforeSignature 'x'
// CHECK:STDOUT: │ │ │ ├─BindingPatternTypeStart ':'
// CHECK:STDOUT: │ │ │ ├─IntTypeLiteral 'i32'
// CHECK:STDOUT: │ │ ├─LetBindingPattern ':'
// CHECK:STDOUT: │ │ ├─StructPatternFieldLetBindingPattern ':'
// CHECK:STDOUT: │ │ ├─PatternListComma ','
// CHECK:STDOUT: │ │ │ ╭─IdentifierNameNotBeforeSignature 'y'
// CHECK:STDOUT: │ │ │ ├─BindingPatternTypeStart ':'
// CHECK:STDOUT: │ │ │ ├─IntTypeLiteral 'i32'
// CHECK:STDOUT: │ │ ├─LetBindingPattern ':'
// CHECK:STDOUT: │ │ ├─StructPatternFieldLetBindingPattern ':'
// CHECK:STDOUT: │ │ ╭─StructPattern '}'
// CHECK:STDOUT: │ ├─UnusedPattern 'unused'
// CHECK:STDOUT: │ ├─LetInitializer '='
@@ -367,12 +367,12 @@ var unused (unused y: i32) = (0,);
// CHECK:STDOUT: │ │ │ ╭─IdentifierNameNotBeforeSignature 'x'
// CHECK:STDOUT: │ │ │ ├─BindingPatternTypeStart ':'
// CHECK:STDOUT: │ │ │ ├─IntTypeLiteral 'i32'
// CHECK:STDOUT: │ │ ├─VarBindingPattern ':'
// CHECK:STDOUT: │ │ ├─StructPatternFieldVarBindingPattern ':'
// CHECK:STDOUT: │ │ ├─PatternListComma ','
// CHECK:STDOUT: │ │ │ ╭─IdentifierNameNotBeforeSignature 'y'
// CHECK:STDOUT: │ │ │ ├─BindingPatternTypeStart ':'
// CHECK:STDOUT: │ │ │ ├─IntTypeLiteral 'i32'
// CHECK:STDOUT: │ │ │ ╭─VarBindingPattern ':'
// CHECK:STDOUT: │ │ │ ╭─StructPatternFieldVarBindingPattern ':'
// CHECK:STDOUT: │ │ ├─UnusedPattern 'unused'
// CHECK:STDOUT: │ │ ╭─StructPattern '}'
// CHECK:STDOUT: │ ├─VariablePattern 'var'
@@ -385,12 +385,12 @@ var unused (unused y: i32) = (0,);
// CHECK:STDOUT: │ │ │ ╭─IdentifierNameNotBeforeSignature 'x'
// CHECK:STDOUT: │ │ │ ├─BindingPatternTypeStart ':'
// CHECK:STDOUT: │ │ │ ├─IntTypeLiteral 'i32'
// CHECK:STDOUT: │ │ ├─VarBindingPattern ':'
// CHECK:STDOUT: │ │ ├─StructPatternFieldVarBindingPattern ':'
// CHECK:STDOUT: │ │ ├─PatternListComma ','
// CHECK:STDOUT: │ │ │ ╭─IdentifierNameNotBeforeSignature 'y'
// CHECK:STDOUT: │ │ │ ├─BindingPatternTypeStart ':'
// CHECK:STDOUT: │ │ │ ├─IntTypeLiteral 'i32'
// CHECK:STDOUT: │ │ ├─VarBindingPattern ':'
// CHECK:STDOUT: │ │ ├─StructPatternFieldVarBindingPattern ':'
// CHECK:STDOUT: │ │ ╭─StructPattern '}'
// CHECK:STDOUT: │ │ ╭─UnusedPattern 'unused'
// CHECK:STDOUT: │ ├─VariablePattern 'var'
+1 -1
View File
@@ -131,7 +131,7 @@ let (x: (), var (y: (), var z: ())) = ((), ((), ()));
// CHECK:STDOUT: │ │ │ ├─BindingPatternTypeStart ':'
// CHECK:STDOUT: │ │ │ │ ╭─TupleLiteralStart '('
// CHECK:STDOUT: │ │ │ ├─TupleLiteral ')'
// CHECK:STDOUT: │ │ │ ╭─VarBindingPattern ':'
// CHECK:STDOUT: │ │ │ ╭─StructPatternFieldVarBindingPattern ':'
// CHECK:STDOUT: │ │ ├─VariablePattern 'var'
// CHECK:STDOUT: │ ├─StructPattern '}'
// CHECK:STDOUT: │ ├─LetInitializer '='
+3 -3
View File
@@ -63,12 +63,12 @@ var {.s = s: str, i: 32} = {.s = "hello mixed", .i = 1};
// CHECK:STDOUT: │ │ │ ╭─IdentifierNameNotBeforeSignature 's'
// CHECK:STDOUT: │ │ │ ├─BindingPatternTypeStart ':'
// CHECK:STDOUT: │ │ │ ├─StringTypeLiteral 'str'
// CHECK:STDOUT: │ │ ├─VarBindingPattern ':'
// CHECK:STDOUT: │ │ ├─StructPatternFieldVarBindingPattern ':'
// CHECK:STDOUT: │ │ ├─PatternListComma ','
// CHECK:STDOUT: │ │ │ ╭─IdentifierNameNotBeforeSignature 'i'
// CHECK:STDOUT: │ │ │ ├─BindingPatternTypeStart ':'
// CHECK:STDOUT: │ │ │ ├─IntLiteral '32'
// CHECK:STDOUT: │ │ ├─VarBindingPattern ':'
// CHECK:STDOUT: │ │ ├─StructPatternFieldVarBindingPattern ':'
// CHECK:STDOUT: │ │ ╭─StructPattern '}'
// CHECK:STDOUT: │ ├─VariablePattern 'var'
// CHECK:STDOUT: │ ├─VariableInitializer '='
@@ -101,7 +101,7 @@ var {.s = s: str, i: 32} = {.s = "hello mixed", .i = 1};
// CHECK:STDOUT: │ │ │ ╭─IdentifierNameNotBeforeSignature 'i'
// CHECK:STDOUT: │ │ │ ├─BindingPatternTypeStart ':'
// CHECK:STDOUT: │ │ │ ├─IntLiteral '32'
// CHECK:STDOUT: │ │ ├─VarBindingPattern ':'
// CHECK:STDOUT: │ │ ├─StructPatternFieldVarBindingPattern ':'
// CHECK:STDOUT: │ │ ╭─StructPattern '}'
// CHECK:STDOUT: │ ├─VariablePattern 'var'
// CHECK:STDOUT: │ ├─VariableInitializer '='
+33 -2
View File
@@ -1594,6 +1594,36 @@ struct StructTypeLiteral {
using StructPatternStart =
LeafNode<NodeKind::StructPatternStart, Lex::OpenCurlyBraceTokenIndex>;
// shorthand let binding in a struct pattern such as `let {a: i32}`.
struct StructPatternFieldLetBindingPattern {
static constexpr auto Kind =
NodeKind::StructPatternFieldLetBindingPattern.Define(
{.category = NodeCategory::Pattern, .child_count = 3});
// TODO: is there some way to reuse AnyRuntimeBindingPatternName here?
NodeIdOneOf<IdentifierNameNotBeforeSignature, SelfValueName, UnderscoreName,
RefBindingName, RuntimeBindingName>
name;
BindingPatternTypeStartId introducer;
Lex::ColonTokenIndex token;
AnyExprId type;
};
// shorthand var binding in a struct pattern such as `var {a: i32}`.
struct StructPatternFieldVarBindingPattern {
static constexpr auto Kind =
NodeKind::StructPatternFieldVarBindingPattern.Define(
{.category = NodeCategory::Pattern, .child_count = 3});
// TODO: is there some way to reuse AnyRuntimeBindingPatternName here?
NodeIdOneOf<IdentifierNameNotBeforeSignature, SelfValueName, UnderscoreName,
RuntimeBindingName>
name;
BindingPatternTypeStartId introducer;
Lex::ColonTokenIndex token;
AnyExprId type;
};
// `.a = pattern`
struct StructPatternDesignatedField {
static constexpr auto Kind = NodeKind::StructPatternDesignatedField.Define(
@@ -1605,8 +1635,9 @@ struct StructPatternDesignatedField {
};
using StructPatternFieldId =
NodeIdOneOf<StructPatternDesignatedField, LetBindingPattern,
VariablePattern, VarBindingPattern, UnusedPattern,
NodeIdOneOf<StructPatternDesignatedField,
StructPatternFieldLetBindingPattern, VariablePattern,
StructPatternFieldVarBindingPattern, UnusedPattern,
UnderscoreName, DefaultValuePattern>;
struct StructPattern {
+1
View File
@@ -160,6 +160,7 @@ CARBON_SEM_IR_INST_KIND(StringLiteral)
CARBON_SEM_IR_INST_KIND(StructAccess)
CARBON_SEM_IR_INST_KIND(StructInit)
CARBON_SEM_IR_INST_KIND(StructLiteral)
CARBON_SEM_IR_INST_KIND(StructPattern)
CARBON_SEM_IR_INST_KIND(StructType)
CARBON_SEM_IR_INST_KIND(StructValue)
CARBON_SEM_IR_INST_KIND(SymbolicBinding)
+5 -1
View File
@@ -65,7 +65,11 @@ auto GetFirstBindingNameFromPatternId(const File& sem_ir, InstId pattern_id)
continue;
}
// TODO: Look through struct patterns.
if (auto struct_patt = inst.TryAs<StructPattern>()) {
auto block = sem_ir.inst_blocks().Get(struct_patt->elements_id);
work_list.append(block.rbegin(), block.rend());
continue;
}
if (auto ref_pattern = inst.TryAs<RefParamPattern>()) {
// TODO: This introduces a name, but we don't model it as a binding.
+15
View File
@@ -2177,6 +2177,21 @@ struct StructLiteral {
InstBlockId elements_id;
};
// A struct pattern, such as `{.x = y: i32, z: i32}`.
struct StructPattern {
static constexpr auto Kind =
InstKind::StructPattern.Define<Parse::StructPatternId>(
{.ir_name = "struct_pattern",
.expr_category = ExprCategory::Pattern,
.constant_kind = InstConstantKind::Always,
.is_lowered = false});
// Always a PatternType whose scrutinee type is a struct of the scrutinee
// types of the elements.
TypeId type_id;
InstBlockId elements_id;
};
// The type of a struct.
struct StructType {
static constexpr auto Kind = InstKind::StructType.Define<