Skip to content
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

Add support for var patterns #5069

Open
wants to merge 6 commits into
base: trunk
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions toolchain/check/handle_binding_pattern.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include "toolchain/diagnostics/format_providers.h"
#include "toolchain/sem_ir/ids.h"
#include "toolchain/sem_ir/inst.h"
#include "toolchain/sem_ir/pattern.h"

namespace Carbon::Check {

Expand Down Expand Up @@ -193,8 +194,6 @@ static auto HandleAnyBindingPattern(Context& context, Parse::NodeId node_id,
case FullPatternStack::Kind::ExplicitParamList: {
// Parameters can have incomplete types in a function declaration, but not
// in a function definition. We don't know which kind we have here.
// TODO: A tuple pattern can appear in other places than function
// parameters.
bool had_error = false;
switch (introducer.kind) {
case Lex::TokenKind::Fn: {
Expand Down Expand Up @@ -250,6 +249,10 @@ static auto HandleAnyBindingPattern(Context& context, Parse::NodeId node_id,
} else {
result_inst_id = make_binding_pattern();
if (node_kind == Parse::NodeKind::LetBindingPattern) {
// A value binding pattern in a function signature is a `Call`
// parameter, but a variable binding pattern is not (instead the
// enclosing `var` pattern is), and a symbolic binding pattern is not
// (because it's not passed to the `Call` inst).
result_inst_id = AddPatternInst<SemIR::ValueParamPattern>(
context, node_id,
{.type_id = context.insts().Get(result_inst_id).type_id(),
Expand Down Expand Up @@ -347,8 +350,7 @@ auto HandleParseNode(Context& context,

auto HandleParseNode(Context& context, Parse::AddrId node_id) -> bool {
auto param_pattern_id = context.node_stack().PopPattern();
if (SemIR::Function::GetNameFromPatternId(
context.sem_ir(), param_pattern_id) == SemIR::NameId::SelfValue) {
if (SemIR::IsSelfPattern(context.sem_ir(), param_pattern_id)) {
auto pointer_type = context.types().TryGetAs<SemIR::PointerType>(
context.insts().Get(param_pattern_id).type_id());
if (pointer_type) {
Expand Down
13 changes: 6 additions & 7 deletions toolchain/check/handle_function.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "toolchain/sem_ir/entry_point.h"
#include "toolchain/sem_ir/function.h"
#include "toolchain/sem_ir/ids.h"
#include "toolchain/sem_ir/pattern.h"
#include "toolchain/sem_ir/typed_insts.h"

namespace Carbon::Check {
Expand Down Expand Up @@ -267,13 +268,11 @@ static auto BuildFunctionDecl(Context& context,
auto self_param_id = SemIR::InstId::None;
auto implicit_param_patterns =
context.inst_blocks().GetOrEmpty(name.implicit_param_patterns_id);
if (const auto* i =
llvm::find_if(implicit_param_patterns,
[&](auto implicit_param_id) {
return SemIR::Function::GetNameFromPatternId(
context.sem_ir(), implicit_param_id) ==
SemIR::NameId::SelfValue;
});
if (const auto* i = llvm::find_if(implicit_param_patterns,
[&](auto implicit_param_id) {
return SemIR::IsSelfPattern(
context.sem_ir(), implicit_param_id);
});
i != implicit_param_patterns.end()) {
self_param_id = *i;
}
Expand Down
43 changes: 27 additions & 16 deletions toolchain/check/handle_let_and_var.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "toolchain/sem_ir/ids.h"
#include "toolchain/sem_ir/inst.h"
#include "toolchain/sem_ir/name_scope.h"
#include "toolchain/sem_ir/pattern.h"
#include "toolchain/sem_ir/typed_insts.h"

namespace Carbon::Check {
Expand Down Expand Up @@ -102,10 +103,10 @@ auto HandleParseNode(Context& context, Parse::VariableIntroducerId node_id)
return HandleIntroducer<Lex::TokenKind::Var>(context, node_id);
}

// Returns a VarStorage inst for the given pattern. If the pattern
// Returns a VarStorage inst for the given `var` pattern. If the pattern
// is the body of a returned var, this reuses the return slot, and otherwise it
// adds a new inst.
static auto GetOrAddStorage(Context& context, SemIR::InstId pattern_id)
static auto GetOrAddStorage(Context& context, SemIR::InstId var_pattern_id)
-> SemIR::InstId {
if (context.decl_introducer_state_stack().innermost().modifier_set.HasAnyOf(
KeywordModifierSet::Returned)) {
Expand All @@ -116,29 +117,39 @@ static auto GetOrAddStorage(Context& context, SemIR::InstId pattern_id)
return GetCurrentReturnSlot(context);
}
}
auto pattern = context.insts().GetWithLocId(pattern_id);
auto subpattern =
context.insts().Get(pattern.inst.As<SemIR::VarPattern>().subpattern_id);

// Try to populate name_id on a best-effort basis.
auto name_id = SemIR::NameId::None;
if (auto binding_pattern = subpattern.TryAs<SemIR::BindingPattern>()) {
name_id =
context.entity_names().Get(binding_pattern->entity_name_id).name_id;
}
auto pattern = context.insts().GetWithLocId(var_pattern_id);

return AddInst(
context,
SemIR::LocIdAndInst::UncheckedLoc(
pattern.loc_id, SemIR::VarStorage{.type_id = pattern.inst.type_id(),
.pretty_name_id = name_id}));
pattern.loc_id,
SemIR::VarStorage{
.type_id = pattern.inst.type_id(),
.pretty_name_id = SemIR::GetPrettyNameFromPatternId(
context.sem_ir(),
pattern.inst.As<SemIR::VarPattern>().subpattern_id)}));
}

auto HandleParseNode(Context& context, Parse::VariablePatternId node_id)
-> bool {
auto subpattern_id = SemIR::InstId::None;
subpattern_id = context.node_stack().PopPattern();
auto subpattern_id = context.node_stack().PopPattern();
auto type_id = context.insts().Get(subpattern_id).type_id();

// In a parameter list, a `var` pattern is always a single `Call` parameter,
// even if it contains multiple binding patterns.
switch (context.full_pattern_stack().CurrentKind()) {
case FullPatternStack::Kind::ExplicitParamList:
case FullPatternStack::Kind::ImplicitParamList:
subpattern_id = AddPatternInst<SemIR::RefParamPattern>(
context, node_id,
{.type_id = type_id,
.subpattern_id = subpattern_id,
.index = SemIR::CallParamIndex::None});
break;
case FullPatternStack::Kind::NameBindingDecl:
break;
}

auto pattern_id = AddPatternInst<SemIR::VarPattern>(
context, node_id, {.type_id = type_id, .subpattern_id = subpattern_id});
context.node_stack().Push(node_id, pattern_id);
Expand Down
133 changes: 97 additions & 36 deletions toolchain/check/pattern_match.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,25 +15,10 @@
#include "toolchain/check/subpattern.h"
#include "toolchain/check/type.h"
#include "toolchain/diagnostics/format_providers.h"
#include "toolchain/sem_ir/pattern.h"

namespace Carbon::Check {

// Returns a best-effort name for the given ParamPattern, suitable for use in
// IR pretty-printing.
template <typename ParamPattern>
static auto GetPrettyName(Context& context, ParamPattern param_pattern)
-> SemIR::NameId {
if (context.insts().Is<SemIR::ReturnSlotPattern>(
param_pattern.subpattern_id)) {
return SemIR::NameId::ReturnSlot;
}
if (auto binding_pattern = context.insts().TryGetAs<SemIR::AnyBindingPattern>(
param_pattern.subpattern_id)) {
return context.entity_names().Get(binding_pattern->entity_name_id).name_id;
}
return SemIR::NameId::None;
}

namespace {

// Selects between the different kinds of pattern matching.
Expand Down Expand Up @@ -110,6 +95,9 @@ class MatchContext {
auto DoEmitPatternMatch(Context& context,
SemIR::ValueParamPattern param_pattern,
SemIR::LocId pattern_loc_id, WorkItem entry) -> void;
auto DoEmitPatternMatch(Context& context,
SemIR::RefParamPattern param_pattern,
SemIR::LocId pattern_loc_id, WorkItem entry) -> void;
auto DoEmitPatternMatch(Context& context,
SemIR::OutParamPattern param_pattern,
SemIR::LocId pattern_loc_id, WorkItem entry) -> void;
Expand Down Expand Up @@ -274,13 +262,12 @@ auto MatchContext::DoEmitPatternMatch(Context& context,
SemIR::ValueParamPattern param_pattern,
SemIR::LocId pattern_loc_id,
WorkItem entry) -> void {
CARBON_CHECK(
param_pattern.index.index < 0 ||
static_cast<size_t>(param_pattern.index.index) == results_.size(),
"Parameters out of order; expecting {0} but got {1}", results_.size(),
param_pattern.index.index);
switch (kind_) {
case MatchKind::Caller: {
CARBON_CHECK(
static_cast<size_t>(param_pattern.index.index) == results_.size(),
"Parameters out of order; expecting {0} but got {1}", results_.size(),
param_pattern.index.index);
CARBON_CHECK(entry.scrutinee_id.has_value());
if (entry.scrutinee_id == SemIR::ErrorInst::SingletonInstId) {
results_.push_back(SemIR::ErrorInst::SingletonInstId);
Expand All @@ -296,15 +283,15 @@ auto MatchContext::DoEmitPatternMatch(Context& context,
break;
}
case MatchKind::Callee: {
if (!param_pattern.index.has_value()) {
param_pattern.index = NextRuntimeIndex();
ReplaceInstBeforeConstantUse(context, entry.pattern_id, param_pattern);
}
CARBON_CHECK(!param_pattern.index.has_value());
param_pattern.index = NextRuntimeIndex();
ReplaceInstBeforeConstantUse(context, entry.pattern_id, param_pattern);
auto param_id = AddInst<SemIR::ValueParam>(
context, pattern_loc_id,
{.type_id = param_pattern.type_id,
.index = param_pattern.index,
.pretty_name_id = GetPrettyName(context, param_pattern)});
.pretty_name_id = SemIR::GetPrettyNameFromPatternId(
context.sem_ir(), entry.pattern_id)});
AddWork({.pattern_id = param_pattern.subpattern_id,
.scrutinee_id = param_id});
results_.push_back(param_id);
Expand All @@ -316,12 +303,57 @@ auto MatchContext::DoEmitPatternMatch(Context& context,
}
}

auto MatchContext::DoEmitPatternMatch(Context& context,
SemIR::RefParamPattern param_pattern,
SemIR::LocId pattern_loc_id,
WorkItem entry) -> void {
switch (kind_) {
case MatchKind::Caller: {
CARBON_CHECK(
static_cast<size_t>(param_pattern.index.index) == results_.size(),
"Parameters out of order; expecting {0} but got {1}", results_.size(),
param_pattern.index.index);
CARBON_CHECK(entry.scrutinee_id.has_value());
auto expr_category =
SemIR::GetExprCategory(context.sem_ir(), entry.scrutinee_id);
CARBON_CHECK(expr_category == SemIR::ExprCategory::EphemeralRef ||
expr_category == SemIR::ExprCategory::DurableRef);
results_.push_back(entry.scrutinee_id);
// Do not traverse farther, because the caller side of the pattern
// ends here.
break;
}
case MatchKind::Callee: {
CARBON_CHECK(!param_pattern.index.has_value());
param_pattern.index = NextRuntimeIndex();
ReplaceInstBeforeConstantUse(context, entry.pattern_id, param_pattern);
auto param_id = AddInst<SemIR::RefParam>(
context, pattern_loc_id,
{.type_id = param_pattern.type_id,
.index = param_pattern.index,
.pretty_name_id = SemIR::GetPrettyNameFromPatternId(
context.sem_ir(), entry.pattern_id)});
AddWork({.pattern_id = param_pattern.subpattern_id,
.scrutinee_id = param_id});
results_.push_back(param_id);
break;
}
case MatchKind::Local: {
CARBON_FATAL("Found RefParamPattern during local pattern match");
}
}
}

auto MatchContext::DoEmitPatternMatch(Context& context,
SemIR::OutParamPattern param_pattern,
SemIR::LocId pattern_loc_id,
WorkItem entry) -> void {
switch (kind_) {
case MatchKind::Caller: {
CARBON_CHECK(
static_cast<size_t>(param_pattern.index.index) == results_.size(),
"Parameters out of order; expecting {0} but got {1}", results_.size(),
param_pattern.index.index);
CARBON_CHECK(entry.scrutinee_id.has_value());
CARBON_CHECK(context.insts().Get(entry.scrutinee_id).type_id() ==
SemIR::GetTypeInSpecific(context.sem_ir(),
Expand All @@ -334,16 +366,16 @@ auto MatchContext::DoEmitPatternMatch(Context& context,
}
case MatchKind::Callee: {
// TODO: Consider ways to address near-duplication with the
// ValueParamPattern case.
if (!param_pattern.index.has_value()) {
param_pattern.index = NextRuntimeIndex();
ReplaceInstBeforeConstantUse(context, entry.pattern_id, param_pattern);
}
// other ParamPattern cases.
CARBON_CHECK(!param_pattern.index.has_value());
param_pattern.index = NextRuntimeIndex();
ReplaceInstBeforeConstantUse(context, entry.pattern_id, param_pattern);
auto param_id = AddInst<SemIR::OutParam>(
context, pattern_loc_id,
{.type_id = param_pattern.type_id,
.index = param_pattern.index,
.pretty_name_id = GetPrettyName(context, param_pattern)});
.pretty_name_id = SemIR::GetPrettyNameFromPatternId(
context.sem_ir(), entry.pattern_id)});
AddWork({.pattern_id = param_pattern.subpattern_id,
.scrutinee_id = param_id});
results_.push_back(param_id);
Expand Down Expand Up @@ -375,7 +407,31 @@ auto MatchContext::DoEmitPatternMatch(Context& context,
SemIR::VarPattern var_pattern,
SemIR::LocId pattern_loc_id,
WorkItem entry) -> void {
auto var_id = context.var_storage_map().Lookup(entry.pattern_id).value();
auto storage_id = SemIR::InstId::None;
switch (kind_) {
case MatchKind::Callee: {
// We're emitting pattern-match IR for the callee, but we're still on
// the caller side of the pattern, so we traverse without emitting any
// insts.
AddWork({.pattern_id = var_pattern.subpattern_id,
.scrutinee_id = SemIR::InstId::None});
return;
}
case MatchKind::Local: {
// In a `var`/`let` declaration, the `VarStorage` inst is created before
// we start pattern matching.
auto lookup_result = context.var_storage_map().Lookup(entry.pattern_id);
CARBON_CHECK(lookup_result);
storage_id = lookup_result.value();
break;
}
case MatchKind::Caller: {
storage_id = AddInst<SemIR::TemporaryStorage>(
context, pattern_loc_id, {.type_id = var_pattern.type_id});
CARBON_CHECK(entry.scrutinee_id.has_value());
break;
}
}
// TODO: Find a more efficient way to put these insts in the global_init
// block (or drop the distinction between the global_init block and the
// file scope?)
Expand All @@ -384,13 +440,14 @@ auto MatchContext::DoEmitPatternMatch(Context& context,
}
if (entry.scrutinee_id.has_value()) {
auto init_id =
Initialize(context, pattern_loc_id, var_id, entry.scrutinee_id);
Initialize(context, pattern_loc_id, storage_id, entry.scrutinee_id);
// TODO: Consider using different instruction kinds for assignment
// versus initialization.
AddInst<SemIR::Assign>(context, pattern_loc_id,
{.lhs_id = var_id, .rhs_id = init_id});
{.lhs_id = storage_id, .rhs_id = init_id});
}
AddWork({.pattern_id = var_pattern.subpattern_id, .scrutinee_id = var_id});
AddWork(
{.pattern_id = var_pattern.subpattern_id, .scrutinee_id = storage_id});
if (context.scope_stack().PeekIndex() == ScopeIndex::Package) {
context.global_init().Suspend();
}
Expand Down Expand Up @@ -488,6 +545,10 @@ auto MatchContext::EmitPatternMatch(Context& context,
DoEmitPatternMatch(context, param_pattern, pattern.loc_id, entry);
break;
}
case CARBON_KIND(SemIR::RefParamPattern param_pattern): {
DoEmitPatternMatch(context, param_pattern, pattern.loc_id, entry);
break;
}
case CARBON_KIND(SemIR::OutParamPattern param_pattern): {
DoEmitPatternMatch(context, param_pattern, pattern.loc_id, entry);
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,13 @@ class Class {
// CHECK:STDERR: var C:! type = Class;
// CHECK:STDERR: ^~~~~~~~
// CHECK:STDERR:
// CHECK:STDERR: fail_var.carbon:[[@LINE+4]]:16: error: `var` declaration cannot declare a compile-time binding [CompileTimeBindingInVarDecl]
// CHECK:STDERR: fail_var.carbon:[[@LINE+4]]:16: error: `var` pattern cannot declare a compile-time binding [CompileTimeBindingInVarDecl]
// CHECK:STDERR: var C:! type = Class;
// CHECK:STDERR: ^
// CHECK:STDERR:
var C:! type = Class;

// CHECK:STDERR: fail_var.carbon:[[@LINE+4]]:25: error: `var` declaration cannot declare a compile-time binding [CompileTimeBindingInVarDecl]
// CHECK:STDERR: fail_var.carbon:[[@LINE+4]]:25: error: `var` pattern cannot declare a compile-time binding [CompileTimeBindingInVarDecl]
// CHECK:STDERR: var template D:! type = Class;
// CHECK:STDERR: ^
// CHECK:STDERR:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ library "[[@TEST_NAME]]";
// CHECK:STDERR: var x:! () = ();
// CHECK:STDERR: ^~~~~~
// CHECK:STDERR:
// CHECK:STDERR: fail_invalid_file_generic_regression_test.carbon:[[@LINE+4]]:12: error: `var` declaration cannot declare a compile-time binding [CompileTimeBindingInVarDecl]
// CHECK:STDERR: fail_invalid_file_generic_regression_test.carbon:[[@LINE+4]]:12: error: `var` pattern cannot declare a compile-time binding [CompileTimeBindingInVarDecl]
// CHECK:STDERR: var x:! () = ();
// CHECK:STDERR: ^
// CHECK:STDERR:
Expand Down
Loading