handle_choice.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  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/context.h"
  5. #include "toolchain/check/convert.h"
  6. #include "toolchain/check/decl_name_stack.h"
  7. #include "toolchain/check/eval.h"
  8. #include "toolchain/check/generic.h"
  9. #include "toolchain/check/handle.h"
  10. #include "toolchain/check/inst.h"
  11. #include "toolchain/check/literal.h"
  12. #include "toolchain/check/name_component.h"
  13. #include "toolchain/check/type.h"
  14. #include "toolchain/diagnostics/diagnostic.h"
  15. #include "toolchain/lex/token_kind.h"
  16. #include "toolchain/sem_ir/ids.h"
  17. #include "toolchain/sem_ir/inst.h"
  18. #include "toolchain/sem_ir/name_scope.h"
  19. #include "toolchain/sem_ir/typed_insts.h"
  20. namespace Carbon::Check {
  21. auto HandleParseNode(Context& context, Parse::ChoiceIntroducerId node_id)
  22. -> bool {
  23. // Create an instruction block to hold the instructions created as part of the
  24. // choice signature, such as generic parameters.
  25. context.inst_block_stack().Push();
  26. // Push the bracketing node.
  27. context.node_stack().Push(node_id);
  28. // The choice's name follows.
  29. context.decl_name_stack().PushScopeAndStartName();
  30. // There's no modifiers on a choice, but this informs how to typecheck any
  31. // generic binding pattern.
  32. context.decl_introducer_state_stack().Push<Lex::TokenKind::Choice>();
  33. // This choice is potentially generic.
  34. StartGenericDefinition(context);
  35. return true;
  36. }
  37. auto HandleParseNode(Context& context, Parse::ChoiceDefinitionStartId node_id)
  38. -> bool {
  39. auto name = PopNameComponent(context);
  40. auto name_context = context.decl_name_stack().FinishName(name);
  41. context.node_stack()
  42. .PopAndDiscardSoloNodeId<Parse::NodeKind::ChoiceIntroducer>();
  43. context.decl_introducer_state_stack().Pop<Lex::TokenKind::Choice>();
  44. auto decl_block_id = context.inst_block_stack().Pop();
  45. // Choices create a ClassId, since they ultimately turn into a class with
  46. // methods and some builtin impls.
  47. auto class_decl =
  48. SemIR::ClassDecl{.type_id = SemIR::TypeType::SingletonTypeId,
  49. .class_id = SemIR::ClassId::None,
  50. .decl_block_id = decl_block_id};
  51. auto class_decl_id =
  52. AddPlaceholderInst(context, SemIR::LocIdAndInst(node_id, class_decl));
  53. context.decl_name_stack().AddNameOrDiagnose(name_context, class_decl_id,
  54. SemIR::AccessKind::Public);
  55. // An inst block for the body of the choice.
  56. context.inst_block_stack().Push();
  57. auto body_block_id = context.inst_block_stack().PeekOrAdd();
  58. SemIR::Class class_info = {
  59. name_context.MakeEntityWithParamsBase(name, class_decl_id,
  60. /*is_extern=*/false,
  61. SemIR::LibraryNameId::None),
  62. {// `.self_type_id` depends on the ClassType, so is set below.
  63. .self_type_id = SemIR::TypeId::None,
  64. .inheritance_kind = SemIR::ClassFields::Final,
  65. // TODO: Handle the case where there's control flow in the alternatives.
  66. // For example:
  67. //
  68. // choice C {
  69. // Alt(x: if true then i32 else f64),
  70. // }
  71. //
  72. // We may need to track a list of instruction blocks here, as we do for a
  73. // function.
  74. .body_block_id = body_block_id}};
  75. // This call finishes the GenericDecl, after which we can use the `Self`
  76. // specific.
  77. class_info.generic_id = BuildGenericDecl(context, class_decl_id);
  78. auto self_specific_id =
  79. context.generics().GetSelfSpecific(class_info.generic_id);
  80. class_info.definition_id = class_decl_id;
  81. class_info.scope_id = context.name_scopes().Add(
  82. class_decl_id, SemIR::NameId::None, class_info.parent_scope_id);
  83. class_decl.class_id = context.classes().Add(class_info);
  84. if (class_info.has_parameters()) {
  85. class_decl.type_id = GetGenericClassType(
  86. context, class_decl.class_id, context.scope_stack().PeekSpecificId());
  87. }
  88. ReplaceInstBeforeConstantUse(context, class_decl_id, class_decl);
  89. // We had to construct the `ClassId` from `Class` in order to build the `Self`
  90. // type below. But it needs to be written back to the `Class` in the
  91. // ValueStore, not the local variable. This gives a mutable reference to the
  92. // `Class` in the ValueStore.
  93. SemIR::Class& mut_class = context.classes().Get(class_decl.class_id);
  94. // Build the `Self` type using the resulting type constant.
  95. auto self_type_id = context.types().GetTypeIdForTypeConstantId(
  96. TryEvalInst(context, SemIR::InstId::None,
  97. SemIR::ClassType{.type_id = SemIR::TypeType::SingletonTypeId,
  98. .class_id = class_decl.class_id,
  99. .specific_id = self_specific_id}));
  100. mut_class.self_type_id = self_type_id;
  101. // Enter the choice scope.
  102. context.scope_stack().Push(class_decl_id, class_info.scope_id,
  103. self_specific_id);
  104. // Checking the binding pattern for an alternative requires a non-empty stack.
  105. // We reuse the Choice token even though we're now checking an alternative
  106. // inside the Choice, since there's no better token to use.
  107. //
  108. // TODO: The token here is _not_ `Choice` though, we shouldn't need to use
  109. // that here. Either remove the need for a token or find a token (a new
  110. // introducer?) for the alternative to name.
  111. context.decl_introducer_state_stack().Push<Lex::TokenKind::Choice>();
  112. StartGenericDefinition(context);
  113. context.name_scopes().AddRequiredName(
  114. class_info.scope_id, SemIR::NameId::SelfType,
  115. context.types().GetInstId(self_type_id));
  116. // Mark the beginning of the choice body.
  117. context.node_stack().Push(node_id, class_decl.class_id);
  118. CARBON_CHECK(context.choice_deferred_bindings().empty(),
  119. "Alternatives left behind in choice_deferred_bindings: {0}",
  120. context.choice_deferred_bindings().size());
  121. return true;
  122. }
  123. static auto AddChoiceAlternative(Context& context, Parse::NodeId node_id)
  124. -> void {
  125. // Note, there is nothing like a ChoiceAlternativeIntroducer node, so no parse
  126. // node to pop here.
  127. auto name_component = PopNameComponent(context);
  128. if (name_component.param_patterns_id == SemIR::InstBlockId::Empty) {
  129. // Treat an empty parameter list the same as no parameter list.
  130. //
  131. // TODO: The current design suggests that we want Foo() to result in a
  132. // member function `ChoiceType.Foo()`, and `Foo` to result in a member
  133. // constant `ChoiceType.Foo`, but that only one of the two is allowed in a
  134. // single choice type. See
  135. // https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/sum_types.md#user-defined-sum-types.
  136. // For now they are not treated differently and both resolve to a member
  137. // constant.
  138. context.TODO(name_component.params_loc_id,
  139. "empty parameter list should make a member function");
  140. name_component.param_patterns_id = SemIR::InstBlockId::None;
  141. }
  142. if (name_component.param_patterns_id.has_value()) {
  143. context.TODO(name_component.params_loc_id,
  144. "choice alternatives with parameters are not yet supported");
  145. return;
  146. }
  147. context.choice_deferred_bindings().push_back({node_id, name_component});
  148. }
  149. // Info about the Choice type, used to construct each alternative member of the
  150. // class representing the Choice.
  151. struct ChoiceInfo {
  152. // The `Self` type.
  153. SemIR::TypeId self_type_id;
  154. // The scope of the class for adding the alternatives to.
  155. SemIR::NameScopeId name_scope_id;
  156. // A struct type with the same fields as `Self`. Used to construct `Self`.
  157. SemIR::TypeId self_struct_type_id;
  158. // The type of the discriminant value.
  159. SemIR::TypeId discriminant_type_id;
  160. int num_alternative_bits;
  161. };
  162. // Builds a `let` binding for an alternative without parameters as a member of
  163. // the resulting class for the Choice definition. If the alternative was `Alt`
  164. // then the binding will be like:
  165. // ```
  166. // let Alt: ChoiceType = <ChoiceType with Alt selected>;
  167. // ```
  168. static auto MakeLetBinding(Context& context, const ChoiceInfo& choice_info,
  169. int alternative_index,
  170. const Context::ChoiceDeferredBinding& binding)
  171. -> void {
  172. SemIR::InstId discriminant_value_id = [&] {
  173. if (choice_info.num_alternative_bits == 0) {
  174. return AddInst(context, SemIR::LocIdAndInst::UncheckedLoc(
  175. binding.node_id,
  176. SemIR::TupleLiteral{
  177. .type_id = GetTupleType(context, {}),
  178. .elements_id = SemIR::InstBlockId::Empty,
  179. }));
  180. } else {
  181. return MakeIntLiteral(context, binding.node_id,
  182. context.ints().Add(alternative_index));
  183. }
  184. }();
  185. discriminant_value_id =
  186. ConvertToValueOfType(context, binding.node_id, discriminant_value_id,
  187. choice_info.discriminant_type_id);
  188. auto self_value_id = ConvertToValueOfType(
  189. context, binding.node_id,
  190. AddInst(context, SemIR::LocIdAndInst::UncheckedLoc(
  191. binding.node_id,
  192. SemIR::StructLiteral{
  193. .type_id = choice_info.self_struct_type_id,
  194. .elements_id =
  195. [&] {
  196. context.inst_block_stack().Push();
  197. context.inst_block_stack().AddInstId(
  198. discriminant_value_id);
  199. return context.inst_block_stack().Pop();
  200. }(),
  201. })),
  202. choice_info.self_type_id);
  203. auto entity_name_id = context.entity_names().Add(
  204. {.name_id = binding.name_component.name_id,
  205. .parent_scope_id = choice_info.name_scope_id});
  206. auto bind_name_id = AddInst(
  207. context, SemIR::LocIdAndInst::UncheckedLoc(
  208. binding.node_id, SemIR::BindName{
  209. .type_id = choice_info.self_type_id,
  210. .entity_name_id = entity_name_id,
  211. .value_id = self_value_id,
  212. }));
  213. context.name_scopes()
  214. .Get(choice_info.name_scope_id)
  215. .AddRequired({.name_id = binding.name_component.name_id,
  216. .result = SemIR::ScopeLookupResult::MakeFound(
  217. bind_name_id, SemIR::AccessKind::Public)});
  218. }
  219. auto HandleParseNode(Context& context, Parse::ChoiceDefinitionId node_id)
  220. -> bool {
  221. // The last alternative may optionally not have a comma after it, in which
  222. // case we get here after the last alternative.
  223. if (!context.node_stack().PeekIs(Parse::NodeKind::ChoiceDefinitionStart)) {
  224. AddChoiceAlternative(context, node_id);
  225. }
  226. auto class_id =
  227. context.node_stack().Pop<Parse::NodeKind::ChoiceDefinitionStart>();
  228. int num_alternatives = context.choice_deferred_bindings().size();
  229. int num_alternative_bits = [&] {
  230. if (num_alternatives > 1) {
  231. return static_cast<int>(ceil(log2(num_alternatives)));
  232. } else {
  233. return 0;
  234. }
  235. }();
  236. SemIR::TypeId discriminant_type_id = [&] {
  237. if (num_alternative_bits == 0) {
  238. // Even though there's no bits needed, we add an empty field. We want to
  239. // prevent constructing the Choice from an empty struct literal instead of
  240. // going through an alternative. And in the case there is no alternative,
  241. // then there's no way to construct the Choice (which can be a useful
  242. // type).
  243. //
  244. // TODO: Find a way to produce a better diagnostic, and not require an
  245. // empty field.
  246. return GetTupleType(context, {});
  247. } else {
  248. return MakeIntType(context, node_id, SemIR::IntKind::Unsigned,
  249. context.ints().Add(num_alternative_bits));
  250. }
  251. }();
  252. llvm::SmallVector<SemIR::StructTypeField, 1> struct_type_fields;
  253. struct_type_fields.push_back({
  254. .name_id = SemIR::NameId::ChoiceDiscriminant,
  255. .type_id = discriminant_type_id,
  256. });
  257. auto fields_id =
  258. context.struct_type_fields().AddCanonical(struct_type_fields);
  259. auto choice_witness_id =
  260. AddInst(context, node_id,
  261. SemIR::CompleteTypeWitness{
  262. .type_id = GetSingletonType(
  263. context, SemIR::WitnessType::SingletonInstId),
  264. .object_repr_id = GetStructType(context, fields_id)});
  265. // Note: avoid storing a reference to the returned Class, since it may be
  266. // invalidated by other type constructions.
  267. context.classes().Get(class_id).complete_type_witness_id = choice_witness_id;
  268. auto self_type_id = context.classes().Get(class_id).self_type_id;
  269. auto name_scope_id = context.classes().Get(class_id).scope_id;
  270. auto self_struct_type_id = GetStructType(
  271. context, context.struct_type_fields().AddCanonical(struct_type_fields));
  272. for (auto [i, deferred_binding] :
  273. llvm::enumerate(context.choice_deferred_bindings())) {
  274. MakeLetBinding(context,
  275. ChoiceInfo{.self_type_id = self_type_id,
  276. .name_scope_id = name_scope_id,
  277. .self_struct_type_id = self_struct_type_id,
  278. .discriminant_type_id = discriminant_type_id,
  279. .num_alternative_bits = num_alternative_bits},
  280. i, deferred_binding);
  281. }
  282. // The scopes and blocks for the choice itself.
  283. context.inst_block_stack().Pop();
  284. context.decl_introducer_state_stack().Pop<Lex::TokenKind::Choice>();
  285. context.scope_stack().Pop();
  286. context.decl_name_stack().PopScope();
  287. FinishGenericDefinition(context, context.classes().Get(class_id).generic_id);
  288. context.choice_deferred_bindings().clear();
  289. return true;
  290. }
  291. auto HandleParseNode(Context& context,
  292. Parse::ChoiceAlternativeListCommaId node_id) -> bool {
  293. AddChoiceAlternative(context, node_id);
  294. return true;
  295. }
  296. } // namespace Carbon::Check