Safety
- No implicit downcasts,
i.e. standard conversions only apply when no information is lost.Not Okor Ok isInt8->Int8,Int16,Int32,Int64,Int128,Int256,BigIntUInt8,UInt16,UInt32,UInt64,UInt128,UInt256,BigUIntFloat16,Float32,Float64,Float128,Float256,BigFloat
UInt8->UInt8,UInt16,UInt32,UInt64,UInt128,UInt256,BigUIntInt8,Int16,Int32,Int64,Int128,Int256,BigIntFloat16,Float32,Float64,Float128,Float256,BigFloat
Int16->Int8,Int16,Int32,Int64,Int128,Int256,BigIntUInt8,UInt16,UInt32,UInt64,UInt128,UInt256,BigUIntFloat16,Float32,Float64,Float128,Float256,BigFloat
UInt16->UInt8,UInt16,UInt32,UInt64,UInt128,UInt256,BigUIntInt8,Int16,Int32,Int64,Int128,Int256,BigIntFloat16,Float32,Float64,Float128,Float256,BigFloat
Int32->Int8,Int16,Int32,Int64,Int128,Int256,BigIntUInt8,UInt16,UInt32,UInt64,UInt128,UInt256,BigUIntFloat16,Float32,Float64,Float128,Float256,BigFloat
UInt32->UInt8,UInt16,UInt32,UInt64,UInt128,UInt256,BigUIntInt8,Int16,Int32,Int64,Int128,Int256,BigIntFloat16,Float32,Float64,Float128,Float256,BigFloat
Int64->Int8,Int16,Int32,Int64,Int128,Int256,BigIntUInt8,UInt16,UInt32,UInt64,UInt128,UInt256,BigUIntFloat16,Float32,Float64,Float128,Float256,BigFloat
UInt64->UInt8,UInt16,UInt32,UInt64,UInt128,UInt256,BigUIntInt8,Int16,Int32,Int64,Int128,Int256,BigIntFloat16,Float32,Float64,Float128,Float256,BigFloat
Int128->Int8,Int16,Int32,Int64,Int128,Int256,BigIntUInt8,UInt16,UInt32,UInt64,UInt128,UInt256,BigUIntFloat16,Float32,Float64,Float128,Float256,BigFloat
UInt128->UInt8,UInt16,UInt32,UInt64,UInt128,UInt256,BigUIntInt8,Int16,Int32,Int64,Int128,Int256,BigIntFloat16,Float32,Float64,Float128,Float256,BigFloat
Int256->Int8,Int16,Int32,Int64,Int128,Int256,BigIntUInt8,UInt16,UInt32,UInt64,UInt128,UInt256,BigUIntFloat16,Float32,Float64,Float128,Float256,BigFloat
UInt256->UInt8,UInt16,UInt32,UInt64,UInt128,UInt256,BigUIntInt8,Int16,Int32,Int64,Int128,Int256,BigIntFloat16,Float32,Float64,Float128,Float256,BigFloat
BigInt->Int8,Int16,Int32,Int64,Int128,Int256,BigIntUInt8,UInt16,UInt32,UInt64,UInt128,UInt256,BigUIntFloat16,Float32,Float64,Float128,Float256,BigFloat
BigUInt->UInt8,UInt16,UInt32,UInt64,UInt128,UInt256,BigUIntInt8,Int16,Int32,Int64,Int128,Int256,BigIntFloat16,Float32,Float64,Float128,Float256,BigFloat
-
Using signed
Intas size - Range & Validation Checks
- The low hanging fruit would be to enable by default, also in release builds (not only in debug):
- range checks, to detect buffer overflows or similar,
- safe iterators, to detect invalid iterators.
- So every safe iterator would have a pointer to the element and a pointer to the container.
- Naive: check at every dereferencing
- Optimized: check at first dereferencing, but thereafter only after a call to a non-const member function of the container (or if such a call cannot be excluded).
- So every safe iterator would have a pointer to the element and a pointer to the container.
- This should fix the majority of C/C++ security issues.
To achieve maximum performance in all cases, there could be a third build configuration for even faster, but potentially unsafe builds. - So we would have:
- Debug
- for debugging,
- with range checks,
- with line by line debug info, and
- often with a modified memory layout (to find more types of errors).
- Release
- for deployment,
- with range checks,
- suitable for most situations,
- by default using safe containers (with safe iterators),
- optionally using unsafe containers (with unsafe iterators),
- with memory layout compatible to
EvenFasterButUnsafeRelease.
EvenFasterButUnsafeRelease- for deployment,
- without range checks (for even better performance),
- by default still using safe containers (with safe iterators),
- so memory layout is compatible to Release,
- optionally using unsafe containers (with unsafe iterators, for even better performance).
- Debug
- The low hanging fruit would be to enable by default, also in release builds (not only in debug):
- Initialization
- No initialization means random values. In this case they are in fact often zero, but not always.
- Initializing large arrays (e.g.
Array,Image,Vector, orMatrixwith many elements) takes a noticeable amount of time, so we don’t want to always initialize everything.- With virtual memory it could actually be (almost) “free” to initialize large arrays with zero. But only when using heap memory directly. Small memory regions, that were used before, still need to be overwritten with zeros.
- We could consider it an error (or at least warn) if not initialized,
and use a keywordnoinitto avoid that warning/error.Int i // Error Int j noinit // Ok Int j = 1 // Ok - Classes / custom types
- Mark constructors with
noinitwhen they do not initialize their values, sonoinitshould be used when calling them consciously. -
class Array<type T> { Array(Int size) noinit { ... } Array(Int size, T value) { ... } } Array<Float> anArray(10) // Warning Array<Float> anArray(10) noinit // No warning Array<Float> anArray(10, 1.0) // No warning
- Mark constructors with
- Also for free memory/heap
-
var arrayPtr = new Array<Float>(10) // Warning var arrayPtr = new Array<Float>(10) noinit // No warning var arrayPtr = new Array<Float>(10, 1.0) // No warning
-
safeas default,unsafecode blocks as escape.- Mainly to guide developers:
- to signal what to do and what not to do,
unsafeis not regularly used, normally you just use the already existing, carefully developed and tested abstractions (likeArray,Vector,Matrix, …).
- Not allowed in safe code:
- Subscript access to pointers,
reinterpretCastTo<T>(...),- calling functions marked as
unsafe, - use of
noinit.
- Still allowed/undetected in unsafe code:
- Integer overflow (checking that all the time seems too costly)
unsafecode is necessary to implement certain abstractions (like container classes):-
operator[Int i] -> T& { if CHECK_BOUNDS and (i < 0 or i >= size) { terminate() } unsafe { return data[i] } }
-
- A function with unsafe code does not necessarily has to be marked as
unsafeitself. unsafeis a marker for those parts (subfunctions or code blocks) that are not safe (i.e. dangerous) and need to be checked carefully.- Functions containing unsafe code enclosed in an
unsafeblock do not have to be marked withunsafethemselves. - Only functions containing unsafe code not enclosed in an
unsafeblock have to be marked withunsafethemselves. - Unsafe is transitive (from an
unsafeinner function to the outer function), but limited to the scope ofunsafeblocks.
- Mainly to guide developers:
cilia::safe::Int- Like
cilia::Int, but with overflow check for all operations,- may throw OverflowException (or abort the program).
- Generally considered to be too costly, even in languages that are otherwise considered as “safe”.
safe::Int8/Int16/Int32/Int64safe::UIntsafe::UInt8/UInt16/UInt32/UInt64
- Like
- No further safety features planned beyond C++:
- Not as in Rust or Hylo,
- that is just out of scope.
- No additional thread safety measures.
- A thread safety issue can easily lead to a deadlock or crash, and sometimes can even be a security problem.
- But while thread safety can be a hard problem, there are currently no plans to extend the possibilities beyond plain C++ here (mainly because I am not aware of / familiar with better solutions than already available/recommended in C++).
- Not as in Rust or Hylo,