The SRC M3 implementation produces pointers for VAR/READONLY parameters. To maintain memory safety GC unsafe code should not treat these compiler produced pointers as a M3 references to heap memory. That is, they may also point to stack resident memory. If the intention of the GC unsafe code is to hold on to the reference, then the M3 interface to the external C code should have used as its parameter type a traced or untraced REF rather than VAR/READONLY. Using the correct interface prevents M3 modules from passing pointers to stack resident data, and serves as documentation that the GC unsafe code may hold on to the reference.
The following example should illustrate what may go wrong when using VAR/READONLY:
<* EXTERNAL *> (* Holds on to elt as a reference *)
PROCEDURE Enqueue(VAR que: Queue.T; VAR elt: QueueEntry.T);
/*
* Insert element at head of queue.
*/
void Enqueue(queue_t que, queue_entry_t elt)
queue_entry_t nxt;
nxt = que->next;
elt->next = nxt;
elt->prev = que;
nxt->prev = elt; /* unsafe if elt is on stack, or if elt is */
que->next = elt; /* traced and not strongref'd, for example */
}
The above C code may cause improper memory usage for future operations
that involve elt. Instead, the proper interface to Enqueue should be:
<* EXTERNAL *> (* Assumes that the REF's are strongref'd *)
PROCEDURE Enqueue(que: REF Queue.T; elt: REF QueueEntry.T)
and
<* EXTERNAL *>
PROCEDURE Enqueue(que: UNTRACED REF Queue.T; elt: UNTRACED REF QueueEntry.T)
The latter two interfaces provide some form of documentation that the C code treats its arguments as heap data.