semantics_ir_formatter.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768
  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/semantics/semantics_ir_formatter.h"
  5. #include "llvm/ADT/Sequence.h"
  6. #include "llvm/ADT/StringExtras.h"
  7. #include "llvm/Support/SaveAndRestore.h"
  8. #include "toolchain/lexer/tokenized_buffer.h"
  9. #include "toolchain/parser/parse_tree.h"
  10. namespace Carbon::SemIR {
  11. namespace {
  12. // Assigns names to nodes, blocks, and scopes in the Semantics IR.
  13. //
  14. // TODOs / future work ideas:
  15. // - Add a documentation file for the textual format and link to the
  16. // naming section here.
  17. // - Consider representing literals as just `literal` in the IR and using the
  18. // type to distinguish.
  19. class NodeNamer {
  20. public:
  21. // int32_t matches the input value size.
  22. // NOLINTNEXTLINE(performance-enum-size)
  23. enum class ScopeIndex : int32_t {
  24. None = -1,
  25. Package = 0,
  26. };
  27. static_assert(sizeof(ScopeIndex) == sizeof(FunctionId));
  28. NodeNamer(const TokenizedBuffer& tokenized_buffer,
  29. const ParseTree& parse_tree, const File& semantics_ir)
  30. : tokenized_buffer_(tokenized_buffer),
  31. parse_tree_(parse_tree),
  32. semantics_ir_(semantics_ir) {
  33. nodes.resize(semantics_ir.nodes_size());
  34. labels.resize(semantics_ir.node_blocks_size());
  35. scopes.resize(1 + semantics_ir.functions_size());
  36. // Build the package scope.
  37. GetScopeInfo(ScopeIndex::Package).name =
  38. globals.AddNameUnchecked("package");
  39. CollectNamesInBlock(ScopeIndex::Package, semantics_ir.top_node_block_id());
  40. // Build each function scope.
  41. for (int i : llvm::seq(semantics_ir.functions_size())) {
  42. auto fn_id = FunctionId(i);
  43. auto fn_scope = GetScopeFor(fn_id);
  44. const auto& fn = semantics_ir.GetFunction(fn_id);
  45. // TODO: Provide a location for the function for use as a
  46. // disambiguator.
  47. auto fn_loc = ParseTree::Node::Invalid;
  48. GetScopeInfo(fn_scope).name = globals.AllocateName(
  49. *this, fn_loc,
  50. fn.name_id.is_valid() ? semantics_ir.GetString(fn.name_id).str()
  51. : "");
  52. CollectNamesInBlock(fn_scope, fn.param_refs_id);
  53. if (fn.return_slot_id.is_valid()) {
  54. nodes[fn.return_slot_id.index] = {
  55. fn_scope,
  56. GetScopeInfo(fn_scope).nodes.AllocateName(
  57. *this, semantics_ir.GetNode(fn.return_slot_id).parse_node(),
  58. "return")};
  59. }
  60. if (!fn.body_block_ids.empty()) {
  61. AddBlockLabel(fn_scope, fn.body_block_ids.front(), "entry", fn_loc);
  62. }
  63. for (auto block_id : fn.body_block_ids) {
  64. CollectNamesInBlock(fn_scope, block_id);
  65. }
  66. for (auto block_id : fn.body_block_ids) {
  67. AddBlockLabel(fn_scope, block_id);
  68. }
  69. }
  70. }
  71. // Returns the scope index corresponding to a function.
  72. auto GetScopeFor(FunctionId fn_id) -> ScopeIndex {
  73. return static_cast<ScopeIndex>(fn_id.index + 1);
  74. }
  75. // Returns the IR name to use for a function.
  76. auto GetNameFor(FunctionId fn_id) -> llvm::StringRef {
  77. if (!fn_id.is_valid()) {
  78. return "invalid";
  79. }
  80. return GetScopeInfo(GetScopeFor(fn_id)).name.str();
  81. }
  82. // Returns the IR name to use for a node, when referenced from a given scope.
  83. auto GetNameFor(ScopeIndex scope_idx, NodeId node_id) -> std::string {
  84. if (!node_id.is_valid()) {
  85. return "invalid";
  86. }
  87. // Check for a builtin.
  88. if (node_id.index < BuiltinKind::ValidCount) {
  89. return BuiltinKind::FromInt(node_id.index).label().str();
  90. }
  91. auto& [node_scope, node_name] = nodes[node_id.index];
  92. if (!node_name) {
  93. // This should not happen in valid IR.
  94. return "<unexpected noderef " + llvm::itostr(node_id.index) + ">";
  95. }
  96. if (node_scope == scope_idx) {
  97. return node_name.str().str();
  98. }
  99. return (GetScopeInfo(node_scope).name.str() + "." + node_name.str()).str();
  100. }
  101. // Returns the IR name to use for a label, when referenced from a given scope.
  102. auto GetLabelFor(ScopeIndex scope_idx, NodeBlockId block_id) -> std::string {
  103. if (!block_id.is_valid()) {
  104. return "!invalid";
  105. }
  106. auto& [label_scope, label_name] = labels[block_id.index];
  107. if (!label_name) {
  108. // This should not happen in valid IR.
  109. return "<unexpected nodeblockref " + llvm::itostr(block_id.index) + ">";
  110. }
  111. if (label_scope == scope_idx) {
  112. return label_name.str().str();
  113. }
  114. return (GetScopeInfo(label_scope).name.str() + "." + label_name.str())
  115. .str();
  116. }
  117. private:
  118. // A space in which unique names can be allocated.
  119. struct Namespace {
  120. // A result of a name lookup.
  121. struct NameResult;
  122. // A name in a namespace, which might be redirected to refer to another name
  123. // for disambiguation purposes.
  124. class Name {
  125. public:
  126. Name() : value_(nullptr) {}
  127. explicit Name(llvm::StringMapIterator<NameResult> it) : value_(&*it) {}
  128. explicit operator bool() const { return value_; }
  129. auto str() const -> llvm::StringRef {
  130. llvm::StringMapEntry<NameResult>* value = value_;
  131. CARBON_CHECK(value) << "cannot print a null name";
  132. while (value->second.ambiguous && value->second.fallback) {
  133. value = value->second.fallback.value_;
  134. }
  135. return value->first();
  136. }
  137. auto SetFallback(Name name) -> void { value_->second.fallback = name; }
  138. auto SetAmbiguous() -> void { value_->second.ambiguous = true; }
  139. private:
  140. llvm::StringMapEntry<NameResult>* value_;
  141. };
  142. struct NameResult {
  143. bool ambiguous = false;
  144. Name fallback = Name();
  145. };
  146. llvm::StringRef prefix;
  147. llvm::StringMap<NameResult> allocated = {};
  148. int unnamed_count = 0;
  149. auto AddNameUnchecked(llvm::StringRef name) -> Name {
  150. return Name(allocated.insert({name, NameResult()}).first);
  151. }
  152. auto AllocateName(const NodeNamer& namer, ParseTree::Node node,
  153. std::string name = "") -> Name {
  154. // The best (shortest) name for this node so far, and the current name
  155. // for it.
  156. Name best;
  157. Name current;
  158. // Add `name` as a name for this entity.
  159. auto add_name = [&](bool mark_ambiguous = true) {
  160. auto [it, added] = allocated.insert({name, NameResult()});
  161. Name new_name = Name(it);
  162. if (!added) {
  163. if (mark_ambiguous) {
  164. // This name was allocated for a different node. Mark it as
  165. // ambiguous and keep looking for a name for this node.
  166. new_name.SetAmbiguous();
  167. }
  168. } else {
  169. if (!best) {
  170. best = new_name;
  171. } else {
  172. CARBON_CHECK(current);
  173. current.SetFallback(new_name);
  174. }
  175. current = new_name;
  176. }
  177. return added;
  178. };
  179. // All names start with the prefix.
  180. name.insert(0, prefix);
  181. // Use the given name if it's available and not just the prefix.
  182. if (name.size() > prefix.size()) {
  183. add_name();
  184. }
  185. // Append location information to try to disambiguate.
  186. if (node.is_valid()) {
  187. auto token = namer.parse_tree_.node_token(node);
  188. llvm::raw_string_ostream(name)
  189. << ".loc" << namer.tokenized_buffer_.GetLineNumber(token);
  190. add_name();
  191. llvm::raw_string_ostream(name)
  192. << "_" << namer.tokenized_buffer_.GetColumnNumber(token);
  193. add_name();
  194. }
  195. // Append numbers until we find an available name.
  196. name += ".";
  197. auto name_size_without_counter = name.size();
  198. for (int counter = 1;; ++counter) {
  199. name.resize(name_size_without_counter);
  200. llvm::raw_string_ostream(name) << counter;
  201. if (add_name(/*mark_ambiguous=*/false)) {
  202. return best;
  203. }
  204. }
  205. }
  206. };
  207. // A named scope that contains named entities.
  208. struct Scope {
  209. Namespace::Name name;
  210. Namespace nodes = {.prefix = "%"};
  211. Namespace labels = {.prefix = "!"};
  212. };
  213. auto GetScopeInfo(ScopeIndex scope_idx) -> Scope& {
  214. return scopes[static_cast<int>(scope_idx)];
  215. }
  216. auto AddBlockLabel(ScopeIndex scope_idx, NodeBlockId block_id,
  217. std::string name = "",
  218. ParseTree::Node parse_node = ParseTree::Node::Invalid)
  219. -> void {
  220. if (!block_id.is_valid() || labels[block_id.index].second) {
  221. return;
  222. }
  223. if (parse_node == ParseTree::Node::Invalid) {
  224. if (const auto& block = semantics_ir_.GetNodeBlock(block_id);
  225. !block.empty()) {
  226. parse_node = semantics_ir_.GetNode(block.front()).parse_node();
  227. }
  228. }
  229. labels[block_id.index] = {scope_idx,
  230. GetScopeInfo(scope_idx).labels.AllocateName(
  231. *this, parse_node, std::move(name))};
  232. }
  233. // Finds and adds a suitable block label for the given semantics node that
  234. // represents some kind of branch.
  235. auto AddBlockLabel(ScopeIndex scope_idx, NodeBlockId block_id, Node node)
  236. -> void {
  237. llvm::StringRef name;
  238. switch (parse_tree_.node_kind(node.parse_node())) {
  239. case ParseNodeKind::IfExpressionIf:
  240. switch (node.kind()) {
  241. case NodeKind::BranchIf:
  242. name = "if.expr.then";
  243. break;
  244. case NodeKind::Branch:
  245. name = "if.expr.else";
  246. break;
  247. case NodeKind::BranchWithArg:
  248. name = "if.expr.result";
  249. break;
  250. default:
  251. break;
  252. }
  253. break;
  254. case ParseNodeKind::IfCondition:
  255. switch (node.kind()) {
  256. case NodeKind::BranchIf:
  257. name = "if.then";
  258. break;
  259. case NodeKind::Branch:
  260. name = "if.else";
  261. break;
  262. default:
  263. break;
  264. }
  265. break;
  266. case ParseNodeKind::IfStatement:
  267. name = "if.done";
  268. break;
  269. case ParseNodeKind::ShortCircuitOperand: {
  270. bool is_rhs = node.kind() == NodeKind::BranchIf;
  271. bool is_and = tokenized_buffer_.GetKind(parse_tree_.node_token(
  272. node.parse_node())) == TokenKind::And;
  273. name = is_and ? (is_rhs ? "and.rhs" : "and.result")
  274. : (is_rhs ? "or.rhs" : "or.result");
  275. break;
  276. }
  277. default:
  278. break;
  279. }
  280. AddBlockLabel(scope_idx, block_id, name.str(), node.parse_node());
  281. }
  282. auto CollectNamesInBlock(ScopeIndex scope_idx, NodeBlockId block_id) -> void {
  283. if (!block_id.is_valid()) {
  284. return;
  285. }
  286. Scope& scope = GetScopeInfo(scope_idx);
  287. // Use bound names where available. Otherwise, assign a backup name.
  288. for (auto node_id : semantics_ir_.GetNodeBlock(block_id)) {
  289. if (!node_id.is_valid()) {
  290. continue;
  291. }
  292. auto node = semantics_ir_.GetNode(node_id);
  293. switch (node.kind()) {
  294. case NodeKind::Branch: {
  295. auto dest_id = node.GetAsBranch();
  296. AddBlockLabel(scope_idx, dest_id, node);
  297. break;
  298. }
  299. case NodeKind::BranchIf: {
  300. auto [dest_id, cond_id] = node.GetAsBranchIf();
  301. AddBlockLabel(scope_idx, dest_id, node);
  302. break;
  303. }
  304. case NodeKind::BranchWithArg: {
  305. auto [dest_id, arg_id] = node.GetAsBranchWithArg();
  306. AddBlockLabel(scope_idx, dest_id, node);
  307. break;
  308. }
  309. case NodeKind::Parameter: {
  310. auto name_id = node.GetAsParameter();
  311. nodes[node_id.index] = {
  312. scope_idx,
  313. scope.nodes.AllocateName(*this, node.parse_node(),
  314. semantics_ir_.GetString(name_id).str())};
  315. break;
  316. }
  317. case NodeKind::VarStorage: {
  318. // TODO: Eventually this name will be optional, and we'll want to
  319. // provide something like `var` as a default. However, that's not
  320. // possible right now so cannot be tested.
  321. auto name_id = node.GetAsVarStorage();
  322. nodes[node_id.index] = {
  323. scope_idx,
  324. scope.nodes.AllocateName(*this, node.parse_node(),
  325. semantics_ir_.GetString(name_id).str())};
  326. break;
  327. }
  328. default: {
  329. // Sequentially number all remaining values.
  330. if (node.kind().value_kind() != NodeValueKind::None) {
  331. nodes[node_id.index] = {
  332. scope_idx, scope.nodes.AllocateName(*this, node.parse_node())};
  333. }
  334. break;
  335. }
  336. }
  337. }
  338. }
  339. const TokenizedBuffer& tokenized_buffer_;
  340. const ParseTree& parse_tree_;
  341. const File& semantics_ir_;
  342. Namespace globals = {.prefix = "@"};
  343. std::vector<std::pair<ScopeIndex, Namespace::Name>> nodes;
  344. std::vector<std::pair<ScopeIndex, Namespace::Name>> labels;
  345. std::vector<Scope> scopes;
  346. };
  347. } // namespace
  348. // Formatter for printing textual Semantics IR.
  349. class Formatter {
  350. public:
  351. explicit Formatter(const TokenizedBuffer& tokenized_buffer,
  352. const ParseTree& parse_tree, const File& semantics_ir,
  353. llvm::raw_ostream& out)
  354. : semantics_ir_(semantics_ir),
  355. out_(out),
  356. node_namer_(tokenized_buffer, parse_tree, semantics_ir) {}
  357. auto Format() -> void {
  358. // TODO: Include information from the package declaration, once we fully
  359. // support it.
  360. out_ << "package {\n";
  361. // TODO: Handle the case where there are multiple top-level node blocks.
  362. // For example, there may be branching in the initializer of a global or a
  363. // type expression.
  364. if (auto block_id = semantics_ir_.top_node_block_id();
  365. block_id.is_valid()) {
  366. llvm::SaveAndRestore package_scope(scope_,
  367. NodeNamer::ScopeIndex::Package);
  368. FormatCodeBlock(block_id);
  369. }
  370. out_ << "}\n";
  371. for (int i : llvm::seq(semantics_ir_.functions_size())) {
  372. FormatFunction(FunctionId(i));
  373. }
  374. }
  375. auto FormatFunction(FunctionId id) -> void {
  376. const Function& fn = semantics_ir_.GetFunction(id);
  377. out_ << "\nfn ";
  378. FormatFunctionName(id);
  379. out_ << "(";
  380. llvm::SaveAndRestore function_scope(scope_, node_namer_.GetScopeFor(id));
  381. llvm::ListSeparator sep;
  382. for (const NodeId param_id : semantics_ir_.GetNodeBlock(fn.param_refs_id)) {
  383. out_ << sep;
  384. if (!param_id.is_valid()) {
  385. out_ << "invalid";
  386. continue;
  387. }
  388. FormatNodeName(param_id);
  389. out_ << ": ";
  390. FormatType(semantics_ir_.GetNode(param_id).type_id());
  391. }
  392. out_ << ")";
  393. if (fn.return_type_id.is_valid()) {
  394. out_ << " -> ";
  395. if (fn.return_slot_id.is_valid()) {
  396. FormatNodeName(fn.return_slot_id);
  397. out_ << ": ";
  398. }
  399. FormatType(fn.return_type_id);
  400. }
  401. if (!fn.body_block_ids.empty()) {
  402. out_ << " {";
  403. for (auto block_id : fn.body_block_ids) {
  404. out_ << "\n";
  405. FormatLabel(block_id);
  406. out_ << ":\n";
  407. FormatCodeBlock(block_id);
  408. }
  409. out_ << "}\n";
  410. } else {
  411. out_ << ";\n";
  412. }
  413. }
  414. auto FormatCodeBlock(NodeBlockId block_id) -> void {
  415. if (!block_id.is_valid()) {
  416. return;
  417. }
  418. for (const NodeId node_id : semantics_ir_.GetNodeBlock(block_id)) {
  419. FormatInstruction(node_id);
  420. }
  421. }
  422. auto FormatInstruction(NodeId node_id) -> void {
  423. if (!node_id.is_valid()) {
  424. out_ << " " << NodeKind::Invalid.ir_name() << "\n";
  425. return;
  426. }
  427. FormatInstruction(node_id, semantics_ir_.GetNode(node_id));
  428. }
  429. auto FormatInstruction(NodeId node_id, Node node) -> void {
  430. // clang warns on unhandled enum values; clang-tidy is incorrect here.
  431. // NOLINTNEXTLINE(bugprone-switch-missing-default-case)
  432. switch (node.kind()) {
  433. #define CARBON_SEMANTICS_NODE_KIND(Name) \
  434. case NodeKind::Name: \
  435. FormatInstruction<Node::Name>(node_id, node); \
  436. break;
  437. #include "toolchain/semantics/semantics_node_kind.def"
  438. }
  439. }
  440. template <typename Kind>
  441. auto FormatInstruction(NodeId node_id, Node node) -> void {
  442. out_ << " ";
  443. FormatInstructionLHS(node_id, node);
  444. out_ << node.kind().ir_name();
  445. FormatInstructionRHS<Kind>(node);
  446. out_ << "\n";
  447. }
  448. auto FormatInstructionLHS(NodeId node_id, Node node) -> void {
  449. switch (node.kind().value_kind()) {
  450. case NodeValueKind::Typed:
  451. FormatNodeName(node_id);
  452. out_ << ": ";
  453. FormatType(node.type_id());
  454. out_ << " = ";
  455. break;
  456. case NodeValueKind::Untyped:
  457. FormatNodeName(node_id);
  458. out_ << " = ";
  459. break;
  460. case NodeValueKind::None:
  461. break;
  462. }
  463. }
  464. template <typename Kind>
  465. auto FormatInstructionRHS(Node node) -> void {
  466. // By default, an instruction has a comma-separated argument list.
  467. FormatArgs(Kind::Get(node));
  468. }
  469. template <>
  470. auto FormatInstructionRHS<Node::BlockArg>(Node node) -> void {
  471. out_ << " ";
  472. FormatLabel(node.GetAsBlockArg());
  473. }
  474. template <>
  475. auto FormatInstruction<Node::BranchIf>(NodeId /*node_id*/, Node node)
  476. -> void {
  477. if (!in_terminator_sequence) {
  478. out_ << " ";
  479. }
  480. auto [label_id, cond_id] = node.GetAsBranchIf();
  481. out_ << "if ";
  482. FormatNodeName(cond_id);
  483. out_ << " " << NodeKind::Branch.ir_name() << " ";
  484. FormatLabel(label_id);
  485. out_ << " else ";
  486. in_terminator_sequence = true;
  487. }
  488. template <>
  489. auto FormatInstruction<Node::BranchWithArg>(NodeId /*node_id*/, Node node)
  490. -> void {
  491. if (!in_terminator_sequence) {
  492. out_ << " ";
  493. }
  494. auto [label_id, arg_id] = node.GetAsBranchWithArg();
  495. out_ << NodeKind::BranchWithArg.ir_name() << " ";
  496. FormatLabel(label_id);
  497. out_ << "(";
  498. FormatNodeName(arg_id);
  499. out_ << ")\n";
  500. in_terminator_sequence = false;
  501. }
  502. template <>
  503. auto FormatInstruction<Node::Branch>(NodeId /*node_id*/, Node node) -> void {
  504. if (!in_terminator_sequence) {
  505. out_ << " ";
  506. }
  507. out_ << NodeKind::Branch.ir_name() << " ";
  508. FormatLabel(node.GetAsBranch());
  509. out_ << "\n";
  510. in_terminator_sequence = false;
  511. }
  512. template <>
  513. auto FormatInstructionRHS<Node::Call>(Node node) -> void {
  514. out_ << " ";
  515. auto [args_id, callee_id] = node.GetAsCall();
  516. FormatArg(callee_id);
  517. llvm::ArrayRef<NodeId> args = semantics_ir_.GetNodeBlock(args_id);
  518. bool has_return_slot =
  519. semantics_ir_.GetFunction(callee_id).return_slot_id.is_valid();
  520. NodeId return_slot_id = NodeId::Invalid;
  521. if (has_return_slot) {
  522. return_slot_id = args.back();
  523. args = args.drop_back();
  524. }
  525. llvm::ListSeparator sep;
  526. out_ << '(';
  527. for (auto node_id : args) {
  528. out_ << sep;
  529. FormatArg(node_id);
  530. }
  531. out_ << ')';
  532. if (has_return_slot) {
  533. out_ << " to ";
  534. FormatArg(return_slot_id);
  535. }
  536. }
  537. template <>
  538. auto FormatInstructionRHS<Node::CrossReference>(Node node) -> void {
  539. // TODO: Figure out a way to make this meaningful. We'll need some way to
  540. // name cross-reference IRs, perhaps by the node ID of the import?
  541. auto [xref_id, node_id] = node.GetAsCrossReference();
  542. out_ << " " << xref_id << "." << node_id;
  543. }
  544. // StructTypeFields are formatted as part of their StructType.
  545. template <>
  546. auto FormatInstruction<Node::StructTypeField>(NodeId /*node_id*/,
  547. Node /*node*/) -> void {}
  548. template <>
  549. auto FormatInstructionRHS<Node::StructType>(Node node) -> void {
  550. out_ << " {";
  551. llvm::ListSeparator sep;
  552. for (auto field_id : semantics_ir_.GetNodeBlock(node.GetAsStructType())) {
  553. out_ << sep << ".";
  554. auto [field_name_id, field_type_id] =
  555. semantics_ir_.GetNode(field_id).GetAsStructTypeField();
  556. FormatString(field_name_id);
  557. out_ << ": ";
  558. FormatType(field_type_id);
  559. }
  560. out_ << "}";
  561. }
  562. auto FormatArgs(Node::NoArgs /*unused*/) -> void {}
  563. template <typename Arg1>
  564. auto FormatArgs(Arg1 arg) -> void {
  565. out_ << ' ';
  566. FormatArg(arg);
  567. }
  568. template <typename Arg1, typename Arg2>
  569. auto FormatArgs(std::pair<Arg1, Arg2> args) -> void {
  570. out_ << ' ';
  571. FormatArg(args.first);
  572. out_ << ",";
  573. FormatArgs(args.second);
  574. }
  575. auto FormatArg(BoolValue v) -> void { out_ << v; }
  576. auto FormatArg(BuiltinKind kind) -> void { out_ << kind.label(); }
  577. auto FormatArg(FunctionId id) -> void { FormatFunctionName(id); }
  578. auto FormatArg(IntegerLiteralId id) -> void {
  579. out_ << semantics_ir_.GetIntegerLiteral(id);
  580. }
  581. auto FormatArg(MemberIndex index) -> void { out_ << index; }
  582. // TODO: Should we be printing scopes inline, or should we have a separate
  583. // step to print them like we do for functions?
  584. auto FormatArg(NameScopeId id) -> void {
  585. // Name scopes aren't kept in any particular order. Sort the entries before
  586. // we print them for stability and consistency.
  587. std::vector<std::pair<NodeId, StringId>> entries;
  588. for (auto [name_id, node_id] : semantics_ir_.GetNameScope(id)) {
  589. entries.push_back({node_id, name_id});
  590. }
  591. llvm::sort(entries,
  592. [](auto a, auto b) { return a.first.index < b.first.index; });
  593. out_ << '{';
  594. llvm::ListSeparator sep;
  595. for (auto [node_id, name_id] : entries) {
  596. out_ << sep << ".";
  597. FormatString(name_id);
  598. out_ << " = ";
  599. FormatNodeName(node_id);
  600. }
  601. out_ << '}';
  602. }
  603. auto FormatArg(NodeId id) -> void { FormatNodeName(id); }
  604. auto FormatArg(NodeBlockId id) -> void {
  605. out_ << '(';
  606. llvm::ListSeparator sep;
  607. for (auto node_id : semantics_ir_.GetNodeBlock(id)) {
  608. out_ << sep;
  609. FormatArg(node_id);
  610. }
  611. out_ << ')';
  612. }
  613. auto FormatArg(RealLiteralId id) -> void {
  614. // TODO: Format with a `.` when the exponent is near zero.
  615. const auto& real = semantics_ir_.GetRealLiteral(id);
  616. out_ << real.mantissa << (real.is_decimal ? 'e' : 'p') << real.exponent;
  617. }
  618. auto FormatArg(StringId id) -> void {
  619. out_ << '"';
  620. out_.write_escaped(semantics_ir_.GetString(id), /*UseHexEscapes=*/true);
  621. out_ << '"';
  622. }
  623. auto FormatArg(TypeId id) -> void { FormatType(id); }
  624. auto FormatArg(TypeBlockId id) -> void {
  625. out_ << '(';
  626. llvm::ListSeparator sep;
  627. for (auto type_id : semantics_ir_.GetTypeBlock(id)) {
  628. out_ << sep;
  629. FormatArg(type_id);
  630. }
  631. out_ << ')';
  632. }
  633. auto FormatNodeName(NodeId id) -> void {
  634. out_ << node_namer_.GetNameFor(scope_, id);
  635. }
  636. auto FormatLabel(NodeBlockId id) -> void {
  637. out_ << node_namer_.GetLabelFor(scope_, id);
  638. }
  639. auto FormatString(StringId id) -> void {
  640. out_ << semantics_ir_.GetString(id);
  641. }
  642. auto FormatFunctionName(FunctionId id) -> void {
  643. out_ << node_namer_.GetNameFor(id);
  644. }
  645. auto FormatType(TypeId id) -> void {
  646. if (!id.is_valid()) {
  647. out_ << "invalid";
  648. } else {
  649. out_ << semantics_ir_.StringifyType(id, /*in_type_context=*/true);
  650. }
  651. }
  652. private:
  653. const File& semantics_ir_;
  654. llvm::raw_ostream& out_;
  655. NodeNamer node_namer_;
  656. NodeNamer::ScopeIndex scope_ = NodeNamer::ScopeIndex::None;
  657. bool in_terminator_sequence = false;
  658. };
  659. auto FormatFile(const TokenizedBuffer& tokenized_buffer,
  660. const ParseTree& parse_tree, const File& semantics_ir,
  661. llvm::raw_ostream& out) -> void {
  662. Formatter(tokenized_buffer, parse_tree, semantics_ir, out).Format();
  663. }
  664. } // namespace Carbon::SemIR