Previous: ick_create, Up: External Calls to C [Index]
Because the external calls system merges the
INTERCAL NEXT stack with the C return
value and data storage stack (note for pedants: the C standards nowhere
mandate the existence of such a stack, or even mention one, but the
restrictions stated in them imply that implementations have to act as
if such a stack existed, because of the way the scoping rules and
recursion work), the external calls system therefore has severe effects
on data that happens to be stored there. (In
INTERCAL terms, imagine what would happen if data
could be stored on the NEXT stack; if C used the more
sensible system of having a STASH for each variable, these
problems would never occur in the first place, instead causing an
entirely different set of problems.) Similar considerations apply to
the common nonstandard C extension alloca, which
dynamically alters the size of the stack; also, in what goes below,
register variables should be considered to be
auto, because the compiler may choose to allocate them on
the stack. Theoretical considerations would lead one to conclude that
variable-length arrays should obey most of the same restrictions; in
practice, though, it’s unwise to attempt to mix those with
INTERCAL code at all, except by separating them into
separate functions which aren’t flagged with
ICK_EC_FUNC_START and use no ick_-prefixed
identifiers, even indirectly. (They may cause a compile to fail
completely because they don’t mix well with goto.)
In the description below, INTERCAL commands should be taken to include the equivalent C macros.
NEXT/NEXT FROM paired with
RESUME have the least effect, and the most obvious effect,
on auto variables in the function that was
NEXTed from, which is the same effect that the standard C
function longjmp has. That is, alloca storage
stays intact, and auto variables have their values
‘clobbered’ (that is, their value is no longer reliable and
should not be used) if they changed since the corresponding
NEXT and are not marked as volatile. (This is
a very easy restriction to get around, because changing the values of
such variables is quite difficult without using statically-allocated
pointers to point to them (a dubious practice in any case), and
volatile is trivial to add to the declaration.)
COME FROM has more restrictions; it deallocates all
alloca storage in the function that was COME
FROM, and functions that called it or that called functions that
called it, etc., using C calls (as opposed to NEXT), and
those invocations of the functions will cease to exist (thus destroying
any auto variables in them), even in the case of
COMING FROM a function into the same function.
auto variables in the function that is come into will
start uninitialised, even if initialisers are given in their
declaration, and it will be a ‘new’ invocation of that
function. (It is quite possible that the uninitialised values in the
auto variables will happen by chance to have the values
they had in some previous invocation of the function, though, because
they are likely to be stored in much the same region of memory; but it
is highly unwise to rely on this.) Note that volatile will
not help here. Observant or source-code-reading readers may note that
there is a mention of an ick_goto in the source code to
C-INTERCAL; this is undocumented and this manual does not
officially claim that such a macro exists (after all, if it did, what
in INTERCAL could it possibly correspond to?), but
if such a macro does exist it obeys the same restrictions as COME
FROM.
FORGET is the worst of all in terms of preserving data on
the stack; it deallocates alloca data and clobbers or
deletes auto variables in all function invocations that
have come into existence since the NEXT that created the
topmost remaining NEXT stack entry was called, or since
the start of the program if the NEXT stack is emptied, and
the current function will continue in a new invocation.
volatile is useless in preventing this, because the
relevant parts of the stack where the data were stored are deleted by
the command (that’s what FORGET does, remove stack).
If any of these data are required, they have to be backed up into
static storage (variables declared with static or global
variables), or into heap storage (as in with malloc), or
other types of storage (such as temporary files) which are not on the
stack. (Incidentally, suddenly deleting parts of the stack is excellent
at confusing C debuggers; but even RESUME and COME
FROM tend to be sufficient to confuse such debuggers. More
worrying is probably the fact that the C standard provides a portable
method for deleting the stack like that, and in fact the external calls
runtime library is written in standard freestanding-legal C89 (with the
exception of +printflow debug output which requires a
hosted implementation), meaning that in theory it would be possible to
split it out to create an implementation of a C-plus-COME-FROM-and-NEXT
language, and doing so would not be particularly difficult.)
Note that INTERCAL variables are not stored on the C stack, nor are any of the metadata surrounding them, and so are not affected unduly by control flow operations.
Previous: ick_create, Up: External Calls to C [Index]