Chapter 15: Program Failure
Level 2 — Cognition
Undefined behavior, error codes, signals -- how things go wrong in C and how Auto prevents them.
Programs fail. The question is not whether failure will occur, but how the language helps you prevent, detect, and recover from it. C's approach to failure is minimal: the language assumes the programmer is always right and provides few safety nets. Auto takes the opposite approach -- the language helps the programmer avoid failure through compile-time checks and runtime safety.
15.1 Wrongdoings
C classifies program misbehavior into several categories. The most dangerous is undefined behavior (UB):
Undefined behavior: behavior for which the standard imposes no requirements.
UB means the compiler is allowed to do literally anything. Common examples:
// C Deep Dive: undefined behavior
int arr[5];
arr[10] = 42; // UB: out-of-bounds access
int *p = NULL;
*p = 42; // UB: null pointer dereference
int x;
printf("%d\n", x); // UB: reading uninitialized variable
int a = INT_MAX;
int b = a + 1; // UB: signed integer overflow
free(p);
*p = 42; // UB: use after freeWhat actually happens with UB in practice:
| Scenario | Possible consequence |
|---|---|
| Buffer overflow | Silent data corruption |
| Null dereference | Segmentation fault (crash) |
| Use after free | Arbitrary code execution |
| Integer overflow | Incorrect results |
| Type confusion | Data corruption, security hole |
Compilers exploit UB for optimization. If the standard says the behavior is undefined, the compiler may assume it never happens:
// C Deep Dive: compiler exploits UB
int *check(int *p) {
int x = *p; // dereference p
if (p == NULL) { // compiler removes this: if p were NULL,
return NULL; // the dereference above would be UB,
} // and UB "doesn't happen"
return p;
}How Auto prevents UB. Auto eliminates entire categories of UB through:
- Bounds checking: array accesses are validated at compile time where possible, runtime where necessary.
- Null safety: there are no null pointers in Auto. Optional values use explicit
Optiontypes. - Initialization enforcement: every variable must be initialized.
- No pointer arithmetic: array indexing replaces all pointer arithmetic.
- Overflow detection: integer overflow is caught at runtime.
// Auto: UB prevention
fn safe_divide(a int, b int) int {
if b == 0 {
print("Error: division by zero")
return 0
}
a / b
}
fn safe_access(arr [5]int, index int) int {
if index < 0 || index >= 5 {
print("Error: index out of bounds")
return 0
}
arr[index]
}