pattern_match.cpp 41 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042
  1. // Part of the Carbon Language project, under the Apache License v2.0 with LLVM
  2. // Exceptions. See /LICENSE for license information.
  3. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  4. #include "toolchain/check/pattern_match.h"
  5. #include <functional>
  6. #include <utility>
  7. #include <variant>
  8. #include <vector>
  9. #include "llvm/ADT/STLExtras.h"
  10. #include "llvm/ADT/SmallVector.h"
  11. #include "toolchain/base/kind_switch.h"
  12. #include "toolchain/check/action.h"
  13. #include "toolchain/check/context.h"
  14. #include "toolchain/check/control_flow.h"
  15. #include "toolchain/check/convert.h"
  16. #include "toolchain/check/pattern.h"
  17. #include "toolchain/check/type.h"
  18. #include "toolchain/diagnostics/format_providers.h"
  19. #include "toolchain/sem_ir/expr_info.h"
  20. #include "toolchain/sem_ir/inst_kind.h"
  21. #include "toolchain/sem_ir/pattern.h"
  22. namespace Carbon::Check {
  23. namespace {
  24. // State for caller-side pattern matching.
  25. struct CallerState {
  26. // The in-progress contents of the `Call` arguments block.
  27. llvm::SmallVector<SemIR::InstId> call_args;
  28. // The SpecificId of the function being called (if any).
  29. SemIR::SpecificId callee_specific_id;
  30. };
  31. // Manages the allocation of call parameter indices.
  32. class IndexSource {
  33. public:
  34. // Creates an IndexSource that will allocate indices starting from
  35. // `next_index`.
  36. explicit IndexSource(SemIR::CallParamIndex next_index)
  37. : next_index_(next_index) {}
  38. // Returns the index that will be allocated next.
  39. auto Peek() const -> SemIR::CallParamIndex { return next_index_; }
  40. // Allocates and returns the next index.
  41. auto Allocate() -> SemIR::CallParamIndex {
  42. auto result = next_index_;
  43. ++next_index_.index;
  44. return result;
  45. }
  46. private:
  47. SemIR::CallParamIndex next_index_;
  48. };
  49. // State for callee-side pattern matching.
  50. struct CalleeState {
  51. IndexSource index;
  52. // The in-progress contents of the `Call` parameters block.
  53. llvm::SmallVector<SemIR::InstId> call_params;
  54. // The in-progress contents of the `Call` parameter patterns block.
  55. llvm::SmallVector<SemIR::InstId> call_param_patterns;
  56. };
  57. // State for local pattern matching.
  58. struct LocalState {};
  59. // State for thunk pattern matching.
  60. struct ThunkState {
  61. // The not-yet-processed `Call` arguments for the outer call.
  62. llvm::ArrayRef<SemIR::InstId> outer_call_args;
  63. };
  64. using State =
  65. std::variant<CallerState*, CalleeState*, LocalState*, ThunkState*>;
  66. // The worklist and state machine for a pattern-matching operation.
  67. //
  68. // Conceptually, pattern matching is a recursive traversal of the pattern inst
  69. // tree: we match a pattern inst to a scrutinee inst by converting the scrutinee
  70. // as needed, matching any subpatterns against corresponding parts of the
  71. // scrutinee, and assembling the results of those sub-matches to form the result
  72. // of the whole match.
  73. //
  74. // This recursive traversal is implemented as a stack of work items, each
  75. // associated with a particular pattern inst. There are two types of work items,
  76. // PreWork and PostWork, which correspond to the work that is done before and
  77. // after visiting an inst's subpatterns, and are handled by DoPreWork and
  78. // DoPostWork overloads, respectively. Note that when there are no subpatterns,
  79. // DoPreWork may push a PostWork onto the stack, or may do the post-work (if
  80. // any) locally.
  81. //
  82. // DoPostWork is primarily responsible for computing the pattern's result and
  83. // adding it to result_stack_. However, the result of matching a pattern is
  84. // often not needed, so to avoid emitting unnecessary SemIR, it should only do
  85. // that if need_subpattern_results() is true.
  86. //
  87. // The traversal behavior depends on the kind of matching being performed. In
  88. // particular, many parts of a function signature pattern are irrelevant to the
  89. // caller, or to the callee, in which case no work will be done in that part of
  90. // the traversal. If an entire subpattern is known to be irrelevant in the
  91. // current matching context, it will not be traversed at all.
  92. class MatchContext {
  93. public:
  94. struct PreWork : Printable<PreWork> {
  95. // `None` when processing the callee side.
  96. SemIR::InstId scrutinee_id;
  97. auto Print(llvm::raw_ostream& out) const -> void {
  98. out << "{PreWork, scrutinee_id: " << scrutinee_id << "}";
  99. }
  100. };
  101. struct PostWork : Printable<PostWork> {
  102. auto Print(llvm::raw_ostream& out) const -> void { out << "{PostWork}"; }
  103. };
  104. struct WorkItem : Printable<WorkItem> {
  105. SemIR::InstId pattern_id;
  106. std::variant<PreWork, PostWork> work;
  107. // If true, disables diagnostics that would otherwise require scrutinee_id
  108. // to be tagged with `ref`. Only affects caller pattern matching.
  109. bool allow_unmarked_ref = false;
  110. auto Print(llvm::raw_ostream& out) const -> void {
  111. out << "{pattern_id: " << pattern_id << ", work: ";
  112. std::visit([&](const auto& work) { out << work; }, work);
  113. out << ", allow_unmarked_ref: " << allow_unmarked_ref << "}";
  114. }
  115. };
  116. // Constructs a MatchContext.
  117. explicit MatchContext(Context& context) : context_(context) {}
  118. // Performs pattern matching for the given work item.
  119. auto Match(State state, WorkItem entry) -> void;
  120. // Performs pattern matching for the given work item, and returns the result.
  121. auto MatchWithResult(State state, WorkItem entry) -> SemIR::InstId;
  122. private:
  123. // Whether the result of the work item at the top of the stack is needed.
  124. auto need_subpattern_results() const -> bool {
  125. return !results_stack_.empty();
  126. }
  127. // Adds `entry` to the front of the worklist.
  128. auto AddWork(WorkItem entry) -> void { stack_.push_back(entry); }
  129. // Sets `entry.work` to `PostWork` and adds it to the front of the worklist.
  130. auto AddAsPostWork(WorkItem entry) -> void {
  131. entry.work = PostWork{};
  132. AddWork(entry);
  133. }
  134. // Dispatches `entry` to the appropriate DoWork method based on the kinds of
  135. // `entry.pattern_id` and `entry.work`.
  136. auto Dispatch(State state, WorkItem entry) -> void;
  137. // Do the pre-work for `entry`. `entry.work` must be a `PreWork` containing
  138. // `scrutinee_id`, and the pattern argument must be the value of
  139. // `entry.pattern_id` in `context`.
  140. auto DoPreWork(State state, SemIR::AnyBindingPattern binding_pattern,
  141. SemIR::InstId scrutinee_id, WorkItem entry) -> void;
  142. auto DoPreWork(State state, SemIR::AnyParamPattern param_pattern,
  143. SemIR::InstId scrutinee_id, WorkItem entry) -> void;
  144. auto DoPreWork(State state, SemIR::AnyReturnPattern return_pattern,
  145. SemIR::InstId scrutinee_id, WorkItem entry) -> void;
  146. auto DoPreWork(State state, SemIR::ExprPattern expr_pattern,
  147. SemIR::InstId scrutinee_id, WorkItem entry) -> void;
  148. auto DoPreWork(State state, SemIR::ReturnSlotPattern return_slot_pattern,
  149. SemIR::InstId scrutinee_id, WorkItem entry) -> void;
  150. auto DoPreWork(State state, SemIR::VarPattern var_pattern,
  151. SemIR::InstId scrutinee_id, WorkItem entry) -> void;
  152. auto DoPreWork(State state, SemIR::TuplePattern tuple_pattern,
  153. SemIR::InstId scrutinee_id, WorkItem entry) -> void;
  154. auto DoPreWork(State state, SemIR::SpliceInst splice,
  155. SemIR::InstId scrutinee_id, WorkItem entry) -> void;
  156. // Do the post-work for `entry`. `entry.work` must be a `PostWork`, and
  157. // the pattern argument must be the value of `entry.pattern_id` in `context_`.
  158. auto DoPostWork(State state, SemIR::AnyBindingPattern binding_pattern,
  159. WorkItem entry) -> void;
  160. auto DoPostWork(State state, SemIR::VarPattern var_pattern, WorkItem entry)
  161. -> void;
  162. auto DoPostWork(State state, SemIR::AnyParamPattern param_pattern,
  163. WorkItem entry) -> void;
  164. auto DoPostWork(State state, SemIR::ExprPattern expr_pattern, WorkItem entry)
  165. -> void;
  166. auto DoPostWork(State state, SemIR::ReturnSlotPattern return_slot_pattern,
  167. WorkItem entry) -> void;
  168. auto DoPostWork(State state, SemIR::TuplePattern tuple_pattern,
  169. WorkItem entry) -> void;
  170. // Asserts that there is a single inst in the top array in `results_stack_`,
  171. // pops that array, and returns the inst.
  172. auto PopResult() -> SemIR::InstId {
  173. CARBON_CHECK(results_stack_.PeekArray().size() == 1);
  174. auto value_id = results_stack_.PeekArray()[0];
  175. results_stack_.PopArray();
  176. return value_id;
  177. }
  178. // Performs the core logic of matching a variable pattern whose type is
  179. // `pattern_type_id`, but returns the scrutinee that its subpattern should be
  180. // matched with, rather than pushing it onto the worklist. This is factored
  181. // out so it can be reused by `VarParamPattern`, which needs to do the
  182. // pre-work of both a `VarPattern` and a `RefParamPattern`.
  183. auto DoVarPreWorkImpl(State state, SemIR::TypeId pattern_type_id,
  184. SemIR::InstId scrutinee_id, WorkItem entry) const
  185. -> SemIR::InstId;
  186. // Returns the scrutinee type from `pattern_id` when passed a `CallerState`,
  187. // and `param_pattern_type_id` otherwise.
  188. auto GetSpecificPatternTypeId(State state, SemIR::InstId pattern_id,
  189. SemIR::TypeId param_pattern_type_id)
  190. -> SemIR::TypeId;
  191. // The stack of work to be processed.
  192. llvm::SmallVector<WorkItem> stack_;
  193. // The stack of in-progress match results. Each array in the stack represents
  194. // a single result, which may have multiple sub-results.
  195. ArrayStack<SemIR::InstId> results_stack_;
  196. Context& context_;
  197. };
  198. } // namespace
  199. auto MatchContext::Match(State state, WorkItem entry) -> void {
  200. CARBON_CHECK(stack_.empty());
  201. stack_.push_back(entry);
  202. while (!stack_.empty()) {
  203. Dispatch(state, stack_.pop_back_val());
  204. }
  205. }
  206. auto MatchContext::MatchWithResult(State state, WorkItem entry)
  207. -> SemIR::InstId {
  208. results_stack_.PushArray();
  209. Match(state, entry);
  210. return PopResult();
  211. }
  212. // Inserts the given region into the current code block. If the region
  213. // consists of a single block, this will be implemented as a `splice_block`
  214. // inst. Otherwise, this will end the current block with a branch to the entry
  215. // block of the region, and add future insts to a new block which is the
  216. // immediate successor of the region's exit block. As a result, this cannot be
  217. // called more than once for the same region.
  218. static auto InsertHere(Context& context, SemIR::ExprRegionId region_id)
  219. -> SemIR::InstId {
  220. auto region = context.sem_ir().expr_regions().Get(region_id);
  221. auto exit_block = context.inst_blocks().Get(region.block_ids.back());
  222. if (region.block_ids.size() == 1) {
  223. // TODO: Is it possible to avoid leaving an "orphan" block in the IR in the
  224. // first two cases?
  225. if (exit_block.empty()) {
  226. return region.result_id;
  227. }
  228. if (exit_block.size() == 1) {
  229. context.inst_block_stack().AddInstId(exit_block.front());
  230. return region.result_id;
  231. }
  232. return AddInst<SemIR::SpliceBlock>(
  233. context, SemIR::LocId(region.result_id),
  234. {.type_id = context.insts().Get(region.result_id).type_id(),
  235. .block_id = region.block_ids.front(),
  236. .result_id = region.result_id});
  237. }
  238. if (context.region_stack().empty()) {
  239. context.TODO(region.result_id,
  240. "Control flow expressions are currently only supported inside "
  241. "functions.");
  242. return SemIR::ErrorInst::InstId;
  243. }
  244. AddInst(context, SemIR::LocIdAndInst::NoLoc<SemIR::Branch>(
  245. {.target_id = region.block_ids.front()}));
  246. context.inst_block_stack().Pop();
  247. // TODO: this will cumulatively cost O(MN) running time for M blocks
  248. // at the Nth level of the stack. Figure out how to do better.
  249. context.region_stack().AddToRegion(region.block_ids);
  250. auto resume_with_block_id =
  251. context.insts().GetAs<SemIR::Branch>(exit_block.back()).target_id;
  252. CARBON_CHECK(context.inst_blocks().GetOrEmpty(resume_with_block_id).empty());
  253. context.inst_block_stack().Push(resume_with_block_id);
  254. context.region_stack().AddToRegion(resume_with_block_id,
  255. SemIR::LocId(region.result_id));
  256. return region.result_id;
  257. }
  258. // Returns the kind of conversion to perform on the scrutinee when matching the
  259. // given pattern. Note that this returns `NoOp` for `var` patterns, because
  260. // their conversion needs special handling, prior to any general-purpose
  261. // conversion that would use this function.
  262. static auto ConversionKindFor(SemIR::Inst pattern, MatchContext::WorkItem entry)
  263. -> ConversionTarget::Kind {
  264. CARBON_KIND_SWITCH(pattern) {
  265. case SemIR::VarParamPattern::Kind:
  266. case SemIR::VarPattern::Kind:
  267. // See function comment.
  268. case SemIR::OutParamPattern::Kind:
  269. // OutParamPattern conversion is handled by the enclosing
  270. // ReturnSlotPattern.
  271. case SemIR::WrapperBindingPattern::Kind:
  272. // WrapperBindingPattern conversion is handled by its subpattern.
  273. return ConversionTarget::NoOp;
  274. case SemIR::RefBindingPattern::Kind:
  275. return ConversionTarget::DurableRef;
  276. case SemIR::RefParamPattern::Kind:
  277. return entry.allow_unmarked_ref ? ConversionTarget::UnmarkedRefParam
  278. : ConversionTarget::RefParam;
  279. case SemIR::SymbolicBindingPattern::Kind:
  280. case SemIR::ValueBindingPattern::Kind:
  281. case SemIR::ValueParamPattern::Kind:
  282. return ConversionTarget::Value;
  283. default:
  284. CARBON_FATAL("Unexpected pattern kind in {0}", pattern);
  285. }
  286. }
  287. auto MatchContext::DoPreWork(State state,
  288. SemIR::AnyBindingPattern binding_pattern,
  289. SemIR::InstId scrutinee_id, WorkItem entry)
  290. -> void {
  291. bool scheduled_post_work = false;
  292. if (!std::holds_alternative<CallerState*>(state)) {
  293. results_stack_.PushArray();
  294. AddAsPostWork(entry);
  295. scheduled_post_work = true;
  296. } else {
  297. CARBON_CHECK(!need_subpattern_results());
  298. }
  299. if (binding_pattern.kind == SemIR::WrapperBindingPattern::Kind) {
  300. AddWork({.pattern_id = binding_pattern.subpattern_id,
  301. .work = PreWork{.scrutinee_id = scrutinee_id},
  302. .allow_unmarked_ref = entry.allow_unmarked_ref});
  303. } else if (scheduled_post_work) {
  304. // PostWork expects a result to bind the name to. If we scheduled PostWork,
  305. // but didn't schedule PreWork for a subpattern, the name should be bound to
  306. // the scrutinee.
  307. results_stack_.AppendToTop(scrutinee_id);
  308. }
  309. }
  310. auto MatchContext::DoPostWork(State state,
  311. SemIR::AnyBindingPattern binding_pattern,
  312. WorkItem entry) -> void {
  313. if (std::holds_alternative<ThunkState*>(state)) {
  314. // Pass through the result from the subpattern.
  315. return;
  316. }
  317. // We're logically consuming this map entry, so we invalidate it in order
  318. // to avoid accidentally consuming it twice.
  319. auto [bind_name_id, type_expr_region_id] =
  320. std::exchange(context_.bind_name_map().Lookup(entry.pattern_id).value(),
  321. {.bind_name_id = SemIR::InstId::None,
  322. .type_expr_region_id = SemIR::ExprRegionId::None});
  323. if (type_expr_region_id.has_value()) {
  324. InsertHere(context_, type_expr_region_id);
  325. }
  326. auto value_id = PopResult();
  327. if (value_id.has_value()) {
  328. auto conversion_kind = ConversionKindFor(binding_pattern, entry);
  329. if (!bind_name_id.has_value()) {
  330. // TODO: Is this appropriate, or should we perform a conversion based on
  331. // the category of the `_` binding first, and then separately discard the
  332. // initializer for a `_` binding?
  333. conversion_kind = ConversionTarget::Discarded;
  334. }
  335. value_id =
  336. Convert(context_, SemIR::LocId(value_id), value_id,
  337. {.kind = conversion_kind,
  338. .type_id = context_.insts().Get(bind_name_id).type_id()});
  339. } else {
  340. CARBON_CHECK(binding_pattern.kind == SemIR::SymbolicBindingPattern::Kind);
  341. }
  342. if (bind_name_id.has_value()) {
  343. auto bind_name = context_.insts().GetAs<SemIR::AnyBinding>(bind_name_id);
  344. CARBON_CHECK(!bind_name.value_id.has_value());
  345. bind_name.value_id = value_id;
  346. ReplaceInstBeforeConstantUse(context_, bind_name_id, bind_name);
  347. context_.inst_block_stack().AddInstId(bind_name_id);
  348. }
  349. if (need_subpattern_results()) {
  350. results_stack_.AppendToTop(value_id);
  351. }
  352. }
  353. // Returns the inst kind to use for the parameter corresponding to the given
  354. // parameter pattern.
  355. static auto ParamKindFor(SemIR::Inst param_pattern) -> SemIR::InstKind {
  356. CARBON_KIND_SWITCH(param_pattern) {
  357. case SemIR::OutParamPattern::Kind:
  358. return SemIR::OutParam::Kind;
  359. case SemIR::RefParamPattern::Kind:
  360. case SemIR::VarParamPattern::Kind:
  361. return SemIR::RefParam::Kind;
  362. case SemIR::ValueParamPattern::Kind:
  363. return SemIR::ValueParam::Kind;
  364. default:
  365. CARBON_FATAL("Unexpected param pattern kind: {0}", param_pattern);
  366. }
  367. }
  368. auto MatchContext::GetSpecificPatternTypeId(State state,
  369. SemIR::InstId pattern_id,
  370. SemIR::TypeId param_pattern_type_id)
  371. -> SemIR::TypeId {
  372. CARBON_KIND_SWITCH(state) {
  373. case CARBON_KIND(CallerState* caller): {
  374. auto& sem_ir = context_.sem_ir();
  375. return ExtractScrutineeType(
  376. sem_ir, SemIR::GetTypeOfInstInSpecific(
  377. sem_ir, caller->callee_specific_id, pattern_id));
  378. }
  379. default:
  380. return param_pattern_type_id;
  381. }
  382. }
  383. auto MatchContext::DoPreWork(State state, SemIR::AnyParamPattern param_pattern,
  384. SemIR::InstId scrutinee_id, WorkItem entry)
  385. -> void {
  386. AddAsPostWork(entry);
  387. auto pattern_type_id =
  388. GetSpecificPatternTypeId(state, entry.pattern_id, param_pattern.type_id);
  389. // If `param_pattern` is a `VarParamPattern`, match it as a `VarPattern` here,
  390. // and then as a `RefParamPattern` below.
  391. if (param_pattern.kind == SemIR::VarParamPattern::Kind) {
  392. scrutinee_id =
  393. DoVarPreWorkImpl(state, pattern_type_id, scrutinee_id, entry);
  394. entry.allow_unmarked_ref = true;
  395. }
  396. CARBON_KIND_SWITCH(state) {
  397. case CARBON_KIND(CallerState* caller_state): {
  398. CARBON_CHECK(scrutinee_id.has_value());
  399. if (scrutinee_id == SemIR::ErrorInst::InstId) {
  400. caller_state->call_args.push_back(SemIR::ErrorInst::InstId);
  401. } else {
  402. caller_state->call_args.push_back(
  403. Convert(context_, SemIR::LocId(scrutinee_id), scrutinee_id,
  404. {.kind = ConversionKindFor(param_pattern, entry),
  405. .type_id = pattern_type_id}));
  406. }
  407. // Do not traverse farther or schedule PostWork, because the caller side
  408. // of the pattern ends here.
  409. break;
  410. }
  411. case CARBON_KIND(CalleeState* callee_state): {
  412. SemIR::Inst param =
  413. SemIR::AnyParam{.kind = ParamKindFor(param_pattern),
  414. .type_id = ExtractScrutineeType(
  415. context_.sem_ir(), param_pattern.type_id),
  416. .index = callee_state->index.Allocate(),
  417. .pretty_name_id = SemIR::GetPrettyNameFromPatternId(
  418. context_.sem_ir(), entry.pattern_id)};
  419. auto loc_id = SemIR::LocId(entry.pattern_id);
  420. auto param_id = SemIR::InstId::None;
  421. // TODO: find a way to avoid this boilerplate.
  422. switch (param.kind()) {
  423. case SemIR::OutParam::Kind:
  424. param_id = AddInst(context_, loc_id, param.As<SemIR::OutParam>());
  425. break;
  426. case SemIR::RefParam::Kind:
  427. param_id = AddInst(context_, loc_id, param.As<SemIR::RefParam>());
  428. break;
  429. case SemIR::ValueParam::Kind:
  430. param_id = AddInst(context_, loc_id, param.As<SemIR::ValueParam>());
  431. break;
  432. default:
  433. CARBON_FATAL("Unexpected parameter kind");
  434. }
  435. if (auto var_param_pattern =
  436. context_.insts().TryGetAs<SemIR::VarParamPattern>(
  437. entry.pattern_id)) {
  438. AddWork({.pattern_id = var_param_pattern->subpattern_id,
  439. .work = PreWork{.scrutinee_id = param_id},
  440. .allow_unmarked_ref = entry.allow_unmarked_ref});
  441. } else {
  442. results_stack_.AppendToTop(param_id);
  443. }
  444. callee_state->call_params.push_back(param_id);
  445. callee_state->call_param_patterns.push_back(entry.pattern_id);
  446. break;
  447. }
  448. case CARBON_KIND(ThunkState* thunk_state): {
  449. auto param_id = thunk_state->outer_call_args.consume_front();
  450. if (auto var_param_pattern =
  451. context_.insts().TryGetAs<SemIR::VarParamPattern>(
  452. entry.pattern_id)) {
  453. AddWork({.pattern_id = var_param_pattern->subpattern_id,
  454. .work = PreWork{.scrutinee_id = param_id},
  455. .allow_unmarked_ref = entry.allow_unmarked_ref});
  456. } else {
  457. results_stack_.AppendToTop(param_id);
  458. }
  459. break;
  460. }
  461. case CARBON_KIND(LocalState* _): {
  462. CARBON_FATAL("Found ValueParamPattern during local pattern match");
  463. }
  464. }
  465. }
  466. auto MatchContext::DoPostWork(State /*state*/,
  467. SemIR::AnyParamPattern /*param_pattern*/,
  468. WorkItem /*entry*/) -> void {
  469. // No-op: the subpattern's result is this pattern's result. Note that if
  470. // there were any post-work corresponding to DoVarPreWorkImpl, that work
  471. // would have to be done here.
  472. }
  473. auto MatchContext::DoPreWork(State /*state*/,
  474. SemIR::ExprPattern /*expr_pattern*/,
  475. SemIR::InstId /*scrutinee_id*/, WorkItem entry)
  476. -> void {
  477. context_.TODO(entry.pattern_id, "expression pattern");
  478. }
  479. auto MatchContext::DoPostWork(State /*state*/,
  480. SemIR::ExprPattern /*expr_pattern*/,
  481. WorkItem /*entry*/) -> void {}
  482. auto MatchContext::DoPreWork(State state,
  483. SemIR::ReturnSlotPattern return_slot_pattern,
  484. SemIR::InstId scrutinee_id, WorkItem entry)
  485. -> void {
  486. if (std::holds_alternative<CalleeState*>(state)) {
  487. CARBON_CHECK(!scrutinee_id.has_value());
  488. results_stack_.PushArray();
  489. AddAsPostWork(entry);
  490. }
  491. AddWork({.pattern_id = return_slot_pattern.subpattern_id,
  492. .work = PreWork{.scrutinee_id = scrutinee_id},
  493. .allow_unmarked_ref = entry.allow_unmarked_ref});
  494. }
  495. auto MatchContext::DoPostWork(State state,
  496. SemIR::ReturnSlotPattern return_slot_pattern,
  497. WorkItem entry) -> void {
  498. CARBON_CHECK(std::holds_alternative<CalleeState*>(state));
  499. auto type_id =
  500. ExtractScrutineeType(context_.sem_ir(), return_slot_pattern.type_id);
  501. auto return_slot_id = AddInst<SemIR::ReturnSlot>(
  502. context_, SemIR::LocId(entry.pattern_id),
  503. {.type_id = type_id,
  504. .type_inst_id = context_.types().GetTypeInstId(type_id),
  505. .storage_id = PopResult()});
  506. bool already_in_lookup =
  507. context_.scope_stack()
  508. .LookupOrAddName(SemIR::NameId::ReturnSlot, return_slot_id)
  509. .has_value();
  510. CARBON_CHECK(!already_in_lookup);
  511. if (need_subpattern_results()) {
  512. results_stack_.AppendToTop(return_slot_id);
  513. }
  514. }
  515. auto MatchContext::DoPreWork(State state, SemIR::VarPattern var_pattern,
  516. SemIR::InstId scrutinee_id, WorkItem entry)
  517. -> void {
  518. auto pattern_type_id =
  519. GetSpecificPatternTypeId(state, entry.pattern_id, var_pattern.type_id);
  520. auto new_scrutinee_id =
  521. DoVarPreWorkImpl(state, pattern_type_id, scrutinee_id, entry);
  522. if (need_subpattern_results()) {
  523. AddAsPostWork(entry);
  524. }
  525. AddWork({.pattern_id = var_pattern.subpattern_id,
  526. .work = PreWork{.scrutinee_id = new_scrutinee_id},
  527. .allow_unmarked_ref = true});
  528. }
  529. auto MatchContext::DoVarPreWorkImpl(State state, SemIR::TypeId pattern_type_id,
  530. SemIR::InstId scrutinee_id,
  531. WorkItem entry) const -> SemIR::InstId {
  532. CARBON_KIND_SWITCH(state) {
  533. case CARBON_KIND(CalleeState* _): {
  534. // We're emitting pattern-match IR for the callee, but we're still on
  535. // the caller side of the pattern, so we traverse without emitting any
  536. // insts.
  537. return scrutinee_id;
  538. }
  539. case CARBON_KIND(ThunkState* _): {
  540. return scrutinee_id;
  541. }
  542. case CARBON_KIND(LocalState* _): {
  543. // TODO: Find a more efficient way to put these insts in the global_init
  544. // block (or drop the distinction between the global_init block and the
  545. // file scope?)
  546. if (context_.scope_stack().PeekIndex() == ScopeIndex::Package) {
  547. context_.global_init().Resume();
  548. }
  549. // In a `var`/`let` declaration, the `VarStorage` inst is created before
  550. // we start pattern matching.
  551. auto lookup_result = context_.var_storage_map().Lookup(entry.pattern_id);
  552. CARBON_CHECK(lookup_result);
  553. auto storage_id = lookup_result.value();
  554. if (scrutinee_id.has_value()) {
  555. auto init_id =
  556. InitializeExisting(context_, SemIR::LocId(entry.pattern_id),
  557. storage_id, scrutinee_id, /*for_return=*/false);
  558. // TODO: It's a bit weird to use an `Assign` instruction to model
  559. // initialization. Consider adding a different instruction for this
  560. // purpose.
  561. AddInst<SemIR::Assign>(context_, SemIR::LocId(entry.pattern_id),
  562. {.lhs_id = storage_id, .rhs_id = init_id});
  563. }
  564. if (context_.scope_stack().PeekIndex() == ScopeIndex::Package) {
  565. context_.global_init().Suspend();
  566. }
  567. return storage_id;
  568. }
  569. case CARBON_KIND(CallerState* _): {
  570. // TODO: This variable's lifetime should end at the end of the call or the
  571. // full-expression. We don't use a temporary here, because temporaries are
  572. // treated as being immutable.
  573. PendingBlock storage_block(&context_);
  574. auto storage_id = storage_block.AddInstWithCleanup<SemIR::VarStorage>(
  575. SemIR::LocId(entry.pattern_id),
  576. {.type_id = pattern_type_id, .pattern_id = entry.pattern_id});
  577. auto init_result = Initialize(
  578. context_, SemIR::LocId(entry.pattern_id),
  579. // Disable broken lint that suggests a "fix" that doesn't compile.
  580. // NOLINTNEXTLINE(performance-move-const-arg)
  581. std::move(storage_id), std::move(storage_block), scrutinee_id);
  582. // TODO: Consider instead creating something like a `Temporary`
  583. // instruction that returns a reference so that constant evaluation can
  584. // obtain the value of the var parameter.
  585. AddInst<SemIR::Assign>(
  586. context_, SemIR::LocId(entry.pattern_id),
  587. {.lhs_id = init_result.storage_id, .rhs_id = init_result.init_id});
  588. return init_result.storage_id;
  589. }
  590. }
  591. }
  592. auto MatchContext::DoPostWork(State /*state*/,
  593. SemIR::VarPattern /*var_pattern*/,
  594. WorkItem /*entry*/) -> void {
  595. // No-op: the subpattern's result is this pattern's result.
  596. }
  597. auto MatchContext::DoPreWork(State state, SemIR::TuplePattern tuple_pattern,
  598. SemIR::InstId scrutinee_id, WorkItem entry)
  599. -> void {
  600. if (tuple_pattern.type_id == SemIR::ErrorInst::TypeId) {
  601. return;
  602. }
  603. auto subpattern_ids = context_.inst_blocks().Get(tuple_pattern.elements_id);
  604. if (need_subpattern_results()) {
  605. results_stack_.PushArray();
  606. AddAsPostWork(entry);
  607. }
  608. auto add_all_subscrutinees =
  609. [&](llvm::ArrayRef<SemIR::InstId> subscrutinee_ids) {
  610. for (auto [subpattern_id, subscrutinee_id] :
  611. llvm::reverse(llvm::zip_equal(subpattern_ids, subscrutinee_ids))) {
  612. AddWork({.pattern_id = subpattern_id,
  613. .work = PreWork{.scrutinee_id = subscrutinee_id},
  614. .allow_unmarked_ref = entry.allow_unmarked_ref});
  615. }
  616. };
  617. if (!scrutinee_id.has_value()) {
  618. CARBON_CHECK(std::holds_alternative<CalleeState*>(state) ||
  619. std::holds_alternative<ThunkState*>(state));
  620. // If we don't have a scrutinee yet, we're still on the caller side of the
  621. // pattern, so the subpatterns don't have a scrutinee either.
  622. for (auto subpattern_id : llvm::reverse(subpattern_ids)) {
  623. AddWork({.pattern_id = subpattern_id,
  624. .work = PreWork{.scrutinee_id = SemIR::InstId::None},
  625. .allow_unmarked_ref = entry.allow_unmarked_ref});
  626. }
  627. return;
  628. }
  629. auto scrutinee = context_.insts().GetWithLocId(scrutinee_id);
  630. if (auto scrutinee_literal = scrutinee.inst.TryAs<SemIR::TupleLiteral>()) {
  631. auto subscrutinee_ids =
  632. context_.inst_blocks().Get(scrutinee_literal->elements_id);
  633. if (subscrutinee_ids.size() != subpattern_ids.size()) {
  634. CARBON_DIAGNOSTIC(TuplePatternSizeDoesntMatchLiteral, Error,
  635. "tuple pattern expects {0} element{0:s}, but tuple "
  636. "literal has {1}",
  637. Diagnostics::IntAsSelect, Diagnostics::IntAsSelect);
  638. context_.emitter().Emit(entry.pattern_id,
  639. TuplePatternSizeDoesntMatchLiteral,
  640. subpattern_ids.size(), subscrutinee_ids.size());
  641. return;
  642. }
  643. add_all_subscrutinees(subscrutinee_ids);
  644. return;
  645. }
  646. auto tuple_type_id =
  647. ExtractScrutineeType(context_.sem_ir(), tuple_pattern.type_id);
  648. auto converted_scrutinee_id = ConvertToValueOrRefOfType(
  649. context_, SemIR::LocId(entry.pattern_id), scrutinee_id, tuple_type_id);
  650. if (auto scrutinee_value = context_.insts().TryGetAs<SemIR::TupleValue>(
  651. converted_scrutinee_id)) {
  652. add_all_subscrutinees(
  653. context_.inst_blocks().Get(scrutinee_value->elements_id));
  654. return;
  655. }
  656. auto tuple_type = context_.types().GetAs<SemIR::TupleType>(tuple_type_id);
  657. auto element_type_inst_ids =
  658. context_.inst_blocks().Get(tuple_type.type_elements_id);
  659. llvm::SmallVector<SemIR::InstId> subscrutinee_ids;
  660. subscrutinee_ids.reserve(element_type_inst_ids.size());
  661. for (auto [i, element_type_id] : llvm::enumerate(
  662. context_.types().GetBlockAsTypeIds(element_type_inst_ids))) {
  663. subscrutinee_ids.push_back(
  664. AddInst<SemIR::TupleAccess>(context_, scrutinee.loc_id,
  665. {.type_id = element_type_id,
  666. .tuple_id = converted_scrutinee_id,
  667. .index = SemIR::ElementIndex(i)}));
  668. }
  669. add_all_subscrutinees(subscrutinee_ids);
  670. }
  671. auto MatchContext::DoPostWork(State /*state*/,
  672. SemIR::TuplePattern tuple_pattern, WorkItem entry)
  673. -> void {
  674. auto elements_id = context_.inst_blocks().Add(results_stack_.PeekArray());
  675. results_stack_.PopArray();
  676. auto tuple_value_id =
  677. AddInst<SemIR::TupleValue>(context_, SemIR::LocId(entry.pattern_id),
  678. {.type_id = SemIR::ExtractScrutineeType(
  679. context_.sem_ir(), tuple_pattern.type_id),
  680. .elements_id = elements_id});
  681. results_stack_.AppendToTop(tuple_value_id);
  682. }
  683. auto MatchContext::DoPreWork(State state, SemIR::SpliceInst splice,
  684. SemIR::InstId scrutinee_id, WorkItem entry)
  685. -> void {
  686. CARBON_KIND_SWITCH(state) {
  687. case CARBON_KIND(CallerState* _): {
  688. CARBON_FATAL("TODO: support caller-side matching of pattern splices");
  689. }
  690. case CARBON_KIND(ThunkState* _): {
  691. CARBON_FATAL("TODO: support thunk matching of pattern splices");
  692. }
  693. case CARBON_KIND(LocalState* _): {
  694. CARBON_FATAL("TODO: support local matching of pattern splices");
  695. }
  696. case CARBON_KIND(CalleeState* callee_state): {
  697. CARBON_CHECK(!scrutinee_id.has_value());
  698. auto result_id = HandleAction<SemIR::CalleePatternMatchAction>(
  699. context_, SemIR::LocId(entry.pattern_id),
  700. context_.types().GetTypeInstId(splice.type_id),
  701. {.type_id = SemIR::InstType::TypeId,
  702. .pattern_id = entry.pattern_id,
  703. .parent_index = callee_state->index.Allocate()});
  704. callee_state->call_param_patterns.push_back(entry.pattern_id);
  705. callee_state->call_params.push_back(result_id);
  706. results_stack_.AppendToTop(result_id);
  707. }
  708. }
  709. }
  710. auto MatchContext::DoPreWork(State state,
  711. SemIR::AnyReturnPattern return_pattern,
  712. SemIR::InstId /*scrutinee_id*/, WorkItem entry)
  713. -> void {
  714. CARBON_CHECK(std::holds_alternative<CalleeState*>(state));
  715. if (need_subpattern_results()) {
  716. auto type_id =
  717. ExtractScrutineeType(context_.sem_ir(), return_pattern.type_id);
  718. SemIR::InstKind result_kind =
  719. return_pattern.kind == SemIR::RefReturnPattern::Kind
  720. ? SemIR::RefReturn::Kind
  721. : SemIR::ValueReturn::Kind;
  722. results_stack_.AppendToTop(AddInst(
  723. context_,
  724. SemIR::LocIdAndInst::RuntimeVerified(
  725. context_.sem_ir(), SemIR::LocId(entry.pattern_id),
  726. SemIR::AnyReturnPattern{.kind = result_kind, .type_id = type_id})));
  727. }
  728. }
  729. auto MatchContext::Dispatch(State state, WorkItem entry) -> void {
  730. if (entry.pattern_id == SemIR::ErrorInst::InstId) {
  731. if (need_subpattern_results()) {
  732. results_stack_.AppendToTop(SemIR::ErrorInst::InstId);
  733. }
  734. return;
  735. }
  736. Diagnostics::AnnotationScope annotate_diagnostics(
  737. &context_.emitter(), [&](auto& builder) {
  738. if (std::holds_alternative<CallerState*>(state)) {
  739. CARBON_DIAGNOSTIC(InCallToFunctionParam, Note,
  740. "initializing function parameter");
  741. builder.Note(entry.pattern_id, InCallToFunctionParam);
  742. }
  743. });
  744. auto pattern = context_.insts().Get(entry.pattern_id);
  745. CARBON_KIND_SWITCH(entry.work) {
  746. case CARBON_KIND(PreWork work): {
  747. // TODO: Require that `work.scrutinee_id` is valid if and only if insts
  748. // should be emitted, once we start emitting `Param` insts in the
  749. // `ParamPattern` case.
  750. CARBON_KIND_SWITCH(pattern) {
  751. case CARBON_KIND_ANY(SemIR::AnyBindingPattern, any_binding_pattern): {
  752. DoPreWork(state, any_binding_pattern, work.scrutinee_id, entry);
  753. break;
  754. }
  755. case CARBON_KIND_ANY(SemIR::AnyParamPattern, any_param_pattern): {
  756. DoPreWork(state, any_param_pattern, work.scrutinee_id, entry);
  757. break;
  758. }
  759. case CARBON_KIND_ANY(SemIR::AnyReturnPattern, return_pattern): {
  760. DoPreWork(state, return_pattern, work.scrutinee_id, entry);
  761. break;
  762. }
  763. case CARBON_KIND(SemIR::ExprPattern expr_pattern): {
  764. DoPreWork(state, expr_pattern, work.scrutinee_id, entry);
  765. break;
  766. }
  767. case CARBON_KIND(SemIR::ReturnSlotPattern return_slot_pattern): {
  768. DoPreWork(state, return_slot_pattern, work.scrutinee_id, entry);
  769. break;
  770. }
  771. case CARBON_KIND(SemIR::VarPattern var_pattern): {
  772. DoPreWork(state, var_pattern, work.scrutinee_id, entry);
  773. break;
  774. }
  775. case CARBON_KIND(SemIR::TuplePattern tuple_pattern): {
  776. DoPreWork(state, tuple_pattern, work.scrutinee_id, entry);
  777. break;
  778. }
  779. case CARBON_KIND(SemIR::SpliceInst splice_inst): {
  780. DoPreWork(state, splice_inst, work.scrutinee_id, entry);
  781. break;
  782. }
  783. default: {
  784. CARBON_FATAL("Inst kind not handled: {0}", pattern.kind());
  785. }
  786. }
  787. break;
  788. }
  789. case CARBON_KIND(PostWork _): {
  790. CARBON_KIND_SWITCH(pattern) {
  791. case CARBON_KIND_ANY(SemIR::AnyBindingPattern, any_binding_pattern): {
  792. DoPostWork(state, any_binding_pattern, entry);
  793. break;
  794. }
  795. case CARBON_KIND_ANY(SemIR::AnyParamPattern, any_param_pattern): {
  796. DoPostWork(state, any_param_pattern, entry);
  797. break;
  798. }
  799. case CARBON_KIND(SemIR::ExprPattern expr_pattern): {
  800. DoPostWork(state, expr_pattern, entry);
  801. break;
  802. }
  803. case CARBON_KIND(SemIR::ReturnSlotPattern return_slot_pattern): {
  804. DoPostWork(state, return_slot_pattern, entry);
  805. break;
  806. }
  807. case CARBON_KIND(SemIR::VarPattern var_pattern): {
  808. DoPostWork(state, var_pattern, entry);
  809. break;
  810. }
  811. case CARBON_KIND(SemIR::TuplePattern tuple_pattern): {
  812. DoPostWork(state, tuple_pattern, entry);
  813. break;
  814. }
  815. default: {
  816. CARBON_FATAL("Inst kind not handled: {0}", pattern.kind());
  817. }
  818. }
  819. break;
  820. }
  821. }
  822. }
  823. auto CalleePatternMatch(Context& context,
  824. SemIR::InstBlockId implicit_param_patterns_id,
  825. SemIR::InstBlockId param_patterns_id,
  826. SemIR::InstId return_pattern_id)
  827. -> CalleePatternMatchResults {
  828. if (!return_pattern_id.has_value() && !param_patterns_id.has_value() &&
  829. !implicit_param_patterns_id.has_value()) {
  830. return {.call_param_patterns_id = SemIR::InstBlockId::None,
  831. .call_params_id = SemIR::InstBlockId::None,
  832. .param_ranges = SemIR::Function::CallParamIndexRanges::Empty};
  833. }
  834. CalleeState state = {.index = IndexSource(SemIR::CallParamIndex(0))};
  835. MatchContext match(context);
  836. // We add work to the stack in reverse so that the results will be produced
  837. // in the original order.
  838. if (implicit_param_patterns_id.has_value()) {
  839. for (SemIR::InstId inst_id :
  840. context.inst_blocks().Get(implicit_param_patterns_id)) {
  841. match.Match(
  842. &state,
  843. {.pattern_id = inst_id,
  844. .work = MatchContext::PreWork{.scrutinee_id = SemIR::InstId::None}});
  845. }
  846. }
  847. auto implicit_end = SemIR::CallParamIndex(state.call_params.size());
  848. if (param_patterns_id.has_value()) {
  849. for (SemIR::InstId inst_id : context.inst_blocks().Get(param_patterns_id)) {
  850. match.Match(
  851. &state,
  852. {.pattern_id = inst_id,
  853. .work = MatchContext::PreWork{.scrutinee_id = SemIR::InstId::None}});
  854. }
  855. }
  856. auto explicit_end = SemIR::CallParamIndex(state.call_params.size());
  857. if (return_pattern_id.has_value()) {
  858. match.Match(
  859. &state,
  860. {.pattern_id = return_pattern_id,
  861. .work = MatchContext::PreWork{.scrutinee_id = SemIR::InstId::None}});
  862. }
  863. auto return_end = SemIR::CallParamIndex(state.call_params.size());
  864. CARBON_CHECK(state.call_params.size() == state.call_param_patterns.size());
  865. return {.call_param_patterns_id =
  866. context.inst_blocks().Add(state.call_param_patterns),
  867. .call_params_id = context.inst_blocks().Add(state.call_params),
  868. .param_ranges = {implicit_end, explicit_end, return_end}};
  869. }
  870. auto ThunkPatternMatch(Context& context, SemIR::InstId self_pattern_id,
  871. llvm::ArrayRef<SemIR::InstId> param_pattern_ids,
  872. llvm::ArrayRef<SemIR::InstId> outer_call_args)
  873. -> ThunkPatternMatchResults {
  874. ThunkState state = {.outer_call_args = outer_call_args};
  875. MatchContext match(context);
  876. llvm::SmallVector<SemIR::InstId> inner_args;
  877. inner_args.reserve(outer_call_args.size() + 1);
  878. if (self_pattern_id.has_value()) {
  879. inner_args.push_back(match.MatchWithResult(
  880. &state,
  881. {.pattern_id = self_pattern_id,
  882. .work = MatchContext::PreWork{.scrutinee_id = SemIR::InstId::None},
  883. .allow_unmarked_ref = true}));
  884. }
  885. for (SemIR::InstId inst_id : param_pattern_ids) {
  886. inner_args.push_back(match.MatchWithResult(
  887. &state,
  888. {.pattern_id = inst_id,
  889. .work = MatchContext::PreWork{.scrutinee_id = SemIR::InstId::None},
  890. .allow_unmarked_ref = true}));
  891. }
  892. return {.syntactic_args = std::move(inner_args),
  893. .ignored_call_args = state.outer_call_args};
  894. }
  895. auto PerformAction(Context& context, SemIR::LocId /*loc_id*/,
  896. SemIR::CalleePatternMatchAction action) -> SemIR::InstId {
  897. CalleeState state = {.index = IndexSource(action.parent_index)};
  898. MatchContext match(context);
  899. auto result_id = match.MatchWithResult(
  900. &state,
  901. {.pattern_id = action.pattern_id,
  902. .work = MatchContext::PreWork{.scrutinee_id = SemIR::InstId::None},
  903. .allow_unmarked_ref = true});
  904. CARBON_CHECK(state.index.Peek().index <= action.parent_index.index + 1,
  905. "TODO: add support for composite forms");
  906. return result_id;
  907. }
  908. auto CallerPatternMatch(Context& context, SemIR::SpecificId specific_id,
  909. SemIR::InstId self_pattern_id,
  910. SemIR::InstBlockId param_patterns_id,
  911. SemIR::InstId return_pattern_id,
  912. SemIR::InstId self_arg_id,
  913. llvm::ArrayRef<SemIR::InstId> arg_refs,
  914. SemIR::InstId return_arg_id, bool is_desugared)
  915. -> SemIR::InstBlockId {
  916. CallerState state = {.callee_specific_id = specific_id};
  917. MatchContext match(context);
  918. if (self_pattern_id.has_value()) {
  919. match.Match(&state,
  920. {.pattern_id = self_pattern_id,
  921. .work = MatchContext::PreWork{.scrutinee_id = self_arg_id},
  922. .allow_unmarked_ref = true});
  923. }
  924. for (auto [arg_id, param_pattern_id] : llvm::zip_equal(
  925. arg_refs, context.inst_blocks().GetOrEmpty(param_patterns_id))) {
  926. match.Match(&state, {.pattern_id = param_pattern_id,
  927. .work = MatchContext::PreWork{.scrutinee_id = arg_id},
  928. .allow_unmarked_ref = is_desugared});
  929. }
  930. // Track the return storage, if present.
  931. if (return_pattern_id.has_value()) {
  932. // TODO: Do the match even if return_arg_id is None, so that subsequent
  933. // args are at the right index in the arg block.
  934. if (return_arg_id.has_value()) {
  935. match.Match(&state, {.pattern_id = return_pattern_id,
  936. .work = MatchContext::PreWork{.scrutinee_id =
  937. return_arg_id}});
  938. }
  939. } else {
  940. CARBON_CHECK(!return_arg_id.has_value(), "No pattern to match return arg");
  941. }
  942. return context.inst_blocks().Add(state.call_args);
  943. }
  944. auto LocalPatternMatch(Context& context, SemIR::InstId pattern_id,
  945. SemIR::InstId scrutinee_id) -> void {
  946. LocalState state;
  947. MatchContext match(context);
  948. match.Match(&state,
  949. {.pattern_id = pattern_id,
  950. .work = MatchContext::PreWork{.scrutinee_id = scrutinee_id}});
  951. }
  952. } // namespace Carbon::Check