A type in IL assembly is written wherever a signature needs one: return types, parameters, locals, fields, casts, calls. The grammar (type and typeSpec in asmparse.y, ECMA-335 II.7) has no declaration of types inline; it names them. ilasm turns each written type into a signature blob of the metadata (the element-type codes of II.23.1.16), so a spelling here is a name for one code there.
Built-in types
| Spelling | Meaning |
|---|---|
void | no value; a return type, or the target of a pointer (void*) |
bool | Boolean, one byte (System.Boolean) |
char, wchar | a 16-bit Unicode code unit (System.Char); wchar is another spelling |
int8, int16, int32, int64 | signed integers of 8, 16, 32 and 64 bits |
uint8, uint16, uint32, uint64 | unsigned integers; unsigned int8 and the rest are the same types written the long way (the standard writes only the long form) |
float32, float64 | IEEE single and double (float is a reserved word with no use in a type) |
native int | a signed integer the size of a pointer on the platform (32 or 64 bits) |
native unsigned int, native uint | the unsigned form |
object | System.Object |
string | System.String |
typedref, refany | a typed reference (System.TypedReference), made by mkrefany and read by refanytype and refanyval |
class System.String and class System.Object are accepted and give the same signature as string and object: ilasm compares the type reference with the two system types and uses the short code.
Classes, value types and scopes
A user-defined type is written with class (a reference type) or valuetype (an unboxed value type; value class is accepted as a synonym), followed by a type reference: an optional resolution scope in square brackets and a name that may nest.
| Form | Meaning |
|---|---|
class Foo.Bar | a type of this module (scope 1, the module itself, in the grammar), or of the current assembly |
class [mscorlib]System.Object | a type of the assembly declared by .assembly extern mscorlib |
class [.module Other.netmodule]Foo | a type of another module of the same assembly (.module extern) |
class [*]Foo | no scope: ilasm leaves the resolution scope empty (the wildcard) |
class [mdtoken(0x23000001)]Foo | a scope given as a metadata token (here an AssemblyRef row) |
class Outer/Inner, class [mscorlib]A.B/C/D | nested types are separated by / |
class .this, .base, .nester | the current class, its base class (extends) and the class that encloses it; an error outside a class (.base also when there is no base) |
mdtoken(0x02000005) | a type given as a metadata token |
Where the grammar wants a TypeSpec the keyword may be left out for a plain type reference (castclass [mscorlib]System.String, .override Foo::Bar); a TypeSpec is also a bare assembly scope [mscorlib] (the assembly itself), a module scope, or any type.
Arrays, pointers and references
| Form | Meaning |
|---|---|
int32[] | a vector: a one-dimensional array with lower bound 0 (SZARRAY) |
int32[,], int32[,,] | an array of rank 2 or 3 with unspecified bounds |
int32[5], int32[2,3] | an array with a size in each dimension, lower bound 0 |
int32[1...5] | lower and upper bound: here 5 elements numbered from 1 |
int32[0...,0...] | lower bounds only (the size is open); ildasm writes this form for the general arrays that C# makes |
int32* | an unmanaged pointer |
int32& | a managed pointer (a by-reference parameter or a ref local); it cannot be nested or point at typedref |
int32 pinned | a local variable the garbage collector shall not move; for locals only |
A bound is written n (size n, lower bound 0), l...u (lower and upper bound), l... (lower bound), or ... or nothing (no information); the parser stores the lower bound and the size (u − l + 1), and it is an error for u to be less than l.
Custom modifiers and method pointers
modopt(type) and modreq(type) follow a type and are part of the signature: an optional modifier may be ignored by a caller, a required one must be understood (C# marks volatile fields with modreq([mscorlib]System.Runtime.CompilerServices.IsVolatile)). A method pointer type is method callconv returntype *(parameters), for example method int32 *(int32, int32) or method unmanaged stdcall void *(native int); it is the type that ldftn and calli work with.
.field public static int32 modreq([mscorlib]System.Runtime.CompilerServices.IsVolatile) flag
.locals init (int32& pinned p, method int32 *(int32) fp, int32[0...,0...] grid)
Generic parameters and instantiations
Inside a generic type or method the parameters are written !0, !1 … for the parameters of the type, counted from zero, and !!0, !!1 … for those of the method; ilasm also accepts the names given in the header (!T, !!U). An instantiation is the generic type followed by its arguments in angle brackets: class [mscorlib]System.Collections.Generic.List`1<int32>, valuetype [mscorlib]System.Nullable`1<!0>. The name carries the number of parameters after a grave accent (`1, `2). How a generic type or method is declared is on the classes page and the methods page.
Signatures
A signature is a list of parameters in parentheses. Each parameter is optional attributes in brackets ([in], [out], [opt], or a number), a type, an optional marshal(…) clause and an optional name; the sentinel ... stands alone and marks where the optional arguments of a vararg call begin. A method reference is callconv returntype [class::]name[<generic arguments>](parameters); callconv is the calling convention (see Methods), and a call to a generic method gives its arguments in angle brackets after the name. A reference that names only the number of type parameters of a generic method uses the arity form name<[2]>. A field reference is type [class::]name.
call instance void [mscorlib]System.Collections.Generic.List`1<string>::Add(!0)
call !!0 [mscorlib]System.Array::Empty<string>()
call vararg int32 Foo::Sum(int32, ..., int32)
ldfld int32 modopt([mscorlib]Foo) Bar::value
calli unmanaged stdcall int32(native int, int32)
Not described
Known, not yet covered
- Reference: the binary side of signatures (element-type codes, compressed integers, the encoding of a type spec) is in the standard (II.23.2) and in the .NET assembly pages, not here. The standard’s
modreq/modoptpositions and the extra type spec forms in the runtime addendum (custom modifiers afterbyrefand in more places) were read but are not described.