pattern_match.cpp 40 KB

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