Core v0.1.0
Installation
Add Core to the [Dependencies] table of your package's Rux.toml:
[Dependencies]
Core = { Namespace = "Rux", Version = "0.1.0" }
Standard packages are declared by hand: the registry does not serve them, so rux add Rux/Core cannot add them.
Guide
Core declares primitive associated constants, diagnostics, value protocols, and compile-time context.
Core is optional. Scalar types, arithmetic, arrays, inferred string literals, and range syntax work without it. Every primitive type is built into the compiler; Core extends them, and importing a primitive from Core imports its associated constants. Slice and range fields are compiler-owned. A replacement package can provide the same extensions and intrinsic declarations; the compiler assigns no privilege to the name or manifest identity of Core.
What it provides
- Compile-time context —
#target(os, arch, abi, endian, pointer width, data model, object format, triple,HasFeature),#build,#compiler,#source, and#config, together with the enums they are typed by:OperatingSystem,Architecture,ApplicationBinaryInterface,Endianness,DataModel,ObjectFormat,BuildMode,OptimizationMode,OutputKind,TargetFeature. - Atomic —
AtomicLoad,AtomicStore,AtomicExchange,AtomicCompareExchangeandAtomicFetchAdd/Sub/And/Or/Xoract on one integer of 8 to 64 bits,boolor pointer so that no other thread sees them half done, andFenceandSpinHintorder and pace a thread without touching memory. Each takes aMemoryOrderwritten at the call as a case such asMemoryOrder::Acquire, and the compiler refuses the orders an operation cannot have. On x86-64 they are plain moves,xchgandlock-prefixed instructions; on AArch64,ldar/stlrand exclusive-access loops.Rux/Atomicwraps them in a type. - Startup —
ProcessStartFrameis the address the kernel started an ELF program at, where its argument count, argument and environment pointers and auxiliary vector are; it is null on Windows and macOS, which supply the command line through system calls, and in a shared library.Rux/Processreads arguments and the environment through it. - Diagnostics —
Assert,DebugAssert, andPanicreport and terminate;#Errorand#Warnreject or flag an unsupported configuration while compiling and produce no runtime code. A release build removesDebugAssertchecks without evaluating their arguments. After the location, the report names the functions that led to the failure, onefrom Nameline each, read from the stack's frame records; a release build that inlined a function names its caller instead. NeitherAssertnorPanicunwinds, so no destructor runs on the way out — anything that must be released on a failing path belongs to a caller that saw the failure, which is what a native fallibleT ! Eis for. - Native outcome questions —
SucceededandFailedanswer which channel a native fallibleT ! Eholds without unwrapping it. Native forms have no methods of their own, so these are ordinary generic functions; each takes the outcome by value, so a call's result is asked directly and a named outcome with a move-only payload is passed as<-outcome. - Ordering — the result of a three-way comparison, with
IsLess,IsEqual,IsGreater, the derivedIsLessOrEqualandIsGreaterOrEqual, andReverse. Absence and failure are not library types: they are the native optionalT?and fallibleT ! E, with the native unit()for an operation with nothing to report. - Interfaces for generic code —
EquatableandComparableborrow theirSelf-typed operands through non-null references,Hashablegives the 64-bit summary a table keys on, andIterator/Iterablename the iteration protocol the compiler drives by shape.Sendableis the marker the compiler decides for a value that may move to another thread: every type satisfies it except a reference and one holding a type declared#Unsendable(). Resource types prohibit copying with a bodyless canonical=and release themselves with~Type. Bounds such asfunc Sort<T: Comparable>(...)resolveSelfto each type argument without dynamic dispatch. - Lazy adaptors —
Map,Filter,Take,Skip,Enumerate,ZipandChainwrap anyCursor<T>— a type whoseNextreturnsT?, such as a container's iterator,Walkover a slice, or another adaptor — and compute each item only when it is asked for. Their item types are deduced from the wrapped cursor, soTake(Filter(Map(Walk(items), Square), IsEven), 3)needs no type arguments. - Integer arithmetic —
AddChecked,SubChecked, andMulCheckedreport whether the true answer was representable and write the machine's answer either way;AddWrapping,SubWrapping, andMulWrappingname the wrapping that+,-, and*already do;AddSaturating,SubSaturating, andMulSaturatingclamp to whichever limit the true answer ran past.MaximumOfandMinimumOfreport a type's limits. All are generic over the integer type and work at every width, signed and unsigned, deriving a type's zero, signedness, and limits from one operand rather than being written out per width. The operation comes first in each name becauseCheckedAdd,CheckedSub, andCheckedMulalready name the compiler'suint64intrinsics, which are emitted inline and are what allocation-size arithmetic is written with. - Bit operations —
CountOnes,CountZeros,LeadingZeros,TrailingZeros,LeadingOnes,TrailingOnes,RotateLeft,RotateRight,ReverseBits,ReverseBytes,BitAt,BitWidthOf,IsPowerOfTwo, andShiftRightLogical. All are generic over the integer type and right at every width, signed and unsigned:>>copies the sign bit down on a signed type, so everything that reads a value's bits as bits goes throughShiftRightLogical, which brings zeros in either way. - Byte order —
ToLittleEndian,FromLittleEndian,ToBigEndian, andFromBigEndianconvert a value already in a register;StoreLittleEndian,StoreBigEndian,LoadLittleEndian, andLoadBigEndianmove one through raw bytes at any alignment, which is what a file or wire format needs.TargetIsLittleEndianreports this target's own order, established from its storage rather than from a list of known targets. - Checked conversion —
ConvertCheckedreports whether a primitive value survived the conversion and writes the machine's answer either way;ConvertWrappingnames the wrappingasalready does, andConvertSaturatingclamps to the destination's range. Every check is a round trip in the source type, plus one test for the pair a round trip cannot see — a value and its conversion that disagree in sign, which is how-1reachesuint64.IsSignedTypereports whether a type has values below zero. - Float classification —
IsNaN,IsZero,IsInfinite,IsFinite,IsNegativeZero,IsSignNegative,IsSignPositive, andSignOf. All are arithmetic rather than bit inspection, so one implementation serves every float width including the software-lowered ones, whose storage carries padding no generic function could locate a sign bit inside of. The sign of a zero, which comparison cannot see, is read from the infinity it divides into. - Primitive associated constants —
Bits,Bytes,Min,Maxand the floating-point set (Lowest,MinPositive,Epsilon,Infinity,NaN) for every integer width and every character width fromchar8tochar512, width metadata for every boolean width frombool8tobool512, and the finite and special constant set for every float width fromfloat8tofloat512. The scalar widths fromchar32tochar512share one range, U+0000 to U+10FFFF, however many bytes they occupy. The string widths expose none: a string is a view over code units rather than a value with a width, so a width or a limit would describe neither it nor the units.
Primitive constants and imports
import Core::int8;
func Main() -> int {
let minimum = int8::Min;
let message: char8[..] = "hello";
return message.length as int + minimum as int;
}
int8 is built into the compiler, so it names the scalar type without an import; import Core::int8; imports Core's extend int8 block, which is what makes int8::Min available. The built-in aliases import their canonical widths: import Core::byte; gives byte::Max, the same constant as uint8::Max, and import Core::bool; gives bool::Bits. Loading a dependency that imports it does not import its APIs into the caller. The same rule applies between source files.
Core declares no primitive type: each primitive file holds one documented extend block. Core defines integer and character limits, width metadata, and finite floating constants using ordinary constant expressions. Native integer metadata uses sizeof, so it follows the compilation target. The float limits of float8 and float16 are literals — float8, the E4M3 format, tops out at 240 and float16 at 65504 — while Infinity and NaN at every width, and every limit of float80, float128, float256 and float512, are bodyless intrinsic const declarations whose values the compiler supplies from the format itself.
Sequence views and ranges
The language supplies read-only T[..] and writable var T[..] views. Neither annotation needs a Core import. Their .data and .length fields are compiler-owned. A view owns no storage, and copying it copies two words. Its backing storage must remain alive while it is used.
func Sum(values: int[..]) -> int {
var total = 0;
for value in values { total += value; }
return total;
}
func Fill(values: var int[..], value: int) {
for index in 0..values.length { values[index] = value; }
}
func Main() -> int {
var storage: int[3] = [1, 2, 3];
let view = storage[..];
Fill(view, 4);
return Sum(view) - 12;
}
A writable view weakens to a read-only one implicitly; a read-only view cannot be strengthened. Binding a view with let does not freeze its pointee. [] constructs an empty view when a slice type is expected. Use pointer[..length] to view raw storage, and view[start..end] to select a subview. Pointer ranges require an end because pointers carry no length. These operations use element counts rather than byte counts.
Ranges are written int..int, int..=int, int.., ..int, ..=int, and .. in annotations. Their .start and .end fields exist only when the corresponding bound exists, with no declaration or import required.
Literal text is char8[..], char16[..], or char32[..]; the prefixes are c8, c16, and c32, and an unprefixed literal uses UTF-8. The descriptor length counts code units, excluding the trailing NUL in literal storage. Text can be passed directly to a function taking the corresponding read-only character slice. Slicing can split an encoded scalar, so validated-text operations belong in Text and Unicode. A literal cannot become a writable view.
Core diagnostic messages and compile-time context text use char8[..]. External functions taking raw pointers receive "literal".data. Concrete slice extensions can add methods for one element type; reusable generic algorithms use ordinary generic functions.
Ownership and borrowing
Core methods borrow ordinary values with &T and mutable receivers with &var T; callers pass the value directly and the compiler creates the short-lived borrow. Named move-only values require <- when ownership crosses a binding, call, assignment, return, or consuming receiver. Copyable Core values use generated structural copying. Canonical constructors are type calls such as SemanticVersion(1, 2, 3), and descriptive factories retain their names.
Raw pointers remain where absence, storage, or address arithmetic is part of the contract: slice fields, nullable TryInto* outputs, unchecked checked-arithmetic/conversion destinations, byte-order buffers, and zeroization.
Assert and Panic failures have the same UTF-8, LF-terminated layout on every supported target:
Assertion failed: message
at Function (path:line:column)
Panic uses Panic: message on the first line. The location uses a package-relative path when the source belongs to the package root, and qualified method or module names are retained.
Example
import Core::{ #target, #Error };
when #target.os {
.Windows, .Linux, .macOS, .FreeBSD => {},
else => #Error("Unsupported operating system")
}
import Core::{ Assert, SemanticVersion };
func Major(version: &SemanticVersion) -> uint {
return version.major;
}
func Main() -> int {
let version = SemanticVersion(1, 2, 3);
let answer: int32? = .Some(42i32);
Assert(Major(version) == 1u, "borrowed version");
Assert((answer ?? 0i32) == 42i32, "present optional value");
return 0;
}
Index
Interfaces
| Name | Summary |
|---|---|
Cursor | Anything that yields items of type T one at a time through Next, which for and every adaptor here drive. |
Comparable | Ordering: which of two values of one type comes first. |
Equatable | Equality: whether two values of one type stand for the same thing. |
Hashable | Hashing: the 64-bit summary a table uses to find a value quickly. |
Iterator | A cursor that yields a sequence of items one at a time. |
Iterable | A value that can hand out an Iterator over its contents. |
Sendable | A type whose values may move to another thread: the bound a thread's context and result are written with. |
Types
| Name | Summary |
|---|---|
SliceCursor<T> | A cursor over the elements of a slice, by copy. |
Mapped<I, T, U> | The items of a cursor, each passed through a function. |
Filtered<I, T> | The items of a cursor that pass a test. |
Taken<I, T> | At most a number of items from the front of a cursor. |
Skipped<I, T> | The items of a cursor after a number of them. |
Indexed<T> | An item and its position, counting from zero. |
Enumerated<I, T> | The items of a cursor with their positions. |
Pair<A, B> | One item from each of two cursors. |
Zipped<I, J, A, B> | The items of two cursors side by side. |
Chained<I, J, T> | The items of one cursor and then of another. |
MemoryOrder | Which other memory operations may be reordered across an atomic one. |
BuildMode | The high-level mode of the active build profile. |
OptimizationMode | What the active build asked the optimizer to favor. |
OutputKind | The kind of artifact the active build produces. |
Build | Properties of the active build profile and its output. |
SemanticVersion | A semantic version, as its three numeric core components. |
Compiler | Properties of the compiler performing the build. |
Config | The set of user-defined build values. |
Ordering | The answer a three-way comparison gives: which side is smaller, or that neither is. |
bool128 | One of two values, stored in 128 bits. |
bool16 | One of two values, stored in 16 bits. |
bool256 | One of two values, stored in 256 bits. |
bool32 | One of two values, stored in 32 bits. |
bool512 | One of two values, stored in 512 bits. |
bool64 | One of two values, stored in 64 bits. |
bool8 | One of two values, stored in 8 bits. |
char128 | One Unicode scalar value, in sixteen bytes. |
char16 | One UTF-16 code unit. |
char256 | One Unicode scalar value, in thirty-two bytes. |
char32 | One Unicode scalar value. |
char512 | One Unicode scalar value, in sixty-four bytes. |
char64 | One Unicode scalar value, in eight bytes. |
char8 | One UTF-8 code unit. |
float128 | An IEEE 754 binary128 floating-point number. |
float16 | An IEEE 754 binary16 floating-point number. |
float256 | An IEEE 754 binary256 floating-point number. |
float32 | An IEEE 754 binary32 floating-point number. |
float512 | A binary512 floating-point number in the IEEE 754 interchange-format progression. |
float64 | An IEEE 754 binary64 floating-point number. |
float8 | An eight-bit E4M3 floating-point number with IEEE 754 infinities and NaNs. |
float80 | An x87 extended-precision floating-point number. |
int | A target pointer-sized signed integer. |
int128 | A signed integer in 128 bits, two's complement. |
int16 | A signed integer in 16 bits, two's complement. |
int256 | A signed integer in 256 bits, two's complement. |
int32 | A signed integer in 32 bits, two's complement. |
int512 | A signed integer in 512 bits, two's complement. |
int64 | A signed integer in 64 bits, two's complement. |
int8 | A signed integer in 8 bits, two's complement. |
uint | A target pointer-sized unsigned integer. |
uint128 | An unsigned integer in 128 bits. |
uint16 | An unsigned integer in 16 bits. |
uint256 | An unsigned integer in 256 bits. |
uint32 | An unsigned integer in 32 bits. |
uint512 | An unsigned integer in 512 bits. |
uint64 | An unsigned integer in 64 bits. |
uint8 | An unsigned integer in 8 bits. |
Source | The location of the expression that reads it. |
OperatingSystem | An operating system a build can target. |
Architecture | A processor architecture a build can target. |
ApplicationBinaryInterface | The calling and layout conventions a target's code follows. |
TargetFeature | A processor feature a target may be built with. |
Endianness | The order a target stores a multi-byte value's bytes in. |
DataModel | The widths a target's C environment assigns to long and to a pointer. |
ObjectFormat | The object-file format a target's compiled code is emitted in. |
Target | Properties of the machine the build targets. |
Functions
| Name | Summary |
|---|---|
Walk<T> | A cursor over the elements of items, so a slice can feed the adaptors. |
Map<I, T, U> | Each item of source passed through transform, as it is asked for. |
Filter<I, T> | The items of source for which keep holds. |
Take<I, T> | The first count items of source, or all of them if it has fewer. |
Skip<I, T> | The items of source after its first count. |
Enumerate<I, T> | Each item of source with its position, counting from zero. |
Zip<I, J, A, B> | The items of first and second in pairs, as long as both last. |
Chain<I, J, T> | The items of first followed by the items of second. |
MaximumOf<T> | The largest value the integer type of sample can hold. |
MinimumOf<T> | The smallest value the integer type of sample can hold. |
AddChecked<T> | Adds two integers and reports whether the true sum is representable. |
SubChecked<T> | Subtracts one integer from another and reports whether the true difference is representable. |
MulChecked<T> | Multiplies two integers and reports whether the true product is representable. |
AddWrapping<T> | Adds two integers, wrapping around the type's range. |
SubWrapping<T> | Subtracts one integer from another, wrapping around the type's range. |
MulWrapping<T> | Multiplies two integers, wrapping around the type's range. |
AddSaturating<T> | Adds two integers, clamping to the type's range instead of wrapping. |
SubSaturating<T> | Subtracts one integer from another, clamping to the type's range instead of wrapping. |
MulSaturating<T> | Multiplies two integers, clamping to the type's range instead of wrapping. |
AtomicLoad<T> | Reads the value at address atomically. |
AtomicStore<T> | Writes value to address atomically. |
AtomicExchange<T> | Writes value to address and returns what was there, as one atomic step. |
AtomicCompareExchange<T> | Writes desired to address if it holds expected, and returns what it held, as one atomic step. |
AtomicFetchAdd<T> | Adds value to the integer at address and returns what was there, as one atomic step. |
AtomicFetchSub<T> | Subtracts value from the integer at address and returns what was there, as one atomic step. |
AtomicFetchAnd<T> | Combines the value at address with value by bitwise and, and returns what was there, as one atomic step. |
AtomicFetchOr<T> | Combines the value at address with value by bitwise or, and returns what was there, as one atomic step. |
AtomicFetchXor<T> | Combines the value at address with value by bitwise exclusive or, and returns what was there, as one atomic step. |
Fence | Orders this thread's memory operations without touching memory. |
SpinHint | Tells the processor that the thread is waiting in a loop for another thread. |
BitWidthOf<T> | The number of bits in the integer type of sample. |
ShiftRightLogical<T> | Shifts an integer right, bringing zeros in at the top whatever its signedness. |
BitAt<T> | Whether the bit at index is set, counting from the least significant bit. |
CountOnes<T> | The number of set bits. |
CountZeros<T> | The number of clear bits. |
LeadingZeros<T> | The number of clear bits above the highest set bit. |
TrailingZeros<T> | The number of clear bits below the lowest set bit. |
LeadingOnes<T> | The number of set bits above the highest clear bit. |
TrailingOnes<T> | The number of set bits below the lowest clear bit. |
RotateLeft<T> | Rotates an integer's bits toward the most significant end, wrapping the ones that run off. |
RotateRight<T> | Rotates an integer's bits toward the least significant end, wrapping the ones that run off. |
ReverseBits<T> | The value with its bits in the opposite order: bit zero becomes the highest bit and back again. |
ReverseBytes<T> | The value with its bytes in the opposite order, leaving the bits within each byte where they were. |
IsPowerOfTwo<T> | Whether the value has exactly one bit set. |
CheckedAdd | Adds two unsigned 64-bit values and reports whether the true sum is representable. |
CheckedSub | Subtracts one unsigned 64-bit value from another and reports whether the true difference is representable. |
CheckedMul | Multiplies two unsigned 64-bit values and reports whether the true product is representable. |
IsSignedType<T> | Whether the integer type of sample has values below zero. |
ConvertChecked<From, To> | Converts a primitive value to another primitive type and reports whether the true value was representable. |
ConvertWrapping<From, To> | Converts a primitive value to another primitive type, keeping whatever fits. |
ConvertSaturating<From, To> | Converts a primitive value to an integer type, clamping to that type's range instead of wrapping. |
Assert | Terminates the program unless condition holds. |
DebugAssert | Terminates the program unless condition holds, in builds that keep debug assertions. |
Panic | Terminates the program, reporting message. |
#Error | Rejects the build where it is written, reporting message. |
#Warn | Reports message as a compilation warning where it is written, without rejecting the build. |
TargetIsLittleEndian | Whether this target stores an integer's least significant byte first. |
ToLittleEndian<T> | The value's bytes as a little-endian value would hold them. |
FromLittleEndian<T> | The value a little-endian bit pattern stands for on this target. |
ToBigEndian<T> | The value's bytes as a big-endian value would hold them. |
FromBigEndian<T> | The value a big-endian bit pattern stands for on this target. |
StoreLittleEndian<T> | Writes an integer to raw bytes, least significant byte first. |
StoreBigEndian<T> | Writes an integer to raw bytes, most significant byte first. |
LoadLittleEndian<T> | Reads an integer from raw bytes holding it least significant byte first. |
LoadBigEndian<T> | Reads an integer from raw bytes holding it most significant byte first. |
IsNaN<T> | Whether the value is not a number. |
IsZero<T> | Whether the value is either zero. |
IsInfinite<T> | Whether the value is an infinity, of either sign. |
IsFinite<T> | Whether the value is an ordinary number: neither a NaN nor an infinity. |
IsNegativeZero<T> | Whether the value is the zero that carries a minus sign. |
IsSignNegative<T> | Whether the value carries a minus sign. |
IsSignPositive<T> | Whether the value carries a plus sign. |
SignOf<T> | The value's sign as one of -1, +1, or the zero it started from. |
Succeeded<T, E> | Whether outcome holds a success. |
Failed<T, E> | Whether outcome holds a failure. |
ProcessStartFrame | The address the kernel started the program at, on targets where that is where the arguments are. |
Zeroize | Overwrites a byte range with zeros, and is never removed however unread those bytes are afterwards. |
Constants
| Name | Summary |
|---|---|
#build | The active build, as a compile-time value. |
#compiler | The compiler performing the build, as a compile-time value. |
#config | The build's user-defined values, as a compile-time value. |
#source | The reading expression's own location, as a compile-time value. |
#target | The machine being compiled for, as a compile-time value. |