Unsafe and Raw Pointers
Sec uses unsafe for operations whose complete safety obligations cannot be
proven automatically at an ordinary safe call site.
The keyword does not disable the language's safety model. Instead, it marks a precise trust boundary where the programmer or a trusted wrapper accepts specific proof obligations.
let value := unsafe pointer.Read()
unsafe pointer.Write(value)
The principal low-level address type used at these boundaries is:
RawPtr[T]
unsafe accepts specific proof obligations.
It does not turn an operation known to be invalid into a valid one.
What unsafe Means
An unsafe operation is an operation whose safety depends on facts that ordinary static analysis cannot completely establish at that source location.
Examples include:
- raw-pointer dereference;
- raw-pointer write;
- raw-pointer arithmetic;
- raw-to-safe reference construction;
- slice construction from raw parts;
- calling an
unsafe fn; - calling an unsafe foreign function;
- inline assembly;
- unchecked representation construction;
- unchecked enum or union construction;
- interpreting an unverified numeric address;
- adopting ownership from raw or foreign storage.
The explicit unsafe marker records that the remaining proof obligations
have been reviewed outside ordinary compiler proof.
What unsafe Does Not Disable
The compiler continues every analysis that remains applicable.
unsafe does not mean:
- stop type checking;
- stop ownership checking;
- stop borrow checking;
- stop lifetime analysis;
- stop effect analysis;
- ignore moves or invalidation;
- ignore cleanup rules;
- ignore target or platform rules;
- permit arbitrary backend undefined behavior.
If the compiler can prove an unsafe obligation false, the operation is rejected even when it is written inside an unsafe context.
// Conceptual example:
// if the compiler proves that `pointer` is null here,
// unsafe does not make this read legal.
let value := unsafe pointer.Read()
Unsafe Operations Are Operation-Specific
The language does not have one universal “trust everything in this file” mode.
Each compiler-known unsafe operation has its own required obligations. For example, a raw read may require:
- non-null address;
- correct alignment;
- readable storage;
- a valid initialized representation;
- sufficient lifetime;
- compatible aliasing;
- correct address space;
- permission under the active platform contract.
An unrelated unsafe operation may have a different set of obligations.
Single-Operation unsafe
A single unsafe expression can be marked directly:
let value := unsafe pointer.Read()
A single unsafe statement can also be marked directly:
unsafe pointer.Write(value)
This is the narrowest form and is preferred when only one operation requires trust.
unsafe Blocks
Several related operations may be grouped into an unsafe block:
let value := unsafe {
Validate(pointer)
pointer.Read()
}
The block follows ordinary Sec block and evaluation rules.
Safe operations inside the block remain ordinary safe operations. The block does not remove their normal checks.
Unsafe Blocks Can Produce Values
Because the block follows normal block-expression semantics, it may produce a value:
let result := unsafe {
Prepare(pointer)
pointer.Read()
}
The resulting type is determined by the ordinary block-expression rules.
unsafe does not create a special “unsafe value” type.
Unsafe Scope Is Lexically Explicit
The marker applies only to the operation or block it introduces.
unsafe {
pointer.Write(value)
}
pointer.Write(value) // still requires unsafe here
A parent scope does not become implicitly unsafe.
Nested unsafe contexts are permitted, although tooling may report a nested marker as redundant when it adds no useful boundary clarity.
Empty and Redundant Unsafe Contexts
An empty unsafe block is valid syntax:
unsafe {
}
An unsafe context containing no operation that requires unsafe is also valid.
By default, these cases should produce informational diagnostics rather than hard errors.
Project policy may promote redundant-unsafe diagnostics to warnings.
unsafe fn
An unsafe function or method exposes proof obligations to its caller:
unsafe fn FromRawParts(
pointer: RawPtr[byte],
length: int,
) Buffer {
// ...
}
Calling it requires an unsafe context:
let buffer := unsafe Buffer.FromRawParts(pointer, length)
Unsafe status is part of the callable safety contract.
It is not the same thing as panic behavior, allocation behavior, blocking behavior or any other effect.
The Body of unsafe fn Is Still Explicit
Declaring a function unsafe does not make its body implicitly unsafe.
unsafe fn ReadRaw(pointer: RawPtr[byte]) byte {
return unsafe pointer.Read()
}
Each actual unsafe operation remains marked.
This keeps the exact implementation trust boundary visible during review.
An unsafe fn may contain only safe operations when the caller obligation
exists for contractual reasons.
Caller Proof Obligations
Every unsafe callable must define what the caller promises.
Obligations may include:
- pointer is non-null;
- pointer is correctly aligned;
- address range is valid;
- storage remains live for the required lifetime;
- storage contains initialized valid representation;
- mutable access is exclusive;
- no conflicting aliases exist;
- the allocator/deallocator pair is correct;
- address space is valid;
- hardware context is valid;
- foreign ABI is correct;
- callback or thread affinity is satisfied;
- concurrent invalidation cannot occur;
- length/capacity relationships are valid.
The compiler may prove some obligations itself. Only the remaining unproven obligations are accepted by the explicit unsafe boundary.
A compiler-proven contradiction remains an error.
Safety Documentation
Public unsafe APIs should document their caller obligations.
Sec 0.1 treats this as strongly recommended rather than a universal hard language requirement.
Project or tooling policy may require safety documentation for exported unsafe APIs.
Compiler-known unsafe operations must not rely solely on comments: their obligations must also exist in compiler-readable metadata.
Safe Wrappers
A safe function may use unsafe operations internally when it establishes every obligation before crossing the boundary.
Conceptually:
fn CreateBuffer(
pointer: RawPtr[byte],
length: int,
) Result[Buffer, BufferError] {
if length < 0 {
return Err(BufferError.InvalidLength)
}
// Additional pointer/storage validation belongs here.
let buffer := unsafe Buffer.FromRawParts(pointer, length)
return Ok(buffer)
}
A safe wrapper must not leave hidden obligations that the safe API cannot express or enforce.
Recoverable validation failure should use normal Sec error handling where appropriate.
Trust Provenance
Encapsulating an unsafe operation in a safe wrapper removes the caller's unsafe obligation only when the wrapper discharges it.
It does not erase the fact that the implementation ultimately depends on trusted behavior.
The compiler/tooling may retain facts such as:
caller-facing safety: safe
implementation provenance: depends on unsafe foreign call
Trust provenance is useful for:
- security audits;
- whole-program trust reports;
- ISR analysis;
- LSP navigation;
- certification profiles;
- invalidating assumptions when a platform or FFI contract changes.
Effects Remain Active Inside Unsafe Code
Unsafe status and effect analysis are independent semantic dimensions.
An unsafe operation may still carry effects such as:
MayPanic
MayAllocate
MayBlock
MaySuspend
MaySpawn
MayIO
MayAccessVolatile
MayMutateExternalState
MayUseNondeterministicInput
Therefore @noPanic, @noAlloc, @noBlock,
ISR restrictions and other verified contracts continue to apply.
Unsafe Does Not Suppress Panic Rules
Panic-capable code does not become compatible with a no-panic context merely because
it appears inside unsafe.
Trusted foreign no-panic claims are accepted only when the foreign contract explicitly provides that fact.
Cleanup code that must remain panic-free remains subject to its normal no-panic restrictions.
Unsafe Does Not Authorize Hidden Allocation
Raw-pointer conversion, raw-to-safe reference construction, ownership adoption and escaping lifetime relationships do not gain permission to allocate merely because the code is unsafe.
Allocation-capable unsafe operations retain their ordinary allocation effects.
@noAlloc and ISR allocation restrictions remain enforced.
Unsafe Does Not Authorize Blocking or Suspension
An unsafe foreign, assembly or platform operation that may block or suspend must publish that effect or be treated conservatively.
An ISR cannot call a blocking operation merely by wrapping the call in
unsafe.
RawPtr[T]
RawPtr[T] is Sec's unchecked typed raw-address value.
let address: RawPtr[byte]
let itemAddress: RawPtr[Item]
A raw pointer is not a safe reference.
It does not inherently carry:
- non-nullness;
- lifetime;
- ownership;
- destruction responsibility;
- bounds;
- borrow authority;
- exclusive access;
- generation validity;
- hardware privilege;
- proof that the address contains a valid
T.
RawPtr[void]
RawPtr[void] represents an untyped raw address:
let handle: RawPtr[void]
It does not directly support typed:
Read();Write(value);VolatileRead();VolatileWrite(value);Offset(elements).
A concrete compatible raw-pointer interpretation must first be established under the applicable conversion and unsafe rules.
Raw Pointers Are Ordinary Address Values
RawPtr[T] itself is copyable.
let first: RawPtr[byte] := pointer
let second := first
Copying the pointer copies the raw address value. It does not copy, retain, borrow, own or move the pointee.
Moving a raw-pointer holder moves only that raw-pointer value. It does not move the memory addressed by the pointer.
Destroying a raw-pointer value never destroys or deallocates its pointee.
Operations on Raw Pointer Values That Are Not Inherently Unsafe
Merely handling a raw address as a value does not require unsafe.
Examples include:
- storing a raw pointer;
- copying it;
- moving it;
- returning it;
- passing it through a compatible FFI boundary;
- comparing compatible raw pointers for equality;
- testing raw-pointer null state under the canonical raw/FFI rules;
- formatting or inspecting an address for diagnostics where permitted;
- representation-compatible raw-pointer conversion explicitly defined as safe.
None of these operations asserts that the pointee is valid.
Raw-Pointer Operations That Require Unsafe
Unless a specialized compiler-verified adapter says otherwise, unsafe is required for:
Read();Write(value);VolatileRead();VolatileWrite(value);- pointer arithmetic;
- integer-to-raw-pointer conversion;
- constructing
ref Tfrom raw storage; - constructing
ref mut Tfrom raw storage; - constructing a slice/view from raw pointer plus extent;
- assuming alignment;
- assuming initialized representation;
- adopting ownership from a raw address;
- constructing a callable function pointer from a raw address;
- reinterpreting unrelated pointer representations without compiler proof.
Null Is Part of the Raw-Pointer Value Domain
Every RawPtr[T] may represent a null address.
A raw pointer may become null through:
- a foreign return;
- an unsafe conversion;
- a platform operation;
- an allocator or operating-system operation;
- another raw-pointer-producing operation.
Storing, moving, returning, passing, comparing or testing a null raw pointer is permitted when the surrounding operation is otherwise valid.
Dereferencing null is invalid.
unsafe does not make a null dereference valid.
A compiler-proven null dereference is rejected.
Safe references remain non-null.
Safe APIs should normally translate nullable foreign outcomes into explicit
Option, Result or validated wrapper types.
Raw-Pointer Construction Is Explicit
A RawPtr[T] may originate from:
- a foreign function result;
- an explicit unsafe safe-reference conversion;
- an explicit unsafe integer/address conversion;
- a platform or hardware address primitive;
- an allocator/runtime primitive;
- another compatible raw-pointer conversion.
A raw pointer is never created implicitly from:
- a safe reference;
- an integer;
- an owning collection;
- a string;
- a slice;
- an unrelated pointer representation.
The exact source syntax for general RawPtr ↔ ref/ref mut conversion is
intentionally not specified here because that spelling is not yet canonically locked.
Raw-Pointer Read
Canonical typed raw read:
let value := unsafe pointer.Read()
For pointer: RawPtr[T], Read() produces a T.
Before reading, the accepted proof obligations include at least:
- the pointer is non-null;
- the address is aligned for
T; - the address is readable in the active address space;
- storage covers at least one
T; - the storage contains a valid initialized
Trepresentation; - the storage remains live for the complete read;
- ownership/aliasing rules permit the access;
- the target/platform policy permits the read.
Reading does not create ownership of the pointee merely because a T
value is produced.
RawPtr[void].Read() is invalid.
Raw-Pointer Write
Canonical typed raw write:
unsafe pointer.Write(value)
Before writing, the accepted obligations include at least:
- the pointer is non-null;
- the address is aligned for
T; - the address is writable in the active address space;
- storage covers at least one
T; - the incoming representation is valid;
- required mutable/exclusive authority exists;
- no conflicting safe reference or foreign access violates the contract;
- the write does not bypass required destruction of a live object;
- the target/platform policy permits the write.
Write() does not grant ownership of destination storage.
It also must not silently overwrite a live non-trivial object while bypassing its required lifecycle semantics.
Volatile Raw-Pointer Access
Volatile access uses separate compiler-known operations:
let value := unsafe pointer.VolatileRead()
unsafe pointer.VolatileWrite(value)
Volatile access is distinct from ordinary Read() and Write().
It does not:
- make
RawPtr[T]a volatile-qualified type; - create ownership;
- establish synchronization;
- grant hardware privilege;
- establish mapping authority;
- establish DMA ownership;
- establish cache coherence;
- provide atomicity.
Exact volatile access width and ordering are defined by the platform/volatile rules.
Raw-Pointer Arithmetic
Pointer arithmetic requires unsafe.
Sec does not use ordinary numeric pointer arithmetic:
pointer + 1 // invalid raw-pointer arithmetic
pointer - 1 // invalid raw-pointer arithmetic
pointer++ // invalid
pointer-- // invalid
Canonical operations are:
pointer.Offset(elements)
pointer.AddBytes(bytes)
pointer.Difference(other)
These operations compute raw addresses only. They do not establish that the result is dereferenceable.
Offset(elements)
Offset(elements: int) computes an address offset measured in elements of
the pointee type.
let next := unsafe pointer.Offset(1)
The byte scaling depends on the selected target layout for T.
Offset() is invalid for RawPtr[void].
The operation does not prove that the result remains within the same allocation, mapped region, hardware window or live object.
AddBytes(bytes)
Byte-oriented pointer arithmetic uses RawPtr[byte]:
let next := unsafe bytes.AddBytes(offset)
This keeps byte arithmetic explicit instead of silently reinterpreting element sizes.
AddBytes() is defined for RawPtr[byte].
Difference(other)
Two compatible raw pointers with the same element type may compute an element difference:
let distance := unsafe end.Difference(begin)
The result is an int where the operation is valid for the selected target
and address domain.
Difference does not create a safe range and does not prove common allocation origin.
Raw-Pointer Comparison
Compatible raw pointers may be compared for equality.
Equality compares raw address identity according to the selected target representation.
Equality does not prove that either pointer is:
- non-null;
- live;
- aligned;
- dereferenceable;
- owned;
- within the same allocation.
Raw-pointer equality is different from safe-reference equality, which may include live storage identity and generation semantics.
Ordering comparisons are not generally meaningful unless a target-specific API defines them.
Integer and Address Conversion
Interpreting an arbitrary integer as a raw pointer requires unsafe.
Converting a raw pointer to a target-sized unsigned integer representation requires the applicable explicit low-level conversion.
Such conversion proves no storage validity.
The compiler must respect:
- selected target pointer width;
- address-space representation;
- capability/tag requirements;
- non-flat pointer models;
- overflow and truncation rules.
The implementation must never use the compiler host's pointer width as a substitute for the selected target's pointer model.
Safe Reference to Raw Pointer
Converting ref T or ref mut T into raw-address semantics
crosses out of the safe-reference model and requires unsafe unless a specialized
compiler-verified adapter defines the operation as safe.
The resulting raw pointer does not keep the referenced value alive.
It also does not preserve safe-reference guarantees after the source reference or source storage lifetime ends.
Raw-pointer use cannot be used to evade an active borrow conflict and later recreate a conflicting safe reference.
The exact source syntax for this conversion is intentionally not specified until the canonical conversion grammar is locked.
Raw Pointer to Safe Reference
Constructing ref T from raw storage requires unsafe unless a trusted
adapter proves the complete contract.
A shared safe reference requires at least:
- non-null address;
- correct alignment;
- valid initialized
T; - readable storage;
- lifetime covering the reference;
- canonical provenance;
- no conflicting mutable access;
- foreign/hardware mutation compatible with shared semantics;
- address-space/platform compatibility.
Constructing ref mut T additionally requires writable storage and
exclusive mutable authority for the complete borrow live range.
The exact source spelling for raw-to-safe conversion is intentionally not invented by this manual until the language grammar is canonically locked.
Slice Construction from Raw Parts
Constructing a safe slice or view from a raw pointer plus extent is unsafe unless a trusted wrapper establishes the complete slice contract.
Required facts include:
- valid range;
- nonnegative and representable extent;
- no length × element-size overflow;
- correct alignment;
- initialized element representations;
- sufficient storage lifetime;
- compatible aliasing and mutability;
- one compatible storage region where required;
- valid target address space.
Constructing a slice from raw parts must not allocate merely to make the conversion work.
A zero-length slice does not justify fabricating an arbitrary valid scalar safe reference.
Unchecked Representation Construction
Constructing a typed value directly from raw bits or raw storage without ordinary constructors is unsafe unless a compiler-verified representation operation proves validity.
Unsafe does not make every bit pattern a valid Sec value.
Validity obligations may include:
- enum discriminant validity;
- union active variant;
boolvalidity;runevalidity;- named-type contracts;
- reference invariants;
- ownership state;
- alignment;
- padding rules;
- FFI layout.
A compiler-proven invalid representation is rejected even inside unsafe.
Ownership Adoption from Raw or Foreign Storage
Turning a raw/foreign resource into an owned Sec resource is unsafe unless a safe wrapper proves the complete ownership contract.
Obligations include:
- correct allocator or provider;
- matching reclamation operation;
- correct extent and alignment;
- uniqueness where exclusive ownership is required;
- initialized object state;
- destruction responsibility;
- foreign lifetime and retention rules.
Numeric address alone never proves ownership.
Double adoption of the same exclusive resource is invalid.
Unsafe does not permit double destruction or double free.
Releasing Ownership to Foreign Code
Passing a raw address to foreign code does not by itself transfer Sec ownership.
Ownership transfer across FFI requires an explicit foreign ownership contract.
After a committed consuming foreign transfer, the Sec source Place becomes unavailable under the ordinary ownership rules.
Failure and non-commit behavior must be specified by the foreign wrapper contract.
Unsafe Does Not Disable Borrow Checking
Safe references continue to follow normal borrow rules inside unsafe code.
Raw aliases may exist outside the safe-reference borrow model, but using them in a way that violates a live safe-reference guarantee is invalid.
Reconstructing a safe reference from raw storage must re-establish borrow compatibility.
Known overlap or alias conflict is rejected.
Unsafe Does Not Extend Lifetime
A raw pointer may physically outlive the storage it once addressed.
That does not make dereference after storage lifetime end valid.
Safe wrappers must not return references that depend on ended local storage.
Unsafe also does not trigger hidden heap or Arena promotion to repair an invalid lifetime.
A known use-after-domain-end is rejected even inside unsafe code.
Unsafe Does Not Disable Destruction Rules
Deterministic destruction remains active.
A raw write must not overwrite a live non-trivial object while silently bypassing required destruction unless a dedicated low-level replacement/adoption contract proves that lifecycle transition.
Unsafe operations inside defer, free or another cleanup
body remain explicitly marked where such operations are otherwise permitted.
Unsafe does not make panic-capable required cleanup legal.
Unsafe Does Not Legalize Concurrency Violations
unsafe does not:
- make a non-transferable value transferable;
- legalize a data race;
- waive thread affinity;
- waive task-migration restrictions;
- waive process-boundary rules;
- waive ISR restrictions;
- waive destruction-context restrictions.
Volatile access is not synchronization.
Foreign/raw concurrent access requires explicit synchronization and ownership contracts.
Address Spaces and Target Dependence
RawPtr[T] uses the selected target's pointer and address-space model.
The compiler must not assume:
- one flat address space;
- host pointer width;
- integer-only pointer representation;
- interchangeability of all address spaces.
A target may use tagged, capability or otherwise richer pointer representations.
A target may reject conversions between incompatible address spaces.
Hardware and Fixed Addresses
Hardware knowledge is not automatically unsafe merely because it refers to physical addresses.
Compiler/platform-verified fixed-address declarations may remain safe:
@address(Platform.GPIOA)
let mut gpio: GpioRegisters
Raw numeric address interpretation requires unsafe when the compiler cannot prove the complete storage/access contract.
Raw-pointer MMIO access remains subject to:
- exact access width;
- volatile ordering;
- mapping validity;
- device availability;
- privilege rules;
- ISR restrictions.
Hardware signal polarity remains application/driver semantics.
Runtime Mappings
Creating and destroying mapped regions follows the platform mapping/resource model.
Unsafe access through a raw pointer into a mapping still requires:
- mapping lifetime;
- valid extent;
- correct address space;
- appropriate access mode.
A raw pointer does not keep the mapping alive.
Remap or unmap may invalidate both raw and safe views.
Safe mapping wrappers may encapsulate the raw platform operations when they preserve the complete contract.
Interrupt Contexts
Unsafe code reachable from an ISR remains subject to all interrupt safety restrictions.
unsafe does not waive:
@isror@interruptSaferequirements;- no-panic rules;
- no-allocation rules;
- no-blocking rules;
- bounded-work requirements;
- synchronization requirements;
- hardware-access restrictions.
Named compiler-known interrupt vectors may be safe when the platform contract verifies them. Raw numeric vector/address interpretation may require an unsafe trust boundary.
Unsafe and the FFI Boundary
Foreign code is a trust boundary because Sec cannot verify its implementation.
A useful FFI contract may need to define:
- ABI and linkage;
- nullability;
- ownership transfer;
- borrowing and retention;
- lifetime;
- thread/callback affinity;
- allocation and deallocation;
- panic/abort/unwind behavior;
- blocking or suspension;
- I/O and external mutation;
- layout, alignment and extent.
Unknown foreign behavior is treated conservatively where proof is required.
The next chapter covers FFI declarations and contracts in detail.
unsafe extern
Unsafe foreign functions combine separate safety and ABI/linkage concepts:
unsafe extern "system" fn rawSysCall(
number: int,
argument1: uint64,
) int64
Here:
unsafedefines caller proof obligations;extern "system"defines foreign linkage/ABI selection;fndeclares the callable.
Calling an unsafe extern function requires an unsafe context:
let result := unsafe rawSysCall(number, argument)
The marker does not prove that the arguments, pointers, ownership or ABI declaration are correct.
Inline Assembly
Inline assembly is an unsafe operation and trust boundary.
Exact assembly syntax belongs to the inline-assembly rules, but the assembly contract must describe enough behavior for compiler correctness.
Relevant facts include:
- register inputs and outputs;
- clobbers;
- memory reads and writes;
- volatile behavior;
- control flow;
- stack behavior;
- possible trap or abort;
- blocking;
- I/O;
- external mutation.
Assembly effects remain active inside unsafe.
Unsafe Function Values
Unsafe status is part of callable compatibility.
An unsafe function cannot implicitly become a safe function value.
Calling through an unsafe function value requires unsafe context.
A safe wrapper may produce a safe callable only after discharging the caller obligations.
Effects and trust provenance remain attached to the callable target set.
Unsafe and Interfaces
Interface method safety contracts are preserved by implementations.
A safe interface method cannot be implemented by a callable that requires additional unsafe caller obligations.
An unsafe interface method may have an internally safe implementation while remaining unsafe to call through the interface contract.
Dynamic dispatch must not erase unsafe status.
Unsafe and Generics
Generic code preserves the unsafe requirements of operations on concrete instantiated types and callable values.
A generic constraint must not assume an operation is safe when the callable safety contract is unknown.
Specialization may prove a previously unnecessary unsafe boundary redundant only when canonical semantics permit that conclusion.
Trust provenance from generic dependencies remains available to analysis and tooling.
Closures and Raw Pointers
A closure may capture a RawPtr[T] as an ordinary copyable address value.
That does not make later pointer interpretation safe.
Dereferencing the captured raw pointer still requires explicit unsafe context and complete validity obligations at the use site.
Closure lifetime and transferability rules remain active normally.
Compile-Time Evaluation
Ordinary compile-time evaluation rejects unsafe operations.
The compiler does not normally execute operations such as:
- raw-pointer dereference;
- foreign calls;
- inline assembly;
- addressed-storage access;
- raw target-memory access;
- unsafe ownership adoption.
A compiler-known intrinsic may be compile-time evaluable only when explicitly defined as such.
Writing an unsafe context does not force or authorize compile-time execution.
Unsafe Is Not a General Backend-UB Switch
Sec should minimize backend undefined behavior even for unsafe operations.
Where practical, unsafe operations should lower to:
- defined target instructions;
- explicit traps;
- validated intrinsics;
- well-specified foreign ABI operations;
- carefully bounded backend assumptions.
Backend assumptions must never be stronger than the obligations or trusted facts accepted at the Sec/Semantic IR boundary.
When violating an unsafe obligation necessarily violates backend assumptions, tooling and documentation should describe that risk accurately.
Unsafe Requires No Mandatory Runtime
Unsafe is primarily a compile-time language and analysis feature.
It requires no mandatory:
- runtime unsafe flag;
- dynamic trust checker;
- pointer metadata;
- provenance object;
- generational reference representation;
- exception machinery;
- general Sec runtime.
A selected target profile may independently use runtime checks for other safety models.
Trust metadata may be erased from release binaries when the selected tooling or certification profile does not require it.
Diagnostics
Unsafe diagnostics should distinguish a missing unsafe context from an operation that is already known to be invalid.
Missing unsafe context
raw-pointer read requires an unsafe context
pointer.Read()
help: write `unsafe pointer.Read()` when the required safety obligations are established
Known invalid operation
this raw-pointer read is known to use a null address
`unsafe` can accept obligations that the compiler cannot prove,
but it does not make a proven null dereference valid
help: validate the raw pointer before reading it
Unsafe function body remains explicit
raw-pointer dereference still requires an explicit unsafe operation
this function is declared `unsafe fn`, but its body is not implicitly unsafe
help: mark the exact operation:
return unsafe pointer.Read()
Important diagnostic categories include:
- missing unsafe context;
- unnecessary or redundant unsafe;
- unsafe callable invoked outside context;
- interface/function-value safety mismatch;
- unknown generic callable safety;
- unknown foreign ownership or effects;
- known raw-pointer null;
- known alignment violation;
- known lifetime violation;
- known alias conflict;
- invalid raw-pointer range;
- uninitialized read;
- invalid representation;
- unverified target address;
- unsafe cleanup effect;
- compile-time-forbidden unsafe operation.
Formatter, LSP and Analysis
The formatter preserves canonical forms:
let value := unsafe pointer.Read()
unsafe pointer.Write(value)
let result := unsafe {
Prepare(pointer)
pointer.Read()
}
unsafe fn FromRawParts(...) Buffer {
}
unsafe extern "system" fn rawSysCall(number: int) int64
The formatter must not invent:
- C-style pointer syntax;
unsafe struct;unsafe type;- unlocked raw/reference conversion syntax.
LSP and analysis tooling may expose:
- unsafe operation identity;
- caller obligations;
- unsafe callable status;
- foreign trust facts;
- effects;
- trust provenance;
- nearest unsafe context;
- safe-wrapper boundary;
- raw address-space/null/alignment/range facts;
- safe alternatives where available.
Tooling must never imply that wrapping a compiler-proven-invalid operation in
unsafe will fix the program.
Best Practices
- Keep unsafe regions as narrow as practical.
- Prefer single-operation
unsafewhen one operation carries the trust boundary. - Document the exact caller obligations of public
unsafe fnAPIs. - Build small safe wrappers that validate all obligations before entering unsafe code.
- Do not use
unsafeas a way to bypass ownership, borrowing or lifetime errors. - Use
RawPtr[T]only when safe references or platform abstractions cannot express the required operation. - Remember that a raw pointer does not keep storage alive.
- Remember that copying a raw pointer does not copy or own the pointee.
- Use
Read/Writeand volatile operations intentionally; they have different semantics. - Use
Offset,AddBytesandDifferencerather than numeric pointer operators. - Do not create safe references from raw storage until every reference guarantee is re-established.
- Do not infer ownership from an address.
- Prefer compiler/platform-known hardware bindings such as
@addressover unverified numeric addresses. - Treat foreign code, inline assembly and unverified target facts as explicit trust boundaries.
- Keep effects such as panic, allocation, blocking and volatile I/O visible even inside unsafe code.
Summary
unsafepermits operations whose complete safety obligations are not automatically proven.unsafedisables no compiler analysis.- A compiler-proven false safety obligation is rejected even inside unsafe.
- Sec supports single-operation
unsafe operationand block-formunsafe { ... }. - Unsafe blocks follow normal block-expression result semantics.
- Unsafe scope is lexical and does not propagate to parent or sibling scopes.
- Empty or redundant unsafe contexts are valid and normally diagnosed informationally.
- Sec supports
unsafe fnand unsafe methods. - Calling an unsafe callable requires an unsafe context.
- The body of
unsafe fnis not implicitly unsafe. - Every actual unsafe operation inside an unsafe function remains explicitly marked.
- Unsafe callable contracts define caller proof obligations.
- Caller obligations may include non-nullness, alignment, lifetime, aliasing, ownership, address-space, ABI and concurrency facts.
- The compiler may prove some obligations and reject obligations known to be false.
- Public unsafe APIs should document their caller obligations.
- Compiler-known unsafe operations carry machine-readable obligation metadata.
- A safe wrapper may encapsulate unsafe code only after establishing every caller obligation.
- Unsafe does not transitively mark callers through a correct safe wrapper.
- Trust provenance may remain available internally even when the wrapper is caller-safe.
- Unsafe status and effects are separate semantic dimensions.
- Unsafe does not waive panic, allocation, blocking, suspension, volatile, I/O or external-mutation effects.
@noPanic,@noAlloc,@noBlockand ISR restrictions remain active.- Unsafe does not authorize hidden allocation.
- Unsafe does not authorize blocking or suspension in contexts that forbid them.
RawPtr[T]is an unchecked typed address value.RawPtr[T]is not a safe reference.RawPtr[T]is copyable and non-owning.- Copying or moving a raw-pointer value does not copy, move, retain or own its pointee.
- Destroying a raw pointer does not destroy or deallocate the pointee.
RawPtr[void]represents an untyped raw address.RawPtr[void]cannot directly perform typed reads, writes, volatile access or element offsets.- Storing, copying, moving, returning, passing, equality-testing and null-testing raw pointers are not inherently unsafe.
- Raw reads, writes, volatile access, pointer arithmetic, integer-address construction, raw-to-safe conversion, raw slice construction and ownership adoption require unsafe unless a specialized verified operation says otherwise.
- Null is part of the value domain of every
RawPtr[T]. - Dereferencing a null raw pointer is invalid.
- Unsafe does not make a known null dereference valid.
- Safe references remain non-null.
- Raw-pointer construction from safe references, integers and unrelated pointer representations is never implicit.
- The exact source spelling for general
RawPtr ↔ ref/ref mutconversion is intentionally not specified until canonically locked. pointer.Read()performs a typed raw read under explicit unsafe obligations.pointer.Write(value)performs a typed raw write under explicit unsafe obligations.- Raw writes must not bypass required destruction of a live non-trivial object.
VolatileRead()andVolatileWrite()are distinct from ordinary raw access.- Volatile access does not imply synchronization, atomicity, ownership or hardware privilege.
- Raw-pointer arithmetic uses
Offset,AddBytesandDifference. - Ordinary numeric operators are not raw-pointer arithmetic.
Offsetscales by targetsizeof(T)and is invalid forRawPtr[void].AddBytesis byte-oriented and defined forRawPtr[byte].Differencerequires compatible same-element-type raw pointers.- Pointer arithmetic does not prove bounds or common allocation identity.
- Raw-pointer equality compares raw address identity and does not imply dereferenceability.
- Raw-pointer equality is distinct from safe-reference identity.
- Integer/address conversion must use the selected target pointer representation, never compiler-host assumptions.
- A raw pointer derived from a safe reference does not keep the referent alive.
- Raw-to-safe reference construction must re-establish non-nullness, alignment, representation, lifetime, provenance and borrow authority.
- Constructing
ref mut Tfrom raw storage additionally requires writable storage and exclusive mutable authority. - Constructing a safe slice from raw parts requires valid extent, alignment, initialization, lifetime, aliasing and address-space proof.
- Unsafe does not make every raw bit pattern a valid typed value.
- Unchecked enum/union/representation construction remains subject to type validity and lifecycle rules.
- Adopting ownership from raw or foreign storage requires the correct provider, reclamation, uniqueness, initialization and lifetime contract.
- Numeric address alone never proves ownership.
- Unsafe does not permit double adoption, double destruction or double free.
- Passing a raw pointer to foreign code does not by itself transfer ownership.
- Committed foreign ownership transfer makes the Sec source unavailable according to ordinary ownership rules.
- Unsafe does not disable safe-reference borrow checking.
- Raw aliases cannot be used to manufacture conflicting safe borrows.
- Unsafe does not extend storage, object or reference lifetime.
- Known use-after-lifetime-end remains invalid inside unsafe.
- Unsafe does not disable deterministic destruction.
- Unsafe does not legalize data races or waive thread/task/process/ISR restrictions.
- Raw-pointer address spaces and representation are selected-target facts.
- The compiler must not assume one flat address space or compiler-host pointer width.
- Compiler/platform-verified hardware addresses may be safe without raw numeric address interpretation.
@addressis not automatically an unsafe operation.- Raw-pointer MMIO access remains subject to volatile, mapping, privilege and ISR rules.
- Raw pointers do not keep runtime mappings alive.
- Unsafe foreign functions use separate
unsafe,extern "<abi>"andfnsemantics. - Foreign undefined behavior is not repaired by Sec's unsafe marker.
- Inline assembly is an unsafe trust boundary and must publish enough behavior for compiler correctness.
- Unsafe status is part of function-value and interface callable compatibility.
- Generic specialization preserves unsafe requirements.
- A closure may safely store a raw-pointer value while later dereference remains unsafe.
- Ordinary compile-time evaluation rejects unsafe operations unless a specific compiler-known intrinsic says otherwise.
- Unsafe is not permission for unrestricted backend undefined behavior.
- Unsafe requires no mandatory Sec runtime or dynamic borrow/trust machinery.