Add userData to the diagnostic handler C API

Previously, there was no way to add context to the diagnostic engine via the C API. Adding this ability makes it much easier to reason about memory ownership, particularly in reference-counted languages such as Swift. There are more details in the review comments.

Reviewed By: ftynse, mehdi_amini

Differential Revision: https://reviews.llvm.org/D91738
This commit is contained in:
George 2020-11-23 09:52:17 -08:00
parent 3a1c6cec15
commit 0c5cff300f
3 changed files with 39 additions and 20 deletions

View File

@ -40,12 +40,14 @@ typedef enum MlirDiagnosticSeverity MlirDiagnosticSeverity;
/// Opaque identifier of a diagnostic handler, useful to detach a handler. /// Opaque identifier of a diagnostic handler, useful to detach a handler.
typedef uint64_t MlirDiagnosticHandlerID; typedef uint64_t MlirDiagnosticHandlerID;
/** Diagnostic handler type. Acceps a reference to a diagnostic, which is only /** Diagnostic handler type. Accepts a reference to a diagnostic, which is only
* guaranteed to be live during the call. If the handler processed the * guaranteed to be live during the call. The handler is passed the `userData`
* diagnostic completely, it is expected to return success. Otherwise, it is * that was provided when the handler was attached to a context. If the handler
* expected to return failure to indicate that other handlers should attempt to * processed the diagnostic completely, it is expected to return success.
* process the diagnostic. */ * Otherwise, it is expected to return failure to indicate that other handlers
typedef MlirLogicalResult (*MlirDiagnosticHandler)(MlirDiagnostic); * should attempt to process the diagnostic. */
typedef MlirLogicalResult (*MlirDiagnosticHandler)(MlirDiagnostic,
void *userData);
/// Prints a diagnostic using the provided callback. /// Prints a diagnostic using the provided callback.
MLIR_CAPI_EXPORTED void mlirDiagnosticPrint(MlirDiagnostic diagnostic, MLIR_CAPI_EXPORTED void mlirDiagnosticPrint(MlirDiagnostic diagnostic,
@ -71,9 +73,15 @@ mlirDiagnosticGetNote(MlirDiagnostic diagnostic, intptr_t pos);
/** Attaches the diagnostic handler to the context. Handlers are invoked in the /** Attaches the diagnostic handler to the context. Handlers are invoked in the
* reverse order of attachment until one of them processes the diagnostic * reverse order of attachment until one of them processes the diagnostic
* completely. Returns an identifier that can be used to detach the handler. */ * completely. When a handler is invoked it is passed the `userData` that was
* provided when it was attached. If non-NULL, `deleteUserData` is called once
* the system no longer needs to call the handler (for instance after the
* handler is detached or the context is destroyed). Returns an identifier that
* can be used to detach the handler.
*/
MLIR_CAPI_EXPORTED MlirDiagnosticHandlerID mlirContextAttachDiagnosticHandler( MLIR_CAPI_EXPORTED MlirDiagnosticHandlerID mlirContextAttachDiagnosticHandler(
MlirContext context, MlirDiagnosticHandler handler); MlirContext context, MlirDiagnosticHandler handler, void *userData,
void (*deleteUserData)(void *));
/** Detaches an attached diagnostic handler from the context given its /** Detaches an attached diagnostic handler from the context given its
* identifier. */ * identifier. */

View File

@ -51,14 +51,19 @@ MlirDiagnostic mlirDiagnosticGetNote(MlirDiagnostic diagnostic, intptr_t pos) {
return wrap(*std::next(unwrap(diagnostic).getNotes().begin(), pos)); return wrap(*std::next(unwrap(diagnostic).getNotes().begin(), pos));
} }
MlirDiagnosticHandlerID static void deleteUserDataNoop(void *userData) {}
mlirContextAttachDiagnosticHandler(MlirContext context,
MlirDiagnosticHandler handler) { MlirDiagnosticHandlerID mlirContextAttachDiagnosticHandler(
MlirContext context, MlirDiagnosticHandler handler, void *userData,
void (*deleteUserData)(void *)) {
assert(handler && "unexpected null diagnostic handler"); assert(handler && "unexpected null diagnostic handler");
if (deleteUserData == NULL)
deleteUserData = deleteUserDataNoop;
std::shared_ptr<void> sharedUserData(userData, deleteUserData);
DiagnosticEngine::HandlerID id = DiagnosticEngine::HandlerID id =
unwrap(context)->getDiagEngine().registerHandler( unwrap(context)->getDiagEngine().registerHandler(
[handler](Diagnostic &diagnostic) { [handler, sharedUserData](Diagnostic &diagnostic) {
return unwrap(handler(wrap(diagnostic))); return unwrap(handler(wrap(diagnostic), sharedUserData.get()));
}); });
return static_cast<MlirDiagnosticHandlerID>(id); return static_cast<MlirDiagnosticHandlerID>(id);
} }

View File

@ -1248,31 +1248,37 @@ int registerOnlyStd() {
} }
// Wraps a diagnostic into additional text we can match against. // Wraps a diagnostic into additional text we can match against.
MlirLogicalResult errorHandler(MlirDiagnostic diagnostic) { MlirLogicalResult errorHandler(MlirDiagnostic diagnostic, void *userData) {
fprintf(stderr, "processing diagnostic <<\n"); fprintf(stderr, "processing diagnostic (userData: %d) <<\n", (int)userData);
mlirDiagnosticPrint(diagnostic, printToStderr, NULL); mlirDiagnosticPrint(diagnostic, printToStderr, NULL);
fprintf(stderr, "\n"); fprintf(stderr, "\n");
MlirLocation loc = mlirDiagnosticGetLocation(diagnostic); MlirLocation loc = mlirDiagnosticGetLocation(diagnostic);
mlirLocationPrint(loc, printToStderr, NULL); mlirLocationPrint(loc, printToStderr, NULL);
assert(mlirDiagnosticGetNumNotes(diagnostic) == 0); assert(mlirDiagnosticGetNumNotes(diagnostic) == 0);
fprintf(stderr, ">> end of diagnostic\n"); fprintf(stderr, ">> end of diagnostic (userData: %d)\n", (int)userData);
return mlirLogicalResultSuccess(); return mlirLogicalResultSuccess();
} }
// Logs when the delete user data callback is called
static void deleteUserData(void *userData) {
fprintf(stderr, "deleting user data (userData: %d)\n", (int)userData);
}
void testDiagnostics() { void testDiagnostics() {
MlirContext ctx = mlirContextCreate(); MlirContext ctx = mlirContextCreate();
MlirDiagnosticHandlerID id = MlirDiagnosticHandlerID id = mlirContextAttachDiagnosticHandler(
mlirContextAttachDiagnosticHandler(ctx, errorHandler); ctx, errorHandler, (void *)42, deleteUserData);
MlirLocation loc = mlirLocationUnknownGet(ctx); MlirLocation loc = mlirLocationUnknownGet(ctx);
fprintf(stderr, "@test_diagnostics\n"); fprintf(stderr, "@test_diagnostics\n");
mlirEmitError(loc, "test diagnostics"); mlirEmitError(loc, "test diagnostics");
mlirContextDetachDiagnosticHandler(ctx, id); mlirContextDetachDiagnosticHandler(ctx, id);
mlirEmitError(loc, "more test diagnostics"); mlirEmitError(loc, "more test diagnostics");
// CHECK-LABEL: @test_diagnostics // CHECK-LABEL: @test_diagnostics
// CHECK: processing diagnostic << // CHECK: processing diagnostic (userData: 42) <<
// CHECK: test diagnostics // CHECK: test diagnostics
// CHECK: loc(unknown) // CHECK: loc(unknown)
// CHECK: >> end of diagnostic // CHECK: >> end of diagnostic (userData: 42)
// CHECK: deleting user data (userData: 42)
// CHECK-NOT: processing diagnostic // CHECK-NOT: processing diagnostic
// CHECK: more test diagnostics // CHECK: more test diagnostics
} }