
|
"http://www.w3.org/TR/REC-html40/loose.dtd">
Frequently Asked Questions
B Frequently Asked Questions
What does $(type1,type2) mean? What does $(expr1, expr2) mean? What does int @ mean? What does int *{37} mean? What does int *`r mean? What does `H mean? What does int @{37}`r mean? What does int ? mean? What is a pointer type's region when it's omitted? What does `a mean? What is a ``suitable'' type for a type variable? How do I cast from void *? What does _ (underscore) mean in types? What do `a::B, `a::M, `a::A, `a::R, and `a::E mean? What does it mean when type variables don't have explicit kinds? What does struct List<`a,`r::R> mean? What are tunion and xtunion? What is abstract? What are the Cyclone keywords? What are namespace and using? What is fallthru? What is new? How do I use tuples? What is {for i < expr1 : expr2}? How do I throw and catch exceptions? How efficient is exception handling? What does let mean? What is a pattern and how do I use it? What does _ mean in a pattern? What does it mean when a function has an argument with type `a? Do functions with type variables get duplicated like C++ template functions? Is there run-time overhead for using type variables? Can I use varargs? Why can't I declare types within functions? What casts are allowed? Why can't I implicitly fall-through to the next switch case? Do I have to initialize global variables? Are there threads? Can I use setjmp and longjmp? What types are allowed for union members? Why can't I do anything with values of type void *? What is aprintf? How do I access command-line arguments? Why can't I pass a stack pointer to certain functions? Why do I get an incomprehensible error when I assign a local's address to a pointer variable? How much pointer arithmetic can I do? What is the type of a literal string? Are strings null-terminated? How do I use malloc? Can I call free? Is there a garbage collector? How can I make a stack-allocated array? Can I use salloc or realloc? Why do I have to cast from * to @ if I've already tested for NULL? Why can't a function parameter or struct field have type `a::M? Can I see how Cyclone compiles the code? Can I use gdb on the output? Can I use gprof on the output? Is there an Emacs mode for Cyclone? Does Cyclone have something to do with runtime code generation? What platforms are supported? Why aren't there more libraries? Why doesn't List::imp_rev(l) change l to its reverse? Can I inline functions? If Cyclone is safe, why does my program crash? What are compile-time constants? How can I get the size of an array?
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.
-
In Cyclone @ is a pointer that is guaranteed not to be
NULL. The Cyclone compiler guarantees through static or
dynamic checks. For example,
int *x = NULL;
is not an error, but
int @x = NULL;
is an error
What does int *{37} mean? |
-
This is the type of pointers to a sequence of at least 37 integers.
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.
-
This is the type of a pointer to an int in region
`r. A region is just a group of objects with the same
lifetime---all objects in a region are freed at once. Cyclone uses
this region information to prevent dereferencing a pointer into a
previously freed region. Regions can have a ``nested'' structure, for
example, if the region for a function parameter is a variable, then
the function may assume that the parameter points into a region whose
lifetime includes the lifetime of the function.
-
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; conceptually, objects in the heap last ``forever,''
since they are always available if needed; garbage collection is
like an optimization that frees objects after they are no longer
needed.
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 puts it in for you implicitly. The region added depends on
where the pointer type occurs. In function arguments, a new region
variable is used. In function results and type definitions (inlcuding
typedef), the heap region (`H) is used. In function
bodies, the compiler looks at the uses (using unification) to try to
determine a region.
-
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.
-
`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,
pointers,
tunion types, and xtunion types. 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.
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 _ is not allowed as part of top-level declarations.
What do `a::B, `a::M, `a::A, `a::R, and `a::E mean? |
-
Types are divided into different groups, which we call kinds. There
are five different kinds: B (for Box), M (for Memory), A (for Any), R
(for Region), and E (for Effect). 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.
Box types include int, pointers (except for questionable
pointers) tagged unions, and extensible tagged unions.
Memory types include all box types, tuples, tunion and
xtunion variants, questionable pointers, and non-abstract structs.
Any types include all types that don't have kind Region or Effect.
Region types are regions, i.e., the heap and stack regions.
Effect types are sets of regions (these are explained elsewhere).
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 are tunion and xtunion? |
-
These are Cyclone's tagged union and extensible tagged union types.
In C, when a value has 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 that lets the
Cyclone compiler determine what type the underlying value actually
has, thus helping to ensure safety.
-
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,
codegen, cut, fallthru, fill,
let, malloc,
namespace, new, NULL,
region_t, regions,
rmalloc, rnew, splice,
throw, try, tunion, using,
xtunion. 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::.
-
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.
-
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).
-
A tuple type is written
$(type1, ..., typen).
A value of the type is constructed by
$(expr1, ..., exprn).
where expri has type typei.
If expr has type
$(type1, ..., 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 : expr2}? |
-
This is an array initializer. It can appear where array
initializers appear in C, and it can appear as the argument to
new.
i
is an identifier. e1 is an unsigned int indicating the size of the
array. e2 is evaluated e1 times, with i having values 0, 1, ..., e1-1
and the result initializes the ith element of the array. The form new
{for i < e1 : e2} 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 < e1:
e2} is not an argument to new, e1 must be constant and e2 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
xtunion 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:
xtunion 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 xtunion type.
Therefore, all the standard rules for extending, creating objects, and
destructing objects of an xtunion type apply.
How efficient is exception handling? |
-
Entering a try block is implemented using setjmp.
Throwing an exception is implemented with longjmp.
Pattern-matching an
xtunion against each case variant in the catch clause is a
pointer-comparsion. In short, exception handling is fairly
lightweight.
-
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 tagged unions. 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 (eg. `a
*), then an actual parameter can have any type, but the restrictions
about using the same type still apply. In general, Cyclone has
parametric polymorphism 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.
-
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 the documentation on 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.
-
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},
type*{const}, 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 objects.
A cast to
type@{const}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*{const1}can be cast to
type*{const2}provided
const2 < const1, and similarly for
type@{const1}and
type@{const2}.
An object of type tunion T.A can be cast to tunion T
if A does not carry values. An object of type tunion
T.A@ can be cast to tunion T if A does
carry values. 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.
-
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 cannot contain pointers. You can
have numeric types (including bit fields and enumerations), structs
and tuples of allowable union-member types, and other 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.
-
The aprintf function is just like printf, but
the output is placed in a new string allocated on the heap.
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 argv.size.
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 initializng the pointer variable
with the local's address, rather than delaying the assignment until later.
How much pointer arithmetic can I do? |
-
On ``questionable'' pointers (pointers with type
type?), 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.) 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 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 null-terminated? |
-
Cyclone follows C's lead on this. String literals like "foo"
are null-terminated. Many of the library functions consider a null
character to mark the end of a string. And library functions that
return strings often ensure that they are null terminated. However,
there is no guarantee that a string is null terminated. For one
thing, as in C, the terminating null 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 null character is found, or when the bounds are exceeded.
-
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.
-
Yes and no. Individual memory objects may not be freed. In future versions,
we may support freeing objects for which you can prove that there are no
other pointers to the object. Until then, you must rely on a garbage
collector to reclaim heap objects or use regions (similar to ``arenas'' or
``zones'') for managing collections of objects.
For porting code, we have defined a free function that behaves as a
no-op, having type
void free(`a::A ?);
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? |
-
The easiest way to do this is to pass the flags -save-c and
-pp to the compiler. This instructs 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
cyclone_dot_emacs.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, but we have reserved the keywords codegen,
splice, cut, and fill in case we get a
chance to add it.
What platforms are supported? |
-
You need a platform that has gcc 2.9, 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 have actively develop Cyclone on cygwin (Windows 98, NT, 2K), and Linux.
We have code for versions on Solaris, OpenBSD, FreeBSD, and Mac OS X. The
platform-specific parts of these non-development distributions, particularly
system call interfaces, may not be correct. We are in the process of
developing a tool to automatically generate system-dependent code that
should be part of future releases.
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 (old last) cell, but l still points to the old first
(new last) cell.
-
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? |
-
Compile-time constants are NULL, integer and character
constants, and arithmetic operations over compile-time constants.
Constructs requiring compile-time constants are: tuple-subscript
(e.g.,
x[3] for tuple x), case argument for
switch "C" argument has a numeric type (e.g., case
3+4:), sizes in array declarations (e.g., int y[37], and
sizes in pointer bounds (e.g., int * x{124}). Unlike in
C, sizeof(t) is not an integral constant expression because
the Cyclone compiler does not know the actual size of aggregate types.
How can I get the size of an array? |
-
If expr is an array, then expr.size returns
the array's size. Note that for ? types, the size is
retrieved at runtime from the object. For other array types,
the size is determined at compile-time.
|