[Mlir-commits] [mlir] [mlir] Add next streaming manipulator to MLIR diagnostic infrastructure (PR #217804)
lonely eagle
llvmlistbot at llvm.org
Wed Sep 23 06:56:21 PDT 2026
https://github.com/linuxlonelyeagle updated https://github.com/llvm/llvm-project/pull/217804
>From a2008f0f221557832cdb770706a5af30ddc47648 Mon Sep 17 00:00:00 2001
From: linuxlonelyeagle <2020382038 at qq.com>
Date: Wed, 9 Sep 2026 10:43:07 +0000
Subject: [PATCH 1/3] add next to InFlightDiagnostic.
---
mlir/include/mlir/IR/Diagnostics.h | 35 +++++++++++++++-
mlir/lib/IR/Diagnostics.cpp | 51 ++++++++++++++++++++---
mlir/test/IR/diagnostic-handler-next.mlir | 8 ++++
mlir/test/lib/IR/CMakeLists.txt | 1 +
mlir/test/lib/IR/TestDiagnosticsNext.cpp | 49 ++++++++++++++++++++++
mlir/tools/mlir-opt/mlir-opt.cpp | 2 +
6 files changed, 138 insertions(+), 8 deletions(-)
create mode 100644 mlir/test/IR/diagnostic-handler-next.mlir
create mode 100644 mlir/test/lib/IR/TestDiagnosticsNext.cpp
diff --git a/mlir/include/mlir/IR/Diagnostics.h b/mlir/include/mlir/IR/Diagnostics.h
index 3b8fb46b06a484..8f802c131f3ceb 100644
--- a/mlir/include/mlir/IR/Diagnostics.h
+++ b/mlir/include/mlir/IR/Diagnostics.h
@@ -249,12 +249,18 @@ class Diagnostic {
return *this;
}
- /// Outputs this diagnostic to a stream.
- void print(raw_ostream &os) const;
+ /// Outputs this diagnostic to a stream. `ChunkIdx` specifies which chunk to
+ /// print to `os`. If empty, all arguments are printed to `os`.
+ void print(raw_ostream &os,
+ std::optional<int64_t> chunkIdx = std::nullopt) const;
/// Converts the diagnostic to a string.
std::string str() const;
+ /// Converts the diagnostic to a vector of strings, where each element
+ /// represents a chunk.
+ SmallVector<std::string> strs() const;
+
/// Attaches a note to this diagnostic. A new location may be optionally
/// provided, if not, then the location defaults to the one specified for this
/// diagnostic. Notes may not be attached to other notes.
@@ -288,6 +294,9 @@ class Diagnostic {
/// Returns the current list of diagnostic metadata.
SmallVectorImpl<DiagnosticArgument> &getMetadata() { return metadata; }
+ /// Finalizes the current group of arguments and starts a new chunk.
+ void checkpointArguments();
+
private:
Diagnostic(const Diagnostic &rhs) = delete;
Diagnostic &operator=(const Diagnostic &rhs) = delete;
@@ -305,6 +314,9 @@ class Diagnostic {
/// liveness of non-constant strings used in diagnostics.
std::vector<std::unique_ptr<char[]>> strings;
+ /// Boundary indices in `arguments` that separate different chunks.
+ SmallVector<size_t, 2> chunkSizes;
+
/// A list of attached notes.
NoteVector notes;
@@ -348,6 +360,17 @@ class InFlightDiagnostic {
return std::move(append(std::forward<Arg>(arg)));
}
+ /// Enables stream-style manipulators to be chained into the diagnostic.
+ InFlightDiagnostic &
+ operator<<(InFlightDiagnostic &(*manip)(InFlightDiagnostic &)) & {
+ return manip(*this);
+ }
+
+ InFlightDiagnostic &&
+ operator<<(InFlightDiagnostic &(*manip)(InFlightDiagnostic &)) && {
+ return std::move(manip(*this));
+ }
+
/// Append arguments to the diagnostic.
template <typename... Args>
InFlightDiagnostic &append(Args &&...args) & {
@@ -408,6 +431,7 @@ class InFlightDiagnostic {
// Allow access to the constructor.
friend DiagnosticEngine;
+ friend inline InFlightDiagnostic &next(InFlightDiagnostic &diag);
/// The engine that this diagnostic is to report to.
DiagnosticEngine *owner = nullptr;
@@ -416,6 +440,13 @@ class InFlightDiagnostic {
std::optional<Diagnostic> impl;
};
+/// A stream manipulator that checkpoints the current messages, and allowing a
+/// single InFlightDiagnostic to output multiple lines.
+inline InFlightDiagnostic &next(InFlightDiagnostic &diag) {
+ diag.impl->checkpointArguments();
+ return diag;
+}
+
//===----------------------------------------------------------------------===//
// DiagnosticEngine
//===----------------------------------------------------------------------===//
diff --git a/mlir/lib/IR/Diagnostics.cpp b/mlir/lib/IR/Diagnostics.cpp
index 5caf826c84bdd0..a10f581f38522b 100644
--- a/mlir/lib/IR/Diagnostics.cpp
+++ b/mlir/lib/IR/Diagnostics.cpp
@@ -161,9 +161,22 @@ Diagnostic &Diagnostic::operator<<(Value val) {
return *this << str;
}
-/// Outputs this diagnostic to a stream.
-void Diagnostic::print(raw_ostream &os) const {
- for (auto &arg : getArguments())
+/// Outputs this diagnostic to a stream. `ChunkIdx` specifies which chunk to
+/// print to `os`. If empty, all arguments are printed to `os`.
+void Diagnostic::print(raw_ostream &os, std::optional<int64_t> chunkIdx) const {
+ if (!chunkIdx.has_value()) {
+ for (auto &arg : getArguments())
+ arg.print(os);
+ return;
+ }
+
+ assert(0 <= chunkIdx && chunkIdx <= chunkSizes.size());
+ size_t argumentStart = *chunkIdx - 1 >= 0 ? chunkSizes[*chunkIdx - 1] : 0;
+ size_t argumentEnd = *chunkIdx == static_cast<int64_t>(chunkSizes.size())
+ ? arguments.size()
+ : chunkSizes[*chunkIdx];
+ for (auto &arg :
+ getArguments().slice(argumentStart, argumentEnd - argumentStart))
arg.print(os);
}
@@ -175,6 +188,19 @@ std::string Diagnostic::str() const {
return str;
}
+/// Converts the diagnostic to a vector of strings, where each element
+/// represents a chunk.
+SmallVector<std::string> Diagnostic::strs() const {
+ SmallVector<std::string, 2> strs;
+ for (size_t i = 0, e = chunkSizes.size(); i <= e; ++i) {
+ std::string str;
+ llvm::raw_string_ostream os(str);
+ print(os, i);
+ strs.push_back(str);
+ }
+ return strs;
+}
+
/// Attaches a note to this diagnostic. A new location may be optionally
/// provided, if not, then the location defaults to the one specified for this
/// diagnostic. Notes may not be attached to other notes.
@@ -196,6 +222,16 @@ Diagnostic &Diagnostic::attachNote(std::optional<Location> noteLoc) {
/// Allow a diagnostic to be converted to 'failure'.
Diagnostic::operator LogicalResult() const { return failure(); }
+/// Finalizes the current group of arguments and starts a new chunk.
+void Diagnostic::checkpointArguments() {
+ size_t size = arguments.size();
+ if (size == 0)
+ return;
+ if (!chunkSizes.empty() && chunkSizes.back() == arguments.size())
+ return;
+ chunkSizes.push_back(arguments.size());
+}
+
//===----------------------------------------------------------------------===//
// InFlightDiagnostic
//===----------------------------------------------------------------------===//
@@ -505,11 +541,13 @@ void SourceMgrDiagnosticHandler::emitDiagnostic(Diagnostic &diag) {
// If the location stack is empty, use the initial location.
if (locationStack.empty()) {
- emitDiagnostic(diag.getLocation(), diag.str(), diag.getSeverity());
+ for (const std::string &str : diag.strs())
+ emitDiagnostic(diag.getLocation(), str, diag.getSeverity());
// Otherwise, use the location stack.
} else {
- emitDiagnostic(locationStack.front().first, diag.str(), diag.getSeverity());
+ for (const std::string &str : diag.strs())
+ emitDiagnostic(locationStack.front().first, str, diag.getSeverity());
for (auto &it : llvm::drop_begin(locationStack))
emitDiagnostic(it.first, it.second, DiagnosticSeverity::Note);
}
@@ -879,7 +917,8 @@ void SourceMgrDiagnosticVerifierHandler::registerInContext(MLIRContext *ctx) {
/// Process a single diagnostic.
void SourceMgrDiagnosticVerifierHandler::process(Diagnostic &diag) {
- return process(diag.getLocation(), diag.str(), diag.getSeverity());
+ for (const std::string &str : diag.strs())
+ process(diag.getLocation(), str, diag.getSeverity());
}
/// Process a diagnostic at a certain location.
diff --git a/mlir/test/IR/diagnostic-handler-next.mlir b/mlir/test/IR/diagnostic-handler-next.mlir
new file mode 100644
index 00000000000000..9f7b6853ef9f2f
--- /dev/null
+++ b/mlir/test/IR/diagnostic-handler-next.mlir
@@ -0,0 +1,8 @@
+// RUN: mlir-opt %s -pass-pipeline="builtin.module(func.func(test-diagnostic-next))" -verify-diagnostics
+
+// expected-remark @+3 {{test1}}
+// expected-remark @+2 {{test3}}
+// expected-remark @+1 {{test2}}
+func.func @test() {
+ return
+}
diff --git a/mlir/test/lib/IR/CMakeLists.txt b/mlir/test/lib/IR/CMakeLists.txt
index 1abcfc77d2d9b0..8310edec228c15 100644
--- a/mlir/test/lib/IR/CMakeLists.txt
+++ b/mlir/test/lib/IR/CMakeLists.txt
@@ -7,6 +7,7 @@ add_mlir_library(MLIRTestIR
TestClone.cpp
TestDiagnostics.cpp
TestDiagnosticsMetadata.cpp
+ TestDiagnosticsNext.cpp
TestDominance.cpp
TestFunc.cpp
TestInterfaces.cpp
diff --git a/mlir/test/lib/IR/TestDiagnosticsNext.cpp b/mlir/test/lib/IR/TestDiagnosticsNext.cpp
new file mode 100644
index 00000000000000..5cab0864c4f764
--- /dev/null
+++ b/mlir/test/lib/IR/TestDiagnosticsNext.cpp
@@ -0,0 +1,49 @@
+//===- TestDiagnosticsNext.cpp - Test Diagnostic Utilities ----------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains test passes for the next diagnostic manipulator.
+//
+//===----------------------------------------------------------------------===//
+
+#include "mlir/IR/BuiltinOps.h"
+#include "mlir/IR/Diagnostics.h"
+#include "mlir/IR/SymbolTable.h"
+#include "mlir/Pass/Pass.h"
+#include "llvm/ADT/StringRef.h"
+
+using namespace mlir;
+
+namespace {
+struct TestDiagnosticsNextPass
+ : public PassWrapper<TestDiagnosticsNextPass,
+ InterfacePass<SymbolOpInterface>> {
+ MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(TestDiagnosticsNextPass)
+
+ StringRef getArgument() const final { return "test-diagnostic-next"; }
+ StringRef getDescription() const final {
+ return "Test diagnostic next support.";
+ }
+
+ void runOnOperation() override {
+ getOperation()->walk([](SymbolOpInterface op) {
+ StringRef opName = op.getNameAttr();
+ InFlightDiagnostic diag = op->emitRemark(opName) << "1" << next;
+ diag << opName << "2" << next << opName << "3";
+ });
+ }
+};
+
+} // namespace
+
+namespace mlir {
+namespace test {
+void registerTestDiagnosticsNextPass() {
+ PassRegistration<TestDiagnosticsNextPass>{};
+}
+} // namespace test
+} // namespace mlir
diff --git a/mlir/tools/mlir-opt/mlir-opt.cpp b/mlir/tools/mlir-opt/mlir-opt.cpp
index 47dffb1b2d28bc..730e48914281a0 100644
--- a/mlir/tools/mlir-opt/mlir-opt.cpp
+++ b/mlir/tools/mlir-opt/mlir-opt.cpp
@@ -93,6 +93,7 @@ void registerTestDeadCodeAnalysisPass();
void registerTestDecomposeCallGraphTypes();
void registerTestDiagnosticsPass();
void registerTestDiagnosticsMetadataPass();
+void registerTestDiagnosticsNextPass();
void registerTestDominancePass();
void registerTestDynamicPipelinePass();
void registerTestRemarkPass();
@@ -242,6 +243,7 @@ static void registerTestPasses() {
mlir::test::registerTestDecomposeCallGraphTypes();
mlir::test::registerTestDiagnosticsPass();
mlir::test::registerTestDiagnosticsMetadataPass();
+ mlir::test::registerTestDiagnosticsNextPass();
mlir::test::registerTestDominancePass();
mlir::test::registerTestDynamicPipelinePass();
mlir::test::registerTestRemarkPass();
>From 8f33a026ee8be9ab52735d9f175f7d6399b45f91 Mon Sep 17 00:00:00 2001
From: linuxlonelyeagle <2020382038 at qq.com>
Date: Fri, 11 Sep 2026 03:12:06 +0000
Subject: [PATCH 2/3] fix commemt and make code more easy to understand.
---
mlir/include/mlir/IR/Diagnostics.h | 29 +++++++++++---------
mlir/lib/IR/Diagnostics.cpp | 43 +++++++++++++++++-------------
2 files changed, 42 insertions(+), 30 deletions(-)
diff --git a/mlir/include/mlir/IR/Diagnostics.h b/mlir/include/mlir/IR/Diagnostics.h
index 8f802c131f3ceb..76ecba5cea481a 100644
--- a/mlir/include/mlir/IR/Diagnostics.h
+++ b/mlir/include/mlir/IR/Diagnostics.h
@@ -153,6 +153,9 @@ inline raw_ostream &operator<<(raw_ostream &os, const DiagnosticArgument &arg) {
/// This class contains all of the information necessary to report a diagnostic
/// to the DiagnosticEngine. It should generally not be constructed directly,
/// and instead used transitively via InFlightDiagnostic.
+///
+/// A diagnostic may contain multiple message parts that share its location,
+/// severity, metadata, and attached notes. Empty message parts are ignored.
class Diagnostic {
using NoteVector = std::vector<std::unique_ptr<Diagnostic>>;
@@ -249,16 +252,17 @@ class Diagnostic {
return *this;
}
- /// Outputs this diagnostic to a stream. `ChunkIdx` specifies which chunk to
- /// print to `os`. If empty, all arguments are printed to `os`.
+ /// Outputs this diagnostic to a stream. If `messagePartIndex` is provided,
+ /// only that message part is printed; otherwise, all arguments are printed
+ /// without separators between message parts.
void print(raw_ostream &os,
- std::optional<int64_t> chunkIdx = std::nullopt) const;
+ std::optional<int64_t> messagePartIndex = std::nullopt) const;
/// Converts the diagnostic to a string.
std::string str() const;
- /// Converts the diagnostic to a vector of strings, where each element
- /// represents a chunk.
+ /// Converts each message part to a separate string. Returns a single string
+ /// if the diagnostic has not been divided into multiple parts.
SmallVector<std::string> strs() const;
/// Attaches a note to this diagnostic. A new location may be optionally
@@ -294,8 +298,9 @@ class Diagnostic {
/// Returns the current list of diagnostic metadata.
SmallVectorImpl<DiagnosticArgument> &getMetadata() { return metadata; }
- /// Finalizes the current group of arguments and starts a new chunk.
- void checkpointArguments();
+ /// Starts a new message part. This has no effect if the current message part
+ /// is empty.
+ void startNewMessagePart();
private:
Diagnostic(const Diagnostic &rhs) = delete;
@@ -314,8 +319,8 @@ class Diagnostic {
/// liveness of non-constant strings used in diagnostics.
std::vector<std::unique_ptr<char[]>> strings;
- /// Boundary indices in `arguments` that separate different chunks.
- SmallVector<size_t, 2> chunkSizes;
+ /// The exclusive end indices in `arguments` of completed message parts.
+ SmallVector<size_t, 2> messagePartEnds;
/// A list of attached notes.
NoteVector notes;
@@ -440,10 +445,10 @@ class InFlightDiagnostic {
std::optional<Diagnostic> impl;
};
-/// A stream manipulator that checkpoints the current messages, and allowing a
-/// single InFlightDiagnostic to output multiple lines.
+/// Starts a new message part in an in-flight diagnostic. Leading, trailing, and
+/// consecutive uses of `next` do not create empty message parts.
inline InFlightDiagnostic &next(InFlightDiagnostic &diag) {
- diag.impl->checkpointArguments();
+ diag.impl->startNewMessagePart();
return diag;
}
diff --git a/mlir/lib/IR/Diagnostics.cpp b/mlir/lib/IR/Diagnostics.cpp
index a10f581f38522b..81e666db485623 100644
--- a/mlir/lib/IR/Diagnostics.cpp
+++ b/mlir/lib/IR/Diagnostics.cpp
@@ -161,20 +161,23 @@ Diagnostic &Diagnostic::operator<<(Value val) {
return *this << str;
}
-/// Outputs this diagnostic to a stream. `ChunkIdx` specifies which chunk to
-/// print to `os`. If empty, all arguments are printed to `os`.
-void Diagnostic::print(raw_ostream &os, std::optional<int64_t> chunkIdx) const {
- if (!chunkIdx.has_value()) {
+/// Outputs this diagnostic to a stream.
+void Diagnostic::print(raw_ostream &os,
+ std::optional<int64_t> messagePartIndex) const {
+ if (!messagePartIndex.has_value()) {
for (auto &arg : getArguments())
arg.print(os);
return;
}
- assert(0 <= chunkIdx && chunkIdx <= chunkSizes.size());
- size_t argumentStart = *chunkIdx - 1 >= 0 ? chunkSizes[*chunkIdx - 1] : 0;
- size_t argumentEnd = *chunkIdx == static_cast<int64_t>(chunkSizes.size())
- ? arguments.size()
- : chunkSizes[*chunkIdx];
+ assert(0 <= *messagePartIndex &&
+ *messagePartIndex <= static_cast<int64_t>(messagePartEnds.size()));
+ size_t argumentStart =
+ *messagePartIndex == 0 ? 0 : messagePartEnds[*messagePartIndex - 1];
+ size_t argumentEnd =
+ *messagePartIndex == static_cast<int64_t>(messagePartEnds.size())
+ ? arguments.size()
+ : messagePartEnds[*messagePartIndex];
for (auto &arg :
getArguments().slice(argumentStart, argumentEnd - argumentStart))
arg.print(os);
@@ -188,11 +191,16 @@ std::string Diagnostic::str() const {
return str;
}
-/// Converts the diagnostic to a vector of strings, where each element
-/// represents a chunk.
+/// Converts each message part to a separate string.
SmallVector<std::string> Diagnostic::strs() const {
SmallVector<std::string, 2> strs;
- for (size_t i = 0, e = chunkSizes.size(); i <= e; ++i) {
+ size_t numMessageParts = messagePartEnds.size();
+
+ // Include the current message part if there are no completed parts or if it
+ // contains arguments after the last completed part.
+ if (messagePartEnds.empty() || messagePartEnds.back() != arguments.size())
+ ++numMessageParts;
+ for (size_t i = 0; i < numMessageParts; ++i) {
std::string str;
llvm::raw_string_ostream os(str);
print(os, i);
@@ -222,14 +230,13 @@ Diagnostic &Diagnostic::attachNote(std::optional<Location> noteLoc) {
/// Allow a diagnostic to be converted to 'failure'.
Diagnostic::operator LogicalResult() const { return failure(); }
-/// Finalizes the current group of arguments and starts a new chunk.
-void Diagnostic::checkpointArguments() {
- size_t size = arguments.size();
- if (size == 0)
+/// Starts a new message part.
+void Diagnostic::startNewMessagePart() {
+ if (arguments.empty())
return;
- if (!chunkSizes.empty() && chunkSizes.back() == arguments.size())
+ if (!messagePartEnds.empty() && messagePartEnds.back() == arguments.size())
return;
- chunkSizes.push_back(arguments.size());
+ messagePartEnds.push_back(arguments.size());
}
//===----------------------------------------------------------------------===//
>From 382d56f4882070559c0ec1871293ed948be757d8 Mon Sep 17 00:00:00 2001
From: lonely eagle <2020382038 at qq.com>
Date: Wed, 23 Sep 2026 21:56:03 +0800
Subject: [PATCH 3/3] Apply suggestion from @joker-eph
Co-authored-by: Mehdi Amini <joker.eph at gmail.com>
---
mlir/include/mlir/IR/Diagnostics.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/mlir/include/mlir/IR/Diagnostics.h b/mlir/include/mlir/IR/Diagnostics.h
index 76ecba5cea481a..7fba0fed449811 100644
--- a/mlir/include/mlir/IR/Diagnostics.h
+++ b/mlir/include/mlir/IR/Diagnostics.h
@@ -258,7 +258,7 @@ class Diagnostic {
void print(raw_ostream &os,
std::optional<int64_t> messagePartIndex = std::nullopt) const;
- /// Converts the diagnostic to a string.
+ /// Converts the diagnostic to a string, concatenating all the parts if there are multiples.
std::string str() const;
/// Converts each message part to a separate string. Returns a single string
More information about the Mlir-commits
mailing list