pattern_match.cpp 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  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. : result_(SemIR::InstId::Invalid),
  40. kind_(kind),
  41. callee_specific_id_(callee_specific_id) {}
  42. // Returns whether there are any work items to process.
  43. auto HasWork() const -> bool {
  44. return !stack_.empty() && !result_.is_valid();
  45. }
  46. // Adds a work item to the stack. Cannot be called after Finish().
  47. auto AddWork(WorkItem work_item) -> void {
  48. CARBON_CHECK(!result_.is_valid());
  49. stack_.push_back(work_item);
  50. }
  51. // Returns the next work item to process.
  52. auto NextWorkItem() -> WorkItem { return stack_.pop_back_val(); }
  53. // Sets the result of this pattern matching operation. Must not be called
  54. // when there is still pending work, except to report an error.
  55. auto Finish(SemIR::InstId result) -> void {
  56. CARBON_CHECK(!HasWork() || result == SemIR::InstId::BuiltinError);
  57. result_ = result;
  58. }
  59. auto result() const -> SemIR::InstId { return result_; }
  60. auto kind() const -> MatchKind { return kind_; }
  61. auto callee_specific_id() const -> SemIR::SpecificId {
  62. return callee_specific_id_;
  63. }
  64. private:
  65. llvm::SmallVector<WorkItem> stack_;
  66. SemIR::InstId result_;
  67. MatchKind kind_;
  68. SemIR::SpecificId callee_specific_id_;
  69. };
  70. // Emits the pattern-match insts necessary to match the pattern inst
  71. // `entry.pattern_id` against the scrutinee value `entry.scrutinee_id`,
  72. // and adds to `match` any work necessary to traverse into its subpatterns.
  73. // This behavior is contingent on the kind of match being performed, as
  74. // indicated by `match.kind()`. For example, when performing a callee
  75. // pattern match, this does not emit insts for patterns on the caller side.
  76. // However, it still traverses into subpatterns if any of their descendants
  77. // might emit insts.
  78. // TODO: Require that `entry.scrutinee_id` is valid if and only if insts should
  79. // be emitted, once we start emitting `Param` insts in the `ParamPattern` case.
  80. auto EmitPatternMatch(Context& context, MatchContext& match,
  81. MatchContext::WorkItem entry) -> void {
  82. auto pattern = context.insts().GetWithLocId(entry.pattern_id);
  83. CARBON_KIND_SWITCH(pattern.inst) {
  84. case SemIR::BindingPattern::Kind:
  85. case SemIR::SymbolicBindingPattern::Kind: {
  86. CARBON_CHECK(match.kind() == MatchKind::Callee);
  87. auto binding_pattern = pattern.inst.As<SemIR::AnyBindingPattern>();
  88. auto bind_name = context.insts().GetAs<SemIR::AnyBindName>(
  89. binding_pattern.bind_name_id);
  90. // bind_name.value_id holds the corresponding `Param` inst.
  91. // TODO: emit the `Param` inst as part of processing `ParamPattern`.
  92. context.inst_block_stack().AddInstId(bind_name.value_id);
  93. context.inst_block_stack().AddInstId(binding_pattern.bind_name_id);
  94. match.Finish(binding_pattern.bind_name_id);
  95. break;
  96. }
  97. case CARBON_KIND(SemIR::AddrPattern addr_pattern): {
  98. if (match.kind() == MatchKind::Callee) {
  99. // We're emitting pattern-match IR for the callee, but we're still on
  100. // the caller side of the pattern, so we traverse without emitting any
  101. // insts.
  102. match.AddWork({.pattern_id = addr_pattern.inner_id,
  103. .scrutinee_id = SemIR::InstId::Invalid});
  104. break;
  105. }
  106. CARBON_CHECK(entry.scrutinee_id.is_valid());
  107. auto scrutinee_ref_id =
  108. ConvertToValueOrRefExpr(context, entry.scrutinee_id);
  109. switch (SemIR::GetExprCategory(context.sem_ir(), scrutinee_ref_id)) {
  110. case SemIR::ExprCategory::Error:
  111. case SemIR::ExprCategory::DurableRef:
  112. case SemIR::ExprCategory::EphemeralRef:
  113. break;
  114. default:
  115. CARBON_DIAGNOSTIC(AddrSelfIsNonRef, Error,
  116. "`addr self` method cannot be invoked on a value");
  117. context.emitter().Emit(
  118. TokenOnly(context.insts().GetLocId(entry.scrutinee_id)),
  119. AddrSelfIsNonRef);
  120. match.Finish(SemIR::InstId::BuiltinError);
  121. return;
  122. }
  123. auto scrutinee_ref = context.insts().Get(scrutinee_ref_id);
  124. auto new_scrutinee = context.AddInst<SemIR::AddrOf>(
  125. context.insts().GetLocId(scrutinee_ref_id),
  126. {.type_id = context.GetPointerType(scrutinee_ref.type_id()),
  127. .lvalue_id = scrutinee_ref_id});
  128. match.AddWork(
  129. {.pattern_id = addr_pattern.inner_id, .scrutinee_id = new_scrutinee});
  130. break;
  131. }
  132. case CARBON_KIND(SemIR::ParamPattern param_pattern): {
  133. switch (match.kind()) {
  134. case MatchKind::Caller: {
  135. CARBON_CHECK(entry.scrutinee_id.is_valid());
  136. match.Finish(ConvertToValueOfType(
  137. context, context.insts().GetLocId(entry.scrutinee_id),
  138. entry.scrutinee_id,
  139. SemIR::GetTypeInSpecific(context.sem_ir(),
  140. match.callee_specific_id(),
  141. param_pattern.type_id)));
  142. // Do not traverse farther, because the caller side of the pattern
  143. // ends here.
  144. break;
  145. }
  146. case MatchKind::Callee: {
  147. match.AddWork({.pattern_id = param_pattern.subpattern_id,
  148. .scrutinee_id = SemIR::InstId::Invalid});
  149. break;
  150. }
  151. }
  152. break;
  153. }
  154. default: {
  155. CARBON_FATAL("Inst kind not handled: {0}", pattern.inst.kind());
  156. }
  157. }
  158. }
  159. auto ProcessParameters(Context& context,
  160. llvm::ArrayRef<SemIR::InstId> pattern_ids)
  161. -> SemIR::InstBlockId {
  162. std::vector<SemIR::InstId> inner_param_insts;
  163. inner_param_insts.reserve(pattern_ids.size());
  164. for (SemIR::InstId inst_id : pattern_ids) {
  165. MatchContext match(MatchKind::Callee);
  166. match.AddWork(
  167. {.pattern_id = inst_id, .scrutinee_id = SemIR::InstId::Invalid});
  168. while (match.HasWork()) {
  169. EmitPatternMatch(context, match, match.NextWorkItem());
  170. }
  171. // TODO: Should we break instead, if match.result_ is an error?
  172. inner_param_insts.push_back(match.result());
  173. }
  174. return context.inst_blocks().Add(inner_param_insts);
  175. }
  176. } // namespace
  177. auto CalleePatternMatch(Context& context,
  178. SemIR::InstBlockId implicit_param_patterns_id,
  179. SemIR::InstBlockId param_patterns_id)
  180. -> ParameterBlocks {
  181. auto params_id = SemIR::InstBlockId::Invalid;
  182. auto implicit_params_id = SemIR::InstBlockId::Invalid;
  183. if (implicit_param_patterns_id.is_valid()) {
  184. implicit_params_id = ProcessParameters(
  185. context, context.inst_blocks().Get(implicit_param_patterns_id));
  186. }
  187. if (param_patterns_id.is_valid()) {
  188. params_id = ProcessParameters(context,
  189. context.inst_blocks().Get(param_patterns_id));
  190. }
  191. return {.implicit_params_id = implicit_params_id, .params_id = params_id};
  192. }
  193. auto CallerPatternMatch(Context& context, SemIR::SpecificId specific_id,
  194. SemIR::InstId param, SemIR::InstId arg)
  195. -> SemIR::InstId {
  196. MatchContext match(MatchKind::Caller, specific_id);
  197. match.AddWork({.pattern_id = param, .scrutinee_id = arg});
  198. while (match.HasWork()) {
  199. EmitPatternMatch(context, match, match.NextWorkItem());
  200. }
  201. return match.result();
  202. }
  203. } // namespace Carbon::Check