This document describes the contents of the __primitives__
module synthesized by the Clay compiler and implicitly available to all Clay programs. This module provides primitive types, fundamental operations, and functions for compile-time introspection of programs. For information about the Clay language itself, consult the Language Reference.
-
Bool
- Integer types
Int8
,Int16
,Int32
,Int64
,Int128
,UInt8
,UInt16
,UInt32
,UInt64
,UInt128
- Floating-point types
Float32
,Float64
,Float80
- Imaginary and complex types
Imag32
,Imag64
,Imag80
,Complex32
,Complex64
,Complex80
Pointer
CodePointer
- External code pointer types
CCodePointer
,VarArgsCCodePointer
,LLVMCodePointer
,StdCallCodePointer
,FastCallCodePointer
,ThisCallCodePointer
Array
Vec
Tuple
Union
Static
-
integerAddChecked
,integerSubtractChecked
,integerMultiplyChecked
,integerDivideChecked
,integerRemainderChecked
,integerNegateChecked
,integerShiftLeftChecked
,integerConvertChecked
-
- Memory order symbols
OrderUnordered
,OrderMonotonic
,OrderAcquire
,OrderRelease
,OrderAcqRel
,OrderSeqCst
atomicLoad
atomicStore
atomicRMW
RMWXchg
,RMWAdd
,RMWSubtract
,RMWAnd
,RMWNAnd
,RMWOr
,RMWXor
,RMWMin
,RMWMax
,RMWUMin
,RMWUMax
atomicCompareExchange
atomicFence
- Memory order symbols
-
- Calling convention attributes
AttributeCCall
,AttributeStdCall
,AttributeFastCall
,AttributeThisCall
,AttributeLLVMCall
- Linkage attributes
AttributeDLLImport
,AttributeDLLExport
- Calling convention attributes
Primitive types correspond more or less to the basic LLVM types, with some occasional added higher-level semantics. The primitive types are defined as overloadable symbols with no initial overloads; library code may overload primitive type symbols in order to define constructor functions for the types. Corresponding LLVM and C types for use with external function linkage is provided for each type.
The Bool
type represents boolean values. Values of the type may only have the values true
or false
. This type corresponds to the LLVM i1
type, the C99 _Bool
type, and the C++ bool
type. false
in LLVM is i1 0
and true
is i1 1
.
Signed and unsigned integer types are provided for sizes of 8, 16, 32, 64, and 128 bits. The signed types are named Int8
, Int16
, etc. and the unsigned types are named UInt8
etc. Both sets of types correspond to the LLVM i8
, i16
, etc. types. (LLVM does not differentiate signed from unsigned types.) They also correspond to the C99 <stdint.h>
typedefs int8_t
etc. and uint8_t
etc.
The compiler internally earmarks the unsigned integer type whose size corresponds to the size of pointer types, and uses that type as the return type for primitives that deal with indexing data structures. This document refers to that type as SizeT
, although this alias name is not actually available from the __primitives__
module.
Float32
and Float64
floating-point types are provided, corresponding to IEEE-754 single and double precision floating point numbers. For Unix x86 targets, the platform-specific Float80
type is also provided. These types correspond respectively to the LLVM float
, double
, and x86_fp80
types, or the C float
, double
, and long double
types (the latter implemented as an extension by many x86-targeting compilers).
Imaginary and complex types are provided for each floating-point type. Imag32
etc. correspond to the same LLVM and C types as Float32
etc., but semantically represent imaginary values in Clay. Complex32
etc. correspond to LLVM {float,float}
etc. types and C99 _Complex float
etc. types.
Pointer[T]
is the type of a pointer to a value of type T
, for any type T
. It corresponds to the %T*
type in LLVM (where %T
is the LLVM type of T
), or to the T*
type in C. Pointers are created by the prefix &
operator, or by the equivalent addressOf
primitive function.
CodePointer[[..In], [..Out]]
is the type of a pointer to a Clay function, instantiated for the argument types ..In
and return types ..Out
. These pointers can be created using the makeCodePointer
primitive function. The Clay calling convention is unspecified, so CodePointer
s have no specific corresponding LLVM or C type beyond being a function pointer of some kind.
CCodePointer[[..In], [..Out]]
is the type of a pointer to a C function with argument types ..In
and return types ..Out
. CCodePointer[[A,B,C],[]]
corresponds to the C void (*)(A,B,C)
type, and CCodePointer[[A,B,C],[D]]
to the D (*)(A,B,C)
type. The LLVM type depends on the target platform's C calling convention.
CCodePointer
s are called using the C calling convention when passed to the callCCodePointer
primitive function. Different symbols are provided for function pointers with different calling conventions.
LLVMCodePointer[[A,B,C],[D]]
corresponds directly to an LLVMD (A,B,C) *
function pointer type.VarArgsCCodePointer[[A,B,C], [D]]
points to a variadic C function; it corresponds to theD (*)(A,B,C,...)
type in C.StdCallCodePointer
,FastCallCodePointer
, andThisCallCodePointer
point to functions using legacy x86 calling conventions on Windows. They correspond to C function pointer types decorated with__stdcall
,__fastcall
, or__thiscall
in Microsoft dialect, or with__attribute__((stdcall))
etc. in GCC dialect.
CCodePointer
s, along with the other external code pointer types, may be obtained by evaluating external function names, or as return values from C functions. They may also be derived directly automatically from some Clay functions using the makeCCodePointer
primitive function.
// Example
// XXX
Array[T,n]
is the type of a fixed-size, locally-allocated array, containing n
elements of type T
. The n
parameter must currently be an Int32
(regardless of the target word size). It corresponds to the LLVM [n x %T]
type, or the C T [n]
type; however, unlike C arrays, Clay arrays never implicitly devolve to pointers. The arrayRef
and arrayElements
primitive functions provide access to array elements.
Vec[T,n]
is the type of a SIMD vector value, containing n
elements of type T
. The n
parameter must currently be an Int32
(regardless of the target word size). It corresponds to the LLVM <n x %T>
type, or the GCC extension T __attribute__((vector_size(n*sizeof(T))))
type.
No primitives are currently provided for manipulating Vec
types; they are primarily intended to be used directly with LLVM's vector intrinsics.
Tuple[..T]
is an anonymous aggregate of the specified types. Tuples are laid out equivalently to naturally-aligned C struct
s containing the same corresponding types in the same order. Tuple[A,B,C]
corresponds to an LLVM {%A, %B, %C}
struct type.
Union[..T]
is an anonymous, nondiscriminated union of the specified types. It is laid out equivalently to a naturally-aligned C union
containing the corresponding types in the same order. LLVM does not represent unions in its type system; an LLVM type is chosen in order to have the proper size and alignment for the member types of the union.
Static[x]
is a stateless type used to represent compile-time values. Clay symbols, static strings, and static
expressions evaluate to values of instances of this type.
(Static
values are actually not quite completely stateless yet; they are emitted as i8 undef
values at the LLVM level. They thus still unfortunately take up space inside tuples and record types.)
These functions provide fundamental data manipulation operations. Unlike normal symbols, they may not be overloaded.
[T]
primitiveCopy(dest:T, src:T) :;
primitiveCopy
performs a bitwise copy of src
into dst
. TypeSize(T)
bytes are copied. The function corresponds to an LLVM load
instruction followed by a store
.
[T,n,I | Integer?(I)]
arrayRef(array:Array[T,n], i:I) : ref T;
arrayRef
returns a reference to element i
of array
. i
is zero-based and is not bounds checked. The function corresponds to an LLVM getelementptr
instruction.
[T,n]
arrayElements(array:Array[T,n]) : ref ..repeatValue(static n, T);
arrayElements
returns a reference to each element of array
in order as a multiple value list.
[..T, n | n >= 0 and n < countValues(..T)]
tupleRef(tuple:Tuple[..T], static n) : ref nthValue(static n, ..T);
tupleRef
returns a reference to the n
th element of tuple
. n
is zero-based, and must be greater than or equal to zero, and less than the number of elements in tuple
. The function corresponds to an LLVM getelementptr
instruction.
[..T]
tupleElements(tuple:Tuple[..T]) : ref ..T;
tupleElements
returns a reference to each element of tuple
in order as a multiple value list.
[R, n | Record?(R) and n >= 0 and n < RecordFieldCount(R)]
recordFieldRef(rec:R, static n) : ref RecordFieldType(R, static N);
recordFieldRef
returns a reference to the n
th field of rec
, which must be of a record type. n
is zero-based, and must be greater than or equal to zero, and less than the number of fields in the record type R
. The function corresponds to an LLVM getelementptr
instruction.
[R, name | Record?(R) and Identifier?(name) and RecordWithField?(R, name)]
recordFieldRefByName(rec:R, static name) : ref RecordFieldTypeByName(R, name);
recordFieldRefByName
returns a reference to the field named by the static string name
in rec
, which must be of a record type with a field of that name. The function corresponds to an LLVM getelementptr
instruction.
[R | Record?(R)]
recordFields(rec:R) : ref ..RecordFieldTypes(R);
recordFields
returns a reference to each field of rec
in order as a multiple value list. rec
must be of a record type.
[E | Enum?(E)]
enumToInt(en:E) : Int32;
enumToInt
returns the ordinal value of en
as an Int32
. en
must be of an enum type.
[E | Enum?(E)]
intToEnum(static E, n:Int32) : E;
intToEnum
returns a value of the enum type E
with the ordinal value n
. n
is not bounds checked against the defined values of E
.
These functions provide fundamental arithmetic operations for the primitive Bool
, integer, floating-point, and imaginary types. Unlike normal symbols, they may not be overloaded. Binary numeric primitives generally require inputs of matching types. Conversion rules for heterogeneous types are left to be implemented by the library. These functions also do not operate on complex types; complex math is also left to the library.
boolNot(x:Bool) : Bool;
boolNot
returns the complement of x
, which must be a Bool
. It is equivalent to the not
operator.
[T | Numeric?(T)]
numericEquals?(a:T, b:T) : Bool;
numericEquals?
returns true
if a
and b
are numerically equal, and false
otherwise. For integer types, the function corresponds to an LLVM icmp eq
instruction, and for floating-point types, the function corresponds to an LLVM fcmp ueq
instruction. Floating-point comparison follows IEEE 754 unordered comparison rules; +0.0
is equal to -0.0
and comparisons involving NaN are always false and non-signaling.
[T | Numeric?(T)]
numericLesser?(a:T, b:T) : Bool;
numericLesser?
returns true
if a
is numerically less than b
, and false
otherwise. For signed integer types, the function corresponds to an LLVM icmp slt
instruction; for unsigned types, icmp ult
; and for floating-point types, fcmp ult
. Floating point comparison follows IEEE 754 unordered comparison rules; -0.0
is not less than +0.0
and comparisons involving NaN are always false and non-signaling.
(__primitives__
does not currently provide the full set of floating-point comparison operators. The Clay library currently implements floating-point ordered and unordered comparison using inline LLVM functions. For floating-point, these primitives are used only for compile-time evaluation, which does not support inline LLVM.)
[T | Numeric?(T)]
numericAdd(a:T, b:T) : T;
numericAdd
returns the sum of a
and b
. Integer signed or unsigned addition overflows following two's-complement arithmetic rules. For integer types, the function corresponds to the LLVM add
instruction, and for floating-point types, the fadd
instruction.
[T | Numeric?(T)]
numericSubtract(a:T, b:T) : T;
numericSubtract
returns the difference of a
and b
. Integer signed or unsigned subtraction overflows following two's-complement arithmetic rules. For integer types, the function corresponds to the LLVM sub
instruction, and for floating-point types, the fsub
instruction.
[T | Numeric?(T)]
numericMultiply(a:T, b:T) : T;
numericMultiply
returns the product of a
and b
. Integer signed or unsigned multiplication overflows following two's-complement arithmetic rules. For integer types, the function corresponds to the LLVM mul
instruction, and for floating-point types, the fmul
instruction.
[T | Numeric?(T)]
numericDivide(a:T, b:T) : T;
numericDivide
returns the quotient of a
and b
. Integer division is truncated towards zero. Integer division by zero is undefined, as is signed division overflow (for example, the result of -0x8000_0000/-1
). Floating-point division by zero, underflow, and overflow follow IEEE 754 rules. For signed integer types, the function corresponds to the LLVM sdiv
instruction; for unsigned types, udiv
; and for floating point types, fdiv
.
[T | Numeric?(T)]
numericNegate(a:T) : T;
numericNegate
returns the negation of a
. Integer negation behaves like two's-complement subtraction from zero (and in fact, LLVM represents integer negation as sub %T 0, %a
). Unsigned negation thus gives the two's complement, and signed negation overflow (for example, the result of negating -0x8000_0000
) gives back the original value. Floating-point negation corresponds to fsub %T -0.0, %a
in LLVM; negating a zero gives the other zero, and negating a NaN gives an unspecified other NaN value.
[T | Integer?(T)]
integerRemainder(a:T, b:T) : T;
integerRemainder
returns the remainder from dividing a
and b
. For signed remainder operations, a nonzero remainder has the same sign as a
. Integer division by zero is undefined, as is signed division overflow (for example, the result of -0x8000_0000/-1
). (Although the result of the remainder operation itself does not overflow, LLVM still specifies that the remainder of an overflowing division operation is undefined.) For signed integer types, the function corresponds to the LLVM srem
instruction, and for unsigned types, urem
.
[T | Integer?(T)]
integerShiftLeft(a:T, b:T) : T;
integerShiftLeft
returns the result of bitshifting a
left by b
bits. The result is undefined is b
is either negative or greater than or equal to the bit size of T
. Overflowed bits are discarded; the value is still defined. The function corresponds to the LLVM shl
instruction.
[T | Integer?(T)]
integerShiftRight(a:T, b:T) : T;
integerShiftRight
returns the result of bitshifting a
right by b
bits. The result is undefined is b
is either negative or greater than or equal to the bit size of T
. For signed integer types, an "arithmetic" shift is performed; the sign bit is propagated rightward, and the operation corresponds to the LLVM ashr
instruction. For unsigned integer types, a "logical" shift is performed; the high bits of the result are set to zero, and the operation corresponds to the LLVM lshr
instruction.
[T | Integer?(T)]
integerBitwiseAnd(a:T, b:T) : T;
integerBitwiseAnd
returns the bitwise-and of a
and b
. It corresponds to the LLVM and
instruction.
[T | Integer?(T)]
integerBitwiseOr(a:T, b:T) : T;
integerBitwiseOr
returns the bitwise-or of a
and b
. It corresponds to the LLVM or
instruction.
[T | Integer?(T)]
integerBitwiseXor(a:T, b:T) : T;
integerBitwiseXor
returns the bitwise-xor of a
and b
. It corresponds to the LLVM xor
instruction.
[T | Integer?(T)]
integerBitwiseNot(a:T) : T;
integerBitwiseNot
returns the bitwise-not of a
. LLVM represents bitwise-not as an xor %T %a, -1
instruction.
[T, U | Numeric?(T) and Numeric?(U)]
numericConvert(static T, a:U) : T;
numericConvert
returns a
converted to type T
while preserving its numeric value. If T
and U
are the same type, the value is simply copied. How it works depends on the output and input types:
Conversion from an integer type to a smaller integer type is done by bitwise truncation, corresponding to LLVM's trunc
instruction. Conversion from an integer type to a larger signed type is done by sign extension, corresponding to LLVM's sext
instruction, and conversion to a larger unsigned type is done by zero extension, corresponding to zext
. Conversion to an equal-sized signed or unsigned type is done by bitwise casting, corresponding to the bitcast
instruction.
Conversion among floating-point types is done by precision truncation or extension, corresponding to the LLVM fptrunc
and fpext
instructions. Overflowing truncation is undefined.
Conversion from integer to floating-point types is done corresponding to the LLVM instructions sitofp
for signed types, and uitofp
for unsigned types. Overflowing conversion is undefined.
Conversion from floating-point to integer types is done corresponding to the LLVM instructions fptosi
for signed types, and fptoui
for unsigned types. Overflowing conversion is undefined.
These operations provide integer arithmetic operations checked for overflow. Unlike the previously-described numeric primitives, the results of these operations are always undefined if they overflow; however, an additional boolean value is also returned that will be true if the operation overflowed. For non-overflowing cases, these functions behave like their non-checked counterparts and return false as their second value. Unlike normal symbols, they may not be overloaded.
[T | Integer?(T)]
integerAddChecked(a:T, b:T) : T, Bool;
[T | Integer?(T)]
integerSubtractChecked(a:T, b:T) : T, Bool;
[T | Integer?(T)]
integerMultiplyChecked(a:T, b:T) : T, Bool;
[T | Integer?(T)]
integerDivideChecked(a:T, b:T) : T, Bool;
[T | Integer?(T)]
integerNegateChecked(a:T) : T, Bool;
[T | Integer?(T)]
integerRemainderChecked(a:T, b:T) : T, Bool;
[T | Integer?(T)]
integerShiftLeftChecked(a:T, b:T) : T, Bool;
[T, U | Integer?(T) and Integer?(U)]
integerConvertChecked(static T, a:U) : T, Bool;
These operations create, dereference, compare, and convert pointers. Unlike normal symbols, they may not be overloaded.
[T]
addressOf(ref x:T) : Pointer[T];
addressOf
returns the address of x
, which must be an lvalue. It is equivalent to the prefix &
operator.
[T]
pointerDereference(p:Pointer[T]) : ref T;
pointerDereference
returns a reference to the object pointed to by p
. (It is effectively a no-op at the LLVM level, because references are themselves represented as pointers.)
[T, U]
pointerEquals?(p:Pointer[T], q:Pointer[U]) : Bool;
pointerEquals?
returns true if p
and q
have the same address value. It corresponds to the LLVM icmp eq
instruction.
[T, U]
pointerLesser?(p:Pointer[T], q:Pointer[U]) : Bool;
pointerLesser?
returns true if the address of p
is numerically less than that of q
. It corresponds to the LLVM icmp lt
instruction.
[T, I | Integer?(I)]
pointerOffset(p:Pointer[T], i:I) : Pointer[T];
pointerOffset
returns a new pointer value, offset from the address of p
by i * TypeSize(T)
bytes. It corresponds to the LLVM getelementptr
instruction.
[T, I | Integer?(I)]
pointerToInt(static I, p:Pointer[T]) : I;
pointerToInt
converts the address value of p
to an integer type I
. If I
is larger than a pointer, the address is zero-extended; if smaller, the address is truncated. The function corresponds to the LLVM ptrtoint
instruction.
[T, I | Integer?(I)]
intToPointer(static T, address:I) : Pointer[T];
intToPointer
converts the integer value address
into a pointer of type T
. If I
is larger than a pointer, its value is truncated; if smaller, the value is zero-extended. The function corresponds to the LLVM inttoptr
instruction.
[P1, P2 | Pointer?(P1) and Pointer?(P2)]
pointerCast(static P1, p:P2) : P1;
pointerCast
converts a pointer p
to a value of another pointer type with the same address value. It corresponds to an LLVM bitcast
instruction. In addition to casting data pointers (such as Pointer[T]
to Pointer[U]
), it may also cast among CodePointer
, CCodePointer
, etc. types, and cast data to code pointers and back.
These operations create and invoke pointers to instances of Clay functions. Unlike normal symbols, they may not be overloaded.
[F, ..T]
makeCodePointer(static F, static ..T) : CodePointer[[..T], [..CallType(F, ..T)]];
makeCodePointer
looks up an overload for F
matching the input types ..T
. If a matching overload is found, the overload is instantiated for the given input types, and a pointer to the function instance is returned as a CodePointer
. The output types parameter of the CodePointer
is deduced from the matched overload's return types. F
must be a symbol or a lambda that does not capture its scope (which is equivalent to a symbol). An error is raised if F
is not a symbol, or no matching overload for F
is found.
makeCodePointer
matches overloads as if all input types are lvalues. Taking CodePointer
s to rvalue functions is currently unsupported.
[F, ..T]
makeCCodePointer(static F, static ..T) : CCodePointer[[..T], [..CallType(F, ..T)]];
makeCCodePointer
looks up an overload for F
matching the input types ..T
. If a matching overload is found and is determined to be C-compatible, the overload is instantiated for the given input types, along with a thunk function adapting the overload to the C calling convention. A pointer to the thunk is returned as a CCodePointer
. The output types parameter of the CCodePointer
is deduced from the matched overload's return types. F
must be a symbol or a lambda that does not capture its scope (which is equivalent to a symbol). An error is raised if F
is not a symbol, no matching overload for F
is found, or the matching overload is not C-compatible.
The matched overload must meet the following criteria to be deemed C-compatible:
- It must return zero or one values.
- It must not have any arguments of types with nontrivial
copy
,move
, ordestroy
operations.
Like an external function, if a Clay exception escapes from the pointed-to overload, the unhandledExceptionInExternal
operator function is called.
define callCCodePointer;
[..In, ..Out]
overload callCCodePointer(f:CCodePointer[[..In], [..Out]], ..args:In) : ..Out;
[..In, ..Out]
overload callCCodePointer(f:VarArgsCCodePointer[[..In], [..Out]], ..args:In, ..varArgs) : ..Out;
[..In, ..Out]
overload callCCodePointer(f:LLVMCodePointer[[..In], [..Out]], ..args:In) : ..Out;
// and so on for StdCallCodePointer, etc.
callCCodePointer
invokes an external function, CCodePointer
, or other external function pointer type.
The following functions provide uninterruptible, lock-free memory access and synchronization. Unlike normal symbols, they may not be overloaded. Atomic operations are currently not supported by the compile-time evaluator; an error will be raised if an atomic operation is evaluated at compile time.
define OrderUnordered;
define OrderMonotonic;
define OrderAcquire;
define OrderRelease;
define OrderAcqRel;
define OrderSeqCst;
These symbols are used as parameters by every atomic operation to specify the memory ordering constraints for that operation. These correspond to LLVM's memory ordering constraints, which in turn are a superset of those specified by the C11 and C++11 memory model. See the LLVM Atomic Instructions and Concurrency Guide for details.
OrderUnordered
corresponds to the LLVMunordered
memory ordering.OrderMonotonic
corresponds to the LLVMmonotonic
and C++11memory_order_relaxed
orderings.OrderAcquire
corresponds to the LLVMacquire
and C++11memory_order_acquire
orderings.OrderRelease
corresponds to the LLVMrelease
and C++11memory_order_release
orderings.OrderAcqRel
corresponds to the LLVMacq_rel
and C++11memory_order_acq_rel
orderings.OrderSeqCst
corresponds to the LLVMseq_cst
and C++11memory_order_seq_cst
orderings.
[Order, T | Order?(Order)]
atomicLoad(static Order, p:Pointer[T]) : T;
atomicLoad
performs an atomic load of the value pointed to by p
. If atomic loads of type T
are not supported by the target platform, an error is raised. The value is bitwise-copied. The load is subject to the memory ordering constraints specified by Order
. The function corresponds to the LLVM load atomic
instruction.
[Order, T | Order?(Order)]
atomicStore(static Order, p:Pointer[T], value:T) :;
atomicStore
performs an atomic store of T
to the address of p
. If atomic stores of type T
are not supported by the target platform, an error is raised. value
is bitwise-copied. The store is subject to the memory ordering constraints specified by Order
. The function corresponds to the LLVM store atomic
instruction.
define RMWXchg;
define RMWAdd;
define RMWSubtract;
define RMWAnd;
define RMWNAnd;
define RMWOr;
define RMWXor;
define RMWMin;
define RMWMax;
define RMWUMin;
define RMWUMax;
[Order, Op, T | Order?(Order) and RMWOp?(Op)]
atomicRMW(static Order, static Op, p:Pointer[T], operand:T) : T;
atomicRMW
applies an atomic read-modify-write update operation to the value pointed to by p
. The value pointed to by p
prior to the operation is returned. The Op
parameter determines the operation used to update p^
:
RMWXchg
causesoperand
to be written top^
. The value is bitwise copied.RMWAdd
causesoperand
to be added top^
.T
must be an integer type.RMWSubtract
causesoperand
to be subtracted fromp^
.T
must be an integer type.RMWMin
causes the signed minimum ofoperand
andp^
to be written top
.T
must be an integer type.RMWMax
causes the signed maximum ofoperand
andp^
to be written top
.T
must be an integer type.RMWUMin
causes the unsigned minimum ofoperand
andp^
to be written top
.T
must be an integer type.RMWUMax
causes the unsigned maximum ofoperand
andp^
to be written top
.T
must be an integer type.RMWAnd
causesoperand
to be bitwise-anded withp^
.RMWNAnd
causesoperand
to be bitwise-anded withp^
, and the bitwise-not of the result is written top
.RMWOr
causesoperand
to be bitwise-ored withp^
.RMWXor
causesoperand
to be bitwise-xored withp^
.
The update is subject to the memory ordering constraints specified by Order
. The function corresponds to the LLVM atomicrmw
instruction. An error is raised if the target platform does not atomically support the specified operation for T
.
[Order, T | Order?(Order)]
atomicCompareExchange(static Order, p:Pointer[T], old:T, new:T) : T;
atomicCompareExchange
performs an atomic compare-and-swap update of the value pointed to by p
. If p^
is bitwise equal to old
, new
is bitwise-written to p
, and old
is returned; otherwise, p^
is unchanged, and its current value is returned. An error is raised if the target platform does not support atomic compare-and-swap for type T
. The operation is subject to the memory ordering constraints specified by Order
. The function corresponds to the LLVM cmpxchg
instruction.
[Order | Order?(Order)]
atomicFence(static Order);
atomicFence
introduces a happens-before edge with OrderAcquire
, OrderRelease
, OrderAcqRel
, or OrderSeqCst
semantics without an associated memory operation. It corresponds to an LLVM fence
instruction.
The following functions support the exception handling implementation. Unlike normal symbols, they may not be overloaded.
activeException() : Pointer[Int8];
activeException()
returns a pointer to the exception that is currently instigating unwinding. It is only valid during unwinding itself, specifically not during catch clauses. This primitive is an implementation detail and should not normally be used.
The following functions query properties of symbols. Unlike normal symbols, they may not be overloaded.
[T]
Type?(static T) : Bool;
Type?
returns true if the compile-time value T
is a symbol, and that symbol names a type. If T
is not a symbol or is a non-type symbol, false is returned.
// Example
define foo;
record bar ();
main() {
println(Type?(Type?)); // false
println(Type?(Int32)); // true
println(Type?(foo)); // false
println(Type?(bar)); // true
println(Type?(static 3)); // false
}
[F, ..T]
CallDefined?(static F, static ..T) : Bool;
CallDefined?
attempts to find an overload of the symbol F
matching the input types ..T
. If successful, it returns true; otherwise, it returns false. To find an overload for calling a non-symbol type, CallDefined?(call, FunctionType, ..T)
can be used.
[S]
ModuleName(static S) : StringConstant;
ModuleName
generates a string literal corresponding to the fully-qualified module name of the module containing the symbol S
. Like a true string literal, it is evaluated into a value using the StringConstant
operator function. If S
is itself a module symbol, the module's own name is returned. If S
is not a symbol, an error is raised.
// Example
import foo;
import foo.bar as bar;
in baz;
main() {
println(ModuleName(main)); // would print "baz"
println(ModuleName(foo.a)); // would print "foo"
println(ModuleName(bar.a)); // would print "foo.bar"
println(ModuleName(bar)); // would print "foo.bar"
}
[S]
IdentifierModuleName(static S);
IdentifierModuleName
is like ModuleName
, except that the module's name is returned as a static string rather than as a string literal.
[x]
StaticName(static x) : StringConstant;
StaticName
generates a string literal corresponding to the name of the static value x
. The name string is generated as follows:
- If
x
is a symbol, the symbol's name (not including its module name, but including its parameters if any) is used. - If
x
is a static string, its string value is used. - If
x
is a numeric value, it is converted into a string in decimal notation. - If
x
is a tuple, it is converted into a comma-delimited string surrounded in square brackets ([]
).
Like a true string literal, the generated string is evaluated into a value using the StringConstant
operator function.
[x]
IdentifierStaticName(static x);
IdentifierStaticName
is like StaticName
, except that the static object's name is returned as a static string rather than as a string literal.
[M, name | Identifier?(name)]
staticFieldRef(static M, static name);
staticFieldRef
looks up a public global value in the module M
. If name
is found in M
, it is evaluated as if referenced directly by name. If name
is not a public member of M
, an error is raised.
The following functions provide operations on static strings. Unlike normal symbols, they may not be overloaded.
[S]
Identifier?(static S) : Bool;
Identifier?
returns true if S
is a static string, or false otherwise.
[S | Identifier?(S)]
IdentifierSize(static S) : SizeT;
IdentifierSize
returns the number of characters in the static string S
.
[..SS | allValues?(Identifier?, ..SS)]
IdentifierConcat(static ..SS);
IdentifierConcat
returns the static string consisting of the concatenation of all of its parameters.
[S, n, m |
Identifier?(S)
and n >= 0 and n < IdentifierSize(n)
and m >= 0 and m < IdentifierSize(m)
]
IdentifierSlice(static S, static n, static m);
IdentifierSlice
returns the static string consisting of the substring of S
from character n
up to but not including character m
.
The following functions provide information about types. Unlike normal symbols, they may not be overloaded.
[T | Type?(T)]
TypeSize(static T) : SizeT;
TypeSize
returns the size in bytes of a value of type T
.
[T | Type?(T)]
TypeAlignment(static T) : SizeT;
TypeAlignment
returns the natural alignment in bytes of a value of type T
.
[T]
CCodePointer?(static T) : Bool;
CCodePointer?
returns true if T
is a symbol and is an instance of one of the external code pointer types, such as CCodePointer
, LLVMCodePointer
, etc.
[..T]
TupleElementCount(static Tuple[..T]) : SizeT;
TupleElementCount
returns the number of elements inside values of the tuple type Tuple[..T]
.
[..T]
UnionMemberCount(static Union[..T]) : SizeT;
UnionMemberCount
returns the number of member types of the union type Union[..T]
.
[R]
Record?(static R) : Bool;
Record?
returns true if R
is a symbol that names a record type, or false if R
is not a symbol or is a non-record-type symbol.
[R | Record?(R)]
RecordFieldCount(static R) : SizeT;
RecordFieldCount
returns the number of fields inside values of the record type R
.
[R, n | Record?(R) and n >= 0 and n < RecordFieldCount(R)]
RecordFieldName(static R, static n);
RecordFieldName
returns the name of the n
th field of the record type R
as a static string. n
is zero-based, and an error is raised if R
is not a record type or if n
is less than zero or greater than or equal to the number of fields in R
.
[R, name | Record?(R) and Identifier?(name)]
RecordWithField?(static R, static name) : Bool;
RecordWithField?
returns true if R
is a record type with a field named by the static string name
, or false otherwise.
[V]
Variant?(static V) : Bool;
Variant?
returns true if V
is a symbol that names a variant type, or false if V
is not a symbol or is a non-variant-type symbol.
[V | Variant?(V)]
VariantMemberCount(static V) : SizeT;
VariantMemberCount
returns the number of instance types of the variant type T
. An error is raised if V
is not a variant type.
[V | Variant?(V) and n >= 0 and n < VariantMemberCount(V)]
VariantMemberIndex(static V, static n);
VariantMemberIndex
returns the n
th instance type of the variant type V
. The correspondence of instance types to indices is unspecified, but calling VariantMemberIndex
for every integer value from zero up to but not including VariantMemberCount(V)
will return each instance type once. n
is zero-based, and an error is raised if V
is not a variant type, or if n
is negative or greater than or equal to the number of instance types of V
.
[E]
Enum?(static E) : Bool;
Enum?
returns true if E
is a symbol that names an enum type, or false if E
is not a symbol or is a non-enum-type symbol.
[E | Enum?(E)]
EnumMemberCount(static E) : SizeT;
EnumMemberCount
returns the number of symbolic values defined for the enum type E
. An error is raised if E
is not an enum type.
[E | Enum?(E) and n >= 0 and n < EnumMemberCount(E)]
EnumMemberName(static E, static n) : StringConstant;
EnumMemberName
generates a string literal corresponding to the symbolic name of the n
th ordinal value of the E
enum type. Like a true string literal, the generated string is evaluated into a value using the StringConstant
operator function. n
is zero-based, and an error is raised if E
is not an enum type or if n
is negative or greater than or equal to the number of values of E
.
The following functions query settings for the current compilation unit. Unlike normal symbols, they may not be overloaded.
// If exceptions are enabled
alias ExceptionsEnabled? = true;
// If exceptions are disabled
alias ExceptionsEnabled? = false;
ExceptionsEnabled?
is a global alias that is set to true if exceptions are enabled.
[name | Identifier?(name)]
Flag?(static name) : Bool;
Flag?
returns true if the compiler was invoked with a -D<name>
or -D<name>=value
flag matching the static string name
.
[name | Identifier?(name)]
Flag(static name);
Flag
returns the value associated with the compiler flag -D<name>=value
flag as a static string. If no -D
flag was provided corresponding to name
, or a -D<name>
flag was provided without an associated value, the empty static string #""
is returned.
The following symbols are defined for use as attributes of external functions.
When used as external attributes, the following symbols control the calling convention used by the external function:
AttributeCCall
causes the function to use the C calling convention.AttributeStdCall
causes the function to use the__stdcall
calling convention on Windows x86 systems.AttributeFastCall
causes the function to use the__fastcall
calling convention on Windows x86 systems.AttributeThisCall
causes the function to use the__thiscall
calling convention on Windows x86 systems.AttributeLLVMCall
causes the function to use the LLVMccc
calling convention.
When used as external attributes, the following symbols affect the linkage of the external function:
AttributeDLLImport
gives the function__dllimport
linkage on Windows targets.AttributeDLLExport
gives the function__dllexport
linkage on Windows targets.
Some additional utility functions are provided as primitives. Unlike normal symbols, these functions may not be overloaded.
[n | n >= 0]
staticIntegers(static n);
The staticIntegers
returns a multiple value list of static
integer values from static 0
up to static n - 1
. staticIntegers(static 0)
returns no values.