Skip to content

Commit

Permalink
Add docs about projecting errors (#259)
Browse files Browse the repository at this point in the history
  • Loading branch information
tristanlabelle authored Sep 3, 2024
1 parent 4c1f26e commit 8211356
Showing 1 changed file with 33 additions and 0 deletions.
33 changes: 33 additions & 0 deletions Docs/Projecting Errors
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Projecting Errors

This document explains how COM, WinRT and Swift errors interoperate.

## The COM error model

The COM and WinRT error models are similar: functions report errors by returning a failure HRESULT value and optionally setting the COM thread-local error info object (`IErrorInfo` for COM, `IRestrictedErrorInfo` for WinRT). WinRT additionally has means of "originating" the error to a debugger and capturing the error context (CPU context).

For the consumer of a COM API, the intended usage when a function returns a failure HRESULT is to call `GetErrorInfo` to get and clear the current thread's `IErrorInfo` object. This must be done immediately upon seeing the failure HRESULT as any other COM calls could override the error info object.

For WinRT APIs, the COM way described above works, but it is better to call `GetRestrictedErrorInfo` to get (and clear) the richer `IRestrictedErrorInfo` object which provides the full "restricted" error description.

## Swift projection

In Swift, we want to lift the COM error model into Swift errors (thrown objects conforming to the `Error`) protocol. We also want to preserve Swift-native errors thrown within COM callbacks such that they can cross into COM and back into Swift unchanged (i.e. catchable using `catch _ as MySwiftError`).

### COM-Originating Errors

We define a `COMError` object conforming to the `Error` protocol so that we can throw it using normal language semantics. This object holds the failure HRESULT value as well as an optional `IErrorInfo` providing additional error information, thus encapsulating the full COM error model.

When a COM function is called from Swift and returns a failure HRESULT, we immediately call `GetErrorInfo`, then create and throw a `COMError`.

When Swift is returning to a COM caller with a thrown error, we catch the `COMError`, call `SetErrorInfo` to restore the thread-local error info and return the HRESULT.

### Swift-Native Errors

If Swift can return to a COM caller with a thrown error, that error could be an arbitrary Swift-native error and not be a `COMError`. In this case, we instantiate a custom implementation of `IErrorInfo` that wraps the Swift error, set it as the COM thread-local error info and figure out an HRESULT value to return (usually `E_FAIL`).

If such a failure HRESULT bubbles through COM layers and back into Swift, we retrieve the `IErrorInfo` object, inspect it to see if it is our custom implementation, and if so throw the wrapped Swift error instead of a generic `COMError`.

### WinRT

Most of the details above apply to WinRT errors, except that we replace the custom `IErrorInfo` object with a vanilla `IRestrictedErrorInfo` object created by `RoOriginateLanguageException` to which we can specify an `IUnknown` that wraps the Swift error and which we can later retrieve via the `ILanguageExceptionErrorInfo` interface.

0 comments on commit 8211356

Please sign in to comment.