Skip to content

Commit

Permalink
[mlir] NFC: move DiagnosedSilenceableFailure to Utils in Transform di…
Browse files Browse the repository at this point in the history
…alect

It was originally placed in TransformInterfaces for convenience, but it
is really a generic utility. It may also create an include cycle between
TransformTypes and TransformInterfaces if the latter needs to include
the former because the former uses the failure util.

Reviewed By: springerm

Differential Revision: https://reviews.llvm.org/D140978
  • Loading branch information
ftynse committed Jan 6, 2023
1 parent 97c0506 commit 054ec47
Show file tree
Hide file tree
Showing 12 changed files with 317 additions and 275 deletions.
261 changes: 2 additions & 259 deletions mlir/include/mlir/Dialect/Transform/IR/TransformInterfaces.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,271 +9,14 @@
#ifndef MLIR_DIALECT_TRANSFORM_IR_TRANSFORMINTERFACES_H
#define MLIR_DIALECT_TRANSFORM_IR_TRANSFORMINTERFACES_H

#include "mlir/Dialect/Transform/IR/TransformTypes.h"
#include "mlir/Dialect/Transform/Utils/DiagnosedSilenceableFailure.h"
#include "mlir/IR/OpDefinition.h"

#include "mlir/Interfaces/SideEffectInterfaces.h"
#include "mlir/Support/LogicalResult.h"
#include "llvm/ADT/ScopeExit.h"

namespace mlir {

/// The result of a transform IR operation application. This can have one of the
/// three states:
/// - success;
/// - silenceable (recoverable) failure with yet-unreported diagnostic;
/// - definite failure.
/// Silenceable failure is intended to communicate information about
/// transformations that did not apply but in a way that supports recovery,
/// for example, they did not modify the payload IR or modified it in some
/// predictable way. They are associated with a Diagnostic that provides more
/// details on the failure. Silenceable failure can be discarded, turning the
/// result into success, or "reported", emitting the diagnostic and turning the
/// result into definite failure.
/// Transform IR operations containing other operations are allowed to do either
/// with the results of the nested transformations, but must propagate definite
/// failures as their diagnostics have been already reported to the user.
class [[nodiscard]] DiagnosedSilenceableFailure {
public:
DiagnosedSilenceableFailure(const DiagnosedSilenceableFailure &) = delete;
DiagnosedSilenceableFailure &
operator=(const DiagnosedSilenceableFailure &) = delete;
DiagnosedSilenceableFailure(DiagnosedSilenceableFailure &&) = default;
DiagnosedSilenceableFailure &
operator=(DiagnosedSilenceableFailure &&) = default;

/// Constructs a DiagnosedSilenceableFailure in the success state.
static DiagnosedSilenceableFailure success() {
return DiagnosedSilenceableFailure(::mlir::success());
}

/// Constructs a DiagnosedSilenceableFailure in the failure state. Typically,
/// a diagnostic has been emitted before this.
static DiagnosedSilenceableFailure definiteFailure() {
return DiagnosedSilenceableFailure(::mlir::failure());
}

/// Constructs a DiagnosedSilenceableFailure in the silenceable failure state,
/// ready to emit the given diagnostic. This is considered a failure
/// regardless of the diagnostic severity.
static DiagnosedSilenceableFailure silenceableFailure(Diagnostic &&diag) {
return DiagnosedSilenceableFailure(std::forward<Diagnostic>(diag));
}
static DiagnosedSilenceableFailure
silenceableFailure(SmallVector<Diagnostic> &&diag) {
return DiagnosedSilenceableFailure(
std::forward<SmallVector<Diagnostic>>(diag));
}

/// Converts all kinds of failure into a LogicalResult failure, emitting the
/// diagnostic if necessary. Must not be called more than once.
LogicalResult checkAndReport() {
#if LLVM_ENABLE_ABI_BREAKING_CHECKS
assert(!reported && "attempting to report a diagnostic more than once");
reported = true;
#endif // LLVM_ENABLE_ABI_BREAKING_CHECKS
if (!diagnostics.empty()) {
for (auto &&diagnostic : diagnostics) {
diagnostic.getLocation().getContext()->getDiagEngine().emit(
std::move(diagnostic));
}
diagnostics.clear();
result = ::mlir::failure();
}
return result;
}

/// Returns `true` if this is a success.
bool succeeded() const {
return ::mlir::succeeded(result) && diagnostics.empty();
}

/// Returns `true` if this is a definite failure.
bool isDefiniteFailure() const {
return ::mlir::failed(result) && diagnostics.empty();
}

/// Returns `true` if this is a silenceable failure.
bool isSilenceableFailure() const { return !diagnostics.empty(); }

/// Returns the diagnostic message without emitting it. Expects this object
/// to be a silenceable failure.
std::string getMessage() const {
std::string res;
for (auto &diagnostic : diagnostics) {
res.append(diagnostic.str());
res.append("\n");
}
return res;
}

/// Returns a string representation of the failure mode (for error reporting).
std::string getStatusString() const {
if (succeeded())
return "success";
if (isSilenceableFailure())
return "silenceable failure";
return "definite failure";
}

/// Converts silenceable failure into LogicalResult success without reporting
/// the diagnostic, preserves the other states.
LogicalResult silence() {
if (!diagnostics.empty()) {
diagnostics.clear();
result = ::mlir::success();
}
return result;
}

/// Take the diagnostics and silence.
void takeDiagnostics(SmallVectorImpl<Diagnostic> &diags) {
assert(!diagnostics.empty() && "expected a diagnostic to be present");
diags.append(std::make_move_iterator(diagnostics.begin()),
std::make_move_iterator(diagnostics.end()));
}

/// Streams the given values into the last diagnostic.
/// Expects this object to be a silenceable failure.
template <typename T>
DiagnosedSilenceableFailure &operator<<(T &&value) & {
assert(isSilenceableFailure() &&
"can only append output in silenceable failure state");
diagnostics.back() << std::forward<T>(value);
return *this;
}
template <typename T>
DiagnosedSilenceableFailure &&operator<<(T &&value) && {
return std::move(this->operator<<(std::forward<T>(value)));
}

/// Attaches a note to the last diagnostic.
/// Expects this object to be a silenceable failure.
Diagnostic &attachNote(Optional<Location> loc = std::nullopt) {
assert(isSilenceableFailure() &&
"can only attach notes to silenceable failures");
return diagnostics.back().attachNote(loc);
}

private:
explicit DiagnosedSilenceableFailure(LogicalResult result) : result(result) {}
explicit DiagnosedSilenceableFailure(Diagnostic &&diagnostic)
: result(failure()) {
diagnostics.emplace_back(std::move(diagnostic));
}
explicit DiagnosedSilenceableFailure(SmallVector<Diagnostic> &&diagnostics)
: diagnostics(std::move(diagnostics)), result(failure()) {}

/// The diagnostics associated with this object. If non-empty, the object is
/// considered to be in the silenceable failure state regardless of the
/// `result` field.
SmallVector<Diagnostic, 1> diagnostics;

/// The "definite" logical state, either success or failure.
/// Ignored if the diagnostics message is present.
LogicalResult result;

#if LLVM_ENABLE_ABI_BREAKING_CHECKS
/// Whether the associated diagnostics have been reported.
/// Diagnostics reporting consumes the diagnostics, so we need a mechanism to
/// differentiate reported diagnostics from a state where it was never
/// created.
bool reported = false;
#endif // LLVM_ENABLE_ABI_BREAKING_CHECKS
};

class DiagnosedDefiniteFailure;

DiagnosedDefiniteFailure emitDefiniteFailure(Location loc,
const Twine &message = {});

/// A compatibility class connecting `InFlightDiagnostic` to
/// `DiagnosedSilenceableFailure` while providing an interface similar to the
/// former. Implicitly convertible to `DiagnosticSilenceableFailure` in definite
/// failure state and to `LogicalResult` failure. Reports the error on
/// conversion or on destruction. Instances of this class can be created by
/// `emitDefiniteFailure()`.
class DiagnosedDefiniteFailure {
friend DiagnosedDefiniteFailure emitDefiniteFailure(Location loc,
const Twine &message);

public:
/// Only move-constructible because it carries an in-flight diagnostic.
DiagnosedDefiniteFailure(DiagnosedDefiniteFailure &&) = default;

/// Forward the message to the diagnostic.
template <typename T>
DiagnosedDefiniteFailure &operator<<(T &&value) & {
diag << std::forward<T>(value);
return *this;
}
template <typename T>
DiagnosedDefiniteFailure &&operator<<(T &&value) && {
return std::move(this->operator<<(std::forward<T>(value)));
}

/// Attaches a note to the error.
Diagnostic &attachNote(Optional<Location> loc = std::nullopt) {
return diag.attachNote(loc);
}

/// Implicit conversion to DiagnosedSilenceableFailure in the definite failure
/// state. Reports the error.
operator DiagnosedSilenceableFailure() {
diag.report();
return DiagnosedSilenceableFailure::definiteFailure();
}

/// Implicit conversion to LogicalResult in the failure state. Reports the
/// error.
operator LogicalResult() {
diag.report();
return failure();
}

private:
/// Constructs a definite failure at the given location with the given
/// message.
explicit DiagnosedDefiniteFailure(Location loc, const Twine &message)
: diag(emitError(loc, message)) {}

/// Copy-construction and any assignment is disallowed to prevent repeated
/// error reporting.
DiagnosedDefiniteFailure(const DiagnosedDefiniteFailure &) = delete;
DiagnosedDefiniteFailure &
operator=(const DiagnosedDefiniteFailure &) = delete;
DiagnosedDefiniteFailure &operator=(DiagnosedDefiniteFailure &&) = delete;

/// The error message.
InFlightDiagnostic diag;
};

/// Emits a definite failure with the given message. The returned object allows
/// for last-minute modification to the error message, such as attaching notes
/// and completing the message. It will be reported when the object is
/// destructed or converted.
inline DiagnosedDefiniteFailure emitDefiniteFailure(Location loc,
const Twine &message) {
return DiagnosedDefiniteFailure(loc, message);
}
inline DiagnosedDefiniteFailure emitDefiniteFailure(Operation *op,
const Twine &message = {}) {
return emitDefiniteFailure(op->getLoc(), message);
}

/// Emits a silenceable failure with the given message. A silenceable failure
/// must be either suppressed or converted into a definite failure and reported
/// to the user.
inline DiagnosedSilenceableFailure
emitSilenceableFailure(Location loc, const Twine &message = {}) {
Diagnostic diag(loc, DiagnosticSeverity::Error);
diag << message;
return DiagnosedSilenceableFailure::silenceableFailure(std::move(diag));
}
inline DiagnosedSilenceableFailure
emitSilenceableFailure(Operation *op, const Twine &message = {}) {
return emitSilenceableFailure(op->getLoc(), message);
}

namespace transform {

class TransformOpInterface;
Expand Down
1 change: 1 addition & 0 deletions mlir/include/mlir/Dialect/Transform/IR/TransformOps.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#define MLIR_DIALECT_TRANSFORM_IR_TRANSFORMOPS_H

#include "mlir/Dialect/PDL/IR/PDLTypes.h"
#include "mlir/Dialect/Transform/IR/TransformInterfaces.h"
#include "mlir/Dialect/Transform/IR/TransformTypes.h"
#include "mlir/IR/OpDefinition.h"
#include "mlir/IR/OpImplementation.h"
Expand Down
2 changes: 1 addition & 1 deletion mlir/include/mlir/Dialect/Transform/IR/TransformTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
#ifndef MLIR_DIALECT_TRANSFORM_IR_TRANSFORMTYPES_H
#define MLIR_DIALECT_TRANSFORM_IR_TRANSFORMTYPES_H

#include "mlir/Dialect/Transform/IR/TransformInterfaces.h"
#include "mlir/Dialect/Transform/Utils/DiagnosedSilenceableFailure.h"
#include "mlir/IR/Types.h"
#include "mlir/Support/LLVM.h"

Expand Down
Loading

0 comments on commit 054ec47

Please sign in to comment.