_Optional: a type qualifier to indicate pointer nullability

Christopher Bazley
2 min readFeb 1, 2023

--

(two minute version)

tl;dr

Pointer-to-const may have undefined behaviour on write access;
pointer-to-_Optional may have undefined behaviour on read or write access.

Abstract

WG14 paper N3089 proposes a new type qualifier for the purpose of adding pointer nullability information to C programs.

Its goal is to provide value not only for static analysis and documentation, but also for compilers which report errors based only on existing type-compatibility rules.

The syntax and semantics are designed to be as familiar (to C programmers) and ergonomic as possible. In contrast, existing solutions are incompatible, confusing, error-prone, and intrusive.

Proposed language extension

  • A new type qualifier, _Optional, indicates that a pointer to a so-qualified type may be null. This does not preclude any other pointer type from being null.
  • Types other than those of a pointed-to object or pointed-to incomplete type shall not be _Optional-qualified in a declaration.
  • The semantics of the unary & operator are modified so that if its operand has type “type” then its result has type “pointer to type”, with the omission of any _Optional qualifier of the pointed-to type.
  • If an operand is a pointer to an _Optional-qualified type and its value cannot be statically proven never to be null, then implementations may generate a warning of any undefined behaviour that would occur if the value were null.
  • A specification of a function type that includes type qualifiers no longer has undefined behaviour. Qualifiers that are not applicable are ignored (as in C++).

The _Optional qualifier is treated like existing qualifiers when determining compatibility between types, and when determining whether a pointer may be implicitly converted to a pointer to a differently qualified type.

Example usage

void foo(int *);

void bar(_Optional int *i)
{
*i = 10; // path-sensitive warning of unguarded dereference

if (i) {
*i = 5; // okay
}

int *j = i; // warning: initializing discard qualifiers
j = i; // warning: assignment discards qualifiers
foo(i); // warning: passing parameter discards qualifiers

foo(&*i); // path-sensitive warning of unguarded dereference
foo(&i[10]); // path-sensitive warning of unguarded dereference

if (i) {
foo(&*i); // okay
foo(&i[10]); // okay
}
}

Further reading

--

--

Christopher Bazley

Married father of two. I’ve been programming for fun and profit since the 1990s, mostly writing C. Staff software engineer at Arm. My opinions are my own.