handle_struct.cpp 6.4 KB

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