diff --git a/mlir/lib/Dialect/Transform/IR/TransformOps.cpp b/mlir/lib/Dialect/Transform/IR/TransformOps.cpp --- a/mlir/lib/Dialect/Transform/IR/TransformOps.cpp +++ b/mlir/lib/Dialect/Transform/IR/TransformOps.cpp @@ -646,7 +646,7 @@ /// consumption effect annotations. If `alsoVerifyInternal`, checks for /// annotations being present even if they can be inferred from the body. static DiagnosedSilenceableFailure -verifyFunctionLikeConsumeAnnotations(FunctionOpInterface op, +verifyFunctionLikeConsumeAnnotations(FunctionOpInterface op, bool emitWarnings, bool alsoVerifyInternal = false) { auto transformOp = cast(op.getOperation()); llvm::SmallDenseSet consumedArguments; @@ -678,12 +678,12 @@ << "argument #" << i << " is consumed in the body but is not marked as such"; } - if (!consumedArguments.contains(i) && isConsumed) { - Diagnostic warning(op->getLoc(), DiagnosticSeverity::Warning); - warning << "argument #" << i - << " is not consumed in the body but is marked as consumed"; - return DiagnosedSilenceableFailure::silenceableFailure( - std::move(warning)); + if (emitWarnings && !consumedArguments.contains(i) && isConsumed) { + // Cannot use op.emitWarning() here as it would attempt to verify the op + // before printing, resulting in infinite recursion. + emitWarning(op->getLoc()) + << "op argument #" << i + << " is not consumed in the body but is marked as consumed"; } } return DiagnosedSilenceableFailure::success(); @@ -710,11 +710,13 @@ return emitError() << "unresolved action symbol " << action; if (failed(verifyFunctionLikeConsumeAnnotations(matcherSymbol, + /*emitWarnings=*/false, /*alsoVerifyInternal=*/true) .checkAndReport())) { return failure(); } if (failed(verifyFunctionLikeConsumeAnnotations(actionSymbol, + /*emitWarnings=*/false, /*alsoVerifyInternal=*/true) .checkAndReport())) { return failure(); @@ -1045,7 +1047,7 @@ } static DiagnosedSilenceableFailure -verifyNamedSequenceOp(transform::NamedSequenceOp op); +verifyNamedSequenceOp(transform::NamedSequenceOp op, bool emitWarnings); void transform::IncludeOp::getEffects( SmallVectorImpl &effects) { @@ -1075,7 +1077,7 @@ if (!callee) return defaultEffects(); DiagnosedSilenceableFailure earlyVerifierResult = - verifyNamedSequenceOp(callee); + verifyNamedSequenceOp(callee, /*emitWarnings=*/false); if (!earlyVerifierResult.succeeded()) { (void)earlyVerifierResult.silence(); return defaultEffects(); @@ -1128,7 +1130,7 @@ } return verifyFunctionLikeConsumeAnnotations( - cast(*target), + cast(*target), /*emitWarnings=*/false, /*alsoVerifyInternal=*/true) .checkAndReport(); } @@ -1414,7 +1416,7 @@ /// immediately, so it can be used to check for op's well-formedness before the /// verifier runs, e.g., during trait verification. static DiagnosedSilenceableFailure -verifyNamedSequenceOp(transform::NamedSequenceOp op) { +verifyNamedSequenceOp(transform::NamedSequenceOp op, bool emitWarnings) { if (Operation *parent = op->getParentWithTrait()) { if (!parent->getAttr( transform::TransformDialect::kWithNamedSequenceAttrName)) { @@ -1437,7 +1439,8 @@ } if (op.isExternal() || op.getBody().empty()) - return verifyFunctionLikeConsumeAnnotations(cast(*op)); + return verifyFunctionLikeConsumeAnnotations(cast(*op), + emitWarnings); if (op.getBody().front().empty()) return emitSilenceableFailure(op) << "expected a non-empty body block"; @@ -1471,7 +1474,7 @@ auto funcOp = cast(*op); DiagnosedSilenceableFailure diag = - verifyFunctionLikeConsumeAnnotations(funcOp); + verifyFunctionLikeConsumeAnnotations(funcOp, emitWarnings); if (!diag.succeeded()) return diag; @@ -1481,7 +1484,7 @@ LogicalResult transform::NamedSequenceOp::verify() { // Actual verification happens in a separate function for reusability. - return verifyNamedSequenceOp(*this).checkAndReport(); + return verifyNamedSequenceOp(*this, /*emitWarnings=*/true).checkAndReport(); } //===----------------------------------------------------------------------===// diff --git a/mlir/test/Dialect/Transform/ops-invalid.mlir b/mlir/test/Dialect/Transform/ops-invalid.mlir --- a/mlir/test/Dialect/Transform/ops-invalid.mlir +++ b/mlir/test/Dialect/Transform/ops-invalid.mlir @@ -1,4 +1,4 @@ -// RUN: mlir-opt %s -split-input-file -verify-diagnostics +// RUN: mlir-opt %s -split-input-file -verify-diagnostics | FileCheck %s // expected-error @below {{expects the entry block to have at least one argument}} transform.sequence failures(propagate) { @@ -517,15 +517,18 @@ // ----- module attributes { transform.with_named_sequence } { + // Note that printing a warning doesn't result in verification failures, so this + // also checks for the IR being printed back. + // CHECK-LABEL: transform.named_sequence @emit_warning_only // expected-warning @below {{argument #0 is not consumed in the body but is marked as consume}} - transform.named_sequence @foo(%op: !transform.any_op {transform.consumed}) { + transform.named_sequence @emit_warning_only(%op: !transform.any_op {transform.consumed}) { transform.test_print_remark_at_operand %op, "message" : !transform.any_op transform.yield } transform.sequence failures(propagate) { ^bb0(%arg0: !transform.any_op): - transform.include @foo failures(propagate) (%arg0) : (!transform.any_op) -> () + transform.include @emit_warning_only failures(propagate) (%arg0) : (!transform.any_op) -> () transform.yield } }