module volta.ir.type

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) { }
}
class Type : Node

Base class for all types.

mangledName: string

Filled in with a pass.

isConst: bool

StorageType flags.

isImmutable: bool

StorageType flags.

isScope: bool

StorageType flags.

class PrimitiveType : Type

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).

originalToken: Token

Used for printing both 'int' and 'i32', etc.

class TypeReference : Type

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.

type: Type

What Type this refers to. Filled in after parsing sometime.

id: QualifiedName

The name of the Type. Filled in the initial parsing.

class TypeOf : Type

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.

exp: Exp

The expression to get the type of.

class PointerType : Type

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.

class NullType : Type

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.

class ArrayType : 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.

class AmbiguousArrayType : Type

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.

class StaticArrayType : 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.

class AAType : Type

An AAType is an associative array -- it associates keys with values.

AAType is mangled as "Aa" + key + value.

class CallableType : Type

The common ancestor of DelegateTypes and FunctionTypes.

typeInfo: Class

When the backend needs a TypeInfo for varargs and such.

class FunctionSetType : Type

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.

isFromCreateDelegate: bool

For use in typer.

class FunctionType : CallableType

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

class DelegateType : CallableType

A DelegateType is a function pointer with an additional context pointer.

DelegateTypes are mangled as 'D' then as a FunctionType.

class StorageType : Type

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.

enum STORAGE_AUTO

For bitfields.

class AutoType : Type

For representing inferred types. auto a = 3; const b = 2;

explicitType: Type

The explicit type to replace this with.

class NoType : Type

For constructs that have no type, used to avoid nulls in the IR.

class AliasStaticIf : Type

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.