handle_struct.cpp 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  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 "common/map.h"
  5. #include "toolchain/check/context.h"
  6. #include "toolchain/check/convert.h"
  7. #include "toolchain/check/handle.h"
  8. #include "toolchain/check/type.h"
  9. #include "toolchain/diagnostics/format_providers.h"
  10. namespace Carbon::Check {
  11. auto HandleParseNode(Context& context, Parse::StructLiteralStartId node_id)
  12. -> bool {
  13. context.scope_stack().Push();
  14. context.node_stack().Push(node_id);
  15. context.struct_type_fields_stack().PushArray();
  16. context.param_and_arg_refs_stack().Push();
  17. return true;
  18. }
  19. auto HandleParseNode(Context& context, Parse::StructTypeLiteralStartId node_id)
  20. -> bool {
  21. context.scope_stack().Push();
  22. context.node_stack().Push(node_id);
  23. context.struct_type_fields_stack().PushArray();
  24. return true;
  25. }
  26. auto HandleParseNode(Context& context,
  27. Parse::StructFieldDesignatorId /*node_id*/) -> bool {
  28. // This leaves the designated name on top because the `.` isn't interesting.
  29. CARBON_CHECK(context.node_stack().PeekIs<SemIR::NameId>());
  30. return true;
  31. }
  32. auto HandleParseNode(Context& context, Parse::StructLiteralCommaId /*node_id*/)
  33. -> bool {
  34. context.param_and_arg_refs_stack().ApplyComma();
  35. return true;
  36. }
  37. auto HandleParseNode(Context& /*context*/,
  38. Parse::StructTypeLiteralCommaId /*node_id*/) -> bool {
  39. return true;
  40. }
  41. auto HandleParseNode(Context& context, Parse::StructLiteralFieldId node_id)
  42. -> bool {
  43. auto value_inst_id = context.node_stack().PopExpr();
  44. // Get the name while leaving it on the stack.
  45. auto name_id = context.node_stack().Peek<Parse::NodeCategory::MemberName>();
  46. // Store the name for the type.
  47. auto value_type_id = context.insts().Get(value_inst_id).type_id();
  48. context.struct_type_fields_stack().AppendToTop(
  49. {.name_id = name_id, .type_id = value_type_id});
  50. // Push the value back on the stack as an argument.
  51. context.node_stack().Push(node_id, value_inst_id);
  52. return true;
  53. }
  54. auto HandleParseNode(Context& context,
  55. Parse::StructTypeLiteralFieldId /*node_id*/) -> bool {
  56. auto [type_node, type_id] = context.node_stack().PopExprWithNodeId();
  57. SemIR::TypeId cast_type_id = ExprAsType(context, type_node, type_id).type_id;
  58. // Get the name while leaving it on the stack.
  59. auto name_id = context.node_stack().Peek<Parse::NodeCategory::MemberName>();
  60. context.struct_type_fields_stack().AppendToTop(
  61. {.name_id = name_id, .type_id = cast_type_id});
  62. return true;
  63. }
  64. // Diagnoses and returns true if there's a duplicate name.
  65. static auto DiagnoseDuplicateNames(
  66. Context& context, llvm::ArrayRef<Parse::NodeId> field_name_nodes,
  67. llvm::ArrayRef<SemIR::StructTypeField> fields, bool is_struct_type_literal)
  68. -> bool {
  69. Map<SemIR::NameId, Parse::NodeId> names;
  70. for (auto [field_name_node, field] : llvm::zip(field_name_nodes, fields)) {
  71. auto result = names.Insert(field.name_id, field_name_node);
  72. if (!result.is_inserted()) {
  73. CARBON_DIAGNOSTIC(StructNameDuplicate, Error,
  74. "duplicated field name `{1}` in "
  75. "{0:struct type literal|struct literal}",
  76. BoolAsSelect, SemIR::NameId);
  77. CARBON_DIAGNOSTIC(StructNamePrevious, Note,
  78. "field with the same name here");
  79. context.emitter()
  80. .Build(result.value(), StructNameDuplicate, is_struct_type_literal,
  81. field.name_id)
  82. .Note(field_name_node, StructNamePrevious)
  83. .Emit();
  84. return true;
  85. }
  86. }
  87. return false;
  88. }
  89. // Pops the names of each field from the stack. These will have been left while
  90. // handling struct fields.
  91. static auto PopFieldNameNodes(Context& context, size_t field_count)
  92. -> llvm::SmallVector<Parse::NodeId> {
  93. llvm::SmallVector<Parse::NodeId> nodes;
  94. nodes.reserve(field_count);
  95. while (true) {
  96. auto [name_node, _] =
  97. context.node_stack().PopWithNodeIdIf<Parse::NodeCategory::MemberName>();
  98. if (name_node.has_value()) {
  99. nodes.push_back(name_node);
  100. } else {
  101. break;
  102. }
  103. }
  104. CARBON_CHECK(nodes.size() == field_count, "Found {0} names, expected {1}",
  105. nodes.size(), field_count);
  106. return nodes;
  107. }
  108. auto HandleParseNode(Context& context, Parse::StructLiteralId node_id) -> bool {
  109. if (!context.node_stack().PeekIs(Parse::NodeCategory::MemberName)) {
  110. // Remove the last parameter from the node stack before collecting names.
  111. context.param_and_arg_refs_stack().EndNoPop(
  112. Parse::NodeKind::StructLiteralStart);
  113. }
  114. auto fields = context.struct_type_fields_stack().PeekArray();
  115. llvm::SmallVector<Parse::NodeId> field_name_nodes =
  116. PopFieldNameNodes(context, fields.size());
  117. auto elements_id = context.param_and_arg_refs_stack().EndAndPop(
  118. Parse::NodeKind::StructLiteralStart);
  119. context.scope_stack().Pop();
  120. context.node_stack()
  121. .PopAndDiscardSoloNodeId<Parse::NodeKind::StructLiteralStart>();
  122. if (DiagnoseDuplicateNames(context, field_name_nodes, fields,
  123. /*is_struct_type_literal=*/false)) {
  124. context.node_stack().Push(node_id, SemIR::ErrorInst::SingletonInstId);
  125. } else {
  126. auto type_id = GetStructType(
  127. context, context.struct_type_fields().AddCanonical(fields));
  128. auto value_id = context.AddInst<SemIR::StructLiteral>(
  129. node_id, {.type_id = type_id, .elements_id = elements_id});
  130. context.node_stack().Push(node_id, value_id);
  131. }
  132. context.struct_type_fields_stack().PopArray();
  133. return true;
  134. }
  135. auto HandleParseNode(Context& context, Parse::StructTypeLiteralId node_id)
  136. -> bool {
  137. auto fields = context.struct_type_fields_stack().PeekArray();
  138. llvm::SmallVector<Parse::NodeId> field_name_nodes =
  139. PopFieldNameNodes(context, fields.size());
  140. context.scope_stack().Pop();
  141. context.node_stack()
  142. .PopAndDiscardSoloNodeId<Parse::NodeKind::StructTypeLiteralStart>();
  143. if (DiagnoseDuplicateNames(context, field_name_nodes, fields,
  144. /*is_struct_type_literal=*/true)) {
  145. context.node_stack().Push(node_id, SemIR::ErrorInst::SingletonInstId);
  146. } else {
  147. auto fields_id = context.struct_type_fields().AddCanonical(fields);
  148. context.AddInstAndPush<SemIR::StructType>(
  149. node_id,
  150. {.type_id = SemIR::TypeType::SingletonTypeId, .fields_id = fields_id});
  151. }
  152. context.struct_type_fields_stack().PopArray();
  153. return true;
  154. }
  155. } // namespace Carbon::Check