2.8. Unsafe
The unsafe
keyword denotes unsafe contents, which is required for operations, but could potentially crash the application:
unsafe {
let px = addr(x)
}
Expressions (and subexpressions) can also be unsafe:
let px = unsafe(addr(x))
Unsafe is followed by a block which can include those operations. Nested unsafe sections are allowed. Unsafe is not inherited in lambda, generator, or local functions; it is, however, inherited in local blocks.
Individual expressions can cause a CompilationError::unsafe error, unless they are part of the unsafe section. Additionally, macros can explicitly set the ExprGenFlags::alwaysSafe flag.
The address of expression is unsafe:
unsafe {
let a : int
let pa = addr(a)
return pa // accessing *pa can potentially corrupt stack
}
Lambdas or generators require unsafe sections for the implicit capture by move or by reference:
var a : array<int>
unsafe {
var counter <- @ (extra:int) : int {
return a[0] + extra // a is implicitly moved
}
}
Deleting any pointer requires an unsafe section:
var p = new Foo()
var q = p
unsafe {
delete p // accessing q can potentially corrupt memory
}
Upcast and reinterpret cast require an unsafe section:
unsafe {
return reinterpret<void?> 13 // reinterpret can create unsafe pointers
}
Indexing into a pointer is unsafe:
unsafe {
var p = new Foo()
return p[13] // accessing out of bounds pointer can potentially corrupt memory
}
A safe index is unsafe when not followed by the null coalescing operator:
var a = { 13 => 12 }
unsafe {
var t = a?[13] ?? 1234 // safe
return a?[13] // unsafe; safe index is a form of 'addr' operation
// it can create pointers to temporary objects
}
Variant ?as
on local variables is unsafe when not followed by the null coalescing operator:
unsafe {
return a ?as Bar // safe as is a form of 'addr' operation
}
Variant .?field
is unsafe when not followed by the null coalescing operator:
unsafe {
return a?.Bar // safe navigation of a variant is a form of 'addr' operation
}
Variant .field
is unsafe:
unsafe {
return a.Bar // this is potentially a reinterpret cast
}
Certain functions and operators are inherently unsafe or marked unsafe via the [unsafe_operation] annotation:
unsafe {
var a : int?
a += 13 // pointer arithmetic can create invalid pointers
var boo : int[13]
var it = each(boo) // each() of array is unsafe, for it does not capture
}
Moving from a smart pointer value requires unsafe, unless that value is the ‘new’ operator:
unsafe {
var a <- new TestObjectSmart() // safe, its explicitly new
var b <- someSmartFunction() // unsafe since lifetime is not obvious
b <- a // safe, values are not lost
}
Moving or copying classes is unsafe:
def foo ( var b : TestClass ) {
unsafe {
var a : TestClass
a <- b // potentially moving from derived class
}
}
Local class variables are unsafe:
unsafe {
var g = Goo() // potential lifetime issues
}
2.9. implicit
implicit keyword is used to specify that type can be either temporary or regular type, and will be treated as defined. For example:
def foo ( a : Foo implicit ) // a will be treated as Foo, but will also accept Foo# as argument
def foo ( a : Foo# implicit ) // a will be treated as Foo#, but will also accept Foo as argument
Unfortunately implicit conversions like this are unsafe, so implicit is unsafe by definition.
2.10. other cases
There are several other cases where unsafe is required, but not explicitly mentioned in the documentation. They are typically controlled via CodeOfPolicies or appropriate option:
options unsafe_table_lookup = false // makes table indexing safe. refers to CodeOfPolicies::unsafe_table_lookup
var tab <- { 1=>"one", 2=>"two" }
tab[3] = "three" // this is unsafe, since it can create a pointer to a temporary object