handle_struct.cpp 6.6 KB

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