OverflowBehaviorTypes¶
Introduction¶
Clang provides overflow behavior types that allow developers to have fine-grained control over the overflow behavior of integer types. Overflow behavior can be specified using either attribute syntax or keyword syntax to control how arithmetic operations on a given integer type should behave upon overflow. This is particularly useful for projects that need to balance performance and safety, allowing developers to enable or disable overflow checks for specific types.
Overflow behavior types can be enabled using the -cc1 compiler option
-fexperimental-overflow-behavior-types.
Note
This feature is experimental. The flag spelling may change in future releases as the feature matures.
There are two syntax options for specifying overflow behavior:
Attribute syntax:
__attribute__((overflow_behavior(behavior)))
Where behavior can be one of the following:
wrap: Specifies that arithmetic operations on the integer type should wrap on overflow. This is equivalent to the behavior of-fwrapv, but it applies only to the attributed type and may be used with both signed and unsigned types. When this is enabled, UBSan’s integer overflow and integer truncation checks (signed-integer-overflow,unsigned-integer-overflow,implicit-signed-integer-truncation, andimplicit-unsigned-integer-truncation) are suppressed for the attributed type. Similar to-fwrapv, this behavior defines wrapping behavior which also disables eager compiler optimizations concerning undefined behaviors.trap: Specifies that arithmetic operations on the integer type should be checked for overflow. When using thesigned-integer-overflowsanitizer or when using-ftrapvalongside a signed type, this is the default behavior. Using this, one may enforce overflow checks for a type even when-fwrapvis enabled globally.
Keyword syntax:
__ob_wrap // equivalent to __attribute__((overflow_behavior(wrap)))
__ob_trap // equivalent to __attribute__((overflow_behavior(trap)))
Either of these spellings can be applied to typedef declarations and to
integer types directly.
Arithmetic operations containing one or more overflow behavior types follow standard C integer promotion and conversion rules while preserving overflow behavior information.
Examples¶
Here are examples using both syntax options:
Using attribute syntax with a typedef:
typedef unsigned int __attribute__((overflow_behavior(trap))) non_wrapping_uint;
non_wrapping_uint add_one(non_wrapping_uint a) {
return a + 1; // Overflow is checked for this operation.
}
Using keyword syntax with a typedef:
typedef unsigned int __ob_trap non_wrapping_uint;
non_wrapping_uint add_one(non_wrapping_uint a) {
return a + 1; // Overflow is checked for this operation.
}
Using attribute syntax with a type directly:
int mul_alot(int n) {
int __attribute__((overflow_behavior(wrap))) a = n;
return a * 1337; // Potential overflow is not checked and is well-defined
}
Using keyword syntax with a type directly:
int mul_alot(int n) {
int __ob_wrap a = n;
return a * 1337; // Potential overflow is not checked and is well-defined
}
“Well-defined” overflow is consistent with two’s complement wrap-around semantics and won’t be removed via eager compiler optimizations (like some undefined behavior might).
Promotion Rules¶
Overflow behavior types (OBTs) follow the traditional C integer promotion and conversion rules while propagating overflow behavior qualifiers through implicit casts. This ensures compatibility with existing C semantics while maintaining overflow behavior information throughout arithmetic expressions.
The resulting type characteristics for overflow behavior types (OBTs) across a variety of scenarios is detailed below.
OBT and Standard Integer Type: The result follows standard C conversion rules, with the OBT qualifier applied to the standard result type.
typedef char __ob_trap trap_char; trap_char c; unsigned long ul; auto result = c + ul; // result is __ob_trap unsigned long
Two OBTs with Same Behavior: When both operands have the same overflow behavior, the result follows standard C arithmetic conversions with that behavior applied.
typedef unsigned char __ob_wrap u8_wrap; typedef unsigned short __ob_wrap u16_wrap; u8_wrap a; u16_wrap b; auto result = a + b; // result is __ob_wrap int (C promotes both to int)
Two OBTs with Different Behaviors:
trapbehavior dominateswrapbehavior. The result follows standard C arithmetic conversions withtrapbehavior applied.typedef unsigned short __ob_wrap u16_wrap; typedef int __ob_trap int_trap; u16_wrap a; int_trap b; auto result = a + b; // result is __ob_trap int (C promotes u16->int, dominance gives trap)
Operation Type |
Result Type |
|---|---|
OBT + Standard Integer |
Standard C conversion result with OBT qualifier applied |
Same Kind OBTs (both |
Standard C conversion result with common overflow behavior |
Different Kind OBTs ( |
Standard C conversion result with |
Overflow Behavior Dominance Rules:
If either operand has
trapbehavior → result hastrapbehaviorIf either operand has
wrapbehavior (and neither hastrap`) → result has ``wrapbehaviorOtherwise → no overflow behavior annotation
This model preserves traditional C semantics while ensuring overflow behavior information is correctly propagated through arithmetic expressions.
Pointer Semantics¶
Overflow behavior types can be used with pointers, where the overflow behavior annotation applies to the pointee type. Pointers to overflow behavior types have specific compatibility and conversion rules.
Pointer Type Compatibility:
Pointers to overflow behavior types are treated as distinct types from their underlying types and from each other when they have different overflow behaviors. This affects assignment, parameter passing, and other pointer operations.
Discarding Overflow Behavior:
When assigning from a overflow behavior annotated type to a regular integer
type, the compiler issues a warning about discarding overflow behavior. This is
controlled by the -Wincompatible-pointer-types-discards-overflow-behavior
diagnostic.
unsigned long *px;
unsigned long __ob_trap *py;
unsigned long __ob_wrap *pz;
// Discarding overflow behavior - warns but allowed
px = py; // warning: assigning to 'unsigned long *' from
// '__ob_trap unsigned long *' discards overflow behavior
py = px; // warning: assigning to '__ob_trap unsigned long *' from
// 'unsigned long *' discards overflow behavior
Conversion Semantics¶
Overflow behavior types are implicitly convertible to and from built-in integral types with specific semantics for warnings and constant evaluation.
Incompatible Overflow Behaviors:
Attempting to assign or convert between types with incompatible overflow
behaviors (trap vs wrap) results in a compilation error, as these
represent fundamentally different behavioral contracts.
int __ob_trap a;
int __ob_wrap b;
a = b; // error: assigning to '__ob_trap int' from '__ob_wrap int' with
// incompatible overflow behavior types ('__ob_trap' and '__ob_wrap')
The Diagnostics section further below details the exact diagnostics Clang provides for overflow behavior types.
Truncation Semantics¶
Truncation and overflow are related and are both often desirable in some contexts and undesirable in others. To provide control over these behaviors, overflow behavior types may also be used to control truncation instrumentation at the type level.
Note that implicit integer truncation is not an undefined behavior in C. Due to this, overflow behavior types make no special guarantees about whether implicit integer truncation is defined or not – the behavior is simply always defined. Use of overflow behavior types in these contexts only control instrumentation related to truncation.
When an overflow behavior type is involved as the source or destination type of truncation, instrumentation checks behave as follows:
One or more types is ‘trap’: Truncation checks are inserted and may issue either a trap or sanitizer warning based on compiler settings.
One or more types is ‘wrap’: No truncation checks are added regardless of compiler flags because truncation with wrapping behavior well-defined. If any of the types is
trapthen the truncation rules match the behavior mentioned above.Both types are standard integer types: Behaviors surrounding standard integer types is unchanged.
void foo(char a, int __ob_trap b) {
a = b; // truncation checks are inserted, may trap
}
void bar(char a, int __ob_wrap b) {
a = b; // sanitizer truncation checks disallowed
}
Note that truncation itself is a form of overflow behavior - when a value is
too large to fit in the destination type, the high-order bits are discarded,
which is a wrapping behavior that wrap types are designed to handle
predictably.
Constant Conversion Semantics¶
When converting constant values to overflow behavior types, the behavior depends on the overflow behavior annotation and whether the conversion would change the value:
With ‘wrap’ types:
Constant conversions that would overflow are accepted without warning when
assigning to or explicitly casting to wrap types, as wrapping is the
intended behavior:
short x1 = (int __ob_wrap)100000; // OK: explicit wrap cast
short __ob_wrap x2 = (int)100000; // OK: wrap destination
unsigned short __ob_wrap ux = 100000; // OK: wrapping expected
With ‘trap’ types:
Constant conversions that would change the value generate a warning when the
destination is a trap type. This matches the behavior of regular non-OBT
constant conversions.
Implicit Conversions Due to Assignment¶
Like with the basic integral types in C and C++, types on the right-hand side of an assignment may be implicitly converted to match the left-hand side.
All built-in integral types can be implicitly converted to an overflow behavior version.
char x = 1;
int __ob_wrap a = x; // x converted to __ob_wrap int
Assigning one overflow behavior type to another is legal only when the two
types have matching overflow behavior kinds (i.e., wrap and wrap or
trap and trap). Assigning overflow behavior types with differing
behavior kinds will result in an error.
int __ob_trap trap_var;
int __ob_wrap wrap_var;
trap_var = wrap_var; // error: assigning to '__ob_trap int' from
// '__ob_wrap int' with incompatible overflow
// behavior types ('__ob_trap' and '__ob_wrap')
However, conversions between compatible overflow behavior types (same kind, different underlying widths or signedness) are allowed:
long __ob_wrap x = __LONG_MAX__;
int __ob_wrap a = x; // OK: both have 'wrap' behavior
C++ Overload Resolution¶
For the purposes of C++ overload set formation, promotions or conversions to and from overflow behavior types are of the same rank as normal integer promotions and conversions. These rules have implications during overload candidate selection which may lead to unexpected but correct ambiguity errors.
The example below shows potential ambiguity as matching just the underlying type of an OBT parameter is not enough to precisely pick an overload candidate.
void foo(int __ob_trap a);
void foo(short a);
void bar(int a) {
foo(a); // call to 'foo' is ambiguous
}
Most integral types can be implicitly converted to match OBT parameter types and this can be done unambiguously in certain cases. Especially, when all other candidates are not implicitly convertible.
void foo(int __ob_trap a);
void foo(char *a);
void bar(int a) {
foo(a); // picks foo(__ob_trap int)
}
Overflow behavior types with differing kinds may also create ambiguity in certain contexts.
void foo(int __ob_trap a);
void foo(int __ob_wrap a);
void bar(int a) {
foo(a); // call to 'foo' is ambiguous
}
Overflow behavior types may also be used as template parameters and used within
C _Generic expressions.
C _Generic Expressions¶
Overflow behavior types may be used within C _Generic expressions.
Overflow behavior types do not match against their underlying types within C
_Generic expressions. This means that an OBT will not be considered
equivalent to its base type for generic selection purposes. OBTs will match
against exact types considering bitwidth, signedness and overflow
behavior kind.
int foo(int __ob_wrap x) {
return _Generic(x, int: 1, char: 2, default: 3); // returns 3
}
int bar(int __ob_wrap x) {
return _Generic(x, int __ob_wrap: 1, int: 2, default: 3); // returns 1
}
C++ Template Specializations¶
Like with _Generic, each OBT is treated as a distinct type for template
specialization purposes, enabling precise type-based template selection.
template<typename T>
struct TypeProcessor {
static constexpr int value = 0; // default case
};
template<>
struct TypeProcessor<int> {
static constexpr int value = 1; // int specialization
};
template<>
struct TypeProcessor<int __ob_wrap> {
static constexpr int value = 2; // __ob_wrap int specialization
};
template<>
struct TypeProcessor<int __ob_trap> {
static constexpr int value = 3; // __ob_trap int specialization
};
When no exact template specialization exists for an OBT, it falls back to the default template rather than matching the underlying type specialization, maintaining type safety and avoiding unexpected behavior.
Interaction with Command-Line Flags and Sanitizer Special Case Lists¶
The overflow_behavior attribute interacts with sanitizers, -ftrapv,
-fwrapv, and Sanitizer Special Case Lists (SSCL) by wholly overriding these
global flags. The following table summarizes the interactions:
Behavior |
Default(No Flags) |
-ftrapv |
-fwrapv |
Sanitizers |
SSCL |
|---|---|---|---|---|---|
|
Wraps |
Wraps |
Wraps |
No report, Wraps |
Overrides SSCL |
|
Traps |
Traps |
Traps |
Reports |
Overrides SSCL |
It is important to note the distinction between signed and unsigned types. For
unsigned integers, which wrap on overflow by default, overflow_behavior(trap)
is particularly useful for enabling overflow checks. For signed integers, whose
overflow behavior is undefined by default, overflow_behavior(wrap) provides
a guaranteed wrapping behavior.
The overflow_behavior attribute can be used to override the behavior of
entries from a Sanitizer special case list. This is useful for allowlisting
specific types into overflow or truncation instrumentation.
Diagnostics¶
Clang provides diagnostics to help developers manage overflow behavior types.
Specification Errors and Warnings¶
When using overflow behavior types, several diagnostics help catch mistakes in how the types are specified.
Conflicting Specifications:
An error is issued when both the keyword syntax and attribute syntax are used with incompatible overflow behaviors on the same type:
int __ob_wrap __attribute__((overflow_behavior(trap))) x;
// error: conflicting overflow behavior specification; specifier
// specifies 'wrap' but attribute specifies 'trap'
Redundant Specifications:
A warning is issued when both syntax forms specify the same behavior, which is redundant but allowed:
int __ob_wrap __attribute__((overflow_behavior(wrap))) x;
// warning: redundant overflow behavior specification; both specifier
// and attribute specify 'wrap'
Feature Not Enabled:
When overflow behavior types are used without enabling the feature via
-fexperimental-overflow-behavior-types, a warning is issued and the attribute is ignored:
// Without -fexperimental-overflow-behavior-types
int __attribute__((overflow_behavior(wrap))) x;
// warning: 'overflow_behavior' attribute is ignored because it is
// not enabled; pass -fexperimental-overflow-behavior-types
-Woverflow-behavior-conversion¶
This warning group is issued when an overflow behavior type is implicitly converted to a standard integer type, which may lead to the loss of the specified overflow behavior.
typedef int __ob_wrap wrapping_int;
void some_function(int);
void another_function(wrapping_int w) {
some_function(w); // warning: implicit conversion from 'wrapping_int' to
// 'int' discards overflow behavior
}
To fix this, you can explicitly cast the overflow behavior type to a standard integer type.
typedef int __ob_wrap wrapping_int;
void some_function(int);
void another_function(wrapping_int w) {
some_function(static_cast<int>(w)); // OK
}
This warning group includes
-Wimplicit-overflow-behavior-conversion, which includes
-Wimplicit-overflow-behavior-conversion-assignment,
-Wimplicit-overflow-behavior-conversion-function-boundary,
-Wimplicit-overflow-behavior-conversion-function-boundary-pedantic, and
-Wimplicit-overflow-behavior-conversion-pedantic.
Note
-Woverflow-behavior-conversion is implied by -Wconversion.
-Wincompatible-pointer-types-discards-overflow-behavior¶
This warning (enabled by default) is issued when converting between pointer types where overflow behavior information is discarded. This occurs when assigning between pointers that differ only in their overflow behavior annotation.
void example() {
unsigned long __ob_trap *trap_ptr;
unsigned long *regular_ptr;
regular_ptr = trap_ptr; // warning: assigning to 'unsigned long *'
// from '__ob_trap unsigned long *' discards
// overflow behavior
trap_ptr = regular_ptr; // warning: assigning to '__ob_trap unsigned long *'
// from 'unsigned long *' discards overflow behavior
}
This warning is part of the -Wincompatible-pointer-types group and helps
catch potential bugs where overflow behavior contracts may be inadvertently
lost through pointer conversions.
-Wimplicit-overflow-behavior-conversion¶
This warning is issued when an overflow behavior type is implicitly converted
to a standard integer type as part of most conversions, which may lead to the
loss of the specified overflow behavior. This is the main warning in the
-Woverflow-behavior-conversion group.
typedef int __ob_wrap wrapping_int;
void some_function() {
wrapping_int w = 1;
int i = w; // warning: implicit conversion from 'wrapping_int' to 'int'
// during assignment discards overflow behavior
// [-Wimplicit-overflow-behavior-conversion]
}
Here’s another example showing function parameter conversion with a trap type:
typedef int __ob_trap safe_int;
void bar(int x); // Function expects standard int
void foo() {
safe_int s = 42;
bar(s); // warning: implicit conversion from 'safe_int' to 'int'
// discards overflow behavior
// [-Wimplicit-overflow-behavior-conversion]
}
To fix this, you can explicitly cast the overflow behavior type to a standard integer type.
typedef int __ob_wrap wrapping_int;
typedef int __ob_trap safe_int;
void some_function() {
wrapping_int w = 1;
int i = static_cast<int>(w); // OK
int j = (int)w; // C-style OK
}
void bar(int x);
void foo() {
safe_int s = 42;
bar(static_cast<int>(s)); // OK
}
-Wimplicit-overflow-behavior-conversion-assignment¶
This warning is issued specifically when an overflow behavior type is
implicitly converted to a standard integer type during assignment operations or
variable initialization. This is a subset of the more general
-Wimplicit-overflow-behavior-conversion warning, allowing developers to
control assignment-specific warnings separately. This warning is disabled by
default.
typedef int __ob_wrap wrapping_int;
void some_function() {
wrapping_int w = 1;
int i = w; // warning: implicit conversion from 'wrapping_int' to 'int'
// during assignment discards overflow behavior
// [-Wimplicit-overflow-behavior-conversion-assignment]
}
This also applies to variable initialization:
void another_example() {
int __ob_trap safe = 42;
int regular = safe; // warning: implicit conversion from '__ob_trap int'
// to 'int' during assignment discards overflow behavior
// [-Wimplicit-overflow-behavior-conversion-assignment]
}
This diagnostic can be controlled independently, allowing projects to suppress assignment-related warnings while still receiving warnings for other types of implicit conversions (such as function parameter passing).
Note that when assigning from a non-OBT type to an OBT type, no warning is issued as overflow behavior is being added, not discarded:
void adding_obt_is_ok() {
int plain = 42;
int __ob_wrap wrapped = plain; // OK - adding overflow behavior, no warning
}
-Wimplicit-overflow-behavior-conversion-assignment-pedantic¶
A less severe version of
-Wimplicit-overflow-behavior-conversion-assignment which is issued only
when an unsigned wrap type is implicitly converted to a standard unsigned
integer type during assignment. This is considered less problematic than other
conversions because unsigned integers already have well-defined wrapping
behavior by the C standard.
void example() {
unsigned int __ob_wrap wrapped_uint = 42;
unsigned int regular = wrapped_uint; // warning: implicit conversion from
// '__ob_wrap unsigned int' to
// 'unsigned int' during assignment
// discards overflow behavior
// [-Wimplicit-overflow-behavior-conversion-assignment-pedantic]
}
This warning is useful for projects that want to track all overflow behavior
annotations but consider unsigned wrapping conversions to be lower priority
than signed conversions or trap conversions.
-Wimplicit-overflow-behavior-conversion-function-boundary¶
This warning (disabled by default) is issued when an overflow behavior type is passed as an argument to a function parameter that does not have the same overflow behavior annotation. This helps identify situations where overflow behavior contracts may be lost at function boundaries.
void process_value(int x); // Function expects standard int
void caller() {
int __ob_trap safe_value = 42;
process_value(safe_value); // warning: passing argument of type
// '__ob_trap int' to parameter of type 'int'
// discards overflow behavior at function boundary
// [-Wimplicit-overflow-behavior-conversion-function-boundary]
}
This warning can be particularly useful for catching cases where overflow
checking behavior is lost when crossing API boundaries. Both the general
version
(-Wimplicit-overflow-behavior-conversion-function-boundary) and a pedantic
version (-Wimplicit-overflow-behavior-conversion-function-boundary-pedantic)
for unsigned wrapping types are disabled by default.
To fix this warning, you can either:
Update the function parameter to accept the overflow behavior type:
void process_value(int __ob_trap x); // Accept trap-annotated int
void caller() {
int __ob_trap safe_value = 42;
process_value(safe_value); // OK
}
Explicitly cast at the call site to acknowledge the loss of overflow behavior:
void process_value(int x);
void caller() {
int __ob_trap safe_value = 42;
process_value((int)safe_value); // OK - explicit cast
}
-Wimplicit-overflow-behavior-conversion-function-boundary-pedantic¶
A less severe version of the warning above which is issued only in the case of
unsigned wrap types being passed to a function expecting a standard
unsigned integer type. This is a less problematic issue since the standard and
well-defined overflow procedure for unsigned integer types is essentially
wrap.
Format String Functions¶
When overflow behavior types are used with format string functions (printf-family
functions like printf, fprintf, sprintf, etc., and scanf-family
functions like scanf, fscanf, sscanf, etc.), they are treated based
on their underlying integer types for format specifier compatibility checking.
More generally, overflow behavior types are ABI-compatible with their underlying
types when passed to any varargs function.
#include <cstdio>
typedef int __ob_wrap wrap_int;
typedef unsigned int __ob_trap nowrap_uint;
void example() {
wrap_int wi = 42;
nowrap_uint su = 100;
scanf("%d\n", &wi); // OK: &wi treated as int* for %d
printf("%d\n", wi); // OK: wi treated as int for %d
printf("%u\n", su); // OK: su treated as unsigned int for %u
printf("%s\n", wi); // Error: int incompatible with %s (same as regular int)
}
This behavior ensures that overflow behavior types work seamlessly with existing format string functions without requiring special format specifiers, while still maintaining their overflow behavior semantics in arithmetic operations.
The format string checker uses the underlying type to determine compatibility,
so int __ob_wrap is fully compatible with %d, %i, %x, etc.,
just like a regular int would be.
Incompatibility With Non-Integer Types¶
An error is issued when attempting to create an overflow behavior type from a non-integer type.
typedef float __attribute__((overflow_behavior(wrap))) wrapping_float;
// error: 'overflow_behavior' attribute cannot be applied to non-integer type 'float'
typedef struct S { int i; } __attribute__((overflow_behavior(wrap))) S_t;
// error: 'overflow_behavior' attribute cannot be applied to non-integer type 'struct S'