Skip to content

Arguments and generics

.args declares parameters and the values passed on every run. ldarg, ldarga, and starg then work by name or by index.

il[1]> .args (int32 x = 5, string s = "ab")
args: 0:int32 x = 5, 1:string s = "ab"
il[1]> ldarg x
il[1]> ldarg.0
il[1]> mul
il[1]> ret
= 25 : int32

Literals follow the declared type: integers in decimal, hex, or binary, floats, true and false, characters and strings with the usual escapes, enum names, and null. A parameter without a literal gets the default value for its type. Arguments persist across cells like locals.

.typeparams makes the cell a generic method. Its parameters are written !!T or !!0 in any type position, including locals and member references.

il[2]> .typeparams (T)
type parameters: !!T
il[2]> .typeargs (int32)
type arguments: int32
il[2]> ldc.i4 7
il[2]> box int32
il[2]> unbox.any !!T
il[2]> box !!T
il[2]> ret
= 7 : int32

.typeargs binds the parameters for the next run and stays bound until you change it. A generic cell without bound type arguments is refused with a reminder.

Members on types instantiated over a cell parameter resolve through the generic definition, so List1<!!T>` behaves as you would expect:

newobj instance void class List`1<!!T>::.ctor()
callvirt instance int32 class List`1<!!T>::get_Count()

Declare .typeparams before the first instruction of a cell, or .clear first. Arguments cannot use the cell’s type parameters because reflection needs concrete values to pass.

.vararg gives the cell the vararg calling convention so arglist is valid. The runtime only supports that convention on Windows; on other platforms the cell is refused with a message that says so.