pattern_match.cpp 41 KB

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