pattern_match.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  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 <vector>
  7. #include "llvm/ADT/STLExtras.h"
  8. #include "llvm/ADT/SmallVector.h"
  9. #include "toolchain/base/kind_switch.h"
  10. #include "toolchain/check/context.h"
  11. #include "toolchain/check/convert.h"
  12. namespace Carbon::Check {
  13. namespace {
  14. // Selects between the different kinds of pattern matching.
  15. enum class MatchKind {
  16. // Caller pattern matching occurs on the caller side of a function call, and
  17. // is responsible for matching the argument expression against the portion
  18. // of the pattern above the ParamPattern insts.
  19. Caller,
  20. // Callee pattern matching occurs in the function decl block, and is
  21. // responsible for matching the function's calling-convention parameters
  22. // against the portion of the pattern below the ParamPattern insts.
  23. Callee,
  24. // TODO: add enumerator for non-function-call pattern match
  25. };
  26. // The collected state of a pattern-matching operation.
  27. class MatchContext {
  28. public:
  29. struct WorkItem {
  30. SemIR::InstId pattern_id;
  31. // Invalid when processing the callee side.
  32. SemIR::InstId scrutinee_id;
  33. };
  34. // Constructs a MatchContext. If `callee_specific_id` is valid, this pattern
  35. // match operation is part of implementing the signature of the given
  36. // specific.
  37. explicit MatchContext(MatchKind kind, SemIR::SpecificId callee_specific_id =
  38. SemIR::SpecificId::Invalid)
  39. : next_index_(0),
  40. result_(SemIR::InstId::Invalid),
  41. kind_(kind),
  42. callee_specific_id_(callee_specific_id) {}
  43. // Returns whether there are any work items to process.
  44. auto HasWork() const -> bool {
  45. return !stack_.empty() && !result_.is_valid();
  46. }
  47. // Adds a work item to the stack. Cannot be called after Finish().
  48. auto AddWork(WorkItem work_item) -> void { stack_.push_back(work_item); }
  49. // Returns the next work item to process.
  50. auto NextWorkItem() -> WorkItem { return stack_.pop_back_val(); }
  51. // Allocates the next unallocated RuntimeParamIndex, starting from 0.
  52. auto NextRuntimeIndex() -> SemIR::RuntimeParamIndex {
  53. auto result = next_index_;
  54. ++next_index_.index;
  55. return result;
  56. }
  57. // TODO: Eliminate the caller/callee API split below, by restructuring
  58. // CallerPatternMatch to operate on the whole pattern.
  59. // Sets the result of this pattern matching operation. Must not be called when
  60. // there is still pending work, except to report an error, or called more than
  61. // once between calls to ConsumeResult. Valid only during caller matching.
  62. auto Finish(SemIR::InstId result) -> void {
  63. CARBON_CHECK(!HasWork() || result == SemIR::InstId::BuiltinError);
  64. CARBON_CHECK(kind_ == MatchKind::Caller);
  65. CARBON_CHECK(result_ == SemIR::InstId::Invalid);
  66. result_ = result;
  67. }
  68. // Consumes and returns the result stored by Finish. Valid only during caller
  69. // matching.
  70. auto ConsumeResult() -> SemIR::InstId {
  71. CARBON_CHECK(stack_.empty() || result_ == SemIR::InstId::BuiltinError);
  72. CARBON_CHECK(kind_ == MatchKind::Caller);
  73. return std::exchange(result_, SemIR::InstId::Invalid);
  74. }
  75. // Records that `bind_name_id` is the ID of an inst in the AnyBindName
  76. // category, emitted as part of this pattern match. Valid only during callee
  77. // pattern matching.
  78. auto RecordBindName(SemIR::InstId bind_name_id) {
  79. CARBON_CHECK(kind_ == MatchKind::Callee);
  80. bind_name_ids_.push_back(bind_name_id);
  81. }
  82. // Allocates an InstBlock containing the IDs recorded by RecordBindName since
  83. // the last call to this function (if any), and returns its ID. Valid only
  84. // during callee pattern matching.
  85. auto ConsumeBindNames(Context& context) -> SemIR::InstBlockId {
  86. CARBON_CHECK(stack_.empty());
  87. CARBON_CHECK(kind_ == MatchKind::Callee);
  88. auto block_id = context.inst_blocks().Add(bind_name_ids_);
  89. bind_name_ids_.clear();
  90. return block_id;
  91. }
  92. auto kind() const -> MatchKind { return kind_; }
  93. auto callee_specific_id() const -> SemIR::SpecificId {
  94. return callee_specific_id_;
  95. }
  96. private:
  97. llvm::SmallVector<WorkItem> stack_;
  98. SemIR::RuntimeParamIndex next_index_;
  99. SemIR::InstId result_;
  100. llvm::SmallVector<SemIR::InstId> bind_name_ids_;
  101. MatchKind kind_;
  102. SemIR::SpecificId callee_specific_id_;
  103. };
  104. // Emits the pattern-match insts necessary to match the pattern inst
  105. // `entry.pattern_id` against the scrutinee value `entry.scrutinee_id`,
  106. // and adds to `match` any work necessary to traverse into its subpatterns.
  107. // This behavior is contingent on the kind of match being performed, as
  108. // indicated by `match.kind()`. For example, when performing a callee
  109. // pattern match, this does not emit insts for patterns on the caller side.
  110. // However, it still traverses into subpatterns if any of their descendants
  111. // might emit insts.
  112. // TODO: Require that `entry.scrutinee_id` is valid if and only if insts should
  113. // be emitted, once we start emitting `Param` insts in the `ParamPattern` case.
  114. auto EmitPatternMatch(Context& context, MatchContext& match,
  115. MatchContext::WorkItem entry) -> void {
  116. if (entry.pattern_id == SemIR::InstId::BuiltinError) {
  117. match.RecordBindName(SemIR::InstId::BuiltinError);
  118. return;
  119. }
  120. auto pattern = context.insts().GetWithLocId(entry.pattern_id);
  121. CARBON_KIND_SWITCH(pattern.inst) {
  122. case SemIR::BindingPattern::Kind:
  123. case SemIR::SymbolicBindingPattern::Kind: {
  124. CARBON_CHECK(match.kind() == MatchKind::Callee);
  125. auto binding_pattern = pattern.inst.As<SemIR::AnyBindingPattern>();
  126. auto bind_name = context.insts().GetAs<SemIR::AnyBindName>(
  127. binding_pattern.bind_name_id);
  128. CARBON_CHECK(!bind_name.value_id.is_valid());
  129. bind_name.value_id = entry.scrutinee_id;
  130. context.ReplaceInstBeforeConstantUse(binding_pattern.bind_name_id,
  131. bind_name);
  132. context.inst_block_stack().AddInstId(binding_pattern.bind_name_id);
  133. match.RecordBindName(binding_pattern.bind_name_id);
  134. break;
  135. }
  136. case CARBON_KIND(SemIR::AddrPattern addr_pattern): {
  137. if (match.kind() == MatchKind::Callee) {
  138. // We're emitting pattern-match IR for the callee, but we're still on
  139. // the caller side of the pattern, so we traverse without emitting any
  140. // insts.
  141. match.AddWork({.pattern_id = addr_pattern.inner_id,
  142. .scrutinee_id = SemIR::InstId::Invalid});
  143. break;
  144. }
  145. CARBON_CHECK(entry.scrutinee_id.is_valid());
  146. auto scrutinee_ref_id =
  147. ConvertToValueOrRefExpr(context, entry.scrutinee_id);
  148. switch (SemIR::GetExprCategory(context.sem_ir(), scrutinee_ref_id)) {
  149. case SemIR::ExprCategory::Error:
  150. case SemIR::ExprCategory::DurableRef:
  151. case SemIR::ExprCategory::EphemeralRef:
  152. break;
  153. default:
  154. CARBON_DIAGNOSTIC(AddrSelfIsNonRef, Error,
  155. "`addr self` method cannot be invoked on a value");
  156. context.emitter().Emit(
  157. TokenOnly(context.insts().GetLocId(entry.scrutinee_id)),
  158. AddrSelfIsNonRef);
  159. match.Finish(SemIR::InstId::BuiltinError);
  160. return;
  161. }
  162. auto scrutinee_ref = context.insts().Get(scrutinee_ref_id);
  163. auto new_scrutinee = context.AddInst<SemIR::AddrOf>(
  164. context.insts().GetLocId(scrutinee_ref_id),
  165. {.type_id = context.GetPointerType(scrutinee_ref.type_id()),
  166. .lvalue_id = scrutinee_ref_id});
  167. match.AddWork(
  168. {.pattern_id = addr_pattern.inner_id, .scrutinee_id = new_scrutinee});
  169. break;
  170. }
  171. case CARBON_KIND(SemIR::ParamPattern param_pattern): {
  172. switch (match.kind()) {
  173. case MatchKind::Caller: {
  174. CARBON_CHECK(entry.scrutinee_id.is_valid());
  175. match.Finish(ConvertToValueOfType(
  176. context, context.insts().GetLocId(entry.scrutinee_id),
  177. entry.scrutinee_id,
  178. SemIR::GetTypeInSpecific(context.sem_ir(),
  179. match.callee_specific_id(),
  180. param_pattern.type_id)));
  181. // Do not traverse farther, because the caller side of the pattern
  182. // ends here.
  183. break;
  184. }
  185. case MatchKind::Callee: {
  186. if (param_pattern.runtime_index ==
  187. SemIR::RuntimeParamIndex::Unknown) {
  188. param_pattern.runtime_index = match.NextRuntimeIndex();
  189. context.ReplaceInstBeforeConstantUse(entry.pattern_id,
  190. param_pattern);
  191. }
  192. match.AddWork({.pattern_id = param_pattern.subpattern_id,
  193. .scrutinee_id = context.AddInst<SemIR::Param>(
  194. pattern.loc_id,
  195. {.type_id = param_pattern.type_id,
  196. .runtime_index = param_pattern.runtime_index})});
  197. } break;
  198. }
  199. break;
  200. }
  201. default: {
  202. CARBON_FATAL("Inst kind not handled: {0}", pattern.inst.kind());
  203. }
  204. }
  205. }
  206. } // namespace
  207. auto CalleePatternMatch(Context& context,
  208. SemIR::InstBlockId implicit_param_patterns_id,
  209. SemIR::InstBlockId param_patterns_id)
  210. -> ParameterBlocks {
  211. auto params_id = SemIR::InstBlockId::Invalid;
  212. auto implicit_params_id = SemIR::InstBlockId::Invalid;
  213. MatchContext match(MatchKind::Callee);
  214. // TODO reserve space in bind_name_ids_
  215. if (implicit_param_patterns_id.is_valid()) {
  216. // We add work to the stack in reverse so that the results will be produced
  217. // in the original order.
  218. for (SemIR::InstId inst_id :
  219. llvm::reverse(context.inst_blocks().Get(implicit_param_patterns_id))) {
  220. match.AddWork(
  221. {.pattern_id = inst_id, .scrutinee_id = SemIR::InstId::Invalid});
  222. }
  223. while (match.HasWork()) {
  224. EmitPatternMatch(context, match, match.NextWorkItem());
  225. }
  226. implicit_params_id = match.ConsumeBindNames(context);
  227. }
  228. if (param_patterns_id.is_valid()) {
  229. for (SemIR::InstId inst_id :
  230. llvm::reverse(context.inst_blocks().Get(param_patterns_id))) {
  231. match.AddWork(
  232. {.pattern_id = inst_id, .scrutinee_id = SemIR::InstId::Invalid});
  233. }
  234. while (match.HasWork()) {
  235. EmitPatternMatch(context, match, match.NextWorkItem());
  236. }
  237. params_id = match.ConsumeBindNames(context);
  238. }
  239. return {.implicit_params_id = implicit_params_id, .params_id = params_id};
  240. }
  241. auto CallerPatternMatch(Context& context, SemIR::SpecificId specific_id,
  242. SemIR::InstId param, SemIR::InstId arg)
  243. -> SemIR::InstId {
  244. MatchContext match(MatchKind::Caller, specific_id);
  245. match.AddWork({.pattern_id = param, .scrutinee_id = arg});
  246. while (match.HasWork()) {
  247. EmitPatternMatch(context, match, match.NextWorkItem());
  248. }
  249. return match.ConsumeResult();
  250. }
  251. } // namespace Carbon::Check