Virgil Language Reference: types

This reference page gives an overview of the basic data types in the Virgil Programming Language, as well as describing the type constructors that can be used to create array types, function types, etc.

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.