diff --git a/toolchain/check/handle_binding_pattern.cpp b/toolchain/check/handle_binding_pattern.cpp index 39ec17ed06d52..d0e6b64e94d7b 100644 --- a/toolchain/check/handle_binding_pattern.cpp +++ b/toolchain/check/handle_binding_pattern.cpp @@ -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 { @@ -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: { @@ -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( context, node_id, {.type_id = context.insts().Get(result_inst_id).type_id(), @@ -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( context.insts().Get(param_pattern_id).type_id()); if (pointer_type) { diff --git a/toolchain/check/handle_function.cpp b/toolchain/check/handle_function.cpp index 62f5577fd99b2..0d122c7162d86 100644 --- a/toolchain/check/handle_function.cpp +++ b/toolchain/check/handle_function.cpp @@ -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 { @@ -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; } diff --git a/toolchain/check/handle_let_and_var.cpp b/toolchain/check/handle_let_and_var.cpp index 9d6e9e8d4da7e..c6c60b6bf2b10 100644 --- a/toolchain/check/handle_let_and_var.cpp +++ b/toolchain/check/handle_let_and_var.cpp @@ -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 { @@ -102,10 +103,10 @@ auto HandleParseNode(Context& context, Parse::VariableIntroducerId node_id) return HandleIntroducer(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)) { @@ -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().subpattern_id); - - // Try to populate name_id on a best-effort basis. - auto name_id = SemIR::NameId::None; - if (auto binding_pattern = subpattern.TryAs()) { - 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().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( + 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( context, node_id, {.type_id = type_id, .subpattern_id = subpattern_id}); context.node_stack().Push(node_id, pattern_id); diff --git a/toolchain/check/pattern_match.cpp b/toolchain/check/pattern_match.cpp index 460f346159c5b..70c88f7ffc806 100644 --- a/toolchain/check/pattern_match.cpp +++ b/toolchain/check/pattern_match.cpp @@ -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 -static auto GetPrettyName(Context& context, ParamPattern param_pattern) - -> SemIR::NameId { - if (context.insts().Is( - param_pattern.subpattern_id)) { - return SemIR::NameId::ReturnSlot; - } - if (auto binding_pattern = context.insts().TryGetAs( - 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. @@ -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; @@ -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(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(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); @@ -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( 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); @@ -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(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( + 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(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(), @@ -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( 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); @@ -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( + 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?) @@ -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(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(); } @@ -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; diff --git a/toolchain/check/testdata/class/no_prelude/comp_time_field.carbon b/toolchain/check/testdata/class/no_prelude/comp_time_field.carbon index 7217e18d15d13..ca7a39110eec4 100644 --- a/toolchain/check/testdata/class/no_prelude/comp_time_field.carbon +++ b/toolchain/check/testdata/class/no_prelude/comp_time_field.carbon @@ -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: diff --git a/toolchain/check/testdata/function/declaration/no_prelude/fail_todo_no_params.carbon b/toolchain/check/testdata/function/declaration/no_prelude/fail_todo_no_params.carbon index 069198302cbba..67a8bdca68502 100644 --- a/toolchain/check/testdata/function/declaration/no_prelude/fail_todo_no_params.carbon +++ b/toolchain/check/testdata/function/declaration/no_prelude/fail_todo_no_params.carbon @@ -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: diff --git a/toolchain/check/testdata/interface/no_prelude/fail_assoc_const_not_binding.carbon b/toolchain/check/testdata/interface/no_prelude/fail_assoc_const_not_binding.carbon index ecc8a4d1bf82e..924d9649827bf 100644 --- a/toolchain/check/testdata/interface/no_prelude/fail_assoc_const_not_binding.carbon +++ b/toolchain/check/testdata/interface/no_prelude/fail_assoc_const_not_binding.carbon @@ -37,13 +37,13 @@ interface I { library "[[@TEST_NAME]]"; interface I { - // CHECK:STDERR: fail_var_pattern.carbon:[[@LINE+8]]:7: error: expected name in binding pattern [ExpectedBindingPattern] + // CHECK:STDERR: fail_var_pattern.carbon:[[@LINE+8]]:11: error: semantics TODO: `handle invalid parse trees in `check`` [SemanticsTodo] // CHECK:STDERR: let var T:! type; - // CHECK:STDERR: ^~~ + // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: - // CHECK:STDERR: fail_var_pattern.carbon:[[@LINE+4]]:7: error: semantics TODO: `handle invalid parse trees in `check`` [SemanticsTodo] + // CHECK:STDERR: fail_var_pattern.carbon:[[@LINE+4]]:19: error: `var` pattern cannot declare a compile-time binding [CompileTimeBindingInVarDecl] // CHECK:STDERR: let var T:! type; - // CHECK:STDERR: ^~~ + // CHECK:STDERR: ^ // CHECK:STDERR: let var T:! type; } @@ -53,13 +53,13 @@ interface I { library "[[@TEST_NAME]]"; interface I { - // CHECK:STDERR: fail_var_pattern_with_default.carbon:[[@LINE+8]]:15: error: expected name in binding pattern [ExpectedBindingPattern] + // CHECK:STDERR: fail_var_pattern_with_default.carbon:[[@LINE+8]]:19: error: semantics TODO: `handle invalid parse trees in `check`` [SemanticsTodo] // CHECK:STDERR: default let var T:! type = {}; - // CHECK:STDERR: ^~~ + // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: - // CHECK:STDERR: fail_var_pattern_with_default.carbon:[[@LINE+4]]:15: error: semantics TODO: `handle invalid parse trees in `check`` [SemanticsTodo] + // CHECK:STDERR: fail_var_pattern_with_default.carbon:[[@LINE+4]]:28: error: `var` pattern cannot declare a compile-time binding [CompileTimeBindingInVarDecl] // CHECK:STDERR: default let var T:! type = {}; - // CHECK:STDERR: ^~~ + // CHECK:STDERR: ^ // CHECK:STDERR: default let var T:! type = {}; } diff --git a/toolchain/check/testdata/var/no_prelude/fail_generic.carbon b/toolchain/check/testdata/var/no_prelude/fail_generic.carbon index 3cdcaf8e64652..123f03f128931 100644 --- a/toolchain/check/testdata/var/no_prelude/fail_generic.carbon +++ b/toolchain/check/testdata/var/no_prelude/fail_generic.carbon @@ -13,7 +13,7 @@ fn Main() { // CHECK:STDERR: var x:! () = (); // CHECK:STDERR: ^~~~~~ // CHECK:STDERR: - // CHECK:STDERR: fail_generic.carbon:[[@LINE+4]]:14: error: `var` declaration cannot declare a compile-time binding [CompileTimeBindingInVarDecl] + // CHECK:STDERR: fail_generic.carbon:[[@LINE+4]]:14: error: `var` pattern cannot declare a compile-time binding [CompileTimeBindingInVarDecl] // CHECK:STDERR: var x:! () = (); // CHECK:STDERR: ^ // CHECK:STDERR: diff --git a/toolchain/check/testdata/var/no_prelude/var_pattern.carbon b/toolchain/check/testdata/var/no_prelude/var_pattern.carbon new file mode 100644 index 0000000000000..fc3d5dfce3f09 --- /dev/null +++ b/toolchain/check/testdata/var/no_prelude/var_pattern.carbon @@ -0,0 +1,372 @@ +// 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 +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/var/no_prelude/var_pattern.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/var/no_prelude/var_pattern.carbon + +// --- basic.carbon + +library "[[@TEST_NAME]]"; + +fn F() { + let var x: () = (); +} + +// --- tuple.carbon + +library "[[@TEST_NAME]]"; + +fn F() { + let (x: (), var y: ()) = ((), ()); +} + +// --- function.carbon + +library "[[@TEST_NAME]]"; + +fn F(x: (), var y: ()); + +fn G() { + var v: () = (); + F(v, ()); +} + +// --- fail_nested.carbon + +library "[[@TEST_NAME]]"; + +fn F() { + // CHECK:STDERR: fail_nested.carbon:[[@LINE+4]]:27: error: `var` nested within another `var` [NestedVar] + // CHECK:STDERR: let (x: (), var (y: (), var z: ())) = ((), ((), ())); + // CHECK:STDERR: ^~~ + // CHECK:STDERR: + let (x: (), var (y: (), var z: ())) = ((), ((), ())); +} + +// --- fail_compile_time.carbon + +library "[[@TEST_NAME]]"; + +fn f() { + // CHECK:STDERR: fail_compile_time.carbon:[[@LINE+8]]:7: error: semantics TODO: `handle invalid parse trees in `check`` [SemanticsTodo] + // CHECK:STDERR: var T:! type; + // CHECK:STDERR: ^~~~~~~~ + // CHECK:STDERR: + // CHECK:STDERR: fail_compile_time.carbon:[[@LINE+4]]:15: error: `var` pattern cannot declare a compile-time binding [CompileTimeBindingInVarDecl] + // CHECK:STDERR: var T:! type; + // CHECK:STDERR: ^ + // CHECK:STDERR: + var T:! type; + let (x: (), T:! type) = ((), ()); +} + +// --- fail_implicit.carbon + +library "[[@TEST_NAME]]"; + +// CHECK:STDERR: fail_implicit.carbon:[[@LINE+4]]:10: error: implicit parameters of functions must be constant or `self` [ImplictParamMustBeConstant] +// CHECK:STDERR: fn F[var u: ()](); +// CHECK:STDERR: ^~~~~ +// CHECK:STDERR: +fn F[var u: ()](); + +// CHECK:STDERR: fail_implicit.carbon:[[@LINE+8]]:10: error: semantics TODO: `handle invalid parse trees in `check`` [SemanticsTodo] +// CHECK:STDERR: fn G[var T:! type](); +// CHECK:STDERR: ^~~~~~~~ +// CHECK:STDERR: +// CHECK:STDERR: fail_implicit.carbon:[[@LINE+4]]:18: error: `var` pattern cannot declare a compile-time binding [CompileTimeBindingInVarDecl] +// CHECK:STDERR: fn G[var T:! type](); +// CHECK:STDERR: ^ +// CHECK:STDERR: +fn G[var T:! type](); + +// --- var_self.carbon + +library "[[@TEST_NAME]]"; + +class C { + fn F[var self: Self](); +} + +// CHECK:STDOUT: --- basic.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %F.type: type = fn_type @F [concrete] +// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] +// CHECK:STDOUT: %F: %F.type = struct_value () [concrete] +// CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [concrete] { +// CHECK:STDOUT: .F = %F.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %x.patt: %empty_tuple.type = binding_pattern x +// CHECK:STDOUT: %.loc5_7.1: %empty_tuple.type = var_pattern %x.patt +// CHECK:STDOUT: } +// CHECK:STDOUT: %x.var: ref %empty_tuple.type = var x +// CHECK:STDOUT: %.loc5_20.1: %empty_tuple.type = tuple_literal () +// CHECK:STDOUT: %.loc5_20.2: init %empty_tuple.type = tuple_init () to %x.var [concrete = constants.%empty_tuple] +// CHECK:STDOUT: %.loc5_7.2: init %empty_tuple.type = converted %.loc5_20.1, %.loc5_20.2 [concrete = constants.%empty_tuple] +// CHECK:STDOUT: assign %x.var, %.loc5_7.2 +// CHECK:STDOUT: %.loc5_15.1: type = splice_block %.loc5_15.3 [concrete = constants.%empty_tuple.type] { +// CHECK:STDOUT: %.loc5_15.2: %empty_tuple.type = tuple_literal () +// CHECK:STDOUT: %.loc5_15.3: type = converted %.loc5_15.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] +// CHECK:STDOUT: } +// CHECK:STDOUT: %x: ref %empty_tuple.type = bind_name x, %x.var +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- tuple.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %F.type: type = fn_type @F [concrete] +// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] +// CHECK:STDOUT: %F: %F.type = struct_value () [concrete] +// CHECK:STDOUT: %tuple.type: type = tuple_type (%empty_tuple.type, %empty_tuple.type) [concrete] +// CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [concrete] { +// CHECK:STDOUT: .F = %F.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %x.patt: %empty_tuple.type = binding_pattern x +// CHECK:STDOUT: %y.patt: %empty_tuple.type = binding_pattern y +// CHECK:STDOUT: %.loc5_15.1: %empty_tuple.type = var_pattern %y.patt +// CHECK:STDOUT: %.loc5_24: %tuple.type = tuple_pattern (%x.patt, %.loc5_15.1) +// CHECK:STDOUT: } +// CHECK:STDOUT: %y.var: ref %empty_tuple.type = var y +// CHECK:STDOUT: %.loc5_30.1: %empty_tuple.type = tuple_literal () +// CHECK:STDOUT: %.loc5_34.1: %empty_tuple.type = tuple_literal () +// CHECK:STDOUT: %.loc5_35: %tuple.type = tuple_literal (%.loc5_30.1, %.loc5_34.1) +// CHECK:STDOUT: %.loc5_12.1: type = splice_block %.loc5_12.3 [concrete = constants.%empty_tuple.type] { +// CHECK:STDOUT: %.loc5_12.2: %empty_tuple.type = tuple_literal () +// CHECK:STDOUT: %.loc5_12.3: type = converted %.loc5_12.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] +// CHECK:STDOUT: } +// CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete = constants.%empty_tuple] +// CHECK:STDOUT: %.loc5_30.2: %empty_tuple.type = converted %.loc5_30.1, %empty_tuple [concrete = constants.%empty_tuple] +// CHECK:STDOUT: %x: %empty_tuple.type = bind_name x, %.loc5_30.2 +// CHECK:STDOUT: %.loc5_34.2: init %empty_tuple.type = tuple_init () to %y.var [concrete = constants.%empty_tuple] +// CHECK:STDOUT: %.loc5_15.2: init %empty_tuple.type = converted %.loc5_34.1, %.loc5_34.2 [concrete = constants.%empty_tuple] +// CHECK:STDOUT: assign %y.var, %.loc5_15.2 +// CHECK:STDOUT: %.loc5_23.1: type = splice_block %.loc5_23.3 [concrete = constants.%empty_tuple.type] { +// CHECK:STDOUT: %.loc5_23.2: %empty_tuple.type = tuple_literal () +// CHECK:STDOUT: %.loc5_23.3: type = converted %.loc5_23.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] +// CHECK:STDOUT: } +// CHECK:STDOUT: %y: ref %empty_tuple.type = bind_name y, %y.var +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- function.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] +// CHECK:STDOUT: %F.type: type = fn_type @F [concrete] +// CHECK:STDOUT: %F: %F.type = struct_value () [concrete] +// CHECK:STDOUT: %G.type: type = fn_type @G [concrete] +// CHECK:STDOUT: %G: %G.type = struct_value () [concrete] +// CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [concrete] { +// CHECK:STDOUT: .F = %F.decl +// CHECK:STDOUT: .G = %G.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { +// CHECK:STDOUT: %x.patt: %empty_tuple.type = binding_pattern x +// CHECK:STDOUT: %x.param_patt: %empty_tuple.type = value_param_pattern %x.patt, call_param0 +// CHECK:STDOUT: %y.patt: %empty_tuple.type = binding_pattern y +// CHECK:STDOUT: %y.param_patt: %empty_tuple.type = ref_param_pattern %y.patt, call_param1 +// CHECK:STDOUT: %.loc4_13: %empty_tuple.type = var_pattern %y.param_patt +// CHECK:STDOUT: } { +// CHECK:STDOUT: %x.param: %empty_tuple.type = value_param call_param0 +// CHECK:STDOUT: %.loc4_10.1: type = splice_block %.loc4_10.3 [concrete = constants.%empty_tuple.type] { +// CHECK:STDOUT: %.loc4_10.2: %empty_tuple.type = tuple_literal () +// CHECK:STDOUT: %.loc4_10.3: type = converted %.loc4_10.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] +// CHECK:STDOUT: } +// CHECK:STDOUT: %x: %empty_tuple.type = bind_name x, %x.param +// CHECK:STDOUT: %y.param: ref %empty_tuple.type = ref_param call_param1 +// CHECK:STDOUT: %.loc4_21.1: type = splice_block %.loc4_21.3 [concrete = constants.%empty_tuple.type] { +// CHECK:STDOUT: %.loc4_21.2: %empty_tuple.type = tuple_literal () +// CHECK:STDOUT: %.loc4_21.3: type = converted %.loc4_21.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] +// CHECK:STDOUT: } +// CHECK:STDOUT: %y: ref %empty_tuple.type = bind_name y, %y.param +// CHECK:STDOUT: } +// CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] {} {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F(%x.param_patt: %empty_tuple.type, %.loc4_13: %empty_tuple.type); +// CHECK:STDOUT: +// CHECK:STDOUT: fn @G() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %v.patt: %empty_tuple.type = binding_pattern v +// CHECK:STDOUT: %.loc7_3.1: %empty_tuple.type = var_pattern %v.patt +// CHECK:STDOUT: } +// CHECK:STDOUT: %v.var: ref %empty_tuple.type = var v +// CHECK:STDOUT: %.loc7_16.1: %empty_tuple.type = tuple_literal () +// CHECK:STDOUT: %.loc7_16.2: init %empty_tuple.type = tuple_init () to %v.var [concrete = constants.%empty_tuple] +// CHECK:STDOUT: %.loc7_3.2: init %empty_tuple.type = converted %.loc7_16.1, %.loc7_16.2 [concrete = constants.%empty_tuple] +// CHECK:STDOUT: assign %v.var, %.loc7_3.2 +// CHECK:STDOUT: %.loc7_11.1: type = splice_block %.loc7_11.3 [concrete = constants.%empty_tuple.type] { +// CHECK:STDOUT: %.loc7_11.2: %empty_tuple.type = tuple_literal () +// CHECK:STDOUT: %.loc7_11.3: type = converted %.loc7_11.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] +// CHECK:STDOUT: } +// CHECK:STDOUT: %v: ref %empty_tuple.type = bind_name v, %v.var +// CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] +// CHECK:STDOUT: %v.ref: ref %empty_tuple.type = name_ref v, %v +// CHECK:STDOUT: %.loc8_9.1: %empty_tuple.type = tuple_literal () +// CHECK:STDOUT: %tuple: %empty_tuple.type = tuple_value () [concrete = constants.%empty_tuple] +// CHECK:STDOUT: %.loc8_5: %empty_tuple.type = converted %v.ref, %tuple [concrete = constants.%empty_tuple] +// CHECK:STDOUT: %.loc4_13.1: ref %empty_tuple.type = temporary_storage +// CHECK:STDOUT: %.loc8_9.2: init %empty_tuple.type = tuple_init () to %.loc4_13.1 [concrete = constants.%empty_tuple] +// CHECK:STDOUT: %.loc4_13.2: init %empty_tuple.type = converted %.loc8_9.1, %.loc8_9.2 [concrete = constants.%empty_tuple] +// CHECK:STDOUT: assign %.loc4_13.1, %.loc4_13.2 +// CHECK:STDOUT: %F.call: init %empty_tuple.type = call %F.ref(%.loc8_5, %.loc4_13.1) +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_nested.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %F.type: type = fn_type @F [concrete] +// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] +// CHECK:STDOUT: %F: %F.type = struct_value () [concrete] +// CHECK:STDOUT: %tuple.type.bcd: type = tuple_type (%empty_tuple.type, %empty_tuple.type) [concrete] +// CHECK:STDOUT: %tuple.type.a21: type = tuple_type (%empty_tuple.type, %tuple.type.bcd) [concrete] +// CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] +// CHECK:STDOUT: %tuple: %tuple.type.bcd = tuple_value (%empty_tuple, %empty_tuple) [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [concrete] { +// CHECK:STDOUT: .F = %F.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %x.patt: %empty_tuple.type = binding_pattern x +// CHECK:STDOUT: %y.patt: %empty_tuple.type = binding_pattern y +// CHECK:STDOUT: %z.patt: %empty_tuple.type = binding_pattern z +// CHECK:STDOUT: %.loc9_27.1: %empty_tuple.type = var_pattern %z.patt +// CHECK:STDOUT: %.loc9_36: %tuple.type.bcd = tuple_pattern (%y.patt, %.loc9_27.1) +// CHECK:STDOUT: %.loc9_15.1: %tuple.type.bcd = var_pattern %.loc9_36 +// CHECK:STDOUT: %.loc9_37: %tuple.type.a21 = tuple_pattern (%x.patt, %.loc9_15.1) +// CHECK:STDOUT: } +// CHECK:STDOUT: %z.var: ref %empty_tuple.type = var z +// CHECK:STDOUT: %.var: ref %tuple.type.bcd = var +// CHECK:STDOUT: %.loc9_43.1: %empty_tuple.type = tuple_literal () +// CHECK:STDOUT: %.loc9_48.1: %empty_tuple.type = tuple_literal () +// CHECK:STDOUT: %.loc9_52.1: %empty_tuple.type = tuple_literal () +// CHECK:STDOUT: %.loc9_53.1: %tuple.type.bcd = tuple_literal (%.loc9_48.1, %.loc9_52.1) +// CHECK:STDOUT: %.loc9_54: %tuple.type.a21 = tuple_literal (%.loc9_43.1, %.loc9_53.1) +// CHECK:STDOUT: %.loc9_12.1: type = splice_block %.loc9_12.3 [concrete = constants.%empty_tuple.type] { +// CHECK:STDOUT: %.loc9_12.2: %empty_tuple.type = tuple_literal () +// CHECK:STDOUT: %.loc9_12.3: type = converted %.loc9_12.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] +// CHECK:STDOUT: } +// CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete = constants.%empty_tuple] +// CHECK:STDOUT: %.loc9_43.2: %empty_tuple.type = converted %.loc9_43.1, %empty_tuple [concrete = constants.%empty_tuple] +// CHECK:STDOUT: %x: %empty_tuple.type = bind_name x, %.loc9_43.2 +// CHECK:STDOUT: %tuple.elem0.loc9_53: ref %empty_tuple.type = tuple_access %.var, element0 +// CHECK:STDOUT: %.loc9_48.2: init %empty_tuple.type = tuple_init () to %tuple.elem0.loc9_53 [concrete = constants.%empty_tuple] +// CHECK:STDOUT: %.loc9_53.2: init %empty_tuple.type = converted %.loc9_48.1, %.loc9_48.2 [concrete = constants.%empty_tuple] +// CHECK:STDOUT: %tuple.elem1.loc9_53: ref %empty_tuple.type = tuple_access %.var, element1 +// CHECK:STDOUT: %.loc9_52.2: init %empty_tuple.type = tuple_init () to %tuple.elem1.loc9_53 [concrete = constants.%empty_tuple] +// CHECK:STDOUT: %.loc9_53.3: init %empty_tuple.type = converted %.loc9_52.1, %.loc9_52.2 [concrete = constants.%empty_tuple] +// CHECK:STDOUT: %.loc9_53.4: init %tuple.type.bcd = tuple_init (%.loc9_53.2, %.loc9_53.3) to %.var [concrete = constants.%tuple] +// CHECK:STDOUT: %.loc9_15.2: init %tuple.type.bcd = converted %.loc9_53.1, %.loc9_53.4 [concrete = constants.%tuple] +// CHECK:STDOUT: assign %.var, %.loc9_15.2 +// CHECK:STDOUT: %tuple.elem0.loc9_15: ref %empty_tuple.type = tuple_access %.var, element0 +// CHECK:STDOUT: %tuple.elem1.loc9_15: ref %empty_tuple.type = tuple_access %.var, element1 +// CHECK:STDOUT: %.loc9_24.1: type = splice_block %.loc9_24.3 [concrete = constants.%empty_tuple.type] { +// CHECK:STDOUT: %.loc9_24.2: %empty_tuple.type = tuple_literal () +// CHECK:STDOUT: %.loc9_24.3: type = converted %.loc9_24.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] +// CHECK:STDOUT: } +// CHECK:STDOUT: %y: ref %empty_tuple.type = bind_name y, %tuple.elem0.loc9_15 +// CHECK:STDOUT: %.loc9_15.3: init %empty_tuple.type = tuple_init () to %z.var [concrete = constants.%empty_tuple] +// CHECK:STDOUT: %.loc9_27.2: init %empty_tuple.type = converted %tuple.elem1.loc9_15, %.loc9_15.3 [concrete = constants.%empty_tuple] +// CHECK:STDOUT: assign %z.var, %.loc9_27.2 +// CHECK:STDOUT: %.loc9_35.1: type = splice_block %.loc9_35.3 [concrete = constants.%empty_tuple.type] { +// CHECK:STDOUT: %.loc9_35.2: %empty_tuple.type = tuple_literal () +// CHECK:STDOUT: %.loc9_35.3: type = converted %.loc9_35.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] +// CHECK:STDOUT: } +// CHECK:STDOUT: %z: ref %empty_tuple.type = bind_name z, %z.var +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_compile_time.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file {} +// CHECK:STDOUT: +// CHECK:STDOUT: fn @f(); +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_implicit.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file {} +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F[%.loc8: ](); +// CHECK:STDOUT: +// CHECK:STDOUT: --- var_self.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %C: type = class_type @C [concrete] +// CHECK:STDOUT: %F.type: type = fn_type @F [concrete] +// CHECK:STDOUT: %F: %F.type = struct_value () [concrete] +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [concrete] { +// CHECK:STDOUT: .C = %C.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @C { +// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { +// CHECK:STDOUT: %self.patt: %C = binding_pattern self +// CHECK:STDOUT: %self.param_patt: %C = ref_param_pattern %self.patt, call_param0 +// CHECK:STDOUT: %.loc5: %C = var_pattern %self.param_patt +// CHECK:STDOUT: } { +// CHECK:STDOUT: %self.param: ref %C = ref_param call_param0 +// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%C [concrete = constants.%C] +// CHECK:STDOUT: %self: ref %C = bind_name self, %self.param +// CHECK:STDOUT: } +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete = constants.%complete_type] +// CHECK:STDOUT: complete_type_witness = %complete_type +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%C +// CHECK:STDOUT: .F = %F.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F[%.loc5: %C](); +// CHECK:STDOUT: diff --git a/toolchain/diagnostics/diagnostic_kind.def b/toolchain/diagnostics/diagnostic_kind.def index 5484ba01fb5b6..ee4ed07a7b7c5 100644 --- a/toolchain/diagnostics/diagnostic_kind.def +++ b/toolchain/diagnostics/diagnostic_kind.def @@ -92,6 +92,7 @@ CARBON_DIAGNOSTIC_KIND(ExpectedVarAfterReturned) CARBON_DIAGNOSTIC_KIND(ExpectedVariableDecl) CARBON_DIAGNOSTIC_KIND(ExpectedChoiceDefinition) CARBON_DIAGNOSTIC_KIND(ExpectedChoiceAlternativeName) +CARBON_DIAGNOSTIC_KIND(NestedVar) CARBON_DIAGNOSTIC_KIND(OperatorRequiresParentheses) CARBON_DIAGNOSTIC_KIND(StatementOperatorAsSubExpr) CARBON_DIAGNOSTIC_KIND(UnaryOperatorRequiresParentheses) diff --git a/toolchain/lower/file_context.cpp b/toolchain/lower/file_context.cpp index 94f6da1f0cdec..b91be4b26de21 100644 --- a/toolchain/lower/file_context.cpp +++ b/toolchain/lower/file_context.cpp @@ -21,6 +21,7 @@ #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/inst.h" #include "toolchain/sem_ir/inst_kind.h" +#include "toolchain/sem_ir/pattern.h" #include "toolchain/sem_ir/typed_insts.h" namespace Carbon::Lower { @@ -222,9 +223,10 @@ auto FileContext::BuildFunctionTypeInfo(const SemIR::Function& function, return GetType(SemIR::GetTypeInSpecific(sem_ir(), specific_id, type_id)); }; + // TODO: expose the `Call` parameter patterns in `Function`, and use them here + // instead of reconstructing them via the syntactic parameter lists. auto implicit_param_patterns = sem_ir().inst_blocks().GetOrEmpty(function.implicit_param_patterns_id); - // TODO: Include parameters corresponding to positional parameters. auto param_patterns = sem_ir().inst_blocks().GetOrEmpty(function.param_patterns_id); @@ -336,7 +338,7 @@ auto FileContext::BuildFunctionDecl(SemIR::FunctionId function_id, arg.addAttr(llvm::Attribute::getWithStructRetType( llvm_context(), function_type_info.return_type)); } else { - name_id = SemIR::Function::GetNameFromPatternId(sem_ir(), inst_id); + name_id = SemIR::GetPrettyNameFromPatternId(sem_ir(), inst_id); } arg.setName(sem_ir().names().GetIRBaseName(name_id)); } diff --git a/toolchain/lower/handle.cpp b/toolchain/lower/handle.cpp index 593a717d0e819..0a460ad52f8ba 100644 --- a/toolchain/lower/handle.cpp +++ b/toolchain/lower/handle.cpp @@ -210,6 +210,11 @@ auto HandleInst(FunctionContext& /*context*/, SemIR::InstId /*inst_id*/, // Parameters are lowered by `BuildFunctionDefinition`. } +auto HandleInst(FunctionContext& /*context*/, SemIR::InstId /*inst_id*/, + SemIR::RefParam /*inst*/) -> void { + // Parameters are lowered by `BuildFunctionDefinition`. +} + auto HandleInst(FunctionContext& /*context*/, SemIR::InstId /*inst_id*/, SemIR::ValueParam /*inst*/) -> void { // Parameters are lowered by `BuildFunctionDefinition`. diff --git a/toolchain/parse/handle_binding_pattern.cpp b/toolchain/parse/handle_binding_pattern.cpp index f70d18d8cf4e9..3cfb0ded4c671 100644 --- a/toolchain/parse/handle_binding_pattern.cpp +++ b/toolchain/parse/handle_binding_pattern.cpp @@ -92,9 +92,8 @@ static auto HandleBindingPatternFinish(Context& context, bool is_compile_time) if (state.in_var_pattern) { node_kind = NodeKind::VarBindingPattern; if (is_compile_time) { - CARBON_DIAGNOSTIC( - CompileTimeBindingInVarDecl, Error, - "`var` declaration cannot declare a compile-time binding"); + CARBON_DIAGNOSTIC(CompileTimeBindingInVarDecl, Error, + "`var` pattern cannot declare a compile-time binding"); context.emitter().Emit(*context.position(), CompileTimeBindingInVarDecl); state.has_error = true; } diff --git a/toolchain/parse/handle_pattern.cpp b/toolchain/parse/handle_pattern.cpp index 4c0fc80e019aa..e0aca4099e204 100644 --- a/toolchain/parse/handle_pattern.cpp +++ b/toolchain/parse/handle_pattern.cpp @@ -9,11 +9,17 @@ namespace Carbon::Parse { auto HandlePattern(Context& context) -> void { auto state = context.PopState(); - if (context.PositionKind() == Lex::TokenKind::OpenParen) { - context.PushStateForPattern(State::PatternListAsTuple, - state.in_var_pattern); - } else { - context.PushStateForPattern(State::BindingPattern, state.in_var_pattern); + switch (context.PositionKind()) { + case Lex::TokenKind::OpenParen: + context.PushStateForPattern(State::PatternListAsTuple, + state.in_var_pattern); + break; + case Lex::TokenKind::Var: + context.PushStateForPattern(State::VariablePattern, state.in_var_pattern); + break; + default: + context.PushStateForPattern(State::BindingPattern, state.in_var_pattern); + break; } } diff --git a/toolchain/parse/handle_var.cpp b/toolchain/parse/handle_var.cpp index 9cd4a2085f64b..eb2ae72f7c54b 100644 --- a/toolchain/parse/handle_var.cpp +++ b/toolchain/parse/handle_var.cpp @@ -117,4 +117,28 @@ auto HandleVarFinishAsFor(Context& context) -> void { context.AddNode(NodeKind::ForIn, end_token, state.has_error); } +auto HandleVariablePattern(Context& context) -> void { + auto state = context.PopState(); + if (state.in_var_pattern) { + CARBON_DIAGNOSTIC(NestedVar, Error, "`var` nested within another `var`"); + context.emitter().Emit(*context.position(), NestedVar); + state.has_error = true; + } + context.PushState(State::FinishVariablePattern); + context.ConsumeChecked(Lex::TokenKind::Var); + + context.PushStateForPattern(State::Pattern, /*in_var_pattern=*/true); +} + +auto HandleFinishVariablePattern(Context& context) -> void { + auto state = context.PopState(); + context.AddNode(NodeKind::VariablePattern, state.token, state.has_error); + + // Propagate errors to the parent state so that they can take different + // actions on invalid patterns. + if (state.has_error) { + context.ReturnErrorOnState(); + } +} + } // namespace Carbon::Parse diff --git a/toolchain/parse/state.def b/toolchain/parse/state.def index 29e0f2f845ca3..95c1106ef7979 100644 --- a/toolchain/parse/state.def +++ b/toolchain/parse/state.def @@ -980,6 +980,11 @@ CARBON_PARSE_STATE(ParenExprFinish) // ^ // 1. PatternListAsTuple // +// var ... +// ^ +// +// 1. VariablePattern +// // ... // ^ // 1. BindingPattern @@ -1027,6 +1032,21 @@ CARBON_PARSE_STATE(BindingPatternAddr) // (state done) CARBON_PARSE_STATE_VARIANTS2(BindingPatternFinish, Generic, Regular) +// Handles `var` in a pattern context. +// +// var ... +// ^~~ +// 1. Pattern +// 2. FinishVariablePattern +CARBON_PARSE_STATE(VariablePattern) + +// Finishes `var` in a pattern context. +// +// var ... +// ^ +// (state done) +CARBON_PARSE_STATE(FinishVariablePattern) + // Handles a single statement. While typically within a statement block, this // can also be used for error recovery where we expect a statement block and // are missing braces. diff --git a/toolchain/parse/testdata/var/var_pattern.carbon b/toolchain/parse/testdata/var/var_pattern.carbon new file mode 100644 index 0000000000000..590caf13b7af0 --- /dev/null +++ b/toolchain/parse/testdata/var/var_pattern.carbon @@ -0,0 +1,132 @@ +// 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 +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/parse/testdata/var/var_pattern.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/parse/testdata/var/var_pattern.carbon + +// --- basic.carbon + +let var x: () = (); + +// --- tuple.carbon + +let (x: (), var y: ()) = ((), ()); + +// --- function.carbon + +fn F(x: (), var y: ()); + +// --- fail_nested.carbon + +// CHECK:STDERR: fail_nested.carbon:[[@LINE+4]]:25: error: `var` nested within another `var` [NestedVar] +// CHECK:STDERR: let (x: (), var (y: (), var z: ())) = ((), ((), ())); +// CHECK:STDERR: ^~~ +// CHECK:STDERR: +let (x: (), var (y: (), var z: ())) = ((), ((), ())); + +// CHECK:STDOUT: - filename: basic.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'LetIntroducer', text: 'let'}, +// CHECK:STDOUT: {kind: 'IdentifierNameNotBeforeParams', text: 'x'}, +// CHECK:STDOUT: {kind: 'TupleLiteralStart', text: '('}, +// CHECK:STDOUT: {kind: 'TupleLiteral', text: ')', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'VarBindingPattern', text: ':', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'VariablePattern', text: 'var', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'LetInitializer', text: '='}, +// CHECK:STDOUT: {kind: 'TupleLiteralStart', text: '('}, +// CHECK:STDOUT: {kind: 'TupleLiteral', text: ')', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'LetDecl', text: ';', subtree_size: 10}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] +// CHECK:STDOUT: - filename: tuple.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'LetIntroducer', text: 'let'}, +// CHECK:STDOUT: {kind: 'TuplePatternStart', text: '('}, +// CHECK:STDOUT: {kind: 'IdentifierNameNotBeforeParams', text: 'x'}, +// CHECK:STDOUT: {kind: 'TupleLiteralStart', text: '('}, +// CHECK:STDOUT: {kind: 'TupleLiteral', text: ')', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'LetBindingPattern', text: ':', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'PatternListComma', text: ','}, +// CHECK:STDOUT: {kind: 'IdentifierNameNotBeforeParams', text: 'y'}, +// CHECK:STDOUT: {kind: 'TupleLiteralStart', text: '('}, +// CHECK:STDOUT: {kind: 'TupleLiteral', text: ')', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'VarBindingPattern', text: ':', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'VariablePattern', text: 'var', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'TuplePattern', text: ')', subtree_size: 12}, +// CHECK:STDOUT: {kind: 'LetInitializer', text: '='}, +// CHECK:STDOUT: {kind: 'TupleLiteralStart', text: '('}, +// CHECK:STDOUT: {kind: 'TupleLiteralStart', text: '('}, +// CHECK:STDOUT: {kind: 'TupleLiteral', text: ')', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'TupleLiteralComma', text: ','}, +// CHECK:STDOUT: {kind: 'TupleLiteralStart', text: '('}, +// CHECK:STDOUT: {kind: 'TupleLiteral', text: ')', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'TupleLiteral', text: ')', subtree_size: 7}, +// CHECK:STDOUT: {kind: 'LetDecl', text: ';', subtree_size: 22}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] +// CHECK:STDOUT: - filename: function.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, +// CHECK:STDOUT: {kind: 'IdentifierNameBeforeParams', text: 'F'}, +// CHECK:STDOUT: {kind: 'ExplicitParamListStart', text: '('}, +// CHECK:STDOUT: {kind: 'IdentifierNameNotBeforeParams', text: 'x'}, +// CHECK:STDOUT: {kind: 'TupleLiteralStart', text: '('}, +// CHECK:STDOUT: {kind: 'TupleLiteral', text: ')', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'LetBindingPattern', text: ':', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'PatternListComma', text: ','}, +// CHECK:STDOUT: {kind: 'IdentifierNameNotBeforeParams', text: 'y'}, +// CHECK:STDOUT: {kind: 'TupleLiteralStart', text: '('}, +// CHECK:STDOUT: {kind: 'TupleLiteral', text: ')', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'VarBindingPattern', text: ':', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'VariablePattern', text: 'var', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'ExplicitParamList', text: ')', subtree_size: 12}, +// CHECK:STDOUT: {kind: 'FunctionDecl', text: ';', subtree_size: 15}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] +// CHECK:STDOUT: - filename: fail_nested.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'LetIntroducer', text: 'let'}, +// CHECK:STDOUT: {kind: 'TuplePatternStart', text: '('}, +// CHECK:STDOUT: {kind: 'IdentifierNameNotBeforeParams', text: 'x'}, +// CHECK:STDOUT: {kind: 'TupleLiteralStart', text: '('}, +// CHECK:STDOUT: {kind: 'TupleLiteral', text: ')', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'LetBindingPattern', text: ':', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'PatternListComma', text: ','}, +// CHECK:STDOUT: {kind: 'TuplePatternStart', text: '('}, +// CHECK:STDOUT: {kind: 'IdentifierNameNotBeforeParams', text: 'y'}, +// CHECK:STDOUT: {kind: 'TupleLiteralStart', text: '('}, +// CHECK:STDOUT: {kind: 'TupleLiteral', text: ')', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'VarBindingPattern', text: ':', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'PatternListComma', text: ','}, +// CHECK:STDOUT: {kind: 'IdentifierNameNotBeforeParams', text: 'z'}, +// CHECK:STDOUT: {kind: 'TupleLiteralStart', text: '('}, +// CHECK:STDOUT: {kind: 'TupleLiteral', text: ')', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'VarBindingPattern', text: ':', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'VariablePattern', text: 'var', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'TuplePattern', text: ')', subtree_size: 12}, +// CHECK:STDOUT: {kind: 'VariablePattern', text: 'var', subtree_size: 13}, +// CHECK:STDOUT: {kind: 'TuplePattern', text: ')', subtree_size: 20}, +// CHECK:STDOUT: {kind: 'LetInitializer', text: '='}, +// CHECK:STDOUT: {kind: 'TupleLiteralStart', text: '('}, +// CHECK:STDOUT: {kind: 'TupleLiteralStart', text: '('}, +// CHECK:STDOUT: {kind: 'TupleLiteral', text: ')', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'TupleLiteralComma', text: ','}, +// CHECK:STDOUT: {kind: 'TupleLiteralStart', text: '('}, +// CHECK:STDOUT: {kind: 'TupleLiteralStart', text: '('}, +// CHECK:STDOUT: {kind: 'TupleLiteral', text: ')', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'TupleLiteralComma', text: ','}, +// CHECK:STDOUT: {kind: 'TupleLiteralStart', text: '('}, +// CHECK:STDOUT: {kind: 'TupleLiteral', text: ')', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'TupleLiteral', text: ')', subtree_size: 7}, +// CHECK:STDOUT: {kind: 'TupleLiteral', text: ')', subtree_size: 12}, +// CHECK:STDOUT: {kind: 'LetDecl', text: ';', subtree_size: 35}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] diff --git a/toolchain/sem_ir/BUILD b/toolchain/sem_ir/BUILD index f00b6ca922243..cd53b63178990 100644 --- a/toolchain/sem_ir/BUILD +++ b/toolchain/sem_ir/BUILD @@ -77,6 +77,7 @@ cc_library( "impl.cpp", "name.cpp", "name_scope.cpp", + "pattern.cpp", "type.cpp", "type_info.cpp", ], @@ -98,6 +99,7 @@ cc_library( "interface.h", "name.h", "name_scope.h", + "pattern.h", "struct_type_field.h", "type.h", "type_info.h", diff --git a/toolchain/sem_ir/entity_with_params_base.h b/toolchain/sem_ir/entity_with_params_base.h index 4dccf140a2b62..162c00d0f4f67 100644 --- a/toolchain/sem_ir/entity_with_params_base.h +++ b/toolchain/sem_ir/entity_with_params_base.h @@ -108,9 +108,8 @@ struct EntityWithParamsBase { // instruction in the entity's pattern block that depends on all other // pattern insts pertaining to that parameter. InstBlockId implicit_param_patterns_id; - // A block containing, for each explicit parameter, a reference to the - // instruction in the entity's pattern block that depends on all other - // pattern insts pertaining to that parameter. + // A block containing, for each element of the explicit parameter list tuple + // pattern, a reference to the root pattern inst for that element. InstBlockId param_patterns_id; // If this entity is a function, this block consists of references to the diff --git a/toolchain/sem_ir/file.cpp b/toolchain/sem_ir/file.cpp index 9299b22d400a7..f8a9198ee9acd 100644 --- a/toolchain/sem_ir/file.cpp +++ b/toolchain/sem_ir/file.cpp @@ -192,6 +192,7 @@ auto GetExprCategory(const File& file, InstId inst_id) -> ExprCategory { case NameBindingDecl::Kind: case Namespace::Kind: case OutParamPattern::Kind: + case RefParamPattern::Kind: case RequirementEquivalent::Kind: case RequirementImpls::Kind: case RequirementRewrite::Kind: @@ -368,6 +369,7 @@ auto GetExprCategory(const File& file, InstId inst_id) -> ExprCategory { return ExprCategory::EphemeralRef; case OutParam::Kind: + case RefParam::Kind: // TODO: Consider introducing a separate category for OutParam: // unlike other DurableRefs, it permits initialization. return ExprCategory::DurableRef; diff --git a/toolchain/sem_ir/formatter.cpp b/toolchain/sem_ir/formatter.cpp index 33faf4d45687a..3748f202ecdfc 100644 --- a/toolchain/sem_ir/formatter.cpp +++ b/toolchain/sem_ir/formatter.cpp @@ -1013,6 +1013,12 @@ class FormatterImpl { // pretty-printing. } + auto FormatInstRhs(RefParam inst) -> void { + FormatArgs(inst.index); + // Omit pretty_name because it's an implementation detail of + // pretty-printing. + } + auto FormatInstRhs(OutParam inst) -> void { FormatArgs(inst.index); // Omit pretty_name because it's an implementation detail of diff --git a/toolchain/sem_ir/function.cpp b/toolchain/sem_ir/function.cpp index 316bf2add7bf6..5616a33258db2 100644 --- a/toolchain/sem_ir/function.cpp +++ b/toolchain/sem_ir/function.cpp @@ -86,32 +86,6 @@ auto Function::GetParamPatternInfoFromPatternId(const File& sem_ir, .entity_name_id = binding_pattern.entity_name_id}}; } -auto Function::GetNameFromPatternId(const File& sem_ir, InstId pattern_id) - -> SemIR::NameId { - auto inst_id = pattern_id; - auto inst = sem_ir.insts().Get(inst_id); - - if (auto addr_pattern = inst.TryAs()) { - inst_id = addr_pattern->inner_id; - inst = sem_ir.insts().Get(inst_id); - } - - if (inst_id == SemIR::ErrorInst::SingletonInstId) { - return SemIR::NameId::None; - } - - if (auto param_pattern_inst = inst.TryAs()) { - inst_id = param_pattern_inst->subpattern_id; - inst = sem_ir.insts().Get(inst_id); - } - - if (inst.Is()) { - return SemIR::NameId::ReturnSlot; - } - auto binding_pattern = inst.As(); - return sem_ir.entity_names().Get(binding_pattern.entity_name_id).name_id; -} - auto Function::GetDeclaredReturnType(const File& file, SpecificId specific_id) const -> TypeId { if (!return_slot_pattern_id.has_value()) { diff --git a/toolchain/sem_ir/function.h b/toolchain/sem_ir/function.h index e91590babd587..8ec7e558af104 100644 --- a/toolchain/sem_ir/function.h +++ b/toolchain/sem_ir/function.h @@ -94,11 +94,6 @@ struct Function : public EntityWithParamsBase, InstId param_pattern_id) -> std::optional; - // Gets the name from the name binding instruction, or `None` if this pattern - // has been replaced with BuiltinErrorInst. - static auto GetNameFromPatternId(const File& sem_ir, InstId param_pattern_id) - -> SemIR::NameId; - // Gets the declared return type for a specific version of this function, or // the canonical return type for the original declaration no specific is // specified. Returns `None` if no return type was specified, in which diff --git a/toolchain/sem_ir/inst_kind.def b/toolchain/sem_ir/inst_kind.def index cf85c828197d4..6f43f8fe74228 100644 --- a/toolchain/sem_ir/inst_kind.def +++ b/toolchain/sem_ir/inst_kind.def @@ -87,6 +87,8 @@ CARBON_SEM_IR_INST_KIND(NamespaceType) CARBON_SEM_IR_INST_KIND(OutParam) CARBON_SEM_IR_INST_KIND(OutParamPattern) CARBON_SEM_IR_INST_KIND(PointerType) +CARBON_SEM_IR_INST_KIND(RefParam) +CARBON_SEM_IR_INST_KIND(RefParamPattern) CARBON_SEM_IR_INST_KIND(RequireCompleteType) CARBON_SEM_IR_INST_KIND(RequirementEquivalent) CARBON_SEM_IR_INST_KIND(RequirementImpls) diff --git a/toolchain/sem_ir/inst_namer.cpp b/toolchain/sem_ir/inst_namer.cpp index 527158db5c751..e119140b71343 100644 --- a/toolchain/sem_ir/inst_namer.cpp +++ b/toolchain/sem_ir/inst_namer.cpp @@ -19,6 +19,7 @@ #include "toolchain/sem_ir/function.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/inst_kind.h" +#include "toolchain/sem_ir/pattern.h" #include "toolchain/sem_ir/type_info.h" #include "toolchain/sem_ir/typed_insts.h" @@ -790,15 +791,16 @@ auto InstNamer::CollectNamesInBlock(ScopeId top_scope_id, continue; } case OutParam::Kind: + case RefParam::Kind: case ValueParam::Kind: { add_inst_name_id(untyped_inst.As().pretty_name_id, ".param"); continue; } case OutParamPattern::Kind: + case RefParamPattern::Kind: case ValueParamPattern::Kind: { - add_inst_name_id( - SemIR::Function::GetNameFromPatternId(*sem_ir_, inst_id), - ".param_patt"); + add_inst_name_id(SemIR::GetPrettyNameFromPatternId(*sem_ir_, inst_id), + ".param_patt"); continue; } case PointerType::Kind: { diff --git a/toolchain/sem_ir/pattern.cpp b/toolchain/sem_ir/pattern.cpp new file mode 100644 index 0000000000000..e25d76c29e057 --- /dev/null +++ b/toolchain/sem_ir/pattern.cpp @@ -0,0 +1,47 @@ +// 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/sem_ir/pattern.h" + +namespace Carbon::SemIR { + +auto IsSelfPattern(const File& sem_ir, InstId pattern_id) -> bool { + // Note that the public contract of GetPrettyNameFromPatternId does not + // guarantee that this is correct; we're relying on knowledge of the + // implementation details. + return GetPrettyNameFromPatternId(sem_ir, pattern_id) == NameId::SelfValue; +} + +auto GetPrettyNameFromPatternId(const File& sem_ir, InstId pattern_id) + -> NameId { + auto inst_id = pattern_id; + auto inst = sem_ir.insts().Get(inst_id); + + if (auto var_pattern = inst.TryAs()) { + inst_id = var_pattern->subpattern_id; + inst = sem_ir.insts().Get(inst_id); + } + + if (auto addr_pattern = inst.TryAs()) { + inst_id = addr_pattern->inner_id; + inst = sem_ir.insts().Get(inst_id); + } + + if (auto param_pattern_inst = inst.TryAs()) { + inst_id = param_pattern_inst->subpattern_id; + inst = sem_ir.insts().Get(inst_id); + } + + if (inst.Is()) { + return NameId::ReturnSlot; + } + + if (auto binding_pattern = inst.TryAs()) { + return sem_ir.entity_names().Get(binding_pattern->entity_name_id).name_id; + } + + return NameId::None; +} + +} // namespace Carbon::SemIR diff --git a/toolchain/sem_ir/pattern.h b/toolchain/sem_ir/pattern.h new file mode 100644 index 0000000000000..322e2070a44fa --- /dev/null +++ b/toolchain/sem_ir/pattern.h @@ -0,0 +1,30 @@ +// 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_SEM_IR_PATTERN_H_ +#define CARBON_TOOLCHAIN_SEM_IR_PATTERN_H_ + +#include "toolchain/sem_ir/file.h" +#include "toolchain/sem_ir/ids.h" + +namespace Carbon::SemIR { + +// Returns true if `pattern_id` is a `self` parameter pattern, such as +// `self: Foo` or `addr self: Self*`. +auto IsSelfPattern(const File& sem_ir, InstId pattern_id) -> bool; + +// If `pattern_id` is a declaration of a single name, this returns that name, +// and otherwise returns `None`. This tries to "see through" wrappers like +// `AddrPattern` and `*ParamPattern`, so this may return the same name for +// different insts if one is an ancestor of the other (or if they represent +// separate declarations of the same name). +// +// This should only be used for decorative purposes such as SemIR +// pretty-printing or LLVM parameter naming. +auto GetPrettyNameFromPatternId(const File& sem_ir, InstId pattern_id) + -> SemIR::NameId; + +} // namespace Carbon::SemIR + +#endif // CARBON_TOOLCHAIN_SEM_IR_PATTERN_H_ diff --git a/toolchain/sem_ir/stringify_type.cpp b/toolchain/sem_ir/stringify_type.cpp index cc806c40ee197..6713386109368 100644 --- a/toolchain/sem_ir/stringify_type.cpp +++ b/toolchain/sem_ir/stringify_type.cpp @@ -599,6 +599,8 @@ auto StringifyTypeExpr(const SemIR::File& sem_ir, InstId outer_inst_id) case NameBindingDecl::Kind: case OutParam::Kind: case OutParamPattern::Kind: + case RefParam::Kind: + case RefParamPattern::Kind: case RequireCompleteType::Kind: case RequirementEquivalent::Kind: case RequirementImpls::Kind: diff --git a/toolchain/sem_ir/typed_insts.h b/toolchain/sem_ir/typed_insts.h index 81af2d537c59e..fa38ae89b367f 100644 --- a/toolchain/sem_ir/typed_insts.h +++ b/toolchain/sem_ir/typed_insts.h @@ -1040,10 +1040,9 @@ struct NamespaceType { TypeId type_id; }; -// A `Call` parameter for a function or other parameterized block. The sub-kinds -// differ only in their expression category. +// A `Call` parameter for a function or other parameterized block. struct AnyParam { - static constexpr InstKind Kinds[] = {InstKind::OutParam, + static constexpr InstKind Kinds[] = {InstKind::OutParam, InstKind::RefParam, InstKind::ValueParam}; InstKind kind; @@ -1067,6 +1066,17 @@ struct OutParam { NameId pretty_name_id; }; +// A by-reference `Call` parameter. See AnyParam for member documentation. +struct RefParam { + // TODO: Make Parse::NodeId more specific. + static constexpr auto Kind = InstKind::RefParam.Define( + {.ir_name = "ref_param", .constant_kind = InstConstantKind::Never}); + + TypeId type_id; + CallParamIndex index; + NameId pretty_name_id; +}; + // A by-value `Call` parameter. See AnyParam for member documentation. struct ValueParam { // TODO: Make Parse::NodeId more specific. @@ -1083,6 +1093,7 @@ struct ValueParam { // of the corresponding parameter inst. struct AnyParamPattern { static constexpr InstKind Kinds[] = {InstKind::OutParamPattern, + InstKind::RefParamPattern, InstKind::ValueParamPattern}; InstKind kind; @@ -1104,6 +1115,19 @@ struct OutParamPattern { CallParamIndex index; }; +// A pattern that represents a by-reference `Call` parameter. +struct RefParamPattern { + // TODO: Make Parse::NodeId more specific. + static constexpr auto Kind = InstKind::RefParamPattern.Define( + {.ir_name = "ref_param_pattern", + .constant_kind = InstConstantKind::Never, + .is_lowered = false}); + + TypeId type_id; + InstId subpattern_id; + CallParamIndex index; +}; + // A pattern that represents a by-value `Call` parameter. struct ValueParamPattern { // TODO: Make Parse::NodeId more specific.