Frequently Asked Questions

What does $(type1,type2) mean? What does $(expr1, expr2) mean?

Cyclone has tuples, which are anonymous structs with fields numbered
0, 1, 2, …. For example, $(int,string_t) is a pair of an int and a
string_t. An example value of this type is $(4,”cyclone”). To extract
a field from a tuple, you use array-like notation: you write x[0], not
x.0.

What does int @ mean?

In Cyclone @ is a pointer that is guaranteed not to be NULL. The
Cyclone compiler guarantees this through static or dynamic checks. For
example,

int *x = NULL;

is not an error, but

int @x = NULL;

is an error. Note that “int @” is shorthand for the more verbose “int
*@notnull”.

What does int *{37} mean?

This is the type of (possibly-null) pointers to a sequence of at least
37 integers, which can also be written as “int *@numelts(37)”. The
extra length information is used by Cyclone to prevent buffer
overflows. For example, Cyclone will compile x[expr] into code that
will evaluate expr, and check that the result is less than 37 before
accessing the element. Note that int * is just shorthand for int
*{1}. Currently, the expression in the braces must be a compile-time
constant.

What does int *`r mean?

This is the type of a pointer to an int in region `r. This can also
be written as “int *@region(`r)”. A region indicates conceptually
where in memory an object is stored; different regions have different
lifetimes and deallocation strategies, and the aliasing into certain
regions may be restricted. Cyclone uses this region information to
prevent dereferencing a pointer whose storage has been
deallocated. See Memory Management Via Regions for more
information on regions.

What does `H mean?

This is Cyclone’s heap region: objects in this region cannot be
explicitly freed, only garbage-collected. Effectively, this means that
pointers into the heap region can always be safely dereferenced.

What does int @{37}`r mean?

A pointer can come with all or none of the nullity, bound, and region
annotation. This type is the type of non-NULL pointers to at least 37
consecutive integers in region `r. When the bound is omitted it
default to 1.

What is a pointer type’s region when it’s omitted?

Every pointer type has a region; if you omit it, the compiler chooses
a region name you implicitly. The name chosen depends on where the
pointer type occurs. In function arguments, a fresh region variable is
used. In function results and type definitions (including typedef),
the heap region (`H) is used. In function bodies, the compiler looks
at the uses (using unification) to try to determine a region. See
Regions for more
information.

What does int ? mean?

The ? a special kind of pointer that carries along bounds
information. It is a “questionable” pointer: it might be NULL or
pointing out of bounds. An int ? is a pointer to an integer, along
with some information that allows Cyclone to check whether the pointer
is in bounds at run-time. These are the only kinds of pointers that
you can use for pointer arithmetic in Cyclone.

What does `a mean?

`a is a type variable. Type variables are typically used in
polymorphic functions. For example, if a function takes a parameter of
type `a, then the function can be called with a value of any
suitable type. If there are two arguments of type `a, then any call
will have to give values of the same type for those parameters. And if
the function returns a type `a, then it must return a result of the
same type as the the argument. Syntactically, a type variable is any
identifier beginning with ` (backquote).

What is a “suitable” type for a type variable?

The last question said that a type variable can stand for a “suitable”
type. Unfortunately, not all types are “suitable.” Briefly, the
“suitable” types are those that fit into a general-purpose machine
register, typically including int, and pointers. Non-suitable types
include float, struct types (which can be of arbitrary size), tuples,
and questionable pointers. Technically, the suitable types are the
types of “box kind,” described below.

How do I cast from void *?

You can’t do this in Cyclone. A void * in C really does not point to
void, it points to a value of some type. However, when you cast from a
void * in C, there is no guarantee that the pointer actually points
to a value of the expected type. This can lead to crashes, so Cyclone
doesn’t permit it. Cyclone’s polymorphism and tagged unions can often
be used in places where C needs to use void *, and they are
safe. Note that you can generally cast to a void * in Cyclone, you
just won’t be able to cast back.

What does _ (underscore) mean in types?

Underscore is a “wildcard” type. It stands for some type that the
programmer doesn’t want to bother writing out; the compiler is
expected to fill in the type for the programmer. Sometimes, the
compiler isn’t smart enough to figure out the type (you will get an
error message if so), but usually there is enough contextual
information for the compiler to succeed. For example, if you write

_ x = new Pair(3,4);

the compiler can easily infer that the wildcard stands for struct Pair
@. In fact, if x is later assigned NULL, the compiler will infer that
x has type struct Pair * instead.

Note that only in restricted cases is _ allowed as part of top-level
declarations.

What do `a::B, `a::M, `a::A, `a::R, `a::Q and `a::E mean?

Types are divided into different groups, which we call kinds. There
are six kinds: B (for Box), M (for Memory), A (for Any), E (for
Effect), R (for Region), and Q (for alias-Qualifier). The notation
typevar::kind says that a type variable belongs to a kind. A type
variable can only be instantiated by types that belong to its kind.

What does it mean when type variables don’t have explicit kinds?

Every type variable has a kind, but usually the programmer doesn’t
have to write it down. In function prototypes, the compiler will infer
the most permissive kind. For example,

void f(`a *`b x, `c * y, `a z);

is shorthand for

void f(`a::B *`b::R x, `c::M * y, `a::B z)

In type definitions, no inference is performed: an omitted kind is
shorthand for ::B. For example,

struct S<`a,`r::R> { `a *`r x; };

is shorthand for

struct S<`a::B,`r::R> { `a *`r x;};

but

struct S<`a,`r>{`a *`r x;};

is not.

What does struct List<`a,`r::R> mean?

struct List takes a type of box kind and a region and produces a
type. For example, struct List<int, `H> is a type, and struct
List<struct List<int,`H>@, `H> is a type. struct List<`a,`r::R>
is a list whose elements all have type `a and live in region `r.

What is a @tagged union?

In C, when a value has a union type, you know that in fact it has one
of the types of the union’s fields, but there is no guarantee which
one. This can lead to crashes in C. Cyclone’s @tagged unions are like
C unions with some additional information (a tag) that lets the
Cyclone compiler determine what type the underlying value actually
has, thus helping to ensure safety.

What is “abstract”?

abstract is a storage-class specifier, like static or extern. When
attached to a top-level type declaration, it means that other files
can use the type but cannot look at the internals of the type (e.g.,
other files cannot access the fields of an abstract struct).
Otherwise, abstract has the same meaning as the auto (default) storage
class. Hence abstract is a way to state within a Cyclone file that a
type’s representation cannot be exported.

What are the Cyclone keywords?

In addition to the C keywords, the following have special meaning and
cannot be used as identifiers: abstract, catch, datatype, fallthru,
let, malloc, namespace, new, NULL, region_t, regions, rmalloc, rnew,
throw, try, using. As in gcc, attribute is reserved as well.

What are “namespace” and “using”?

These constructs provide a convenient way to help avoid name
clashes. namespace X prepends X:: to the declarations in its body
(rest of file in case of namespace X;) and using X makes the
identifiers prepended with X:: available without having to write the
X::.

What is “fallthru”?

In Cyclone, you cannot implicitly fall through from one switch case to
the next (a common source of bugs in C). Instead, you must explicitly
fall through with a fallthru statement. So, to port C code, place
fallthru; at the end of each case that implicitly falls through; note
that fallthru may not appear in the last case of a switch.

fallthru is useful for more than just catching bugs. For instance, it
can appear anywhere in a case; its meaning is to immediately goto the
next case. Second, when the next case of the switch has pattern
variables, a fallthru can (and must) be used to specify expressions
that will be bound to those variables in the next case. Hence fallthru
is more powerful (but more verbose) than “or patterns” in ML.

What is “new”?

new expr allocates space in the heap region, initializes it with the
result of evaluating expr, and returns a pointer to the space. It is
roughly equivalent to

type @temp = malloc(sizeof(type));
*temp = expr;

where type is the type of expr. You can also write

new { for i < expr1 : expr2 }

to heap-allocate an array of size expr1 with the ith element
initialized to expr2 (which may mention i).

How do I use tuples?

A tuple type is written $(type0, …, typen). A value of the
type is constructed by $(expr0, …, exprn), where expri has
type typei. If expr has type $(type0, …, typen), you can
extract the component i using expr[i]. The expression in the
brackets must be a compile-time constant. In short, tuples are like
anonymous structs where you use expr[i] to extract fields instead of
expr.i. There is no analogue of the -> syntax that can be used with
pointers of structs; if expr has type $(type1, …, typen) *,
you can extract component i by (*expr)[i].

What is { for i < expr1 : expr1 }?

This is an array initializer. It can appear where array initializers
appear in C, and it can appear as the argument to new. It declares an
identifier (in this case, i) whose scope is expr2. expr1 is an
expression which is evaluated to an unsigned integer giving the
desired size of the array. The expression expr2 is evaluated expr1
times, with i ranging over 0, 1, …, expr1-1; the result of each
evaluation initializes the ith element of the array.

The form new {for i < expr1 : expr2} allocates space for a new
array and initializes it as just described. This form is the only way
to create arrays whose size depends on run-time information. When {for
i < expr1 : expr2} is not an argument to new, expr1 must be
constant and expr2 may not mention i. This restriction includes all
uses at top-level (for global variables).

How do I throw and catch exceptions?

A new exception is declared as in

datatype exn { MyExn };

The exception can be thrown with the statement

throw MyExn;

You can catch the expression with a try/catch statement:

try statement1 catch { case MyExn: statement2 }

If statement1 throws an MyExn and no inner catch handles it, control
transfers to statement2.

The catch body can have any number of case clauses. If none match, the
exception is re-thrown.

Exceptions can carry values with them. For example, here’s how to
declare an exception that carries an integer:

datatype exn { MyIntExn(int) };

Values of such exceptions must be heap-allocated. For example, you can
create and throw a MyIntExn exception with

throw new MyIntExn(42);

To catch such an exception you must use an &-pattern:

try statement1
catch {
case &MyIntExn(x): statement2
}

When the exception is caught, the integer value is bound to x.

The exn type is just a pre-defined @extensible datatype
type. Therefore, all the standard rules for extending, creating
objects, and destructing objects of a datatype apply.

How efficient is exception handling?

Entering a try block is implemented using setjmp. Throwing an
exception is implemented with longjmp. Pattern-matching a datatype
against each case variant in the catch clause is a
pointer-comparsion. In short, exception handling is fairly
lightweight.

What does “let” mean?

In Cyclone, let is used to declare variables. For example,

let x,y,z;

declares the three variables x, y, and z. The types of the variables
do not need to be filled in by the programmer, they are filled in by
the compiler’s type inference algorithm. The let declaration above is
equivalent to

_ x;
_ y;
_ z;

There is a second kind of let declaration, with form

let pattern = expr;

It evaluates expr and matches it against pattern, initializing the
pattern variables of pattern with values drawn from expr. For
example,

let x = 3;

declares a new variable x and initializes it to 3, and

let $(y,z) = $(3,4);

declares new variables y and z, and initializes y to 3 and z to 4.

What is a pattern and how do I use it?

Cyclone’s patterns are a convenient way to destructure aggregate
objects, such as structs and tuples. They are also the only way to
destructure datatypes. Patterns are used in Cyclone’s let
declarations, switch statements, and try/catch statements.

What does _ mean in a pattern?

It is a wildcard pattern, matching any value. For example, if f is a
function that returns a pair, then

let $(_,y) = f(5);

is a way to extract the second element of the pair and bind it to a
new variable y.

What does it mean when a function has an argument with type `a?

Any type that looks like \(backquote) followed (without whitespace)
by an identifier is a type variable. If a function parameter has a
type variable for its type, it means the function can be called with
any pointer or with an int. However, if two parameters have the same
type variable, they must be instantiated with the same type. If all
occurrences of \a appear directly under pointers (e.g., `a *), then an
actual parameter can have any type, but the restrictions about using
the same type still apply. This is called parametric polymorphism,
and it’s used in Cyclone as a safe alternative to casts and void *.

Do functions with type variables get duplicated like C++ template functions? Is there run-time overhead for using type variables?

No and no. Each Cyclone function gives rise to one function in the
output, and types are not present at run-time. When a function is
called, it does not need to know the types with which the caller is
instantiating the type variables, so no instantiation actually
occurs—the types are not present at run-time. We do not have to
duplicate the code because we either know the size of the type or the
size does not matter. This is why we don’t allow type variables of
memory kind as parameters—doing so would require code duplication or
run-time types.

Can I use varargs?

Yes, Cyclone has a way of supporting variable-argument functions. It
is not quite the same as C’s, but it is safe. For instance, we have
written type-safe versions of printf and scanf all within Cyclone. See
Varargs for more information.

Why can’t I declare types within functions?

We just haven’t implemented this support yet. For now, you need to
hoist type declarations and typedefs to the top-level.

What casts are allowed?

Cyclone doesn’t support all of the casts that C does, because
incorrect casts can lead to crashes. Instead, Cyclone supports a safe
subset of C’s casts. Here are some examples.

All of C’s numeric casts, conversions, and promotions are unchanged.

You can always cast between type@{const-expr},
type*{const-expr}, and type ?. A cast from type ? to one of the
other types includes a run-time check that the pointer points to a
sequence of at least const-expr objects. A cast to
type@{const-expr}from one of the other types includes a run-time
check that the pointer is not NULL. No other casts between these type
have run-time checks. A failed run-time check throws Null_Exception. A
pointer into the heap can be cast to a pointer into another region. A
pointer to a struct or tuple can be cast to a pointer to another
struct or tuple provided the “target type” is narrower (it has fewer
fields after “flattening out” nested structs and tuples) and each
(flattened out) field of the target type could be the target of a cast
from the corresponding field of the source type. A pointer can be cast
to int. The type type*{const-expr1}can be cast to
type*{const-expr2}provided const-expr2 < const-expr1, and
similarly for type@{const-expr1}and type@{const-expr2}.

An object of type datatype T.A @ can be cast to datatype T @. The
current implementation isn’t quite as lenient as it should be. For
example, it rejects a cast from int *{4} to $(int,int)*{2}, but this
cast is safe.

For all non-pointer-containing types type, you can cast from a
type ? to a char ?. This allows you to make frequent use of memcpy,
memset, etc.

Why can’t I implicitly fall-through to the next switch case?

We wanted to add an explicit fallthru construct in conjunction with
pattern matching, and we decided to enforce use of fallthru in all
cases because this is a constant source of bugs in C code.

Do I have to initialize global variables?

You currently must provide explicit initializers for global variables
that may contain pointers, so that the compiler can be sure that
uninitialized memory containing pointers is not read. In the future,
we expect to provide some support for initializing globals in
constructor functions.

Two techniques help with initializing global arrays. First, if an
array element could be 0 or NULL, the compiler will insert 0 for any
elements you do not specify. For example, you can write

int x[37];

to declare a global array x initialized with 37 elements, all
0. Second, you can use the comprehension form

int x[37] = { for i < expr1 : expr2 }

provided that expr1 and expr2 and constant expressions. Currently,
expr2 may not use the variable i, but in the future it will be able
to. Note that it is not possible to have a global variable of an
abstract type because it is impossible to know any constant expression
of that type.

Are there threads?

Cyclone does not yet have a threads library and some of the libraries
are not re-entrant. In addition, because Cyclone uses unboxed structs
of three words to represent fat pointers, and updating them is not an
atomic operation, it’s possible to introduce unsoundnesses by adding
concurrent threads. However, in the future, we plan to provide support
for threads and a static analysis for preventing these and other forms
of data races.

Can I use “setjmp” and “longjmp”?

No. However, Cyclone has exceptions, which can be used for non-local
control flow. The problem with setjmp and longjmp is that safety
demands we prohibit a longjmp to a place no longer on the stack. A
future release may have more support for non-local control flow.

What types are allowed for union members?

Currently, union members can be just about any type, other than those
with kind (A) (see question on kinds, above). Examples include numeric
types (including bit fields and enumerations), structs and tuples of
allowable union-member types, and other unions. However, if a union
contains a pointer type, you can only write the pointer, not read
it. This prevents effectively casting an int to a pointer by writing
an int member and then reading the pointer, for example. To use
pointers as normal within a union, you must use @tagged unions.

Why can’t I do anything with values of type void?

Because we cannot know the size of an object pointed to by a pointer
of type void *, we prohibit derefencing the pointer or casting it to a
different pointer type. To write code that works for all pointer
types, use type variables and polymorphism. Tagged unions can also
substitute in some cases where void * is used in C.

What is “aprintf”?

The aprintf function is just like printf, but the output is placed in
a new string allocated on the heap. Note that you can use the more
general function rprintf to allocate the output in a region of your
choosing.

How do I access command-line arguments?

The type of main should be

int main(int argc, char ?? argv);

As in C, argc is the number of command-line arguments and argv[i] is a
string with the ith argument. Unlike C, argv and each element of
argv carry bounds information. Note that argc is redundant—it is
always equal to numelts(argv).

Why can’t I pass a stack pointer to certain functions?

If the type of a function parameter is a pointer into the heap region,
it cannot be passed a stack parameter. Pointer types in typedef and
struct definitions refer to the heap region unless there is an
explicit region annotation.

Why do I get an incomprehensible error when I assign a local’s address to a pointer variable?

If the pointer variable has a type indicating that it points into the
heap, then the assignment is illegal. Try initializing the pointer
variable with the local’s address, rather than delaying the assignment
until later.

How much pointer arithmetic can I do?

On fat pointers, you can add or subtract an int (including via
increment/decrement), as in C. It is okay for the result to be outside
the bounds of the object pointed to; it is a run-time error to
dereference outside of the bounds. (The compiler inserts bounds
information and a run-time check; an exception is thrown if the check
fails.) You can also do pointer arithmetic on zero-terminated
pointers. Currently, we do not support pointer arithmetic on the other
pointer types. As in C, you can subtract two pointers of the same
type; the type of the result is unsigned int.

What is the type of a literal string?

The type of the string constant “foo” is const char @{4} (remember the
trailing null character). However, there are implicit casts from char
@{4} to char @{2}, char *{4}, and char ?, so you shouldn’t have to
think too much about this.

Are strings NUL-terminated?

Cyclone follows C’s lead on this. String literals like “foo” are
NUL-terminated. Many of the library functions consider a NUL character
to mark the end of a string. And library functions that return strings
often ensure that they are NUL terminated. However, there is no
guarantee that a string is NUL terminated. For one thing, as in C, the
terminating NUL may be overwritten by any character. In C this can be
exploited to cause buffer overflows. To avoid this in Cyclone, strings
generally have type char ?, that is, they carry bounds information. In
Cyclone a string ends when a NUL character is found, or when the
bounds are exceeded.

Did you just misspell NULL in the last question and answer?

No.

How do I use malloc?

malloc is a Cyclone primitive, not a library function. Currently it
has an extremely restricted syntax: You must write
malloc(sizeof(type)). The result has type type@, so usually there
is no need to explicitly cast the result (but doing so is
harmless). Usually the construct new expr is more convenient than
malloc followed by initialization, but malloc can be useful for
certain idioms and when porting C code.

Notice that you cannot (yet) use malloc to allocate space for arrays
(as in the common idiom, malloc(n*sizeof(type)). Also, the
type-checker uses a conservative analysis to ensure that the fields of
the allocated space are written before they are used.

Can I call free?

Yes, but there are restrictions. If you are using a unique pointer,
free will work as expected (but there are restrictions on using unique
pointers). If you want to free a non-unique pointer, we have
implemented free as a no-op, with type

void free(`a::A ?);

The actual reclamation has to be done by the garbage collector or
region system.

Is there a garbage collector?

Yes, we use the Boehm-Demers-Weiser conservative collector. If you
don’t want to use the garbage collector (e.g., because you know that
your program does little or no heap allocation), you can use the -nogc
flag when linking your executable. This will make the executable
smaller.

If you link against additional C code, that code must obey the usual
rules for conservative garbage collection: no wild pointers and no
calling malloc behind the collector’s back. Instead, you should call
GC_malloc. See the collector’s documentation for more information.

Note that if you allocate all objects on the stack, garbage collection
will never occur. If you allocate all objects on the stack or in
regions, it is very unlikely collection will occur and nothing will
actually get collected.

How can I make a stack-allocated array?

As in C, you declare a local variable with an array type. Also as in
C, all uses of the variable, except as an argument to sizeof and &,
are promoted to a pointer. If your declaration is

int x[256];

then uses of x have type int @`L{256} where L is the name of the block
in which x is declared. (Most blocks are unnamed and the compiler just
makes up a name.)

Stack-allocated arrays must be initialized when they are declared
(unlike other local variables). Use an array-initializer, as in

int y[] = { 0, 1, 2, 3 };
int z[] = { for i < 256 : i };

To pass (a pointer to) the array to another function, the function
must have a type indicating it can accept stack pointers, as explained
elsewhere.

Can I use salloc or realloc?

Currently, we don’t provide support for salloc. For realloc, we do
provide support, but only on heap-allocated char ? buffers.

Why do I have to cast from * to @ if I’ve already tested for NULL?

Our compiler is not as smart as you are. It does not realize that you
have tested for NULL, and it insists on a check (the cast) just to be
sure. You can leave the cast implicit, but the compiler will emit a
warning. We are currently working to incorporate a flow analysis to
omit spurious warning. Because of aliasing, threads, and undefined
evaluation order, a sound analysis is non-trivial.

Why can’t a function parameter or struct field have type `a::M?

Type variables of memory kind can be instantiated with types of any
size. There is no straightforward way to compile a function with an
argument of arbitrary size. The obvious way to write such a function
is to manipulate a pointer to the arbitrary size value instead. So
your parameter should have type `a::M * or `a::M @.

Can I see how Cyclone compiles the code?

Just compile with flags -save-c and -pp. This tells the compiler to
save the C code that it builds and passes to gcc, and print it out
using the pretty-printer. You will have to work to make some sense out
of the C code, though. It will likely contain many extern declarations
(because the code has already gone through the preprocessor) and
generated type definitions (because of tuples, tagged unions, and
questionable pointers). Pattern-matching code gets translated to a
mess of temporary variables and goto statements. Array-bounds checks
and NULL checks can clutter array-intensive and pointer-intensive
code. And all typedefs are expanded away before printing the output.

Can I use gdb on the output?

You can run gdb, but debugging support is not all the way there
yet. By default, source-level debugging operations within gdb will
reference the C code generated by the Cyclone compiler, not the
Cyclone source itself. In this case, you need to be aware of three
things. First, you have to know how Cyclone translates top-level
identifiers to C identifiers (it prepends Cyc_ and separates
namespaces by _ instead of ::) so you can set breakpoints at
functions. Second, it can be hard to print values because many Cyclone
types get translated to void *. Third, we do not yet have source
correlation, so if you step through code, you’re stepping through C
code, not Cyclone code.

To improve this situation somehwat, you can compile your files with
the option -lineno. This will insert #line directives in the generated
C code that refer to the original Cyclone code. This will allow you to
step through the program and view the Cyclone source rather than the
generated C. However, doing this has two drawbacks. First, it may
occlude some operation in the generated C code that is causing your
bug. Second, compilation with -lineno is significantly slower than
without. Finally, the result is not bug-free; sometimes the debugger
will fall behind the actual program point and print the wrong source
lines; we hope to fix this problem soon.

Two more hints: First, on some architectures, the first memory
allocation appears to seg fault in GC_findlimit. This is correct and
documented garbage-collector behavior (it handles the signal but gdb
doesn’t know that); simply continue execution. Second, a common use of
gdb is to find the location of an uncaught exception. To do this, set
a breakpoint at throw (a function in the Cyclone runtime).

Can I use gprof on the output?

Yes, just use the -pg flag. You should also rebuild the Cyclone
libraries and the garbage collector with the -pg flag. The results of
gprof make sense because a Cyclone function is compiled to a C
function.

Notes for Cygwin users: First, the versions of libgmon.a we have
downloaded from cygnus are wrong (every call gets counted as a
self-call). We have modified libgmon.a to fix this bug, so download
our version and put it in your cygwin/lib directory. Second, timing
information should be ignored because gprof is only sampling 100 or
1000 times a second (because it is launching threads instead of using
native Windows profiling). Neither of these problems are
Cyclone-specific.

Is there an Emacs mode for Cyclone?

Sort of. In the doc/ directory of the distribution you will find a
font-lock.el file and elisp code (in cyclonedotemacs.el) suitable
for inclusion in your .emacs file. However, these files change C++
mode and use it for Cyclone rather than creating a new Cyclone
mode. Of course, we intend to make our own mode rather than destroy
C++-mode’s ability to be good for C++. Note that we have not changed
the C++ indentation rules at all; our elisp code is useful only for
syntax highlighting.

Does Cyclone have something to do with runtime code generation?

Cyclone has its roots in Popcorn, a language which was safe but not as
compatible with C. An offshoot of Popcorn added safe runtime code
generation, and was called Cyclone. The current Cyclone language is a
merger of the two, refocused on safety and C compatibility. Currently,
the language does not have support for runtime code generation.

What platforms are supported?

You need a platform that has gcc, GNU make, ar, sed, either bash or
ksh, and the ability to build the Boehm-Demers-Weiser garbage
collector. Furthermore, the size of int and all C pointers must be the
same. We actively develop Cyclone in Cygwin (a Unix emulation layer
for Windows 98, NT, 2K), Linux, and Mac OS X. Versions have run on
OpenBSD and FreeBSD.

Why aren’t there more libraries?

We are eager to have a wider code base, but we are compiler writers
with limited resources. Let us know of useful code you write.

Why doesn’t List::imp_rev(l) change l to its reverse?

The library function List::imp_rev mutates its argument by reversing
the tl fields. It returns a pointer to the new first cell (the old
last cell), but l still points to the old first cell (the new last
cell).

Can I inline functions?

Functions can be declared inline as in ISO C99. You can get additional
inlining by compiling the Cyclone output with the -O2 flag. Whether a
function is inlined or not has no effect on Cyclone type-checking.

If Cyclone is safe, why does my program crash?

There are certain classes of errors that Cyclone does not attempt to
prevent. Two examples are stack overflow and various numeric traps,
such as division-by-zero. It is also possible to run out of
memory. Other crashes could be due to compiler bugs or linking against
buggy C code (or linking incorrectly against C code).

Note that when using gdb, it may appear there is a seg fault in
GC_findlimit(). This behavior is correct; simply continue execution.

What are compile-time constants?

Cyclone’s compile-time constants are NULL, integer and character
constants, and arithmetic operations over compile-time
constants. Unlike C, sizeof(t) is not an integral constant expression
in our current implementation of Cyclone because our compiler does not
know the actual size of aggregate types; we hope to repair this in a
future version. Constructs requiring compile-time constants are:
tuple-subscript (e.g., x[3] for tuple x), sizes in array declarations
(e.g., int y[37]), and sizes in pointer bounds (e.g., int * x{124}).

How can I get the size of an array?

If expr is an array, then numelts(expr) returns the number of
elements in the array. If expr is a pointer to an array,
numelts(expr) returns the number of elements in the array pointed
to. If expr is a fat pointer, then the number of elements is
calculated at runtime from the bounds information contained in the fat
pointer. For other types, the size is determined at compile-time.