pattern_match.cpp 40 KB

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