Code Map
module volta.ir.type;
//! Base class for all types.
class Type : Node
{
public:
//! Filled in with a pass.
mangledName: string;
//! StorageType flags.
isConst: bool;
//! StorageType flags.
isImmutable: bool;
//! StorageType flags.
isScope: bool;
glossedName: string;
}
//! PrimitiveTypes are types that are entirely well known to the compiler
//! ahead of time. Consisting mostly of integral types, the only abstract
//! one is Void, which indicates no type or (in the case of arrays and
//! pointers) any type.
class PrimitiveType : Type
{
public:
enum Kind
{
Invalid,
Void,
Bool,
Char,
Wchar,
Dchar,
Byte,
Ubyte,
Short,
Ushort,
Int,
Uint,
Long,
Ulong,
Float,
Double,
Real,
}
public:
type: Kind;
//! Used for printing both 'int' and 'i32', etc.
originalToken: Token;
public:
this() { }
this(kind: Kind) { }
this(old: PrimitiveType) { }
}
//! A TypeReference is generated for user defined types, and a pass fills
//! in the information so it can act as a cache, and not require multiple
//! lookups.
class TypeReference : Type
{
public:
//! What Type this refers to. Filled in after parsing sometime.
type: Type;
//! The name of the Type. Filled in the initial parsing.
id: QualifiedName;
public:
this() { }
this(old: TypeReference) { }
}
//! TypeOf is generated for typeof(expression), anywhere a type could be
//! used. This is then used as if the type of the expression had been
//! written out by hand.
class TypeOf : Type
{
public:
//! The expression to get the type of.
exp: Exp;
public:
this() { }
this(old: TypeOf) { }
}
//! A pointer is an abstraction of an address. You can dereference a
//! pointer (get the thing it's pointing to), perform pointer arithmetic
//! (move to the next possible address that could contain the same type),
//! or reseat a pointer (change what it points to). You can also slice a
//! pointer to get an array.
class PointerType : Type
{
public:
base: Type;
isReference: bool;
public:
this() { }
this(loc: const(Location), base: Type) { }
this(old: PointerType) { }
}
//! The NullType represents the Type of a null.
class NullType : Type
{
public:
this() { }
this(old: NullType) { }
}
//! An ArrayType represents a slice of memory. It contains a pointer, that
//! contains elements of the base type, and a length, which says how many
//! elements this slice shows. This is lowered into a struct before the
//! backend gets it.
class ArrayType : Type
{
public:
base: Type;
public:
this() { }
this(loc: const(Location), base: Type) { }
this(old: ArrayType) { }
}
//! A type that is either an AAType or a StaticArrayType, but we cannot
//! tell which yet.
class AmbiguousArrayType : Type
{
public:
base: Type;
child: Exp;
public:
this() { }
this(old: AmbiguousArrayType) { }
}
//! A StaticArray is a list of elements of type base with a statically
//! (known at compile time) number of elements. Unlike C, these are passed
//! by value to functions.
class StaticArrayType : Type
{
public:
base: Type;
length: size_t;
public:
this() { }
this(old: StaticArrayType) { }
}
//! An AAType is an associative array -- it associates keys with values.
class AAType : Type
{
public:
value: Type;
key: Type;
public:
this() { }
this(old: AAType) { }
}
//! The common ancestor of DelegateTypes and FunctionTypes.
class CallableType : Type
{
public:
linkage: Linkage;
ret: Type;
params: Type[];
isArgRef: bool[];
isArgOut: bool[];
hiddenParameter: bool;
hasVarArgs: bool;
varArgsProcessed: bool;
varArgsTypeids: Variable;
varArgsArgs: Variable;
isProperty: bool;
homogenousVariadic: bool;
forceLabel: bool;
//! When the backend needs a TypeInfo for varargs and such.
typeInfo: Class;
abiModified: bool;
abiData: void*[][];
public:
this(nt: NodeType) { }
this(nt: NodeType, old: CallableType) { }
}
//! Exists as a proxy for a FunctionSet.
class FunctionSetType : Type
{
public:
set: FunctionSet;
//! For use in typer.
isFromCreateDelegate: bool;
public:
this() { }
this(set: FunctionSet) { }
this(old: FunctionSetType) { }
}
//! A FunctionType represents the form of a function, and defines how it is
//! called. It has a return type and a number of parameters.
class FunctionType : CallableType
{
public:
this() { }
this(old: CallableType) { }
}
//! A DelegateType is a function pointer with an additional context
//! pointer.
class DelegateType : CallableType
{
public:
this() { }
this(old: CallableType) { }
}
//! A StorageType changes how a Type behaves.
class StorageType : Type
{
public:
//! For bitfields.
enum STORAGE_AUTO;
enum STORAGE_CONST;
enum STORAGE_IMMUTABLE;
enum STORAGE_SCOPE;
enum STORAGE_REF;
enum STORAGE_OUT;
public:
enum Kind
{
Invalid,
Auto,
Const,
Immutable,
Scope,
Ref,
Out,
}
public:
type: Kind;
base: Type;
public:
this() { }
this(old: StorageType) { }
}
//! For representing inferred types. auto a = 3; const b = 2;
class AutoType : Type
{
public:
//! The explicit type to replace this with.
explicitType: Type;
isForeachRef: bool;
public:
this() { }
this(old: AutoType) { }
}
//! For constructs that have no type, used to avoid nulls in the IR.
class NoType : Type
{
public:
this() { }
this(old: NoType) { }
}
//! A special kind of type that allows an alias to have multiple
//! configurations.
class AliasStaticIf : Type
{
public:
conditions: Exp[];
types: Type[];
public:
this() { }
this(old: AliasStaticIf) { }
}
Base class for all types.
Filled in with a pass.
StorageType flags.
StorageType flags.
StorageType flags.
PrimitiveTypes are types that are entirely well known to the compiler ahead of time. Consisting mostly of integral types, the only abstract one is Void, which indicates no type or (in the case of arrays and pointers) any type.
PrimitiveTypes are mangled as follows:
@li Void
is mangled as 'v'.
@li Bool
is mangled as 'B'.
@li Char
is mangled as 'c'.
@li Byte
is mangled as 'b'.
@li Ubyte
is mangled as 'ub'.
@li Short
is mangled as 's'.
@li Ushort
is mangled as 'us'.
@li Int
is mangled as 'i'.
@li Uint
is mangled as 'ui'.
@li Long
is mangled as 'l'.
@li Ulong
is mangled as 'ul'.
@li Float
is mangled as 'ff'.
@li Double
is mangled as 'fd'.
@li Real
is mangled as 'fr'.
PrimitiveTypes aren't mangled on their own, usually forming
a part of a larger composite type (e.g. a function).
Used for printing both 'int' and 'i32', etc.
A TypeReference is generated for user defined types, and a pass fills in the information so it can act as a cache, and not require multiple lookups.
TypeReference
is just a cache, and as such it is type
that is mangled.
What Type this refers to. Filled in after parsing sometime.
The name of the Type. Filled in the initial parsing.
TypeOf is generated for typeof(expression), anywhere a type could be used. This is then used as if the type of the expression had been written out by hand.
The expression to get the type of.
A pointer is an abstraction of an address. You can dereference a pointer (get the thing it's pointing to), perform pointer arithmetic (move to the next possible address that could contain the same type), or reseat a pointer (change what it points to). You can also slice a pointer to get an array.
Volt pointers are compatible with C pointers (and by association, D pointers).
PointerTypes
are mangled as "p" + base
.
The NullType represents the Type of a null.
null's are like water, they fill the shape of their container, hence they need their own type.
An ArrayType represents a slice of memory. It contains a pointer, that contains elements of the base type, and a length, which says how many elements this slice shows. This is lowered into a struct before the backend gets it.
While arrays are often allocated by the GC, the memory can be from anywhere.
ArrayType
is mangled as "a" + the mangle of base
.
A type that is either an AAType
or a StaticArrayType
,
but we cannot tell which yet.
At parse time, i32[A] could be either, depending on whether A is a constant or a type.
A StaticArray is a list of elements of type base with a statically (known at compile time) number of elements. Unlike C, these are passed by value to functions.
StaticArrayTypes
are mangled as "at" + length
+ base
.
An AAType is an associative array -- it associates keys with values.
AAType
is mangled as "Aa" + key
+ value
.
The common ancestor of DelegateTypes and FunctionTypes.
When the backend needs a TypeInfo for varargs and such.
Exists as a proxy for a FunctionSet.
The reason this exists is for a couple of reasons.
Firstly, and mainly, if we look up a name and get
back a set of functions, the type look up has no
way of selecting the function at that time (consider
int function(int, string) = foo;
where foo is an overloaded
function).
Secondly, when a single function is retrieved no function set or assorted types are returned, so the extyper can use the presence of a FunctionSetType as meaning "overloaded function" without issue.
For use in typer.
A FunctionType represents the form of a function, and defines how it is called. It has a return type and a number of parameters.
The Linkages define how the function is mangled and called by the backend.
FunctionTypes are mangled like so: Linkage Attributes Parameters "Z" Ret
A DelegateType is a function pointer with an additional context pointer.
DelegateTypes
are mangled as 'D' then as a FunctionType.
A StorageType changes how a Type behaves.
Nested storage types are culled like so:
Remove duplicate storage types, keeping the first. const(const(immutable(const(T)))) => const(immutable(T)) If there is more than one storage type, remove auto. const(auto(T)) => const(T) Only one of immutable, const, and inout can exist in a single chain, with the following priority: immutable, inout, const const(immutable(inout(T))) => immutable(T) const(inout(T)) => inout(T) That is, const can only remain if inout or immutable are not present. inout can only remain if immutable is not present. If immutable is present, it will always remain. If after the above, there is one storage type with a base of a non-storage type, the collapsing is done. (If the result is auto(T), the type becomes T) Otherwise, the following assertions hold: There are no duplicate storage types. auto is not in the list. there may be immutable, const, inout, but only one. Given that, sort the storage types in the given order:
scope const/immutable/inout const(scope (T))) => scope (const(T)))
The Kinds Scope, Const, Immutable, Ref, and Out are mangled as 'e', 'o', 'm', 'r', and 'O' respectively.
For bitfields.
For representing inferred types. auto a = 3; const b = 2;
The explicit type to replace this with.
For constructs that have no type, used to avoid nulls in the IR.
A special kind of type that allows an alias to have multiple configurations.
Only occurs in alias declarations and takes this form:
alias foo = static if (condition) {
T1;
} else if (condition2) {
T2;
} else {
T3;
}
Where condition
is a compile time expression, and in the braces
are types.
The resolution is just as the static if
form suggests. The first
condition is checked, if it is true, the associated type is used,
otherwise the next condition is checked, and so on. If they all fail,
the last blank else condition is used (if any).
If there are no matching condition blocks, then an error is generated.