Rantanen / intercom

Object based cross-language FFI for Rust
MIT License
63 stars 7 forks source link

Detect HRESULT/Non-HRESULT functions and adapt error handling in the generated layer #38

Closed Rantanen closed 5 years ago

Rantanen commented 6 years ago

There are various internal error conditions that we would love to signal forward. These include things such as:

If the method is one that returns HRESULT (or specifically Result<...>), Intercom can ride on top of this and convert these error conditions into HRESULTs automatically. However if the method is not one that returns HRESULT, there's no other option than to panic! (well... the other option would be to ignore the errors, but that's even worse).

For this reason Intercom would need to recognize the method type and behave accordingly.


Maybe we could generate something similar to:

fn hresult(...) -> HRESULT {
    type ErrType = HRESULT;  // <- Depends on method return value.
    if( some error happened ) {
        return ErrType::From( E_FAIL as an example );
    }
    ...
}

fn not_hresult(...) -> bool {
    type ErrType = IntoPanic;  // <- Depends on method return value.
    if( some error happened ) {
        return ErrType::From( E_FAIL as an example );
    }
    ...
}

This would minimize the amount of changes the return value would cause for the function. It would need to be taken into account in one place only. IntoPanic would implement:


impl<T> From<T> for IntoPanic {
    fn from(t:T) -> IntoPanic {
        panic!(..);
    }
}`
Rantanen commented 6 years ago

The string-conversions branch defines a trait FromError that tries to achieve this.

The idea is that there exists a default implementation that terminates the process (we can't panic! as panics can't unwind from extern functions).

impl<T> FromError for T {
    default fn handle( _ : ComError ) -> Self { os.terminate() }
}

Then for types that can return proper error status codes we have specialized implementations such as:

impl FromError for HRESULT {
    fn handle( err : ComError ) -> Self { store_error( err ); err.hresult }
}

If the user wants their own error types, such as "number of bytes read or -1 if an error occurred", then they can use new types and implement the error code for that:

struct ReadStatus( isize );
fn read_file( &self, path : &str ) -> ReadStatus { ... }

impl FromError for ReadStatus {
    fn handle( err : ComError ) -> Self { ReadStatus( -1 ) }
}

(We'll need to figure out how to support transparent new types. One option would be to handle all tuple structs with one field as new types)

Rantanen commented 5 years ago

The string changes touched upon this and now that #97 has landed, I feel like this has been taken care of. Some individual tweaks might come at some point, but the main issue has been resolved.