[m-users.] Documenting univ data passing to and from the C FFI

Julien Fischer jfischer at opturion.com
Thu May 20 15:50:23 AEST 2021


Hi Fabrice,

On Wed, 19 May 2021, fabrice nicol wrote:

> 1. In the Reference manual FFI section, there is no hint as to how to pass 'univ' values to and from the C FFI.

Sure there is:

     "Mercury variables of any other type are passed as a MR_Word"

That covers univs as well.

> Looking into 'runtime/mercury_univ.h', I found 'MR_unravel_univ' and 'MR_new_univ_on_hp'.
> 
> These two macros are pretty straightforward to use so that I think
> there is no obvious reason not to mention them in 15.3.1 "C data
> passing convention".
> 
> All the more so as there seem to be caveats (like I could not use NULL
> in the second 'typeinfo' argument, even if it is actually a pointer).

Why do you need to pass univs across the Mercury-C boundary?
Why do you need to unpack them in C code?

> 2. It would also be nice to minimally document how to access say, at least, the 'MR_type_ctor_name' field of MR_TypeInfo.

The details of the RTTI system are deliberately not documented at the
target language level since they are (and have been) subject to change.
If you want to manipulate such things then the supported interface is in
Mercury (in the standard library modules, type_desc, construct,
deconstruct etc).

> I've gone through some difficulties retrieving that knowledge on my own (ok, it is part of the learning experience). The following code builds all right into an executable:
> 
> 
> %--------------------------------------------------------------------------------------------------------------------------------------%
> 
> :- module example.                                                                                                                                                                  
> :- interface.                                                                                                                                                                       
> :- import_module  io.                                                                                                                                                               
>                                                                                                                                                                                     
> :- pred main(io::di, io::uo) is det.                                                                                                                                                
>                                                                                                                                                                                     
> :- implementation.                                                                                                                                                                  
> :- import_module univ.                                                                                                                                                              
> :- pragma foreign_decl("C", "#include ""mercury_univ.h""").                                                                                                                         
> :- pred test(univ::in, float::out, string::out) is det.                                                                                                                             
>                                                                                                                                                                                     
> :- pragma foreign_proc("C",                                                                                                                                                         
>     test(X::in, Y::out, Ty::out),                                                                                                                                                   
>     [promise_pure],                                                                                                                                                                 
> "                                                                                                                                                                                   
>     MR_TypeInfo ti;                                                                                                                                                                 
>     MR_Word value;                                                                                                                                                                  
>     MR_unravel_univ(X, ti, value);                                                                                                                                                  
>     Y = MR_word_to_float(value);                                                                                                                                                    
>     MR_ConstString s = ((MR_TypeCtorInfo)(ti->MR_ti_type_ctor_info))->MR_type_ctor_name;                                                                                            
>     Ty = s;                                                                                                                                                                         
> ").

I _really_ wouldn't do things like that.
                                                                                                                                                                                 
> main(!IO) :-                                                                                                                                                                        
>    test(univ(2.0), Y, Ty), write_string(Ty, !IO), write_float(Y, !IO).
> 
> %--------------------------------------------------------------------------------------------------------------------------------------%
> 
> Yet segfaults at runtime:
> 
> "*** Mercury runtime: caught segmentation violation ***                                                                                                                              
> cause: address not mapped to object                                                                                                                                                 
> PC at signal: 94533097284735 (55fa33aee47f)                                                                                                                                         
> address involved: 0x28                                                                                                                                                              
> This may have been caused by a stack overflow, due to unbounded recursion.                                                                                                          
> exiting from signal handler."
> 
> I finally found the way out using:
> 
> %--------------------------------------------------------------------------------------------------------------------------------------%
> 
> (...)
> 
>  MR_ConstString s = (MR_TYPEINFO_GET_TYPE_CTOR_INFO(ti))->MR_type_ctor_name;
> 
> (...)
> 
> %--------------------------------------------------------------------------------------------------------------------------------------%
> 
> and looking at the code for the macro, one understands why the above executable segfaults at runtime.
> 
> Yet this is pretty much implementation engineering and it would be nice to save users from perusing the runtime C code to get a clue.

If you have to pack values in univs like that you would be far better
implementing test/3 as a Mercury predicate and exporting that to C,
e.g.

     :- pragma foreign_export("C". test(in, out, out), "exported_test").

     test(Univ, Float, TypeCtorName) :-
         TypeDesc = univ.univ_type(Univ),
         type_desc.type_ctor_and_args(TypeDesc, TypeCtorDesc, _),
         TypeCtorName = type_desc.type_ctor_name(TypeCtorDesc),
         univ.det_univ_to_type(Univ, Float)

(Although, obviously, if we know the univs contain floats, there's not
much point wrapping them in univs in the first place -- I assume that
are attempting something a bit more general in practice?)

Julien.


More information about the users mailing list