pattern_match.cpp 40 KB

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