Skip to content

Commit 148d725

Browse files
committed
Hack in support for Maybe ForeignPtr
1 parent 46c5f90 commit 148d725

File tree

4 files changed

+80
-9
lines changed

4 files changed

+80
-9
lines changed

src/Language/Rust/Inline.hs

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,7 @@ processQQ safety isPure (QQParse rustRet rustBody rustNamedArgs) = do
314314
-- Convert the Haskell return type to a marshallable FFI type
315315
(returnFfi, haskRet') <- do
316316
marshalForm <- ghcMarshallable haskRet
317+
let fptrRet haskRet' = [t|Ptr (Ptr $(pure haskRet'), FunPtr (Ptr $(pure haskRet') -> IO ())) -> IO ()|]
317318
ret <- case marshalForm of
318319
BoxedDirect -> [t|IO $(pure haskRet)|]
319320
BoxedIndirect -> [t|Ptr $(pure haskRet) -> IO ()|]
@@ -324,8 +325,11 @@ processQQ safety isPure (QQParse rustRet rustBody rustNamedArgs) = do
324325
in fail ("Cannot put unlifted type ‘" ++ retTy ++ "’ in IO")
325326
ByteString -> [t|Ptr (Ptr Word8, Word, FunPtr (Ptr Word8 -> Word -> IO ())) -> IO ()|]
326327
ForeignPtr
327-
| AppT _ haskRet' <- haskRet -> [t|Ptr (Ptr $(pure haskRet'), FunPtr (Ptr $(pure haskRet') -> IO ())) -> IO ()|]
328+
| AppT _ haskRet' <- haskRet -> fptrRet haskRet'
328329
| otherwise -> fail ("Cannot marshal " ++ showTy haskRet ++ " using the ForeignPtr calling convention")
330+
OptionalForeignPtr
331+
| AppT _ (AppT _ haskRet') <- haskRet -> fptrRet haskRet'
332+
| otherwise -> fail ("Cannot marshal " ++ showTy haskRet ++ " as an optional ForeignPtr")
329333
pure (marshalForm, pure ret)
330334

331335
-- Convert the Haskell arguments to marshallable FFI types
@@ -357,6 +361,11 @@ processQQ safety isPure (QQParse rustRet rustBody rustNamedArgs) = do
357361
ptr <- [t|Ptr $(pure haskArg')|]
358362
pure (ForeignPtr, ptr)
359363
| otherwise -> fail ("Cannot marshal " ++ showTy haskRet ++ " using the ForeignPtr calling convention")
364+
OptionalForeignPtr
365+
| AppT _ (AppT _ haskArg') <- haskArg -> do
366+
ptr <- [t|Ptr $(pure haskArg')|]
367+
pure (OptionalForeignPtr, ptr)
368+
| otherwise -> fail ("Cannot marshal " ++ showTy haskRet ++ " as an optional ForeignPtr")
360369
_ -> pure (marshalForm, haskArg)
361370

362371
-- Generate the Haskell FFI import declaration and emit it
@@ -408,6 +417,20 @@ processQQ safety isPure (QQParse rustRet rustBody rustNamedArgs) = do
408417
newForeignPtr $(varE finalizer) $(varE ptr)
409418
)
410419
|]
420+
| returnFfi == OptionalForeignPtr = do
421+
finalizer <- newName "finalizer"
422+
ptr <- newName "ptr"
423+
ret <- newName "ret"
424+
[e|
425+
alloca
426+
( \($(varP ret)) -> do
427+
$(appsE (varE qqName : reverse (varE ret : acc)))
428+
($(varP ptr), $(varP finalizer)) <- peek $(varE ret)
429+
if $(varE ptr) == nullPtr
430+
then pure Nothing
431+
else Just <$> newForeignPtr $(varE finalizer) $(varE ptr)
432+
)
433+
|]
411434
| returnByValue returnFfi = appsE (varE qqName : reverse acc)
412435
| otherwise = do
413436
ret <- newName "ret"
@@ -443,6 +466,15 @@ processQQ safety isPure (QQParse rustRet rustBody rustNamedArgs) = do
443466
[e|
444467
withForeignPtr $(varE argName) (\($(varP ptr)) -> $(goArgs (varE ptr : acc) args))
445468
|]
469+
| marshalForm == OptionalForeignPtr -> do
470+
ptr <- newName "ptr"
471+
fptr <- newName "fptr"
472+
[e|
473+
case $(varE argName) of
474+
Nothing -> let $(varP ptr) = nullPtr in $(goArgs (varE ptr : acc) args)
475+
Just $(varP fptr) ->
476+
withForeignPtr $(varE fptr) (\($(varP ptr)) -> $(goArgs (varE ptr : acc) args))
477+
|]
446478
| passByValue marshalForm -> goArgs (varE argName : acc) args
447479
| otherwise -> do
448480
x <- newName "x"

src/Language/Rust/Inline/Context.hs

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
{-# LANGUAGE OverloadedStrings #-}
66
{-# LANGUAGE QuasiQuotes #-}
77
{-# LANGUAGE TemplateHaskell #-}
8+
{-# OPTIONS_GHC -Wno-name-shadowing #-}
89

910
module Language.Rust.Inline.Context where
1011

@@ -314,17 +315,27 @@ pointers = do
314315
]
315316

316317
foreignPointers :: Q Context
317-
foreignPointers =
318-
pure $ Context ([rule], [], [foreignPtr, constPtr, mutPtr])
318+
foreignPointers = do
319+
foreignPtrT <- [t|ForeignPtr|]
320+
pure $ Context ([rule], [rev foreignPtrT], [foreignPtr, constPtr, mutPtr])
319321
where
320322
rule (Ptr _ t _) context
321323
| First (Just (t', Nothing)) <- lookupRTypeInContext t context = pure ([t|ForeignPtr $t'|], Nothing)
322324
rule (Rptr _ _ t _) context
323325
| First (Just (t', Nothing)) <- lookupRTypeInContext t context = pure ([t|ForeignPtr $t'|], Nothing)
324326
rule (PathTy Nothing (Path False [PathSegment "ForeignPtr" (Just (AngleBracketed [] [t] [] _)) _] _) _) context
325327
| First (Just (t', Nothing)) <- lookupRTypeInContext t context = pure ([t|ForeignPtr $t'|], Nothing)
328+
rule (PathTy Nothing (Path False [PathSegment "Option" (Just (AngleBracketed [] [PathTy Nothing (Path False [PathSegment "ForeignPtr" (Just (AngleBracketed [] [t] [] _)) _] _) _] [] _)) _] _) _) context
329+
| First (Just (t', Nothing)) <- lookupRTypeInContext t context =
330+
pure ([t|Maybe (ForeignPtr $t')|], pure . pure $ PathTy Nothing (Path False [PathSegment "ForeignPtr" (Just (AngleBracketed [] [t] [] ())) ()] ()) ())
326331
rule _ _ = mempty
327332

333+
rev foreignPtrT (AppT foreignPtr t) context
334+
| foreignPtr == foreignPtrT = do
335+
t' <- lookupHTypeInContext t context
336+
pure (Ptr Mutable <$> t' <*> pure ())
337+
rev _ _ _ = mempty
338+
328339
foreignPtr =
329340
unlines
330341
[ "#[repr(C)]"
@@ -359,7 +370,7 @@ foreignPointers =
359370
, ""
360371
, "impl<T> From<Box<T>> for ForeignPtr<T> {"
361372
, " fn from(p: Box<T>) -> ForeignPtr<T> {"
362-
, " extern fn free<T> (ptr: *mut T) {"
373+
, " extern fn free<T>(ptr: *mut T) {"
363374
, " let t = unsafe { Box::from_raw(ptr) };"
364375
, " drop(t);"
365376
, " }"
@@ -374,6 +385,15 @@ foreignPointers =
374385
, "impl<'a, T> MarshalInto<&'a mut T> for &'a mut T {"
375386
, " fn marshal(self) -> &'a mut T { self }"
376387
, "}"
388+
, ""
389+
, "impl<T> MarshalInto<ForeignPtr<T>> for Option<ForeignPtr<T>> {"
390+
, " fn marshal(self) -> ForeignPtr<T> {"
391+
, " extern fn panic<T>(_ptr: *mut T) {"
392+
, " panic!(\"Attempted to free a null ForeignPtr\")"
393+
, " }"
394+
, " self.unwrap_or(ForeignPtr(std::ptr::null_mut(), panic))"
395+
, " }"
396+
, "}"
377397
]
378398

379399
{- | This maps a Rust function type into the corresponding 'FunPtr' wrapped

src/Language/Rust/Inline/Marshal.hs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ data MarshalForm
3838
| BoxedIndirect -- ^ value isn't marshallable directly but may be passed indirectly via a 'Ptr'
3939
| ByteString
4040
| ForeignPtr
41+
| OptionalForeignPtr
4142
deriving (Eq)
4243

4344
passByValue :: MarshalForm -> Bool
@@ -61,6 +62,7 @@ ghcMarshallable ty = do
6162
tyconsB <- sequence qTyconsBoxed
6263
bytestring <- [t| ByteString |]
6364
fptrCons <- [t| ForeignPtr |]
65+
maybeCons <- [t| Maybe |]
6466

6567
case ty of
6668
_ | ty `elem` simpleU -> pure UnboxedDirect
@@ -69,6 +71,8 @@ ghcMarshallable ty = do
6971
AppT con _ | con `elem` tyconsU -> pure UnboxedDirect
7072
| con `elem` tyconsB -> pure BoxedDirect
7173
| con == fptrCons -> pure ForeignPtr
74+
AppT mb (AppT fptr _)
75+
| mb == maybeCons && fptr == fptrCons -> pure OptionalForeignPtr
7276
_ -> pure BoxedIndirect
7377
where
7478
qSimpleUnboxed = [ [t| Char# |]

tests/ForeignPtr.hs

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,15 @@ module ForeignPtr where
66

77
import Language.Rust.Inline
88

9+
import Data.Maybe (fromJust)
910
import Data.Word (Word64)
1011
import Foreign (Storable (..))
1112
import Foreign.ForeignPtr
1213
import Foreign.Ptr
1314
import Test.Hspec
1415

1516
extendContext foreignPointers
17+
extendContext prelude
1618
extendContext basic
1719
setCrateModule
1820

@@ -36,11 +38,11 @@ foreignPtrTypes = describe "ForeignPtr types" $ do
3638
withForeignPtr p (`poke` 42)
3739
let prev =
3840
[rust| u64 {
39-
let p = $(p: &mut u64);
40-
let ret = *p;
41-
*p = 43;
42-
ret
43-
} |]
41+
let p = $(p: &mut u64);
42+
let ret = *p;
43+
*p = 43;
44+
ret
45+
} |]
4446
prev `shouldBe` 42
4547
withForeignPtr p peek >>= (`shouldBe` 43)
4648

@@ -56,3 +58,16 @@ foreignPtrTypes = describe "ForeignPtr types" $ do
5658
let p = [rust| ForeignPtr<u64> { Box::new(42).into() }|]
5759
val <- withForeignPtr p peek
5860
val `shouldBe` 42
61+
62+
it "Can marshal optional ForeignPtr returns" $ do
63+
let mp =
64+
[rust| Option<ForeignPtr<u64>> {
65+
None
66+
} |]
67+
mp `shouldBe` Nothing
68+
69+
let mp =
70+
[rust| Option<ForeignPtr<u64>> {
71+
Some(Box::new(42).into())
72+
} |]
73+
withForeignPtr (fromJust mp) peek >>= (`shouldBe` 42)

0 commit comments

Comments
 (0)