pattern.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  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.h"
  5. #include "toolchain/base/kind_switch.h"
  6. #include "toolchain/check/control_flow.h"
  7. #include "toolchain/check/inst.h"
  8. #include "toolchain/check/return.h"
  9. #include "toolchain/check/type.h"
  10. #include "toolchain/sem_ir/inst.h"
  11. namespace Carbon::Check {
  12. auto BeginSubpattern(Context& context) -> void {
  13. context.inst_block_stack().Push();
  14. // TODO: This allocates an InstBlockId even in the case where the pattern has
  15. // no associated expression. Find a way to avoid this.
  16. context.region_stack().PushRegion(context.inst_block_stack().PeekOrAdd());
  17. }
  18. static auto PopSubpatternExpr(Context& context, SemIR::InstId result_id)
  19. -> SemIR::ExprRegionId {
  20. if (context.region_stack().PeekRegion().size() > 1) {
  21. // End the exit block with a branch to a successor block, whose contents
  22. // will be determined later.
  23. AddInst(context,
  24. SemIR::LocIdAndInst::NoLoc<SemIR::Branch>(
  25. {.target_id = context.inst_blocks().AddPlaceholder()}));
  26. } else {
  27. // This single-block region will be inserted as a SpliceBlock, so we don't
  28. // need control flow out of it.
  29. }
  30. auto block_id = context.inst_block_stack().Pop();
  31. CARBON_CHECK(block_id == context.region_stack().PeekRegion().back());
  32. // TODO: Is it possible to validate that this region is genuinely
  33. // single-entry, single-exit?
  34. return context.sem_ir().expr_regions().Add(
  35. {.block_ids = context.region_stack().PopRegion(),
  36. .result_id = result_id});
  37. }
  38. auto ConsumeSubpatternExpr(Context& context, SemIR::InstId result_id)
  39. -> SemIR::ExprRegionId {
  40. auto region_id = PopSubpatternExpr(context, result_id);
  41. // Push an empty, unreachable region so that we can later detect the region
  42. // has been consumed.
  43. context.region_stack().PushUnreachableRegion();
  44. return region_id;
  45. }
  46. auto EndEmptySubpattern(Context& context) -> void {
  47. if (!context.region_stack().PeekRegion().empty()) {
  48. CARBON_CHECK(context.inst_block_stack().PeekCurrentBlockContents().empty());
  49. auto block_id = context.inst_block_stack().Pop();
  50. CARBON_CHECK(block_id == context.region_stack().PeekRegion().back());
  51. CARBON_CHECK(context.region_stack().PeekRegion().size() == 1);
  52. }
  53. context.region_stack().PopAndDiscardRegion();
  54. }
  55. auto EndSubpattern(Context& context, NodeStack& node_stack) -> void {
  56. auto [node_id, maybe_expr_id] =
  57. node_stack.PopWithNodeIdIf<Parse::NodeCategory::Expr>();
  58. if (maybe_expr_id) {
  59. // We formed an expression, not a pattern, so convert it to an expression
  60. // pattern now.
  61. auto expr_region_id = PopSubpatternExpr(context, *maybe_expr_id);
  62. auto pattern_type_id =
  63. GetPatternType(context, context.insts().Get(*maybe_expr_id).type_id());
  64. node_stack.Push(node_id, AddInst<SemIR::ExprPattern>(
  65. context, node_id,
  66. {.type_id = pattern_type_id,
  67. .expr_region_id = expr_region_id}));
  68. } else {
  69. // The expression region should have been consumed when forming the pattern
  70. // instruction, so should now effectively be empty.
  71. EndEmptySubpattern(context);
  72. }
  73. }
  74. auto AddBindingEntityName(Context& context, SemIR::NameId name_id,
  75. SemIR::InstId form_id, bool is_unused,
  76. BindingPhase phase) -> SemIR::EntityNameId {
  77. SemIR::EntityName entity_name = {
  78. .name_id = name_id,
  79. .parent_scope_id = context.scope_stack().PeekNameScopeId(),
  80. .is_unused = is_unused || name_id == SemIR::NameId::Underscore};
  81. if (phase != BindingPhase::Runtime) {
  82. entity_name.bind_index_value =
  83. context.scope_stack().AddCompileTimeBinding().index;
  84. entity_name.is_template = phase == BindingPhase::Template;
  85. }
  86. entity_name.form_id = form_id;
  87. return context.entity_names().Add(entity_name);
  88. }
  89. auto AddBindingPattern(Context& context, SemIR::LocId name_loc,
  90. SemIR::ExprRegionId type_region_id,
  91. SemIR::AnyBindingPattern pattern) -> BindingPatternInfo {
  92. SemIR::InstKind bind_name_kind;
  93. switch (pattern.kind) {
  94. case SemIR::FormBindingPattern::Kind:
  95. bind_name_kind = SemIR::FormBinding::Kind;
  96. break;
  97. case SemIR::RefBindingPattern::Kind:
  98. bind_name_kind = SemIR::RefBinding::Kind;
  99. break;
  100. case SemIR::SymbolicBindingPattern::Kind:
  101. bind_name_kind = SemIR::SymbolicBinding::Kind;
  102. break;
  103. case SemIR::ValueBindingPattern::Kind:
  104. bind_name_kind = SemIR::ValueBinding::Kind;
  105. break;
  106. case SemIR::WrapperBindingPattern::Kind: {
  107. auto subpattern = context.insts().Get(pattern.subpattern_id);
  108. CARBON_KIND_SWITCH(subpattern) {
  109. case SemIR::FormParamPattern::Kind:
  110. bind_name_kind = SemIR::FormBinding::Kind;
  111. break;
  112. case SemIR::RefParamPattern::Kind:
  113. case SemIR::VarPattern::Kind:
  114. bind_name_kind = SemIR::RefBinding::Kind;
  115. break;
  116. case SemIR::ValueParamPattern::Kind:
  117. bind_name_kind = SemIR::ValueBinding::Kind;
  118. break;
  119. default:
  120. CARBON_FATAL("Unexpected subpattern kind for at_binding_pattern: {0}",
  121. subpattern);
  122. }
  123. break;
  124. }
  125. default:
  126. CARBON_FATAL("pattern_kind {0} is not a binding pattern kind",
  127. pattern.kind);
  128. }
  129. auto type_id = SemIR::ExtractScrutineeType(context.sem_ir(), pattern.type_id);
  130. auto bind_id = AddInstInNoBlock(
  131. context, SemIR::LocIdAndInst::RuntimeVerified(
  132. context.sem_ir(), name_loc,
  133. SemIR::AnyBinding{.kind = bind_name_kind,
  134. .type_id = type_id,
  135. .entity_name_id = pattern.entity_name_id,
  136. .value_id = SemIR::InstId::None}));
  137. auto binding_pattern_id =
  138. AddInst(context, SemIR::LocIdAndInst::RuntimeVerified(context.sem_ir(),
  139. name_loc, pattern));
  140. if (pattern.kind == SemIR::SymbolicBindingPattern::Kind) {
  141. context.scope_stack().PushCompileTimeBinding(bind_id);
  142. }
  143. bool inserted =
  144. context.bind_name_map()
  145. .Insert(binding_pattern_id, {.bind_name_id = bind_id,
  146. .type_expr_region_id = type_region_id})
  147. .is_inserted();
  148. CARBON_CHECK(inserted);
  149. return {.pattern_id = binding_pattern_id, .bind_id = bind_id};
  150. }
  151. // Returns a VarStorage inst for the given `var` pattern. If the pattern
  152. // is the body of a returned var, this reuses the return parameter, and
  153. // otherwise it adds a new inst.
  154. static auto GetOrAddVarStorage(Context& context, SemIR::InstId var_pattern_id,
  155. bool is_returned_var) -> SemIR::InstId {
  156. if (is_returned_var) {
  157. if (auto return_param_id =
  158. GetReturnedVarParam(context, GetCurrentFunctionForReturn(context));
  159. return_param_id.has_value()) {
  160. return return_param_id;
  161. }
  162. }
  163. auto pattern = context.insts().GetWithLocId(var_pattern_id);
  164. return AddInstWithCleanup(
  165. context, pattern.loc_id,
  166. SemIR::VarStorage{.type_id = ExtractScrutineeType(context.sem_ir(),
  167. pattern.inst.type_id()),
  168. .pattern_id = var_pattern_id});
  169. }
  170. auto AddPatternVarStorage(Context& context, SemIR::InstBlockId pattern_block_id,
  171. bool is_returned_var) -> void {
  172. // We need to emit the VarStorage insts early, because they may be output
  173. // arguments for the initializer. However, we can't emit them when we emit
  174. // the corresponding `AnyVarPattern`s because they're part of the pattern
  175. // match, not part of the pattern.
  176. // TODO: Find a way to do this without walking the whole pattern block.
  177. for (auto inst_id : context.inst_blocks().Get(pattern_block_id)) {
  178. if (context.insts().Is<SemIR::AnyVarPattern>(inst_id)) {
  179. context.var_storage_map().Insert(
  180. inst_id, GetOrAddVarStorage(context, inst_id, is_returned_var));
  181. }
  182. }
  183. }
  184. auto AddParamPattern(Context& context, SemIR::LocId loc_id,
  185. SemIR::NameId name_id,
  186. SemIR::ExprRegionId type_expr_region_id,
  187. SemIR::TypeId type_id, ParamPatternKind kind)
  188. -> SemIR::InstId {
  189. auto param_pattern_kind = [kind]() -> SemIR::InstKind {
  190. switch (kind) {
  191. case ParamPatternKind::Value:
  192. return SemIR::ValueParamPattern::Kind;
  193. case ParamPatternKind::Ref:
  194. return SemIR::RefParamPattern::Kind;
  195. case ParamPatternKind::Var:
  196. return SemIR::VarParamPattern::Kind;
  197. }
  198. }();
  199. auto entity_name_id = AddBindingEntityName(context, name_id,
  200. /*form_id=*/SemIR::InstId::None,
  201. /*is_unused=*/false,
  202. /*phase=*/BindingPhase::Runtime);
  203. auto pattern_type_id = GetPatternType(context, type_id);
  204. if (kind == ParamPatternKind::Var) {
  205. auto pattern_id = AddBindingPattern(context, loc_id, type_expr_region_id,
  206. {.kind = SemIR::RefBindingPattern::Kind,
  207. .type_id = pattern_type_id,
  208. .entity_name_id = entity_name_id,
  209. .subpattern_id = SemIR::InstId::None});
  210. return AddInst(context, SemIR::LocIdAndInst::RuntimeVerified(
  211. context.sem_ir(), loc_id,
  212. SemIR::VarParamPattern{
  213. .type_id = pattern_type_id,
  214. .subpattern_id = pattern_id.pattern_id}));
  215. } else {
  216. auto pattern_id = AddInst(
  217. context, SemIR::LocIdAndInst::RuntimeVerified(
  218. context.sem_ir(), loc_id,
  219. SemIR::AnyLeafParamPattern{.kind = param_pattern_kind,
  220. .type_id = pattern_type_id,
  221. .pretty_name_id = name_id}));
  222. return AddBindingPattern(context, loc_id, type_expr_region_id,
  223. {.kind = SemIR::WrapperBindingPattern::Kind,
  224. .type_id = GetPatternType(context, type_id),
  225. .entity_name_id = entity_name_id,
  226. .subpattern_id = pattern_id})
  227. .pattern_id;
  228. }
  229. }
  230. } // namespace Carbon::Check