[llvm] [llvm][YAML] Support aliases (anchors) in YAMLIO (PR #146371)
via llvm-commits
llvm-commits at lists.llvm.org
Tue Apr 28 02:18:50 PDT 2026
https://github.com/sizn-sc updated https://github.com/llvm/llvm-project/pull/146371
>From 4b9d32a7f5b6bcab509441ffa579bee4014aba87 Mon Sep 17 00:00:00 2001
From: Sergei Zimmerman <sergei.zimmerman at syntacore.com>
Date: Mon, 30 Jun 2025 15:38:21 +0000
Subject: [PATCH 1/2] [llvm][YAML] Support aliases (anchors) in YAMLIO
This change makes it possible to use YAML anchors [1], [2]
with YAMLTraits. All of the necessary parser machinery already
exists, so the only change that is necessary is to wire it
up to YAMLTraits.
This is done by keeping track of all `Anchor` -> `HNode *`
mappings and reusing those when an `AliasNode` is encountered.
In accordance with the spec [2], anchors do not have to be unique and
refer to the last occurrence in the serialization.
Example usage:
```yaml
foo: &a 42
bar: *a
```
The above would be deserialized as:
```yaml
foo: 42
bar: 42
```
Note that aliases are a serialization detail and can be discarded during
composition into a Representation Graph (`HNode` hierarchy).
[1]: https://yaml.org/spec/1.2.2/#692-node-anchors
[2]: https://yaml.org/spec/1.2.2/#3222-anchors-and-aliases
---
llvm/include/llvm/Support/YAMLTraits.h | 3 +
llvm/lib/Support/YAMLTraits.cpp | 34 ++++++++-
llvm/unittests/Support/YAMLIOTest.cpp | 101 +++++++++++++++++++++++++
3 files changed, 136 insertions(+), 2 deletions(-)
diff --git a/llvm/include/llvm/Support/YAMLTraits.h b/llvm/include/llvm/Support/YAMLTraits.h
index b53b28dd00fd1..7ba43176c6e79 100644
--- a/llvm/include/llvm/Support/YAMLTraits.h
+++ b/llvm/include/llvm/Support/YAMLTraits.h
@@ -11,6 +11,7 @@
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/BitVector.h"
+#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringMap.h"
@@ -1422,6 +1423,7 @@ class LLVM_ABI Input : public IO {
std::vector<HNode *> Entries;
};
+ void saveAliasHNode(Node *node, HNode *hnode);
Input::HNode *createHNodes(Node *node);
void setError(HNode *hnode, const Twine &message);
void setError(Node *node, const Twine &message);
@@ -1460,6 +1462,7 @@ class LLVM_ABI Input : public IO {
HNode *CurrentNode = nullptr;
bool ScalarMatchFound = false;
bool AllowUnknownKeys = false;
+ DenseMap<StringRef, HNode *> AliasMap;
};
///
diff --git a/llvm/lib/Support/YAMLTraits.cpp b/llvm/lib/Support/YAMLTraits.cpp
index d24e95e1e7487..0c29ca0b2a3ed 100644
--- a/llvm/lib/Support/YAMLTraits.cpp
+++ b/llvm/lib/Support/YAMLTraits.cpp
@@ -397,6 +397,17 @@ void Input::releaseHNodeBuffers() {
MapHNodeAllocator.DestroyAll();
}
+void Input::saveAliasHNode(Node *N, HNode *HN) {
+ if (auto Anchor = N->getAnchor(); !Anchor.empty())
+ // YAML 1.2.2 - 3.2.2.2. Anchors and Aliases:
+ //
+ // An alias event refers to the most recent event in the serialization
+ // having the specified anchor. Therefore, anchors need not be unique within
+ // a serialization. In addition, an anchor need not have an alias node
+ // referring to it.
+ AliasMap[Anchor] = HN;
+}
+
Input::HNode *Input::createHNodes(Node *N) {
SmallString<128> StringStorage;
switch (N->getType()) {
@@ -407,12 +418,17 @@ Input::HNode *Input::createHNodes(Node *N) {
// Copy string to permanent storage
KeyStr = StringStorage.str().copy(StringAllocator);
}
- return new (ScalarHNodeAllocator.Allocate()) ScalarHNode(N, KeyStr);
+ auto *SHNode = new (ScalarHNodeAllocator.Allocate()) ScalarHNode(N, KeyStr);
+ saveAliasHNode(SN, SHNode);
+ return SHNode;
}
case Node::NK_BlockScalar: {
BlockScalarNode *BSN = dyn_cast<BlockScalarNode>(N);
StringRef ValueCopy = BSN->getValue().copy(StringAllocator);
- return new (ScalarHNodeAllocator.Allocate()) ScalarHNode(N, ValueCopy);
+ auto *BSHNode =
+ new (ScalarHNodeAllocator.Allocate()) ScalarHNode(N, ValueCopy);
+ saveAliasHNode(BSN, BSHNode);
+ return BSHNode;
}
case Node::NK_Sequence: {
SequenceNode *SQ = dyn_cast<SequenceNode>(N);
@@ -423,6 +439,7 @@ Input::HNode *Input::createHNodes(Node *N) {
break;
SQHNode->Entries.push_back(Entry);
}
+ saveAliasHNode(SQ, SQHNode);
return SQHNode;
}
case Node::NK_Mapping: {
@@ -456,10 +473,23 @@ Input::HNode *Input::createHNodes(Node *N) {
mapHNode->Mapping[KeyStr] =
std::make_pair(std::move(ValueHNode), KeyNode->getSourceRange());
}
+ saveAliasHNode(Map, mapHNode);
return std::move(mapHNode);
}
case Node::NK_Null:
+ // TODO: Anchor is not set for NullNode in the parser. Update the parser to
+ // faithfully preserve anchors.
return new (EmptyHNodeAllocator.Allocate()) EmptyHNode(N);
+ case Node::NK_Alias: {
+ AliasNode *AN = dyn_cast<AliasNode>(N);
+ auto AliasName = AN->getName();
+ auto AHN = AliasMap.find(AliasName);
+ if (AHN == AliasMap.end()) {
+ setError(AN, Twine("undefined alias '" + AliasName + "'"));
+ return nullptr;
+ }
+ return AHN->second;
+ }
default:
setError(N, "unknown node kind");
return nullptr;
diff --git a/llvm/unittests/Support/YAMLIOTest.cpp b/llvm/unittests/Support/YAMLIOTest.cpp
index ff5744bc7fa54..78e19c5ff4f2c 100644
--- a/llvm/unittests/Support/YAMLIOTest.cpp
+++ b/llvm/unittests/Support/YAMLIOTest.cpp
@@ -2889,6 +2889,107 @@ TEST(YAMLIO, TestEmptySequenceWrite) {
}
}
+TEST(YAMLIO, TestScalarAlias) {
+ FooBar doc;
+ {
+ Input yin("---\nfoo: &three 3\nbar: *three\n...\n");
+ yin >> doc;
+
+ EXPECT_FALSE(yin.error());
+ EXPECT_EQ(doc.foo, 3);
+ EXPECT_EQ(doc.bar, 3);
+ }
+
+ {
+ auto testDiagnostic = [](const llvm::SMDiagnostic &Error, void *) {
+ EXPECT_EQ(Error.getMessage(), "undefined alias 'unknown'");
+ };
+ Input yin("---\nfoo: &anchor 3\nbar: *unknown\n...\n", nullptr,
+ testDiagnostic);
+ yin >> doc;
+ EXPECT_TRUE(!!yin.error());
+ }
+}
+
+TEST(YAMLIO, TestBlockScalarAliases) {
+ std::vector<std::string> doc;
+ {
+ Input yin(R"(---
+ - &alias |
+ some
+ multi
+ line
+ string
+ - *alias
+ ...
+ )");
+
+ yin >> doc;
+
+ EXPECT_FALSE(yin.error());
+ ASSERT_EQ(doc.size(), 2ul);
+ EXPECT_EQ(doc[0], "some\nmulti\nline\nstring\n");
+ EXPECT_EQ(doc[1], doc[0]);
+ }
+}
+
+TEST(YAMLIO, TestMapAlias) {
+ {
+ FooBarContainer cont;
+
+ Input yin(R"(---
+ fbs:
+ - foo: 13
+ bar: &anchor 42
+ - &seq_elmt
+ foo: &anchor 19
+ bar: *anchor
+ - *seq_elmt
+ ...
+ )");
+ yin >> cont;
+ EXPECT_FALSE(yin.error());
+
+ auto &fbs = cont.fbs;
+ EXPECT_EQ(fbs.size(), 3ul);
+ EXPECT_EQ(fbs[0].foo, 13);
+ EXPECT_EQ(fbs[0].bar, 42);
+ EXPECT_EQ(fbs[1].foo, 19);
+ EXPECT_EQ(fbs[1].bar, 19);
+ EXPECT_EQ(fbs[2].foo, fbs[1].foo);
+ EXPECT_EQ(fbs[2].bar, fbs[1].bar);
+ }
+
+ {
+ FooBarMapMap cont;
+ Input yin(R"(---
+ fbm:
+ one: &forty_two
+ foo: 42
+ bar: 42
+ forty_two: *forty_two
+ precedence: &forty_two
+ foo: 13
+ bar: 42
+ not_forty_two: *forty_two
+ ...
+ )");
+ auto &fbm = cont.fbm;
+ yin >> cont;
+ EXPECT_FALSE(yin.error());
+
+ EXPECT_EQ(fbm.size(), 4ul);
+ EXPECT_EQ(fbm["one"].foo, 42);
+ EXPECT_EQ(fbm["one"].bar, 42);
+ EXPECT_EQ(fbm["forty_two"].foo, 42);
+ EXPECT_EQ(fbm["forty_two"].bar, 42);
+ EXPECT_EQ(fbm["precedence"].foo, 13);
+ EXPECT_EQ(fbm["precedence"].bar, 42);
+ EXPECT_EQ(fbm["not_forty_two"].foo, 13);
+ EXPECT_EQ(fbm["not_forty_two"].bar, 42);
+ }
+}
+
static void TestEscaped(llvm::StringRef Input, llvm::StringRef Expected) {
std::string out;
llvm::raw_string_ostream ostr(out);
>From 81d5945ebd11b5be991290be9ed778ed328dcd50 Mon Sep 17 00:00:00 2001
From: Sergei Zimmerman <sergei.zimmerman at syntacore.com>
Date: Tue, 21 Apr 2026 11:50:57 +0000
Subject: [PATCH 2/2] [llvm][YAML] Clean up saveAliasHNode
Addresses review comment https://github.com/llvm/llvm-project/pull/146371#discussion_r3115961233
---
llvm/lib/Support/YAMLTraits.cpp | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/llvm/lib/Support/YAMLTraits.cpp b/llvm/lib/Support/YAMLTraits.cpp
index 0c29ca0b2a3ed..622806423635f 100644
--- a/llvm/lib/Support/YAMLTraits.cpp
+++ b/llvm/lib/Support/YAMLTraits.cpp
@@ -398,7 +398,8 @@ void Input::releaseHNodeBuffers() {
}
void Input::saveAliasHNode(Node *N, HNode *HN) {
- if (auto Anchor = N->getAnchor(); !Anchor.empty())
+ StringRef Anchor = N->getAnchor();
+ if (!Anchor.empty())
// YAML 1.2.2 - 3.2.2.2. Anchors and Aliases:
//
// An alias event refers to the most recent event in the serialization
More information about the llvm-commits
mailing list