Integers - int
Unlike languages like C, C++ and Java, Virgil has just one integer type,
int
, which represents a signed, 32-bit integer quantity
whose operations follow the standard two's complement semantics.
Virgil has no need of intermediate integer sizes because it encourages
the use of the raw types 1
. . . 64
to represent
raw data and bit-level quantities with precise sizes. Integers can be converted to
raw types implicitly for bit-level operations, but raw types must be
explicitly cast back to int
. Integer literals are always
written using positive or negative decimal notation; hexadecimal
and binary constants have a raw type by default.
Characters - char
The char
type represents character values such as
'a'
, 'b'
, '\n'
, etc. in the program.
All characters that appear
in literals and strings are represented as 8-bit quantities
using the ASCII character set.
The character type defines operations for character comparison, but not
arithmetic. For arithmetic, quantities of type char
are
implicitly convertible to type int
, but conversion
back requires explicit conversion. For use in bitwise (raw)
operations, characters are implicitly convertible to the raw type 8
,
which represents a raw 8-bit value corresponding to the
ASCII code for the character.
Booleans - boolean
The boolean
type represents boolean values in the program. There
are just two boolean values in Virgil: true
and false
.
Booleans are used in the conditions for branch and loop statements. Also,
the value comparison operators ==
and
!=
return values of type boolean
. Boolean values
can be implicitly converted into the raw type 1
(with true
converting to the raw value 0b1
and false
converting to the raw value 0b0
),
allowing them to be used to query or update individual bits in status registers, for
example.
Raw Types - 1...64
To represent bit-level values, Virgil
defines a family of types called the raw types which
represent values of a particular known width in bits.
These types are denoted simply by the integer constants
1
to 64
. Context in Virgil
programs avoids confusion of the raw types and integer constants representing
integer values. Because raw types represent bit-level quantities, they define
a number of familiar bit-level operations such as bitwise and,
left shift, etc. Additionally, raw types in Virgil define the
bit access operator, which allows reads and writes of individual
bits of a variable of raw type, without the need to mask or shift.
Hexadecimal constants such as 0xfe45
have a raw type inferred by the
compiler that is four times the number of hexadecimal characters
(16
in this case), and
binary constants such as 0b100101
have a raw type inferred that is the number of binary characters
(6
in this case).
Objects - class C extends D
Like Java and other class-based languages, Virgil allows the programmer to define
new classes of objects. Each declared class introduces a new type into the type
system, with the extends
clause representing both inheritance
and subtyping. This means that if class C
is declared to extend
class D
, then class C
inherits the members of D
and expressions of type C
are subtypes of type D
.
Every object in Virgil has a dynamic type that is fixed when the object is
allocated. Virgil uses virtual method dispatch on the dynamic type of
an object in the same manner as Java. Similarly, the dynamic type of an object can be
queried with the dynamic type query operator <:
,
which returns true
if the object is a subtype of the specified type. Virgil
also supports the dynamic type cast operator ::
on
object types, which will check the dynamic type of an object and throws an exception
if the test fails.
Virgil objects carry type information with them in their runtime representation if so required. In the situation of a orphan class that has no related classes, the runtime representation of the type is not necessary. Such objects are represented more efficiently by omitting the object header, because the complete type information for the object is known statically.
A comprehensive parametric type system for classes is planned for Virgil II.
Arrays - T[]
Virgil provides the array type constructor []
that
can be used to denote the array type T[]
for any type T
.
Arrays have their length fixed upon allocation, and are always passed by reference.
The index must be of type int
, and all element accesses are checked against
the bounds of the array and produce a language-level exception upon failure.
Virgil arrays are not objects, and do not implicitly extend any object class as
they do in C# and Java. Also, unlike Java, arrays are not
covariantly typed, meaning that if the type B
is a
subtype of A
, that does not imply B[]
subtype of
A[]
. Instead, a comprehensive system for parametric types
with the notion of variance is being designed for
Virgil II.
Delegates - function(T...): T
Virgil provides the concept of a delegate method, which allows
programs to use references to individual methods of classes and components as
first class values. For example, given some object L1
of
class List
that has method
add
, the expression L1.add
represents a first
class value that is a reference to
the add
method bound to the object L1
.
This reference can later be invoked by client code.
Virgil includes the function
type constructor that can be used to
denote the expected type of a delegate method. For example, the type
function(List, int): int
denotes a delegate that takes two parameters,
the first of type List
, the second of type int
, and
returns a value of type int
. Similarly, the type
function(int)
denotes a delegate that takes a single parameter of
type int
and does not return a value.
Variance for function types is planned for Virgil II as part of the complete parametric type system.