[m-users.] Using MR_list_cons inside of C functions

M McDonough foolkingcrown at gmail.com
Wed Jul 19 11:48:55 AEST 2023


I have a program that uses a separate C library to load data, which is
then turned into Mercury data types using foreign procs.

One of the data structures is a tree, where child nodes are stored in
a list. The general structure is this (with simplified data types):

```
% A node with some values in it, and a set of zero or more child nodes.
:- type node ---> node(int, list.list(node)).

% A wrapper so that we can construct nodes from foreign code.
:- func init_node(int, list.list(node)) = node.
init_node(I, Children) = node(I, Children).
:- pragma foreign_export("C", init_node(in, in) = (out), "MakeNode").

% The interface between the C library and the Mercury data structures.
:- pragma foreign_decl("C", "
// Assume this comes from the header for the C library that loads data.
struct C_Node{
    int i;
    struct C_Node *children;
    unsigned num_children;
};
struct C_Node *load();
// This is defined in the Mercury source file, inside a foreign_code.
MR_Word CreateNode(const struct C_Node *c_node);
    ").

:- pragma foreign_code("C", "
// Note that this is NOT a foreign_proc, but it is recursive, it
// calls Mercury code, and it also uses MR_list_cons.
MR_Word CreateNode(const struct C_Node *c_node){
    MR_Integer i;
    MR_Word child_nodes = MR_list_empty();
    // Iterate in reverse because we are constructing a list in reverse.
    i = c_node->num_children;
    while(i--){
        child_nodes = MR_list_cons(
            CreateNode(node->children + i),
            child_nodes);
    }
    // Create a Mercury node.
    return MakeNode(node->i, child_nodes);
}
    ").

% Wrapper around the foreign `load` function, and then creates
% the Mercury type from that result.
:- pred load(node::out, io.io::di, io.io::uo) is det.
:- pragma foreign_proc("C",
    load(Node::out, _IOi::di, _IOo::uo),
    [may_call_mercury, thread_safe, promise_pure],
    "
    struct C_Node *node = load();
    Node = CreateNode(node);
    ").

```

I'm having an issue where, in the low-level C grades only, this
repeatably causes certain tree structures to have grandchild node
lists be returned by some calls to MR_list_cons.

After some debugging, I now suspect that I know the answer, and it's
that MR_list_cons isn't safe to call directly outside of a
foreign_proc because it can interact with the Mercury register usage.
I fixed the problem by just including a func that performs the cons,
and using foreign_export on that. I was hoping to avoid that, because
it's getting a bit tiresome to expose all these functors and cons
expressions as their own Mercury items and then exporting them.

I'd just like to check if I'm correct about this property of
MR_list_cons. I'd suggest that whatever the case is, this should
probably be documented.


More information about the users mailing list