pattern.cpp 13 KB

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