[Lldb-commits] [clang] [lldb] [Clang] Introduce OverflowBehaviorType for fine-grained overflow control (PR #148914)
Justin Stitt via lldb-commits
lldb-commits at lists.llvm.org
Tue Dec 2 15:20:26 PST 2025
https://github.com/JustinStitt updated https://github.com/llvm/llvm-project/pull/148914
>From 31330361fc7d4c00faf1bfb97d0d1c90d8cd2e70 Mon Sep 17 00:00:00 2001
From: Justin Stitt <justinstitt at google.com>
Date: Mon, 14 Jul 2025 17:30:14 -0700
Subject: [PATCH 1/4] [Clang] implement OverflowBehaviorTypes
Signed-off-by: Justin Stitt <justinstitt at google.com>
---
clang/docs/OverflowBehaviorTypes.rst | 827 ++++++++++++++++++
clang/docs/ReleaseNotes.rst | 6 +
clang/docs/SanitizerSpecialCaseList.rst | 34 +
clang/docs/UndefinedBehaviorSanitizer.rst | 36 +
clang/docs/index.rst | 1 +
clang/include/clang/AST/ASTContext.h | 36 +
clang/include/clang/AST/ASTNodeTraverser.h | 3 +
clang/include/clang/AST/PropertiesBase.td | 2 +
clang/include/clang/AST/RecursiveASTVisitor.h | 6 +
clang/include/clang/AST/TypeBase.h | 58 +-
clang/include/clang/AST/TypeLoc.h | 28 +
clang/include/clang/AST/TypeProperties.td | 13 +
clang/include/clang/Basic/Attr.td | 7 +
clang/include/clang/Basic/DiagnosticGroups.td | 64 +-
.../clang/Basic/DiagnosticSemaKinds.td | 66 ++
clang/include/clang/Basic/LangOptions.def | 2 +
clang/include/clang/Basic/LangOptions.h | 19 +
clang/include/clang/Basic/TokenKinds.def | 4 +
clang/include/clang/Basic/TypeNodes.td | 1 +
clang/include/clang/Options/Options.td | 6 +
clang/include/clang/Sema/DeclSpec.h | 45 +-
clang/include/clang/Sema/Sema.h | 33 +
.../clang/Serialization/TypeBitCodes.def | 1 +
clang/lib/AST/ASTContext.cpp | 227 ++++-
clang/lib/AST/ASTImporter.cpp | 12 +
clang/lib/AST/ASTStructuralEquivalence.cpp | 7 +
clang/lib/AST/ExprConstant.cpp | 6 +-
clang/lib/AST/FormatString.cpp | 3 +
clang/lib/AST/ItaniumMangle.cpp | 12 +
clang/lib/AST/MicrosoftMangle.cpp | 18 +
clang/lib/AST/Type.cpp | 77 +-
clang/lib/AST/TypeLoc.cpp | 8 +
clang/lib/AST/TypePrinter.cpp | 20 +
clang/lib/CodeGen/CGDebugInfo.cpp | 7 +
clang/lib/CodeGen/CGDebugInfo.h | 1 +
clang/lib/CodeGen/CGExprScalar.cpp | 299 ++++---
clang/lib/CodeGen/CodeGenFunction.cpp | 2 +
clang/lib/CodeGen/CodeGenTypes.cpp | 4 +
clang/lib/CodeGen/ItaniumCXXABI.cpp | 4 +
clang/lib/Driver/ToolChains/Clang.cpp | 3 +
clang/lib/Parse/ParseDecl.cpp | 24 +
clang/lib/Parse/ParseTentative.cpp | 5 +
clang/lib/Sema/DeclSpec.cpp | 31 +
clang/lib/Sema/Sema.cpp | 16 +
clang/lib/Sema/SemaChecking.cpp | 66 ++
clang/lib/Sema/SemaDecl.cpp | 4 +
clang/lib/Sema/SemaExpr.cpp | 204 ++++-
clang/lib/Sema/SemaExprCXX.cpp | 29 +
clang/lib/Sema/SemaLookup.cpp | 2 +
clang/lib/Sema/SemaOverload.cpp | 112 ++-
clang/lib/Sema/SemaTemplate.cpp | 5 +
clang/lib/Sema/SemaTemplateDeduction.cpp | 2 +
clang/lib/Sema/SemaType.cpp | 138 +++
clang/lib/Sema/TreeTransform.h | 21 +
clang/lib/Serialization/ASTReader.cpp | 4 +
clang/lib/Serialization/ASTWriter.cpp | 4 +
.../test/AST/ast-print-overflow-behavior.cpp | 17 +
.../AST/overflow-behavior-keywords-ast.cpp | 16 +
.../CodeGen/mangle-ms-overflow-behavior.cpp | 12 +
.../overflow-behavior-types-extensions.c | 104 +++
.../overflow-behavior-types-operators.cpp | 70 ++
.../overflow-behavior-types-promotions.cpp | 49 ++
.../CodeGen/overflow-behavior-types-scl.c | 43 +
clang/test/CodeGen/overflow-behavior-types.c | 176 ++++
.../test/CodeGen/overflow-behavior-types.cpp | 51 ++
...a-attribute-supported-attributes-list.test | 1 +
.../Sema/attr-overflow-behavior-constexpr.cpp | 43 +
.../attr-overflow-behavior-format-strings.c | 93 ++
clang/test/Sema/attr-overflow-behavior-off.c | 3 +
.../Sema/attr-overflow-behavior-templates.cpp | 73 ++
clang/test/Sema/attr-overflow-behavior.c | 222 +++++
clang/test/Sema/attr-overflow-behavior.cpp | 178 ++++
.../overflow-behavior-assignment-pedantic.c | 17 +
...rflow-behavior-function-boundary-default.c | 16 +
clang/test/Sema/overflow-behavior-keywords.c | 66 ++
clang/test/SemaCXX/sugar-common-types.cpp | 41 +-
clang/tools/libclang/CIndex.cpp | 4 +
.../TypeSystem/Clang/TypeSystemClang.cpp | 3 +
78 files changed, 3805 insertions(+), 168 deletions(-)
create mode 100644 clang/docs/OverflowBehaviorTypes.rst
create mode 100644 clang/test/AST/ast-print-overflow-behavior.cpp
create mode 100644 clang/test/AST/overflow-behavior-keywords-ast.cpp
create mode 100644 clang/test/CodeGen/mangle-ms-overflow-behavior.cpp
create mode 100644 clang/test/CodeGen/overflow-behavior-types-extensions.c
create mode 100644 clang/test/CodeGen/overflow-behavior-types-operators.cpp
create mode 100644 clang/test/CodeGen/overflow-behavior-types-promotions.cpp
create mode 100644 clang/test/CodeGen/overflow-behavior-types-scl.c
create mode 100644 clang/test/CodeGen/overflow-behavior-types.c
create mode 100644 clang/test/CodeGen/overflow-behavior-types.cpp
create mode 100644 clang/test/Sema/attr-overflow-behavior-constexpr.cpp
create mode 100644 clang/test/Sema/attr-overflow-behavior-format-strings.c
create mode 100644 clang/test/Sema/attr-overflow-behavior-off.c
create mode 100644 clang/test/Sema/attr-overflow-behavior-templates.cpp
create mode 100644 clang/test/Sema/attr-overflow-behavior.c
create mode 100644 clang/test/Sema/attr-overflow-behavior.cpp
create mode 100644 clang/test/Sema/overflow-behavior-assignment-pedantic.c
create mode 100644 clang/test/Sema/overflow-behavior-function-boundary-default.c
create mode 100644 clang/test/Sema/overflow-behavior-keywords.c
diff --git a/clang/docs/OverflowBehaviorTypes.rst b/clang/docs/OverflowBehaviorTypes.rst
new file mode 100644
index 0000000000000..7492d7819cb51
--- /dev/null
+++ b/clang/docs/OverflowBehaviorTypes.rst
@@ -0,0 +1,827 @@
+=====================
+OverflowBehaviorTypes
+=====================
+
+.. contents::
+ :local:
+
+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 compiler option
+``-foverflow-behavior-types``.
+
+There are two syntax options for specifying overflow behavior:
+
+**Attribute syntax:**
+
+.. code-block:: c++
+
+ __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``, and
+ ``implicit-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 the ``signed-integer-overflow`` sanitizer
+ or when using ``-ftrapv`` alongside a signed type, this is the default
+ behavior. Using this, one may enforce overflow checks for a type even when
+ ``-fwrapv`` is enabled globally.
+
+**Keyword syntax:**
+
+.. code-block:: c++
+
+ __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:**
+
+.. code-block:: c++
+
+ 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:**
+
+.. code-block:: c++
+
+ 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:**
+
+.. code-block:: c++
+
+ 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:**
+
+.. code-block:: c++
+
+ 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.
+
+ .. code-block:: c++
+
+ 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.
+
+ .. code-block:: c++
+
+ 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**: ``trap`` behavior dominates ``wrap``
+ behavior. The result follows standard C arithmetic conversions with ``trap``
+ behavior applied.
+
+ .. code-block:: c++
+
+ 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)
+
+
+.. list-table:: Promotion Rules Summary
+ :widths: 30 70
+ :header-rows: 1
+
+ * - Operation Type
+ - Result Type
+ * - OBT + Standard Integer
+ - Standard C conversion result with OBT qualifier applied
+ * - Same Kind OBTs (both ``wrap`` or both ``trap``)
+ - Standard C conversion result with common overflow behavior
+ * - Different Kind OBTs (``wrap`` + ``trap``)
+ - Standard C conversion result with ``trap`` behavior (dominance)
+
+**Overflow Behavior Dominance Rules:**
+
+1. If either operand has ``trap`` behavior → result has ``trap`` behavior
+2. If either operand has ``wrap`` behavior (and neither has ``trap`) → result has ``wrap`` behavior
+3. Otherwise → 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.
+
+.. code-block:: c++
+
+ 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.
+
+.. code-block:: c++
+
+ 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 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 ``trap`` then the truncation rules match the behavior
+ mentioned above.
+
+* **Both types are standard integer types**: Behaviors surrounding standard
+ integer types is unchanged.
+
+.. code-block:: c++
+
+ 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 the 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:
+
+.. code-block:: c++
+
+ 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.
+
+.. code-block:: c++
+
+ 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.
+
+.. code-block:: c++
+
+ 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:
+
+.. code-block:: c++
+
+ 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.
+
+.. code-block:: c++
+
+ 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.
+
+.. code-block:: c++
+
+ 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.
+
+.. code-block:: c++
+
+ 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.
+
+.. code-block:: c++
+
+ 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.
+
+.. code-block:: c++
+
+ 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:
+
+.. list-table:: Overflow Behavior Precedence
+ :widths: 15 15 15 15 20 15
+ :header-rows: 1
+
+ * - Behavior
+ - Default(No Flags)
+ - -ftrapv
+ - -fwrapv
+ - Sanitizers
+ - SSCL
+ * - ``overflow_behavior(wrap)``
+ - Wraps
+ - Wraps
+ - Wraps
+ - No report, Wraps
+ - Overrides SSCL
+ * - ``overflow_behavior(trap)``
+ - 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 :doc:`SanitizerSpecialCaseList`. 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:
+
+.. code-block:: c++
+
+ 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:
+
+.. code-block:: c++
+
+ 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
+``-foverflow-behavior-types``, a warning is issued and the attribute is ignored:
+
+.. code-block:: c++
+
+ // Without -foverflow-behavior-types
+ int __attribute__((overflow_behavior(wrap))) x;
+ // warning: 'overflow_behavior' attribute is ignored because it is
+ // not enabled; pass -foverflow-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.
+
+.. code-block:: c++
+
+ 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.
+
+.. code-block:: c++
+
+ 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.
+
+.. code-block:: c++
+
+ 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.
+
+.. code-block:: c++
+
+ 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:
+
+.. code-block:: c++
+
+ 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.
+
+.. code-block:: c++
+
+ 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.
+
+.. code-block:: c++
+
+ 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:
+
+.. code-block:: c++
+
+ 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:
+
+.. code-block:: c++
+
+ 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.
+
+.. code-block:: c++
+
+ 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.
+
+.. code-block:: c++
+
+ 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:
+
+1. Update the function parameter to accept the overflow behavior type:
+
+.. code-block:: c++
+
+ void process_value(int __ob_trap x); // Accept trap-annotated int
+
+ void caller() {
+ int __ob_trap safe_value = 42;
+ process_value(safe_value); // OK
+ }
+
+2. Explicitly cast at the call site to acknowledge the loss of overflow behavior:
+
+.. code-block:: c++
+
+ 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.
+
+.. code-block:: c++
+
+ #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.
+
+.. code-block:: c++
+
+ 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'
+
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 3526ffb40f350..6d9738cdffb5f 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -331,6 +331,7 @@ New Compiler Flags
- New option ``-fsanitize-debug-trap-reasons=`` added to control emitting trap reasons into the debug info when compiling with trapping UBSan (e.g. ``-fsanitize-trap=undefined``).
- New options for enabling allocation token instrumentation: ``-fsanitize=alloc-token``, ``-falloc-token-max=``, ``-fsanitize-alloc-token-fast-abi``, ``-fsanitize-alloc-token-extended``.
- The ``-resource-dir`` option is now displayed in the list of options shown by ``--help``.
+- New option ``-foverflow-behavior-types`` added to enable parsing of the ``overflow_behavior`` type attribute and type specifiers.
Lanai Support
^^^^^^^^^^^^^^
@@ -360,6 +361,11 @@ Attribute Changes in Clang
attribute, but `malloc_span` applies not to functions returning pointers, but to functions returning
span-like structures (i.e. those that contain a pointer field and a size integer field or two pointers).
+- Introduced a new type attribute ``__attribute__((overflow_behavior))`` which
+ currently accepts either ``wrap`` or ``trap`` as an argument, enabling
+ type-level control over overflow behavior. There is also an accompanying type
+ specifier for each behavior kind via `__ob_wrap` and `__ob_trap`.
+
Improvements to Clang's diagnostics
-----------------------------------
- Diagnostics messages now refer to ``structured binding`` instead of ``decomposition``,
diff --git a/clang/docs/SanitizerSpecialCaseList.rst b/clang/docs/SanitizerSpecialCaseList.rst
index 307c001664fba..a2d942154a830 100644
--- a/clang/docs/SanitizerSpecialCaseList.rst
+++ b/clang/docs/SanitizerSpecialCaseList.rst
@@ -134,6 +134,40 @@ precedence. Here are a few examples.
fun:*bar
fun:bad_bar=sanitize
+Interaction with Overflow Behavior Types
+----------------------------------------
+
+The ``overflow_behavior`` attribute provides a more granular, source-level
+control that takes precedence over the Sanitizer Special Case List. If a type
+is given an ``overflow_behavior`` attribute, it will override any matching
+``type:`` entry in a special case list.
+
+This allows developers to enforce a specific overflow behavior for a critical
+type, even if a broader rule in the special case list would otherwise disable
+instrumentation for it.
+
+.. code-block:: bash
+
+ $ cat ignorelist.txt
+ # Disable signed overflow checks for all types by default.
+ [signed-integer-overflow]
+ type:*
+
+ $ cat foo.c
+ // Force 'critical_type' to always have overflow checks,
+ // overriding the ignorelist.
+ typedef int __attribute__((overflow_behavior(trap))) critical_type;
+
+ void foo(int x) {
+ critical_type a = x;
+ a++; // Overflow is checked here due to the 'trap' attribute.
+
+ int b = x;
+ b++; // Overflow is NOT checked here due to the ignorelist.
+ }
+
+For more details on overflow behavior types, see :doc:`OverflowBehaviorTypes`.
+
Format
======
diff --git a/clang/docs/UndefinedBehaviorSanitizer.rst b/clang/docs/UndefinedBehaviorSanitizer.rst
index 0a2d833783e57..565bb2697b156 100644
--- a/clang/docs/UndefinedBehaviorSanitizer.rst
+++ b/clang/docs/UndefinedBehaviorSanitizer.rst
@@ -380,6 +380,42 @@ This attribute may not be
supported by other compilers, so consider using it together with
``#if defined(__clang__)``.
+Disabling Overflow Instrumentation with ``__attribute__((overflow_behavior(wrap)))``
+------------------------------------------------------------------------------------
+
+For more fine-grained control over how integer overflow is handled, you can use
+the ``__attribute__((overflow_behavior(wrap)))`` attribute. This attribute can
+be applied to ``typedef`` declarations and integer types to specify that
+arithmetic operations on that type should wrap on overflow. This can be used to
+disable overflow sanitization for specific types, while leaving it enabled for
+all other types.
+
+The ``overflow_behavior`` attribute not only affects UBSan instrumentation
+but also changes the fundamental overflow behavior of arithmetic operations
+on the annotated type. Operations on types marked with ``wrap`` will have
+well-defined wrapping semantics, while operations on types marked with
+``trap`` will be checked for overflow (regardless of global flags like
+``-fwrapv``).
+
+The attribute also affects implicit type promotion rules: when an overflow
+behavior type participates in arithmetic operations with standard integer
+types, the result maintains the overflow behavior type's characteristics,
+including its bit-width. This means annotated types can preserve narrower
+widths that would normally be promoted, allowing operations to stay within
+the constraints of the smallest annotated type in the expression.
+
+For more information, see :doc:`OverflowBehaviorTypes`.
+
+Enforcing Overflow Instrumentation with ``__attribute__((overflow_behavior(trap)))``
+---------------------------------------------------------------------------------------
+
+Conversely, you can use ``__attribute__((overflow_behavior(trap)))`` to
+enforce overflow checks for a specific type, even when ``-fwrapv`` is enabled
+globally. This is useful for ensuring that critical calculations are always
+checked for overflow, regardless of the global compiler settings.
+
+For more information, see :doc:`OverflowBehaviorTypes`.
+
Suppressing Errors in Recompiled Code (Ignorelist)
--------------------------------------------------
diff --git a/clang/docs/index.rst b/clang/docs/index.rst
index 272ae54bd9278..a95e303ec9c65 100644
--- a/clang/docs/index.rst
+++ b/clang/docs/index.rst
@@ -41,6 +41,7 @@ Using Clang as a Compiler
SanitizerStats
SanitizerSpecialCaseList
AllocToken
+ OverflowBehaviorTypes
BoundsSafety
BoundsSafetyAdoptionGuide
BoundsSafetyImplPlans
diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index f64e29be3205f..2b52a73a0cebc 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -14,6 +14,7 @@
#ifndef LLVM_CLANG_AST_ASTCONTEXT_H
#define LLVM_CLANG_AST_ASTCONTEXT_H
+#include "Type.h"
#include "clang/AST/ASTFwd.h"
#include "clang/AST/CanonicalType.h"
#include "clang/AST/CommentCommandTraits.h"
@@ -291,6 +292,7 @@ class ASTContext : public RefCountedBase<ASTContext> {
mutable llvm::ContextualFoldingSet<DependentBitIntType, ASTContext &>
DependentBitIntTypes;
mutable llvm::FoldingSet<BTFTagAttributedType> BTFTagAttributedTypes;
+ mutable llvm::FoldingSet<OverflowBehaviorType> OverflowBehaviorTypes;
llvm::FoldingSet<HLSLAttributedResourceType> HLSLAttributedResourceTypes;
llvm::FoldingSet<HLSLInlineSpirvType> HLSLInlineSpirvTypes;
@@ -942,6 +944,8 @@ class ASTContext : public RefCountedBase<ASTContext> {
bool isTypeIgnoredBySanitizer(const SanitizerMask &Mask,
const QualType &Ty) const;
+ bool isUnaryOverflowPatternExcluded(const UnaryOperator *UO);
+
const XRayFunctionFilter &getXRayFilter() const {
return *XRayFilter;
}
@@ -1042,6 +1046,15 @@ class ASTContext : public RefCountedBase<ASTContext> {
comments::FullComment *getCommentForDecl(const Decl *D,
const Preprocessor *PP) const;
+ /// Attempts to merge two types that may be OverflowBehaviorTypes.
+ ///
+ /// \returns A QualType if the types were handled, std::nullopt otherwise.
+ /// A null QualType indicates an incompatible merge.
+ std::optional<QualType>
+ tryMergeOverflowBehaviorTypes(QualType LHS, QualType RHS, bool OfBlockPointer,
+ bool Unqualified, bool BlockReturnType,
+ bool IsConditionalOperator);
+
/// Return parsed documentation comment attached to a given declaration.
/// Returns nullptr if no comment is attached. Does not look at any
/// redeclarations of the declaration.
@@ -1929,6 +1942,13 @@ class ASTContext : public RefCountedBase<ASTContext> {
QualType getBTFTagAttributedType(const BTFTypeTagAttr *BTFAttr,
QualType Wrapped) const;
+ QualType getOverflowBehaviorType(const OverflowBehaviorAttr *Attr,
+ QualType Wrapped) const;
+
+ QualType
+ getOverflowBehaviorType(OverflowBehaviorType::OverflowBehaviorKind Kind,
+ QualType Wrapped) const;
+
QualType getHLSLAttributedResourceType(
QualType Wrapped, QualType Contained,
const HLSLAttributedResourceType::Attributes &Attrs);
@@ -2626,6 +2646,22 @@ class ASTContext : public RefCountedBase<ASTContext> {
/// types.
bool areCompatibleVectorTypes(QualType FirstVec, QualType SecondVec);
+ /// Return true if two OverflowBehaviorTypes are compatible for assignment.
+ /// This checks both the underlying type compatibility and the overflow
+ /// behavior kind (trap vs wrap).
+ bool areCompatibleOverflowBehaviorTypes(QualType LHS, QualType RHS);
+
+ enum class OBTAssignResult {
+ Compatible, // No OBT issues
+ IncompatibleKinds, // __ob_trap vs __ob_wrap (error)
+ Discards, // OBT -> non-OBT on integer types (warning)
+ NotApplicable // Not both integers, fall through to normal checking
+ };
+
+ /// Check overflow behavior type compatibility for assignments.
+ /// Returns detailed information about OBT compatibility for assignment checking.
+ OBTAssignResult checkOBTAssignmentCompatibility(QualType LHS, QualType RHS);
+
/// Return true if the given types are an RISC-V vector builtin type and a
/// VectorType that is a fixed-length representation of the RISC-V vector
/// builtin type for a specific vector-length.
diff --git a/clang/include/clang/AST/ASTNodeTraverser.h b/clang/include/clang/AST/ASTNodeTraverser.h
index e74bb72571d64..b438a9b250b0e 100644
--- a/clang/include/clang/AST/ASTNodeTraverser.h
+++ b/clang/include/clang/AST/ASTNodeTraverser.h
@@ -447,6 +447,9 @@ class ASTNodeTraverser
void VisitBTFTagAttributedType(const BTFTagAttributedType *T) {
Visit(T->getWrappedType());
}
+ void VisitOverflowBehaviorType(const OverflowBehaviorType *T) {
+ Visit(T->getUnderlyingType());
+ }
void VisitHLSLAttributedResourceType(const HLSLAttributedResourceType *T) {
QualType Contained = T->getContainedType();
if (!Contained.isNull())
diff --git a/clang/include/clang/AST/PropertiesBase.td b/clang/include/clang/AST/PropertiesBase.td
index 5b10127526e4e..4581e55c28027 100644
--- a/clang/include/clang/AST/PropertiesBase.td
+++ b/clang/include/clang/AST/PropertiesBase.td
@@ -81,6 +81,8 @@ def AutoTypeKeyword : EnumPropertyType;
def Bool : PropertyType<"bool">;
def BuiltinTypeKind : EnumPropertyType<"BuiltinType::Kind">;
def BTFTypeTagAttr : PropertyType<"const BTFTypeTagAttr *">;
+def OverflowBehaviorKind
+ : EnumPropertyType<"OverflowBehaviorType::OverflowBehaviorKind">;
def CallingConv : EnumPropertyType;
def DeclarationName : PropertyType;
def DeclarationNameKind : EnumPropertyType<"DeclarationName::NameKind">;
diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h
index 8f427427d71ed..1a785a9183a4f 100644
--- a/clang/include/clang/AST/RecursiveASTVisitor.h
+++ b/clang/include/clang/AST/RecursiveASTVisitor.h
@@ -1157,6 +1157,9 @@ DEF_TRAVERSE_TYPE(CountAttributedType, {
DEF_TRAVERSE_TYPE(BTFTagAttributedType,
{ TRY_TO(TraverseType(T->getWrappedType())); })
+DEF_TRAVERSE_TYPE(OverflowBehaviorType,
+ { TRY_TO(TraverseType(T->getUnderlyingType())); })
+
DEF_TRAVERSE_TYPE(HLSLAttributedResourceType,
{ TRY_TO(TraverseType(T->getWrappedType())); })
@@ -1512,6 +1515,9 @@ DEF_TRAVERSE_TYPELOC(CountAttributedType,
DEF_TRAVERSE_TYPELOC(BTFTagAttributedType,
{ TRY_TO(TraverseTypeLoc(TL.getWrappedLoc())); })
+DEF_TRAVERSE_TYPELOC(OverflowBehaviorType,
+ { TRY_TO(TraverseTypeLoc(TL.getWrappedLoc())); })
+
DEF_TRAVERSE_TYPELOC(HLSLAttributedResourceType,
{ TRY_TO(TraverseTypeLoc(TL.getWrappedLoc())); })
diff --git a/clang/include/clang/AST/TypeBase.h b/clang/include/clang/AST/TypeBase.h
index f07861f50fe8c..a3dfc6d480406 100644
--- a/clang/include/clang/AST/TypeBase.h
+++ b/clang/include/clang/AST/TypeBase.h
@@ -1149,6 +1149,12 @@ class QualType {
/// Returns true if it is a WebAssembly Funcref Type.
bool isWebAssemblyFuncrefType() const;
+ /// Returns true if it is a OverflowBehaviorType of Wrap kind.
+ bool isWrapType() const;
+
+ /// Returns true if it is a OverflowBehaviorType of Trap kind.
+ bool isTrapType() const;
+
// Don't promise in the API that anything besides 'const' can be
// easily added.
@@ -2643,6 +2649,7 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase {
bool isSubscriptableVectorType() const;
bool isMatrixType() const; // Matrix type.
bool isConstantMatrixType() const; // Constant matrix type.
+ bool isOverflowBehaviorType() const; // __attribute__((no_sanitize))
bool isDependentAddressSpaceType() const; // value-dependent address space qualifier
bool isObjCObjectPointerType() const; // pointer to ObjC object
bool isObjCRetainableType() const; // ObjC object or block pointer
@@ -6678,6 +6685,44 @@ class BTFTagAttributedType : public Type, public llvm::FoldingSetNode {
}
};
+class OverflowBehaviorType : public Type, public llvm::FoldingSetNode {
+public:
+ enum OverflowBehaviorKind { Wrap, Trap };
+
+private:
+ friend class ASTContext; // ASTContext creates these
+
+ QualType UnderlyingType;
+ OverflowBehaviorKind BehaviorKind;
+
+ OverflowBehaviorType(QualType Canon, QualType Underlying,
+ OverflowBehaviorKind Kind);
+
+public:
+ QualType getUnderlyingType() const { return UnderlyingType; }
+ OverflowBehaviorKind getBehaviorKind() const { return BehaviorKind; }
+
+ bool isWrapKind() const { return BehaviorKind == OverflowBehaviorKind::Wrap; }
+ bool isTrapKind() const { return BehaviorKind == OverflowBehaviorKind::Trap; }
+
+ bool isSugared() const { return false; }
+ QualType desugar() const { return getUnderlyingType(); }
+
+ void Profile(llvm::FoldingSetNodeID &ID) {
+ Profile(ID, UnderlyingType, BehaviorKind);
+ }
+
+ static void Profile(llvm::FoldingSetNodeID &ID, QualType Underlying,
+ OverflowBehaviorKind Kind) {
+ ID.AddPointer(Underlying.getAsOpaquePtr());
+ ID.AddInteger((int)Kind);
+ }
+
+ static bool classof(const Type *T) {
+ return T->getTypeClass() == OverflowBehavior;
+ }
+};
+
class HLSLAttributedResourceType : public Type, public llvm::FoldingSetNode {
public:
struct Attributes {
@@ -8677,6 +8722,10 @@ inline bool Type::isConstantMatrixType() const {
return isa<ConstantMatrixType>(CanonicalType);
}
+inline bool Type::isOverflowBehaviorType() const {
+ return isa<OverflowBehaviorType>(CanonicalType);
+}
+
inline bool Type::isDependentAddressSpaceType() const {
return isa<DependentAddressSpaceType>(CanonicalType);
}
@@ -8921,6 +8970,10 @@ inline bool Type::isIntegerType() const {
return IsEnumDeclComplete(ET->getDecl()) &&
!IsEnumDeclScoped(ET->getDecl());
}
+
+ if (const auto *OT = dyn_cast<OverflowBehaviorType>(CanonicalType))
+ return OT->getUnderlyingType()->isIntegerType();
+
return isBitIntType();
}
@@ -8983,7 +9036,7 @@ inline bool Type::isScalarType() const {
isa<MemberPointerType>(CanonicalType) ||
isa<ComplexType>(CanonicalType) ||
isa<ObjCObjectPointerType>(CanonicalType) ||
- isBitIntType();
+ isOverflowBehaviorType() || isBitIntType();
}
inline bool Type::isIntegralOrEnumerationType() const {
@@ -8995,6 +9048,9 @@ inline bool Type::isIntegralOrEnumerationType() const {
if (const auto *ET = dyn_cast<EnumType>(CanonicalType))
return IsEnumDeclComplete(ET->getDecl());
+ if (const auto *OBT = dyn_cast<OverflowBehaviorType>(CanonicalType))
+ return OBT->getUnderlyingType()->isIntegralOrEnumerationType();
+
return isBitIntType();
}
diff --git a/clang/include/clang/AST/TypeLoc.h b/clang/include/clang/AST/TypeLoc.h
index 2cefaa9611c98..9d0c516f07322 100644
--- a/clang/include/clang/AST/TypeLoc.h
+++ b/clang/include/clang/AST/TypeLoc.h
@@ -1073,6 +1073,34 @@ class BTFTagAttributedTypeLoc
QualType getInnerType() const { return getTypePtr()->getWrappedType(); }
};
+struct OverflowBehaviorLocInfo {
+ SourceLocation AttrLoc;
+};
+
+class OverflowBehaviorTypeLoc
+ : public ConcreteTypeLoc<UnqualTypeLoc, OverflowBehaviorTypeLoc,
+ OverflowBehaviorType, OverflowBehaviorLocInfo> {
+public:
+ TypeLoc getWrappedLoc() const { return getInnerTypeLoc(); }
+
+ /// The no_sanitize type attribute.
+ OverflowBehaviorType::OverflowBehaviorKind getBehaviorKind() const {
+ return getTypePtr()->getBehaviorKind();
+ }
+
+ SourceRange getLocalSourceRange() const;
+
+ void initializeLocal(ASTContext &Context, SourceLocation loc) {
+ setAttrLoc(loc);
+ }
+
+ SourceLocation getAttrLoc() const { return getLocalData()->AttrLoc; }
+
+ void setAttrLoc(SourceLocation loc) { getLocalData()->AttrLoc = loc; }
+
+ QualType getInnerType() const { return getTypePtr()->getUnderlyingType(); }
+};
+
struct HLSLAttributedResourceLocInfo {
SourceRange Range;
TypeSourceInfo *ContainedTyInfo;
diff --git a/clang/include/clang/AST/TypeProperties.td b/clang/include/clang/AST/TypeProperties.td
index 03613d53b2776..17bc9d05fae44 100644
--- a/clang/include/clang/AST/TypeProperties.td
+++ b/clang/include/clang/AST/TypeProperties.td
@@ -652,6 +652,19 @@ let Class = BTFTagAttributedType in {
}]>;
}
+let Class = OverflowBehaviorType in {
+ def : Property<"behaviorKind", OverflowBehaviorKind> {
+ let Read = [{ node->getBehaviorKind() }];
+ }
+ def : Property<"underlyingType", QualType> {
+ let Read = [{ node->getUnderlyingType() }];
+ }
+
+ def : Creator<[{
+ return ctx.getOverflowBehaviorType(behaviorKind, underlyingType);
+ }]>;
+}
+
let Class = HLSLAttributedResourceType in {
def : Property<"resClass", UInt32> {
let Read = [{ static_cast<uint32_t>(node->getAttrs().ResourceClass) }];
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index c929da7d538bd..35d93597a2786 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -5331,3 +5331,10 @@ def NonString : InheritableAttr {
let Subjects = SubjectList<[Var, Field]>;
let Documentation = [NonStringDocs];
}
+
+def OverflowBehavior : TypeAttr {
+ let Spellings = [Clang<"overflow_behavior">];
+ let Args = [IdentifierArgument<"BehaviorKind">];
+ let Subjects = SubjectList<[Var, TypedefName, Field]>;
+ let Documentation = [Undocumented];
+}
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index 2fff32bbc4d6c..b72f97aae64f5 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -117,8 +117,9 @@ def ObjCSignedCharBoolImplicitIntConversion :
DiagGroup<"objc-signed-char-bool-implicit-int-conversion">;
def Shorten64To32 : DiagGroup<"shorten-64-to-32">;
def ImplicitIntConversionOnNegation : DiagGroup<"implicit-int-conversion-on-negation">;
-def ImplicitIntConversion : DiagGroup<"implicit-int-conversion",
- [Shorten64To32,
+def ImplicitIntConversion
+ : DiagGroup<
+ "implicit-int-conversion", [Shorten64To32,
ObjCSignedCharBoolImplicitIntConversion,
ImplicitIntConversionOnNegation]>;
def ImplicitConstIntFloatConversion : DiagGroup<"implicit-const-int-float-conversion">;
@@ -130,6 +131,26 @@ def ImplicitFloatConversion : DiagGroup<"implicit-float-conversion",
[ImplicitIntFloatConversion,
ObjCSignedCharBoolImplicitFloatConversion]>;
def ImplicitFixedPointConversion : DiagGroup<"implicit-fixed-point-conversion">;
+def ImplicitOverflowBehaviorConversionAssignment
+ : DiagGroup<"implicit-overflow-behavior-conversion-assignment">;
+def ImplicitOverflowBehaviorConversionAssignmentPedantic
+ : DiagGroup<"implicit-overflow-behavior-conversion-assignment-pedantic">;
+def ImplicitOverflowBehaviorConversionAtFunctionBoundary
+ : DiagGroup<"implicit-overflow-behavior-conversion-function-boundary">;
+def ImplicitOverflowBehaviorConversionAtFunctionBoundaryPedantic
+ : DiagGroup<"implicit-overflow-behavior-conversion-function-boundary-pedantic">;
+def ImplicitOverflowBehaviorConversionPedantic
+ : DiagGroup<"implicit-overflow-behavior-conversion-pedantic">;
+def ImplicitOverflowBehaviorConversion
+ : DiagGroup<"implicit-overflow-behavior-conversion",
+ [ImplicitOverflowBehaviorConversionAssignment,
+ ImplicitOverflowBehaviorConversionAssignmentPedantic,
+ ImplicitOverflowBehaviorConversionPedantic,
+ ImplicitOverflowBehaviorConversionAtFunctionBoundary,
+ ImplicitOverflowBehaviorConversionAtFunctionBoundaryPedantic]>;
+def OverflowBehaviorConversion
+ : DiagGroup<
+ "overflow-behavior-conversion", [ImplicitOverflowBehaviorConversion]>;
def FloatOverflowConversion : DiagGroup<"float-overflow-conversion">;
def FloatZeroConversion : DiagGroup<"float-zero-conversion">;
@@ -561,12 +582,15 @@ def IncompatibleMSStruct : DiagGroup<"incompatible-ms-struct">;
def IncompatibleMSPragmaSection : DiagGroup<"incompatible-ms-pragma-section">;
def IncompatiblePointerTypesDiscardsQualifiers
: DiagGroup<"incompatible-pointer-types-discards-qualifiers">;
+def IncompatiblePointerTypesDiscardsOverflowBehavior
+ : DiagGroup<"incompatible-pointer-types-discards-overflow-behavior">;
def IncompatibleFunctionPointerTypes
: DiagGroup<"incompatible-function-pointer-types">;
def IncompatiblePointerTypes
- : DiagGroup<"incompatible-pointer-types",
- [IncompatiblePointerTypesDiscardsQualifiers,
- IncompatibleFunctionPointerTypes]>;
+ : DiagGroup<"incompatible-pointer-types",
+ [IncompatiblePointerTypesDiscardsQualifiers,
+ IncompatiblePointerTypesDiscardsOverflowBehavior,
+ IncompatibleFunctionPointerTypes]>;
def IncompleteUmbrella : DiagGroup<"incomplete-umbrella">;
def IncompleteFrameworkModuleDeclaration
: DiagGroup<"incomplete-framework-module-declaration">;
@@ -1147,23 +1171,16 @@ def Parentheses : DiagGroup<"parentheses",
// - conversion warnings for literals are on by default
// - bool-to-pointer conversion warnings are on by default
// - __null-to-integer conversion warnings are on by default
-def Conversion : DiagGroup<"conversion",
- [BoolConversion,
- CharacterConversion,
- ConstantConversion,
- EnumConversion,
- BitFieldEnumConversion,
- FloatConversion,
- IntConversion,
- ImplicitIntConversion,
- ImplicitFloatConversion,
- LiteralConversion,
- NonLiteralNullConversion, // (1-1)->pointer (etc)
- NullConversion, // NULL->non-pointer
- ObjCLiteralConversion,
- SignConversion,
- StringConversion]>,
- DiagCategory<"Value Conversion Issue">;
+def Conversion
+ : DiagGroup<"conversion",
+ [BoolConversion, CharacterConversion, ConstantConversion,
+ EnumConversion, BitFieldEnumConversion, FloatConversion,
+ IntConversion, ImplicitIntConversion, ImplicitFloatConversion,
+ OverflowBehaviorConversion, LiteralConversion,
+ NonLiteralNullConversion, // (1-1)->pointer (etc)
+ NullConversion, // NULL->non-pointer
+ ObjCLiteralConversion, SignConversion, StringConversion]>,
+ DiagCategory<"Value Conversion Issue">;
def Unused : DiagGroup<"unused",
[UnusedArgument, UnusedFunction, UnusedLabel,
@@ -1669,6 +1686,9 @@ def CTADMaybeUnsupported : DiagGroup<"ctad-maybe-unsupported">;
def FortifySource : DiagGroup<"fortify-source", [FormatOverflow, FormatTruncation]>;
+def OverflowBehaviorAttributeIgnored
+ : DiagGroup<"overflow-behavior-attribute-ignored">;
+
def MaxTokens : DiagGroup<"max-tokens"> {
code Documentation = [{
The warning is issued if the number of pre-processor tokens exceeds
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 69ed958a2a2aa..c0e380ce38e8d 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -4078,6 +4078,58 @@ def note_cannot_use_trivial_abi_reason : Note<
"it has a __weak field|it has a field of a non-trivial class type|"
"it has an address-discriminated '__ptrauth' field}1">;
+// OverflowBehavior attribute
+def err_overflow_behavior_unknown_ident
+ : Error<"'%0' is not a valid argument to attribute %1, only 'wrap' and "
+ "'trap' are supported">;
+def err_overflow_behavior_non_integer_type
+ : Error<"%0 %select{attribute|specifier}2 cannot be applied to "
+ "non-integer type '%1'">;
+def err_conflicting_overflow_behaviors
+ : Error<"conflicting %select{'overflow_behavior' attributes|overflow "
+ "behavior specification; specifier specifies '%1' but attribute "
+ "specifies "
+ "'%2'}0 on the same type">;
+def err_incompatible_obt_kinds_assignment
+ : Error<"assigning to %0 from %1 with incompatible overflow behavior types "
+ "('%2' and '%3')">;
+def warn_overflow_behavior_attribute_disabled
+ : Warning<"%0 attribute is ignored because it is not enabled; pass "
+ "-foverflow-behavior-types">,
+ InGroup<OverflowBehaviorAttributeIgnored>;
+def warn_impcast_overflow_behavior
+ : Warning<"implicit conversion from %0 to %1 discards overflow behavior">,
+ InGroup<ImplicitOverflowBehaviorConversion>,
+ DefaultIgnore;
+def warn_redundant_overflow_behaviors_mixed
+ : Warning<"redundant overflow behavior specification; both specifier and "
+ "attribute specify '%0'">,
+ InGroup<OverflowBehaviorAttributeIgnored>;
+def warn_impcast_overflow_behavior_assignment
+ : Warning<"implicit conversion from %0 to %1 during assignment discards "
+ "overflow behavior">,
+ InGroup<ImplicitOverflowBehaviorConversionAssignment>,
+ DefaultIgnore;
+def warn_impcast_overflow_behavior_assignment_pedantic
+ : Warning<"implicit conversion from %0 to %1 during assignment discards "
+ "overflow behavior">,
+ InGroup<ImplicitOverflowBehaviorConversionAssignmentPedantic>,
+ DefaultIgnore;
+def warn_impcast_overflow_behavior_pedantic
+ : Warning<"implicit conversion from %0 to %1 discards overflow behavior">,
+ InGroup<ImplicitOverflowBehaviorConversionPedantic>,
+ DefaultIgnore;
+def warn_obt_discarded_at_function_boundary
+ : Warning<"passing argument of type %0 to parameter of type %1 discards "
+ "overflow behavior at function boundary">,
+ InGroup<ImplicitOverflowBehaviorConversionAtFunctionBoundary>,
+ DefaultIgnore;
+def warn_obt_discarded_at_function_boundary_pedantic
+ : Warning<"passing argument of type %0 to parameter of type %1 discards "
+ "overflow behavior at function boundary">,
+ InGroup<ImplicitOverflowBehaviorConversionAtFunctionBoundaryPedantic>,
+ DefaultIgnore;
+
// Availability attribute
def warn_availability_unknown_platform : Warning<
"unknown platform %0 in availability macro">, InGroup<Availability>;
@@ -9159,6 +9211,20 @@ def ext_typecheck_convert_discards_qualifiers : ExtWarn<
"|%diff{casting $ to type $|casting between types}0,1}2"
" discards qualifiers">,
InGroup<IncompatiblePointerTypesDiscardsQualifiers>;
+def ext_typecheck_convert_discards_overflow_behavior : ExtWarn<
+ "%select{%diff{assigning to $ from $|assigning to different types}0,1"
+ "|%diff{passing $ to parameter of type $|"
+ "passing to parameter of different type}0,1"
+ "|%diff{returning $ from a function with result type $|"
+ "returning from function with different return type}0,1"
+ "|%diff{converting $ to type $|converting between types}0,1"
+ "|%diff{initializing $ with an expression of type $|"
+ "initializing with expression of different type}0,1"
+ "|%diff{sending $ to parameter of type $|"
+ "sending to parameter of different type}0,1"
+ "|%diff{casting $ to type $|casting between types}0,1}2"
+ " discards overflow behavior">,
+ InGroup<IncompatiblePointerTypesDiscardsOverflowBehavior>;
def err_typecheck_convert_discards_qualifiers : Error<
"%select{%diff{assigning to $ from $|assigning to different types}0,1"
"|%diff{passing $ to parameter of type $|"
diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def
index 40fc66ea12e34..4f3dd765b25f3 100644
--- a/clang/include/clang/Basic/LangOptions.def
+++ b/clang/include/clang/Basic/LangOptions.def
@@ -427,6 +427,8 @@ LANGOPT(FixedPoint, 1, 0, NotCompatible, "fixed point types")
LANGOPT(PaddingOnUnsignedFixedPoint, 1, 0, NotCompatible,
"unsigned fixed point types having one extra padding bit")
+LANGOPT(OverflowBehaviorTypes, 1, 0, NotCompatible, "overflow behavior types")
+
ENUM_LANGOPT(RegisterStaticDestructors, RegisterStaticDestructorsKind, 2,
RegisterStaticDestructorsKind::All, NotCompatible,
"Register C++ static destructors")
diff --git a/clang/include/clang/Basic/LangOptions.h b/clang/include/clang/Basic/LangOptions.h
index 3f042f8ddb5a1..82c6d187a4e91 100644
--- a/clang/include/clang/Basic/LangOptions.h
+++ b/clang/include/clang/Basic/LangOptions.h
@@ -113,6 +113,25 @@ class LangOptionsBase {
SOB_Trapping
};
+ // Used by __attribute__((overflow_behavior())) to describe overflow behavior
+ // on a per-type basis.
+ enum OverflowBehaviorKind {
+ // Default C standard behavior (type dependent).
+ OB_Unset,
+
+ // __attribute__((overflow_behavior("wrap")))
+ OB_Wrap,
+
+ // __attribute__((overflow_behavior("trap")))
+ OB_Trap,
+
+ // Signed types defined as wrapping via -fwrapv can still be instrumented
+ // by sanitizers (PR82432). This field is needed to disambiguate canonical
+ // wrapping type behaviors from -fwrapv behaviors.
+ // -fwrapv
+ OB_SignedAndDefined
+ };
+
// FIXME: Unify with TUKind.
enum CompilingModuleKind {
/// Not compiling a module interface at all.
diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def
index 564d6010181cc..cc3acd62cfcf1 100644
--- a/clang/include/clang/Basic/TokenKinds.def
+++ b/clang/include/clang/Basic/TokenKinds.def
@@ -350,6 +350,10 @@ KEYWORD(__objc_yes , KEYALL)
KEYWORD(__objc_no , KEYALL)
KEYWORD(__ptrauth , KEYALL)
+// Overflow behavior types
+KEYWORD(__ob_wrap , KEYALL)
+KEYWORD(__ob_trap , KEYALL)
+
// C2y
UNARY_EXPR_OR_TYPE_TRAIT(_Countof, CountOf, KEYNOCXX)
diff --git a/clang/include/clang/Basic/TypeNodes.td b/clang/include/clang/Basic/TypeNodes.td
index db43a8529f02b..a9965a4a89aa1 100644
--- a/clang/include/clang/Basic/TypeNodes.td
+++ b/clang/include/clang/Basic/TypeNodes.td
@@ -110,3 +110,4 @@ def AtomicType : TypeNode<Type>;
def BitIntType : TypeNode<Type>;
def DependentBitIntType : TypeNode<Type>, AlwaysDependent;
def PredefinedSugarType : TypeNode<Type>, NeverCanonical;
+def OverflowBehaviorType : TypeNode<Type>;
diff --git a/clang/include/clang/Options/Options.td b/clang/include/clang/Options/Options.td
index d31bd7d6be322..38661b0d8a8b2 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -2349,6 +2349,12 @@ defm fixed_point : BoolFOption<"fixed-point",
PosFlag<SetTrue, [], [ClangOption, CC1Option], "Enable">,
NegFlag<SetFalse, [], [ClangOption], "Disable">,
BothFlags<[], [ClangOption], " fixed point types">>;
+defm overflow_behavior_types
+ : BoolFOption<"overflow-behavior-types", LangOpts<"OverflowBehaviorTypes">,
+ DefaultFalse,
+ PosFlag<SetTrue, [], [ClangOption, CC1Option], "Enable">,
+ NegFlag<SetFalse, [], [ClangOption], "Disable">,
+ BothFlags<[], [ClangOption], " overflow behavior types">>;
def cxx_static_destructors_EQ : Joined<["-"], "fc++-static-destructors=">, Group<f_Group>,
HelpText<"Controls which variables C++ static destructors are registered for">,
Values<"all,thread-local,none">,
diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h
index 43a48c92fc305..ec6baeff568f9 100644
--- a/clang/include/clang/Sema/DeclSpec.h
+++ b/clang/include/clang/Sema/DeclSpec.h
@@ -48,6 +48,7 @@ namespace clang {
class ObjCDeclSpec;
class Sema;
class Declarator;
+ class OverflowBehaviorType;
struct TemplateIdAnnotation;
/// Represents a C++ nested-name-specifier or a global scope specifier.
@@ -322,6 +323,12 @@ class DeclSpec {
enum FriendSpecified : bool { No, Yes };
+ enum class OverflowBehaviorState {
+ Unspecified, // No overflow behavior specified
+ Wrap, // __ob_wrap or __attribute__((overflow_behavior(wrap)))
+ Trap // __ob_trap or __attribute__((overflow_behavior(trap)))
+ };
+
private:
// storage-class-specifier
LLVM_PREFERRED_TYPE(SCS)
@@ -359,6 +366,10 @@ class DeclSpec {
LLVM_PREFERRED_TYPE(TQ)
unsigned TypeQualifiers : 5; // Bitwise OR of TQ.
+ // overflow behavior qualifiers
+ LLVM_PREFERRED_TYPE(OverflowBehaviorState)
+ unsigned OB_state : 2;
+
// function-specifier
LLVM_PREFERRED_TYPE(bool)
unsigned FS_inline_specified : 1;
@@ -409,6 +420,7 @@ class DeclSpec {
SourceRange TypeofParensRange;
SourceLocation TQ_constLoc, TQ_restrictLoc, TQ_volatileLoc, TQ_atomicLoc,
TQ_unalignedLoc;
+ SourceLocation OB_Loc;
SourceLocation FS_inlineLoc, FS_virtualLoc, FS_explicitLoc, FS_noreturnLoc;
SourceLocation FS_explicitCloseParenLoc;
SourceLocation FS_forceinlineLoc;
@@ -460,11 +472,12 @@ class DeclSpec {
TypeSpecType(TST_unspecified), TypeAltiVecVector(false),
TypeAltiVecPixel(false), TypeAltiVecBool(false), TypeSpecOwned(false),
TypeSpecPipe(false), TypeSpecSat(false), ConstrainedAuto(false),
- TypeQualifiers(TQ_unspecified), FS_inline_specified(false),
- FS_forceinline_specified(false), FS_virtual_specified(false),
- FS_noreturn_specified(false), FriendSpecifiedFirst(false),
- ConstexprSpecifier(
- static_cast<unsigned>(ConstexprSpecKind::Unspecified)),
+ TypeQualifiers(TQ_unspecified),
+ OB_state(static_cast<unsigned>(OverflowBehaviorState::Unspecified)),
+ FS_inline_specified(false), FS_forceinline_specified(false),
+ FS_virtual_specified(false), FS_noreturn_specified(false),
+ FriendSpecifiedFirst(false), ConstexprSpecifier(static_cast<unsigned>(
+ ConstexprSpecKind::Unspecified)),
Attrs(attrFactory), writtenBS(), ObjCQualifiers(nullptr) {}
// storage-class-specifier
@@ -579,6 +592,7 @@ class DeclSpec {
static const char *getSpecifierName(DeclSpec::SCS S);
static const char *getSpecifierName(DeclSpec::TSCS S);
static const char *getSpecifierName(ConstexprSpecKind C);
+ static const char *getSpecifierName(OverflowBehaviorState S);
// type-qualifiers
@@ -592,6 +606,25 @@ class DeclSpec {
SourceLocation getPipeLoc() const { return TQ_pipeLoc; }
SourceLocation getEllipsisLoc() const { return EllipsisLoc; }
+ // overflow behavior qualifiers
+ OverflowBehaviorState getOverflowBehaviorState() const {
+ return static_cast<OverflowBehaviorState>(OB_state);
+ }
+ bool isWrapSpecified() const {
+ return getOverflowBehaviorState() == OverflowBehaviorState::Wrap;
+ }
+ bool isTrapSpecified() const {
+ return getOverflowBehaviorState() == OverflowBehaviorState::Trap;
+ }
+ bool isOverflowBehaviorSpecified() const {
+ return getOverflowBehaviorState() != OverflowBehaviorState::Unspecified;
+ }
+ SourceLocation getOverflowBehaviorLoc() const { return OB_Loc; }
+
+ bool SetOverflowBehavior(OverflowBehaviorType::OverflowBehaviorKind Kind,
+ SourceLocation Loc, const char *&PrevSpec,
+ unsigned &DiagID);
+
/// Clear out all of the type qualifiers.
void ClearTypeQualifiers() {
TypeQualifiers = 0;
@@ -601,6 +634,8 @@ class DeclSpec {
TQ_atomicLoc = SourceLocation();
TQ_unalignedLoc = SourceLocation();
TQ_pipeLoc = SourceLocation();
+ OB_state = static_cast<unsigned>(OverflowBehaviorState::Unspecified);
+ OB_Loc = SourceLocation();
}
// function-specifier
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 19cca691e9859..ad3b04de63a8c 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -735,6 +735,11 @@ enum class AssignConvertType {
/// like address spaces.
IncompatiblePointerDiscardsQualifiers,
+ /// IncompatiblePointerDiscardsOverflowBehavior - The assignment
+ /// discards overflow behavior annotations between otherwise compatible
+ /// pointer types.
+ IncompatiblePointerDiscardsOverflowBehavior,
+
/// IncompatibleNestedPointerAddressSpaceMismatch - The assignment
/// changes address spaces in nested pointer types which is not allowed.
/// For instance, converting __private int ** to __generic int ** is
@@ -768,6 +773,13 @@ enum class AssignConvertType {
/// object with __weak qualifier.
IncompatibleObjCWeakRef,
+ /// IncompatibleOBTKinds - Assigning between incompatible OverflowBehaviorType
+ /// kinds, e.g., from __ob_trap to __ob_wrap or vice versa.
+ IncompatibleOBTKinds,
+
+ /// CompatibleOBTDiscards - Assignment discards overflow behavior
+ CompatibleOBTDiscards,
+
/// Incompatible - We reject this conversion outright, it is invalid to
/// represent it in the AST.
Incompatible
@@ -1341,6 +1353,10 @@ class Sema final : public SemaBase {
/// whether the next info diagnostic should be immediate.
bool IsLastErrorImmediate = true;
+ /// Track if we're currently analyzing overflow behavior types in assignment
+ /// context.
+ bool InOverflowBehaviorAssignmentContext = false;
+
class DelayedDiagnostics;
class DelayedDiagnosticsState {
@@ -2871,6 +2887,11 @@ class Sema final : public SemaBase {
bool *ICContext = nullptr,
bool IsListInit = false);
+ /// Check for overflow behavior type related implicit conversion diagnostics.
+ /// Returns true if OBT-related diagnostic was issued, false otherwise.
+ bool CheckOverflowBehaviorTypeConversion(Expr *E, QualType T,
+ SourceLocation CC);
+
bool
BuiltinElementwiseTernaryMath(CallExpr *TheCall,
EltwiseBuiltinArgTyRestriction ArgTyRestr =
@@ -10121,6 +10142,18 @@ class Sema final : public SemaBase {
/// floating-point or integral promotion.
bool IsComplexPromotion(QualType FromType, QualType ToType);
+ /// IsOverflowBehaviorTypePromotion - Determines whether the conversion from
+ /// FromType to ToType involves an OverflowBehaviorType FromType being
+ /// promoted to an OverflowBehaviorType ToType which has a larger bitwidth.
+ /// If so, returns true and sets FromType to ToType.
+ bool IsOverflowBehaviorTypePromotion(QualType FromType, QualType ToType);
+
+ /// IsOverflowBehaviorTypeConversion - Determines whether the conversion from
+ /// FromType to ToType necessarily involves both an OverflowBehaviorType and
+ /// a non-OverflowBehaviorType. If so, returns true and sets FromType to
+ /// ToType.
+ bool IsOverflowBehaviorTypeConversion(QualType FromType, QualType ToType);
+
/// IsPointerConversion - Determines whether the conversion of the
/// expression From, which has the (possibly adjusted) type FromType,
/// can be converted to the type ToType via a pointer conversion (C++
diff --git a/clang/include/clang/Serialization/TypeBitCodes.def b/clang/include/clang/Serialization/TypeBitCodes.def
index d6c484563409c..9f1da65e0f940 100644
--- a/clang/include/clang/Serialization/TypeBitCodes.def
+++ b/clang/include/clang/Serialization/TypeBitCodes.def
@@ -69,5 +69,6 @@ TYPE_BIT_CODE(HLSLAttributedResource, HLSLRESOURCE_ATTRIBUTED, 59)
TYPE_BIT_CODE(HLSLInlineSpirv, HLSL_INLINE_SPIRV, 60)
TYPE_BIT_CODE(PredefinedSugar, PREDEFINED_SUGAR, 61)
TYPE_BIT_CODE(SubstBuiltinTemplatePack, SUBST_BUILTIN_TEMPLATE_PACK, 62)
+TYPE_BIT_CODE(OverflowBehavior, OVERFLOWBEHAVIOR, 63)
#undef TYPE_BIT_CODE
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index b359fc8350375..e5b668eab72e7 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -781,6 +781,42 @@ ASTContext::insertCanonicalTemplateTemplateParmDeclInternal(
return CanonTTP;
}
+/// For the purposes of overflow pattern exclusion, does this match the
+/// while(i--) pattern?
+static bool matchesPostDecrInWhile(const UnaryOperator *UO, ASTContext &Ctx) {
+ if (UO->getOpcode() != UO_PostDec)
+ return false;
+
+ if (!UO->getType()->isUnsignedIntegerType())
+ return false;
+
+ // -fsanitize-undefined-ignore-overflow-pattern=unsigned-post-decr-while
+ if (!Ctx.getLangOpts().isOverflowPatternExcluded(
+ LangOptions::OverflowPatternExclusionKind::PostDecrInWhile))
+ return false;
+
+ // all Parents (usually just one) must be a WhileStmt
+ return llvm::all_of(
+ Ctx.getParentMapContext().getParents(*UO),
+ [](const DynTypedNode &P) { return P.get<WhileStmt>() != nullptr; });
+}
+
+bool ASTContext::isUnaryOverflowPatternExcluded(const UnaryOperator *UO) {
+ // -fsanitize-undefined-ignore-overflow-pattern=negated-unsigned-const
+ // ... like -1UL;
+ if (UO->getOpcode() == UO_Minus &&
+ getLangOpts().isOverflowPatternExcluded(
+ LangOptions::OverflowPatternExclusionKind::NegUnsignedConst) &&
+ UO->isIntegerConstantExpr(*this)) {
+ return true;
+ }
+
+ if (matchesPostDecrInWhile(UO, *this))
+ return true;
+
+ return false;
+}
+
/// Check if a type can have its sanitizer instrumentation elided based on its
/// presence within an ignorelist.
bool ASTContext::isTypeIgnoredBySanitizer(const SanitizerMask &Mask,
@@ -1927,6 +1963,11 @@ bool ASTContext::isPromotableIntegerType(QualType T) const {
return true;
}
+ // OverflowBehaviorTypes are promotable if their underlying type is promotable
+ if (const auto *OBT = T->getAs<OverflowBehaviorType>()) {
+ return isPromotableIntegerType(OBT->getUnderlyingType());
+ }
+
return false;
}
@@ -2471,6 +2512,10 @@ TypeInfo ASTContext::getTypeInfoImpl(const Type *T) const {
return getTypeInfo(
cast<BTFTagAttributedType>(T)->getWrappedType().getTypePtr());
+ case Type::OverflowBehavior:
+ return getTypeInfo(
+ cast<OverflowBehaviorType>(T)->getUnderlyingType().getTypePtr());
+
case Type::HLSLAttributedResource:
return getTypeInfo(
cast<HLSLAttributedResourceType>(T)->getWrappedType().getTypePtr());
@@ -3504,6 +3549,9 @@ static void encodeTypeForFunctionPointerAuth(const ASTContext &Ctx,
case Type::HLSLInlineSpirv:
llvm_unreachable("should never get here");
break;
+ case Type::OverflowBehavior:
+ llvm_unreachable("should never get here");
+ break;
case Type::DeducedTemplateSpecialization:
case Type::Auto:
#define NON_CANONICAL_TYPE(Class, Base) case Type::Class:
@@ -3648,6 +3696,12 @@ ASTContext::adjustType(QualType Orig,
adjustType(BTFT->getWrappedType(), Adjust));
}
+ case Type::OverflowBehavior: {
+ const auto *OB = dyn_cast<OverflowBehaviorType>(Orig);
+ return getOverflowBehaviorType(OB->getBehaviorKind(),
+ adjustType(OB->getUnderlyingType(), Adjust));
+ }
+
case Type::Paren:
return getParenType(
adjustType(cast<ParenType>(Orig)->getInnerType(), Adjust));
@@ -4215,6 +4269,7 @@ QualType ASTContext::getVariableArrayDecayedType(QualType type) const {
case Type::ArrayParameter:
case Type::HLSLAttributedResource:
case Type::HLSLInlineSpirv:
+ case Type::OverflowBehavior:
llvm_unreachable("type should never be variably-modified");
// These types can be variably-modified but should never need to
@@ -5680,6 +5735,51 @@ QualType ASTContext::getBTFTagAttributedType(const BTFTypeTagAttr *BTFAttr,
return QualType(Ty, 0);
}
+QualType ASTContext::getOverflowBehaviorType(const OverflowBehaviorAttr *Attr,
+ QualType Underlying) const {
+ IdentifierInfo *II = Attr->getBehaviorKind();
+ StringRef IdentName = II->getName();
+ OverflowBehaviorType::OverflowBehaviorKind Kind;
+ if (IdentName == "wrap") {
+ Kind = OverflowBehaviorType::OverflowBehaviorKind::Wrap;
+ } else if (IdentName == "trap") {
+ Kind = OverflowBehaviorType::OverflowBehaviorKind::Trap;
+ } else {
+ return Underlying;
+ }
+
+ return getOverflowBehaviorType(Kind, Underlying);
+}
+
+QualType ASTContext::getOverflowBehaviorType(
+ OverflowBehaviorType::OverflowBehaviorKind Kind,
+ QualType Underlying) const {
+ llvm::FoldingSetNodeID ID;
+ OverflowBehaviorType::Profile(ID, Underlying, Kind);
+ void *InsertPos = nullptr;
+
+ if (OverflowBehaviorType *OBT =
+ OverflowBehaviorTypes.FindNodeOrInsertPos(ID, InsertPos)) {
+ return QualType(OBT, 0);
+ }
+
+ QualType Canonical;
+ if (!Underlying.isCanonical() || Underlying.hasLocalQualifiers()) {
+ SplitQualType canonSplit = getCanonicalType(Underlying).split();
+ Canonical = getOverflowBehaviorType(Kind, QualType(canonSplit.Ty, 0));
+ Canonical = getQualifiedType(Canonical, canonSplit.Quals);
+ assert(!OverflowBehaviorTypes.FindNodeOrInsertPos(ID, InsertPos) &&
+ "Shouldn't be in the map");
+ }
+
+ OverflowBehaviorType *Ty = new (*this, alignof(OverflowBehaviorType))
+ OverflowBehaviorType(Canonical, Underlying, Kind);
+
+ Types.push_back(Ty);
+ OverflowBehaviorTypes.InsertNode(Ty, InsertPos);
+ return QualType(Ty, 0);
+}
+
QualType ASTContext::getHLSLAttributedResourceType(
QualType Wrapped, QualType Contained,
const HLSLAttributedResourceType::Attributes &Attrs) {
@@ -8099,6 +8199,9 @@ unsigned ASTContext::getIntegerRank(const Type *T) const {
if (const auto *EIT = dyn_cast<BitIntType>(T))
return 0 + (EIT->getNumBits() << 3);
+ if (const auto *OBT = dyn_cast<OverflowBehaviorType>(T))
+ return getIntegerRank(OBT->getUnderlyingType().getTypePtr());
+
switch (cast<BuiltinType>(T)->getKind()) {
default: llvm_unreachable("getIntegerRank(): not a built-in integer");
case BuiltinType::Bool:
@@ -8215,6 +8318,14 @@ QualType ASTContext::getPromotedIntegerType(QualType Promotable) const {
if (const auto *ED = Promotable->getAsEnumDecl())
return ED->getPromotionType();
+ // OverflowBehaviorTypes promote their underlying type and preserve OBT
+ // qualifier.
+ if (const auto *OBT = Promotable->getAs<OverflowBehaviorType>()) {
+ QualType PromotedUnderlying =
+ getPromotedIntegerType(OBT->getUnderlyingType());
+ return getOverflowBehaviorType(OBT->getBehaviorKind(), PromotedUnderlying);
+ }
+
if (const auto *BT = Promotable->getAs<BuiltinType>()) {
// C++ [conv.prom]: A prvalue of type char16_t, char32_t, or wchar_t
// (3.9.1) can be converted to a prvalue of the first of the following
@@ -9578,6 +9689,7 @@ void ASTContext::getObjCEncodingForTypeImpl(QualType T, std::string &S,
case Type::HLSLAttributedResource:
case Type::HLSLInlineSpirv:
+ case Type::OverflowBehavior:
llvm_unreachable("unexpected type");
case Type::ArrayParameter:
@@ -10530,6 +10642,37 @@ bool ASTContext::areCompatibleVectorTypes(QualType FirstVec,
return false;
}
+bool ASTContext::areCompatibleOverflowBehaviorTypes(QualType LHS,
+ QualType RHS) {
+ auto Result = checkOBTAssignmentCompatibility(LHS, RHS);
+ return Result != OBTAssignResult::IncompatibleKinds;
+}
+
+ASTContext::OBTAssignResult
+ASTContext::checkOBTAssignmentCompatibility(QualType LHS, QualType RHS) {
+ const auto *LHSOBT = LHS->getAs<OverflowBehaviorType>();
+ const auto *RHSOBT = RHS->getAs<OverflowBehaviorType>();
+
+ if (!LHSOBT && !RHSOBT)
+ return OBTAssignResult::Compatible;
+
+ if (LHSOBT && RHSOBT) {
+ if (LHSOBT->getBehaviorKind() != RHSOBT->getBehaviorKind())
+ return OBTAssignResult::IncompatibleKinds;
+ return OBTAssignResult::Compatible;
+ }
+
+ QualType LHSUnderlying = LHSOBT ? LHSOBT->desugar() : LHS;
+ QualType RHSUnderlying = RHSOBT ? RHSOBT->desugar() : RHS;
+
+ if (RHSOBT && !LHSOBT) {
+ if (LHSUnderlying->isIntegerType() && RHSUnderlying->isIntegerType())
+ return OBTAssignResult::Discards;
+ }
+
+ return OBTAssignResult::NotApplicable;
+}
+
/// getRVVTypeSize - Return RVV vector register size.
static uint64_t getRVVTypeSize(ASTContext &Context, const BuiltinType *Ty) {
assert(Ty->isRVVVLSBuiltinType() && "Invalid RVV Type");
@@ -11598,6 +11741,48 @@ QualType ASTContext::mergeTagDefinitions(QualType LHS, QualType RHS) {
return Ctx.IsEquivalent(LHS, RHS) ? LHS : QualType{};
}
+std::optional<QualType> ASTContext::tryMergeOverflowBehaviorTypes(
+ QualType LHS, QualType RHS, bool OfBlockPointer, bool Unqualified,
+ bool BlockReturnType, bool IsConditionalOperator) {
+ const auto *LHSOBT = LHS->getAs<OverflowBehaviorType>();
+ const auto *RHSOBT = RHS->getAs<OverflowBehaviorType>();
+
+ if (!LHSOBT && !RHSOBT)
+ return std::nullopt;
+
+ if (LHSOBT) {
+ if (RHSOBT) {
+ if (LHSOBT->getBehaviorKind() != RHSOBT->getBehaviorKind())
+ return QualType();
+
+ QualType MergedUnderlying = mergeTypes(
+ LHSOBT->getUnderlyingType(), RHSOBT->getUnderlyingType(),
+ OfBlockPointer, Unqualified, BlockReturnType, IsConditionalOperator);
+
+ if (MergedUnderlying.isNull())
+ return QualType();
+
+ if (getCanonicalType(LHSOBT) == getCanonicalType(RHSOBT)) {
+ if (LHSOBT->getUnderlyingType() == RHSOBT->getUnderlyingType())
+ return getCommonSugaredType(LHS, RHS);
+ return getOverflowBehaviorType(
+ LHSOBT->getBehaviorKind(),
+ getCanonicalType(LHSOBT->getUnderlyingType()));
+ }
+
+ // For different underlying types that successfully merge, wrap the
+ // merged underlying type with the common overflow behavior
+ return getOverflowBehaviorType(LHSOBT->getBehaviorKind(),
+ MergedUnderlying);
+ }
+ return mergeTypes(LHSOBT->getUnderlyingType(), RHS, OfBlockPointer,
+ Unqualified, BlockReturnType, IsConditionalOperator);
+ }
+
+ return mergeTypes(LHS, RHSOBT->getUnderlyingType(), OfBlockPointer,
+ Unqualified, BlockReturnType, IsConditionalOperator);
+}
+
QualType ASTContext::mergeTypes(QualType LHS, QualType RHS, bool OfBlockPointer,
bool Unqualified, bool BlockReturnType,
bool IsConditionalOperator) {
@@ -11618,6 +11803,11 @@ QualType ASTContext::mergeTypes(QualType LHS, QualType RHS, bool OfBlockPointer,
if (LHSRefTy || RHSRefTy)
return {};
+ if (std::optional<QualType> MergedOBT =
+ tryMergeOverflowBehaviorTypes(LHS, RHS, OfBlockPointer, Unqualified,
+ BlockReturnType, IsConditionalOperator))
+ return *MergedOBT;
+
if (Unqualified) {
LHS = LHS.getUnqualifiedType();
RHS = RHS.getUnqualifiedType();
@@ -11740,6 +11930,7 @@ QualType ASTContext::mergeTypes(QualType LHS, QualType RHS, bool OfBlockPointer,
case Type::VariableArray:
case Type::FunctionProto:
case Type::ExtVector:
+ case Type::OverflowBehavior:
llvm_unreachable("Types are eliminated above");
case Type::Pointer:
@@ -13795,23 +13986,30 @@ static QualType getCommonElementType(const ASTContext &Ctx, const T *X,
return Ctx.getCommonSugaredType(X->getElementType(), Y->getElementType());
}
-template <class T>
-static QualType getCommonArrayElementType(const ASTContext &Ctx, const T *X,
- Qualifiers &QX, const T *Y,
- Qualifiers &QY) {
- QualType EX = X->getElementType(), EY = Y->getElementType();
- QualType R = Ctx.getCommonSugaredType(EX, EY,
+static QualType getCommonTypeWithQualifierLifting(const ASTContext &Ctx,
+ QualType X, QualType Y,
+ Qualifiers &QX,
+ Qualifiers &QY) {
+ QualType R = Ctx.getCommonSugaredType(X, Y,
/*Unqualified=*/true);
// Qualifiers common to both element types.
Qualifiers RQ = R.getQualifiers();
// For each side, move to the top level any qualifiers which are not common to
// both element types. The caller must assume top level qualifiers might
// be different, even if they are the same type, and can be treated as sugar.
- QX += EX.getQualifiers() - RQ;
- QY += EY.getQualifiers() - RQ;
+ QX += X.getQualifiers() - RQ;
+ QY += Y.getQualifiers() - RQ;
return R;
}
+template <class T>
+static QualType getCommonArrayElementType(const ASTContext &Ctx, const T *X,
+ Qualifiers &QX, const T *Y,
+ Qualifiers &QY) {
+ return getCommonTypeWithQualifierLifting(Ctx, X->getElementType(),
+ Y->getElementType(), QX, QY);
+}
+
template <class T>
static QualType getCommonPointeeType(const ASTContext &Ctx, const T *X,
const T *Y) {
@@ -14212,6 +14410,15 @@ static QualType getCommonNonSugarTypeNode(const ASTContext &Ctx, const Type *X,
getCommonTypeKeyword(NX, NY, /*IsSame=*/true),
getCommonQualifier(Ctx, NX, NY, /*IsSame=*/true), NX->getIdentifier());
}
+ case Type::OverflowBehavior: {
+ const auto *NX = cast<OverflowBehaviorType>(X),
+ *NY = cast<OverflowBehaviorType>(Y);
+ assert(NX->getBehaviorKind() == NY->getBehaviorKind());
+ return Ctx.getOverflowBehaviorType(
+ NX->getBehaviorKind(),
+ getCommonTypeWithQualifierLifting(Ctx, NX->getUnderlyingType(),
+ NY->getUnderlyingType(), QX, QY));
+ }
case Type::UnaryTransform: {
const auto *TX = cast<UnaryTransformType>(X),
*TY = cast<UnaryTransformType>(Y);
@@ -14285,6 +14492,7 @@ static QualType getCommonSugarTypeNode(const ASTContext &Ctx, const Type *X,
CANONICAL_TYPE(ObjCInterface)
CANONICAL_TYPE(ObjCObject)
CANONICAL_TYPE(ObjCObjectPointer)
+ CANONICAL_TYPE(OverflowBehavior)
CANONICAL_TYPE(Pipe)
CANONICAL_TYPE(Pointer)
CANONICAL_TYPE(Record)
@@ -14545,7 +14753,8 @@ QualType ASTContext::getCommonSugaredType(QualType X, QualType Y,
// The desired behaviour is the same as for the 'Unqualified' case here:
// treat the redundant qualifiers as sugar, remove the ones which are not
// common to both sides.
- bool KeepCommonQualifiers = Unqualified || isa<ArrayType>(SX.Ty);
+ bool KeepCommonQualifiers =
+ Unqualified || isa<ArrayType, OverflowBehaviorType>(SX.Ty);
if (SX.Ty != SY.Ty) {
// The canonical nodes differ. Build a common canonical node out of the two,
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index c1441744c8578..d20728d2c74ba 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -2009,6 +2009,18 @@ ExpectedType clang::ASTNodeImporter::VisitBTFTagAttributedType(
ToWrappedType);
}
+ExpectedType clang::ASTNodeImporter::VisitOverflowBehaviorType(
+ const clang::OverflowBehaviorType *T) {
+ Error Err = Error::success();
+ OverflowBehaviorType::OverflowBehaviorKind ToKind = T->getBehaviorKind();
+ QualType ToUnderlyingType = importChecked(Err, T->getUnderlyingType());
+ if (Err)
+ return std::move(Err);
+
+ return Importer.getToContext().getOverflowBehaviorType(ToKind,
+ ToUnderlyingType);
+}
+
ExpectedType clang::ASTNodeImporter::VisitHLSLAttributedResourceType(
const clang::HLSLAttributedResourceType *T) {
Error Err = Error::success();
diff --git a/clang/lib/AST/ASTStructuralEquivalence.cpp b/clang/lib/AST/ASTStructuralEquivalence.cpp
index da64c92221837..f3458ab1dd911 100644
--- a/clang/lib/AST/ASTStructuralEquivalence.cpp
+++ b/clang/lib/AST/ASTStructuralEquivalence.cpp
@@ -1155,6 +1155,13 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
return false;
break;
+ case Type::OverflowBehavior:
+ if (!IsStructurallyEquivalent(
+ Context, cast<OverflowBehaviorType>(T1)->getUnderlyingType(),
+ cast<OverflowBehaviorType>(T2)->getUnderlyingType()))
+ return false;
+ break;
+
case Type::HLSLAttributedResource:
if (!IsStructurallyEquivalent(
Context, cast<HLSLAttributedResourceType>(T1)->getWrappedType(),
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 3eedf0d6795d8..c2dba77af056d 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -2891,7 +2891,7 @@ static bool CheckedIntArithmetic(EvalInfo &Info, const Expr *E,
APSInt Value(Op(LHS.extend(BitWidth), RHS.extend(BitWidth)), false);
Result = Value.trunc(LHS.getBitWidth());
- if (Result.extend(BitWidth) != Value) {
+ if (Result.extend(BitWidth) != Value && !E->getType().isWrapType()) {
if (Info.checkingForUndefinedBehavior())
Info.Ctx.getDiagnostics().Report(E->getExprLoc(),
diag::warn_integer_constant_overflow)
@@ -15057,6 +15057,7 @@ GCCTypeClass EvaluateBuiltinClassifyType(QualType T,
case Type::Pipe:
case Type::HLSLAttributedResource:
case Type::HLSLInlineSpirv:
+ case Type::OverflowBehavior:
// Classify all other types that don't fit into the regular
// classification the same way.
return GCCTypeClass::None;
@@ -18199,7 +18200,8 @@ bool IntExprEvaluator::VisitUnaryOperator(const UnaryOperator *E) {
return false;
if (!Result.isInt()) return Error(E);
const APSInt &Value = Result.getInt();
- if (Value.isSigned() && Value.isMinSignedValue() && E->canOverflow()) {
+ if (Value.isSigned() && Value.isMinSignedValue() && E->canOverflow() &&
+ !E->getType().isWrapType()) {
if (Info.checkingForUndefinedBehavior())
Info.Ctx.getDiagnostics().Report(E->getExprLoc(),
diag::warn_integer_constant_overflow)
diff --git a/clang/lib/AST/FormatString.cpp b/clang/lib/AST/FormatString.cpp
index d4cb89b43ae87..5f48e759b833b 100644
--- a/clang/lib/AST/FormatString.cpp
+++ b/clang/lib/AST/FormatString.cpp
@@ -405,6 +405,9 @@ ArgType::matchesType(ASTContext &C, QualType argTy) const {
argTy = PT->getPointeeType();
}
+ if (const auto *OBT = argTy->getAs<OverflowBehaviorType>())
+ argTy = OBT->getUnderlyingType();
+
switch (K) {
case InvalidTy:
llvm_unreachable("ArgType must be valid");
diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp
index 5572e0a7ae59c..dbc49843dd001 100644
--- a/clang/lib/AST/ItaniumMangle.cpp
+++ b/clang/lib/AST/ItaniumMangle.cpp
@@ -2411,6 +2411,7 @@ bool CXXNameMangler::mangleUnresolvedTypeOrSimpleId(QualType Ty,
case Type::Paren:
case Type::Attributed:
case Type::BTFTagAttributed:
+ case Type::OverflowBehavior:
case Type::HLSLAttributedResource:
case Type::HLSLInlineSpirv:
case Type::Auto:
@@ -4581,6 +4582,17 @@ void CXXNameMangler::mangleType(const PipeType *T) {
Out << "8ocl_pipe";
}
+void CXXNameMangler::mangleType(const OverflowBehaviorType *T) {
+ // Vender-extended type mangling for OverflowBehaviorType
+ // <type> ::= U <behavior> <underlying_type>
+ if (T->isWrapKind()) {
+ Out << "U8ObtWrap_";
+ } else {
+ Out << "U8ObtTrap_";
+ }
+ mangleType(T->getUnderlyingType());
+}
+
void CXXNameMangler::mangleType(const BitIntType *T) {
// 5.1.5.2 Builtin types
// <type> ::= DB <number | instantiation-dependent expression> _
diff --git a/clang/lib/AST/MicrosoftMangle.cpp b/clang/lib/AST/MicrosoftMangle.cpp
index f1baf9f49384b..676d874db821e 100644
--- a/clang/lib/AST/MicrosoftMangle.cpp
+++ b/clang/lib/AST/MicrosoftMangle.cpp
@@ -3785,6 +3785,24 @@ void MicrosoftCXXNameMangler::mangleType(const HLSLInlineSpirvType *T,
llvm_unreachable("HLSL uses Itanium name mangling");
}
+void MicrosoftCXXNameMangler::mangleType(const OverflowBehaviorType *T,
+ Qualifiers, SourceRange Range) {
+ QualType UnderlyingType = T->getUnderlyingType();
+
+ llvm::SmallString<64> TemplateMangling;
+ llvm::raw_svector_ostream Stream(TemplateMangling);
+ MicrosoftCXXNameMangler Extra(Context, Stream);
+ Stream << "?$";
+ if (T->isWrapKind()) {
+ Extra.mangleSourceName("ObtWrap_");
+ } else {
+ Extra.mangleSourceName("ObtTrap_");
+ }
+ Extra.mangleType(UnderlyingType, Range, QMM_Escape);
+
+ mangleArtificialTagType(TagTypeKind::Struct, TemplateMangling, {"__clang"});
+}
+
// <this-adjustment> ::= <no-adjustment> | <static-adjustment> |
// <virtual-adjustment>
// <no-adjustment> ::= A # private near
diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index 4548af17e37f2..2bbd0089efe9e 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -1153,6 +1153,18 @@ struct SimpleTransformVisitor : public TypeVisitor<Derived, QualType> {
T->getNumColumns());
}
+ QualType VisitOverflowBehaviorType(const OverflowBehaviorType *T) {
+ QualType UnderlyingType = recurse(T->getUnderlyingType());
+ if (UnderlyingType.isNull())
+ return {};
+
+ if (UnderlyingType.getAsOpaquePtr() ==
+ T->getUnderlyingType().getAsOpaquePtr())
+ return QualType(T, 0);
+
+ return Ctx.getOverflowBehaviorType(T->getBehaviorKind(), UnderlyingType);
+ }
+
QualType VisitFunctionNoProtoType(const FunctionNoProtoType *T) {
QualType returnType = recurse(T->getReturnType());
if (returnType.isNull())
@@ -2042,6 +2054,10 @@ class GetContainedDeducedTypeVisitor
return Visit(T->getUnderlyingType());
}
+ Type *VisitOverflowBehaviorType(const OverflowBehaviorType *T) {
+ return Visit(T->getUnderlyingType());
+ }
+
Type *VisitAdjustedType(const AdjustedType *T) {
return Visit(T->getOriginalType());
}
@@ -2105,10 +2121,15 @@ bool Type::isIntegralType(const ASTContext &Ctx) const {
return BT->isInteger();
// Complete enum types are integral in C.
- if (!Ctx.getLangOpts().CPlusPlus)
+ if (!Ctx.getLangOpts().CPlusPlus) {
if (const auto *ET = dyn_cast<EnumType>(CanonicalType))
return IsEnumDeclComplete(ET->getDecl());
+ if (const OverflowBehaviorType *OBT =
+ dyn_cast<OverflowBehaviorType>(CanonicalType))
+ return OBT->getUnderlyingType()->isIntegralOrEnumerationType();
+ }
+
return isBitIntType();
}
@@ -2116,6 +2137,9 @@ bool Type::isIntegralOrUnscopedEnumerationType() const {
if (const auto *BT = dyn_cast<BuiltinType>(CanonicalType))
return BT->isInteger();
+ if (const auto *OBT = dyn_cast<OverflowBehaviorType>(CanonicalType))
+ return OBT->getUnderlyingType()->isIntegerType();
+
if (isBitIntType())
return true;
@@ -2219,6 +2243,9 @@ bool Type::isSignedIntegerType() const {
if (const auto *IT = dyn_cast<DependentBitIntType>(CanonicalType))
return IT->isSigned();
+ if (const auto *OBT = dyn_cast<OverflowBehaviorType>(CanonicalType))
+ return OBT->getUnderlyingType()->isSignedIntegerType();
+
return false;
}
@@ -2237,6 +2264,9 @@ bool Type::isSignedIntegerOrEnumerationType() const {
if (const auto *IT = dyn_cast<DependentBitIntType>(CanonicalType))
return IT->isSigned();
+ if (const auto *OBT = dyn_cast<OverflowBehaviorType>(CanonicalType))
+ return OBT->getUnderlyingType()->isSignedIntegerOrEnumerationType();
+
return false;
}
@@ -2267,6 +2297,9 @@ bool Type::isUnsignedIntegerType() const {
if (const auto *IT = dyn_cast<DependentBitIntType>(CanonicalType))
return IT->isUnsigned();
+ if (const auto *OBT = dyn_cast<OverflowBehaviorType>(CanonicalType))
+ return OBT->getUnderlyingType()->isUnsignedIntegerType();
+
return false;
}
@@ -2285,6 +2318,9 @@ bool Type::isUnsignedIntegerOrEnumerationType() const {
if (const auto *IT = dyn_cast<DependentBitIntType>(CanonicalType))
return IT->isUnsigned();
+ if (const auto *OBT = dyn_cast<OverflowBehaviorType>(CanonicalType))
+ return OBT->getUnderlyingType()->isUnsignedIntegerOrEnumerationType();
+
return false;
}
@@ -2348,6 +2384,11 @@ bool Type::isArithmeticType() const {
const auto *ED = ET->getDecl();
return !ED->isScoped() && ED->getDefinitionOrSelf()->isComplete();
}
+
+ if (isOverflowBehaviorType() &&
+ getAs<OverflowBehaviorType>()->getUnderlyingType()->isArithmeticType())
+ return true;
+
return isa<ComplexType>(CanonicalType) || isBitIntType();
}
@@ -2394,6 +2435,8 @@ Type::ScalarTypeKind Type::getScalarTypeKind() const {
return STK_IntegralComplex;
} else if (isBitIntType()) {
return STK_Integral;
+ } else if (isa<OverflowBehaviorType>(T)) {
+ return STK_Integral;
}
llvm_unreachable("unknown scalar type");
@@ -2739,6 +2782,7 @@ bool QualType::isCXX98PODType(const ASTContext &Context) const {
case Type::Vector:
case Type::ExtVector:
case Type::BitInt:
+ case Type::OverflowBehavior:
return true;
case Type::Enum:
@@ -2949,6 +2993,22 @@ bool QualType::isWebAssemblyFuncrefType() const {
getAddressSpace() == LangAS::wasm_funcref;
}
+bool QualType::isWrapType() const {
+ if (const auto *OBT = getCanonicalType()->getAs<OverflowBehaviorType>())
+ return OBT->getBehaviorKind() ==
+ OverflowBehaviorType::OverflowBehaviorKind::Wrap;
+
+ return false;
+}
+
+bool QualType::isTrapType() const {
+ if (const auto *OBT = getCanonicalType()->getAs<OverflowBehaviorType>())
+ return OBT->getBehaviorKind() ==
+ OverflowBehaviorType::OverflowBehaviorKind::Trap;
+
+ return false;
+}
+
QualType::PrimitiveDefaultInitializeKind
QualType::isNonTrivialToPrimitiveDefaultInitialize() const {
if (const auto *RD =
@@ -3047,6 +3107,9 @@ bool Type::isLiteralType(const ASTContext &Ctx) const {
if (const auto *AT = BaseTy->getAs<AtomicType>())
return AT->getValueType()->isLiteralType(Ctx);
+ if (const auto *OBT = BaseTy->getAs<OverflowBehaviorType>())
+ return OBT->getUnderlyingType()->isLiteralType(Ctx);
+
// If this type hasn't been deduced yet, then conservatively assume that
// it'll work out to be a literal type.
if (isa<AutoType>(BaseTy->getCanonicalTypeInternal()))
@@ -3977,6 +4040,12 @@ void TypeCoupledDeclRefInfo::setFromOpaqueValue(void *V) {
Data.setFromOpaqueValue(V);
}
+OverflowBehaviorType::OverflowBehaviorType(
+ QualType Canon, QualType Underlying,
+ OverflowBehaviorType::OverflowBehaviorKind Kind)
+ : Type(OverflowBehavior, Canon, Underlying->getDependence()),
+ UnderlyingType(Underlying), BehaviorKind(Kind) {}
+
BoundsAttributedType::BoundsAttributedType(TypeClass TC, QualType Wrapped,
QualType Canon)
: Type(TC, Canon, Wrapped->getDependence()), WrappedTy(Wrapped) {}
@@ -4882,6 +4951,8 @@ static CachedProperties computeCachedProperties(const Type *T) {
return Cache::get(cast<HLSLAttributedResourceType>(T)->getWrappedType());
case Type::HLSLInlineSpirv:
return CachedProperties(Linkage::External, false);
+ case Type::OverflowBehavior:
+ return Cache::get(cast<OverflowBehaviorType>(T)->getUnderlyingType());
}
llvm_unreachable("unhandled type class");
@@ -4977,6 +5048,9 @@ LinkageInfo LinkageComputer::computeTypeLinkageInfo(const Type *T) {
return computeTypeLinkageInfo(cast<AtomicType>(T)->getValueType());
case Type::Pipe:
return computeTypeLinkageInfo(cast<PipeType>(T)->getElementType());
+ case Type::OverflowBehavior:
+ return computeTypeLinkageInfo(
+ cast<OverflowBehaviorType>(T)->getUnderlyingType());
case Type::HLSLAttributedResource:
return computeTypeLinkageInfo(cast<HLSLAttributedResourceType>(T)
->getContainedType()
@@ -5171,6 +5245,7 @@ bool Type::canHaveNullability(bool ResultIfUnknown) const {
case Type::ArrayParameter:
case Type::HLSLAttributedResource:
case Type::HLSLInlineSpirv:
+ case Type::OverflowBehavior:
return false;
}
llvm_unreachable("bad type kind!");
diff --git a/clang/lib/AST/TypeLoc.cpp b/clang/lib/AST/TypeLoc.cpp
index f54ccf0932bc7..53edfdb65a4d5 100644
--- a/clang/lib/AST/TypeLoc.cpp
+++ b/clang/lib/AST/TypeLoc.cpp
@@ -598,6 +598,10 @@ SourceRange BTFTagAttributedTypeLoc::getLocalSourceRange() const {
return getAttr() ? getAttr()->getRange() : SourceRange();
}
+SourceRange OverflowBehaviorTypeLoc::getLocalSourceRange() const {
+ return SourceRange();
+}
+
void TypeOfTypeLoc::initializeLocal(ASTContext &Context,
SourceLocation Loc) {
TypeofLikeTypeLoc<TypeOfTypeLoc, TypeOfType, TypeOfTypeLocInfo>
@@ -856,6 +860,10 @@ namespace {
return Visit(T.getWrappedLoc());
}
+ TypeLoc VisitOverflowBehaviorTypeLoc(OverflowBehaviorTypeLoc T) {
+ return Visit(T.getWrappedLoc());
+ }
+
TypeLoc
VisitHLSLAttributedResourceTypeLoc(HLSLAttributedResourceTypeLoc T) {
return Visit(T.getWrappedLoc());
diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp
index d2881d5ac518a..22b4ddc9eedc8 100644
--- a/clang/lib/AST/TypePrinter.cpp
+++ b/clang/lib/AST/TypePrinter.cpp
@@ -286,6 +286,7 @@ bool TypePrinter::canPrefixQualifiers(const Type *T,
case Type::PackExpansion:
case Type::SubstTemplateTypeParm:
case Type::MacroQualified:
+ case Type::OverflowBehavior:
case Type::CountAttributed:
CanPrefixQualifiers = false;
break;
@@ -2060,6 +2061,7 @@ void TypePrinter::printAttributedAfter(const AttributedType *T,
case attr::PreserveAll:
case attr::PreserveMost:
case attr::PreserveNone:
+ case attr::OverflowBehavior:
llvm_unreachable("This attribute should have been handled already");
case attr::NSReturnsRetained:
@@ -2137,6 +2139,24 @@ void TypePrinter::printBTFTagAttributedAfter(const BTFTagAttributedType *T,
printAfter(T->getWrappedType(), OS);
}
+void TypePrinter::printOverflowBehaviorBefore(const OverflowBehaviorType *T,
+ raw_ostream &OS) {
+ switch (T->getBehaviorKind()) {
+ case clang::OverflowBehaviorType::OverflowBehaviorKind::Wrap:
+ OS << "__ob_wrap ";
+ break;
+ case clang::OverflowBehaviorType::OverflowBehaviorKind::Trap:
+ OS << "__ob_trap ";
+ break;
+ }
+ printBefore(T->getUnderlyingType(), OS);
+}
+
+void TypePrinter::printOverflowBehaviorAfter(const OverflowBehaviorType *T,
+ raw_ostream &OS) {
+ printAfter(T->getUnderlyingType(), OS);
+}
+
void TypePrinter::printHLSLAttributedResourceBefore(
const HLSLAttributedResourceType *T, raw_ostream &OS) {
printBefore(T->getWrappedType(), OS);
diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp
index c50f372c1f331..e8b3a77c31dcd 100644
--- a/clang/lib/CodeGen/CGDebugInfo.cpp
+++ b/clang/lib/CodeGen/CGDebugInfo.cpp
@@ -1188,6 +1188,11 @@ llvm::DIType *CGDebugInfo::CreateType(const BitIntType *Ty) {
Ty->getNumBits());
}
+llvm::DIType *CGDebugInfo::CreateType(const OverflowBehaviorType *Ty,
+ llvm::DIFile *U) {
+ return getOrCreateType(Ty->getUnderlyingType(), U);
+}
+
llvm::DIType *CGDebugInfo::CreateType(const ComplexType *Ty) {
// Bit size and offset of the type.
llvm::dwarf::TypeKind Encoding = llvm::dwarf::DW_ATE_complex_float;
@@ -4185,6 +4190,8 @@ llvm::DIType *CGDebugInfo::CreateTypeNode(QualType Ty, llvm::DIFile *Unit) {
case Type::BitInt:
return CreateType(cast<BitIntType>(Ty));
+ case Type::OverflowBehavior:
+ return CreateType(cast<OverflowBehaviorType>(Ty), Unit);
case Type::Pipe:
return CreateType(cast<PipeType>(Ty), Unit);
diff --git a/clang/lib/CodeGen/CGDebugInfo.h b/clang/lib/CodeGen/CGDebugInfo.h
index 2378bdd780b3b..c0bc3e6d54334 100644
--- a/clang/lib/CodeGen/CGDebugInfo.h
+++ b/clang/lib/CodeGen/CGDebugInfo.h
@@ -198,6 +198,7 @@ class CGDebugInfo {
llvm::DIType *CreateType(const BuiltinType *Ty);
llvm::DIType *CreateType(const ComplexType *Ty);
llvm::DIType *CreateType(const BitIntType *Ty);
+ llvm::DIType *CreateType(const OverflowBehaviorType *Ty, llvm::DIFile *U);
llvm::DIType *CreateQualifiedType(QualType Ty, llvm::DIFile *Fg);
llvm::DIType *CreateQualifiedType(const FunctionProtoType *Ty,
llvm::DIFile *Fg);
diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp
index 714192db1b15c..f00a55aaaafae 100644
--- a/clang/lib/CodeGen/CGExprScalar.cpp
+++ b/clang/lib/CodeGen/CGExprScalar.cpp
@@ -192,8 +192,42 @@ static bool IsWidenedIntegerOp(const ASTContext &Ctx, const Expr *E) {
return getUnwidenedIntegerType(Ctx, E).has_value();
}
+/// Consider OverflowBehaviorType and language options to calculate the final
+/// overflow behavior for an expression. There are no language options for
+/// unsigned overflow semantics so there is nothing to consider there.
+static LangOptions::OverflowBehaviorKind
+getOverflowBehaviorConsideringType(const CodeGenFunction &CGF,
+ const QualType Ty) {
+ const OverflowBehaviorType *OBT = Ty->getAs<OverflowBehaviorType>();
+ /// FIXME: Having two enums named `OverflowBehaviorKind` is not ideal, these
+ /// should be unified into one coherent enum that supports both unsigned and
+ /// signed overflow behavior semantics.
+ if (OBT) {
+ switch (OBT->getBehaviorKind()) {
+ case OverflowBehaviorType::OverflowBehaviorKind::Wrap:
+ return LangOptions::OverflowBehaviorKind::OB_Wrap;
+ case OverflowBehaviorType::OverflowBehaviorKind::Trap:
+ return LangOptions::OverflowBehaviorKind::OB_Trap;
+ }
+ llvm_unreachable("Unknown OverflowBehaviorKind");
+ }
+
+ if (Ty->isUnsignedIntegerType()) {
+ return LangOptions::OverflowBehaviorKind::OB_Unset;
+ }
+
+ switch (CGF.getLangOpts().getSignedOverflowBehavior()) {
+ case LangOptions::SignedOverflowBehaviorTy::SOB_Defined:
+ return LangOptions::OverflowBehaviorKind::OB_SignedAndDefined;
+ case LangOptions::SignedOverflowBehaviorTy::SOB_Undefined:
+ return LangOptions::OverflowBehaviorKind::OB_Unset;
+ case LangOptions::SignedOverflowBehaviorTy::SOB_Trapping:
+ return LangOptions::OverflowBehaviorKind::OB_Trap;
+ }
+}
+
/// Check if we can skip the overflow check for \p Op.
-static bool CanElideOverflowCheck(const ASTContext &Ctx, const BinOpInfo &Op) {
+static bool CanElideOverflowCheck(ASTContext &Ctx, const BinOpInfo &Op) {
assert((isa<UnaryOperator>(Op.E) || isa<BinaryOperator>(Op.E)) &&
"Expected a unary or binary operator");
@@ -202,6 +236,19 @@ static bool CanElideOverflowCheck(const ASTContext &Ctx, const BinOpInfo &Op) {
if (!Op.mayHaveIntegerOverflow())
return true;
+ const UnaryOperator *UO = dyn_cast<UnaryOperator>(Op.E);
+ if (UO && Ctx.isUnaryOverflowPatternExcluded(UO))
+ return true;
+
+ const auto *BO = dyn_cast<BinaryOperator>(Op.E);
+ if (BO && BO->hasExcludedOverflowPattern())
+ return true;
+
+ if (Op.Ty.isWrapType())
+ return true;
+ if (Op.Ty.isTrapType())
+ return false;
+
if (Op.Ty->isSignedIntegerType() &&
Ctx.isTypeIgnoredBySanitizer(SanitizerKind::SignedIntegerOverflow,
Op.Ty)) {
@@ -214,24 +261,12 @@ static bool CanElideOverflowCheck(const ASTContext &Ctx, const BinOpInfo &Op) {
return true;
}
- const UnaryOperator *UO = dyn_cast<UnaryOperator>(Op.E);
-
- if (UO && UO->getOpcode() == UO_Minus &&
- Ctx.getLangOpts().isOverflowPatternExcluded(
- LangOptions::OverflowPatternExclusionKind::NegUnsignedConst) &&
- UO->isIntegerConstantExpr(Ctx))
- return true;
-
// If a unary op has a widened operand, the op cannot overflow.
if (UO)
return !UO->canOverflow();
// We usually don't need overflow checks for binops with widened operands.
// Multiplication with promoted unsigned operands is a special case.
- const auto *BO = cast<BinaryOperator>(Op.E);
- if (BO->hasExcludedOverflowPattern())
- return true;
-
auto OptionalLHSTy = getUnwidenedIntegerType(Ctx, BO->getLHS());
if (!OptionalLHSTy)
return false;
@@ -362,7 +397,8 @@ class ScalarExprEmitter
/// Emit a check that an [implicit] truncation of an integer does not
/// discard any bits. It is not UB, so we use the value after truncation.
void EmitIntegerTruncationCheck(Value *Src, QualType SrcType, Value *Dst,
- QualType DstType, SourceLocation Loc);
+ QualType DstType, SourceLocation Loc,
+ bool OBTrapInvolved = false);
/// Emit a check that an [implicit] conversion of an integer does not change
/// the sign of the value. It is not UB, so we use the value after conversion.
@@ -782,19 +818,28 @@ class ScalarExprEmitter
// Binary Operators.
Value *EmitMul(const BinOpInfo &Ops) {
- if (Ops.Ty->isSignedIntegerOrEnumerationType()) {
- switch (CGF.getLangOpts().getSignedOverflowBehavior()) {
- case LangOptions::SOB_Defined:
- if (!CGF.SanOpts.has(SanitizerKind::SignedIntegerOverflow))
+ if (Ops.Ty->isSignedIntegerOrEnumerationType() ||
+ Ops.Ty->isUnsignedIntegerType()) {
+ const bool isSigned = Ops.Ty->isSignedIntegerOrEnumerationType();
+ const bool hasSan =
+ isSigned ? CGF.SanOpts.has(SanitizerKind::SignedIntegerOverflow)
+ : CGF.SanOpts.has(SanitizerKind::UnsignedIntegerOverflow);
+ switch (getOverflowBehaviorConsideringType(CGF, Ops.Ty)) {
+ case LangOptions::OB_Wrap:
+ return Builder.CreateMul(Ops.LHS, Ops.RHS, "mul");
+ case LangOptions::OB_SignedAndDefined:
+ if (!hasSan)
return Builder.CreateMul(Ops.LHS, Ops.RHS, "mul");
[[fallthrough]];
- case LangOptions::SOB_Undefined:
- if (!CGF.SanOpts.has(SanitizerKind::SignedIntegerOverflow))
- return Builder.CreateNSWMul(Ops.LHS, Ops.RHS, "mul");
+ case LangOptions::OB_Unset:
+ if (!hasSan)
+ return isSigned ? Builder.CreateNSWMul(Ops.LHS, Ops.RHS, "mul")
+ : Builder.CreateMul(Ops.LHS, Ops.RHS, "mul");
[[fallthrough]];
- case LangOptions::SOB_Trapping:
+ case LangOptions::OB_Trap:
if (CanElideOverflowCheck(CGF.getContext(), Ops))
- return Builder.CreateNSWMul(Ops.LHS, Ops.RHS, "mul");
+ return isSigned ? Builder.CreateNSWMul(Ops.LHS, Ops.RHS, "mul")
+ : Builder.CreateMul(Ops.LHS, Ops.RHS, "mul");
return EmitOverflowCheckedBinOp(Ops);
}
}
@@ -816,11 +861,6 @@ class ScalarExprEmitter
return MB.CreateScalarMultiply(Ops.LHS, Ops.RHS);
}
- if (Ops.Ty->isUnsignedIntegerType() &&
- CGF.SanOpts.has(SanitizerKind::UnsignedIntegerOverflow) &&
- !CanElideOverflowCheck(CGF.getContext(), Ops))
- return EmitOverflowCheckedBinOp(Ops);
-
if (Ops.LHS->getType()->isFPOrFPVectorTy()) {
// Preserve the old values
CodeGenFunction::CGFPOptionsRAII FPOptsRAII(CGF, Ops.FPFeatures);
@@ -1117,8 +1157,10 @@ static bool PromotionIsPotentiallyEligibleForImplicitIntegerConversionCheck(
void ScalarExprEmitter::EmitIntegerTruncationCheck(Value *Src, QualType SrcType,
Value *Dst, QualType DstType,
- SourceLocation Loc) {
- if (!CGF.SanOpts.hasOneOf(SanitizerKind::ImplicitIntegerTruncation))
+ SourceLocation Loc,
+ bool OBTrapInvolved) {
+ if (!CGF.SanOpts.hasOneOf(SanitizerKind::ImplicitIntegerTruncation) &&
+ !OBTrapInvolved)
return;
// We only care about int->int conversions here.
@@ -1164,14 +1206,27 @@ void ScalarExprEmitter::EmitIntegerTruncationCheck(Value *Src, QualType SrcType,
}
// Do we care about this type of truncation?
- if (!CGF.SanOpts.has(Check.second.second))
+ if (!CGF.SanOpts.has(Check.second.second)) {
+ // Just emit a trap check if an __ob_trap was involved but appropriate
+ // sanitizer isn't enabled.
+ if (OBTrapInvolved)
+ CGF.EmitTrapCheck(Check.second.first, CheckHandler);
return;
+ }
SanitizerDebugLocation SanScope(&CGF, {Check.second.second}, CheckHandler);
// Does some SSCL ignore this type?
- if (CGF.getContext().isTypeIgnoredBySanitizer(
- SanitizerMask::bitPosToMask(Check.second.second), DstType))
+ const bool ignoredBySanitizer = CGF.getContext().isTypeIgnoredBySanitizer(
+ SanitizerMask::bitPosToMask(Check.second.second), DstType);
+
+ // Consider OverflowBehaviorTypes which override SSCL type entries for
+ // truncation sanitizers.
+ if (const auto *OBT = DstType->getAs<OverflowBehaviorType>()) {
+ if (OBT->isWrapKind())
+ return;
+ }
+ if (ignoredBySanitizer)
return;
llvm::Constant *StaticArgs[] = {
@@ -1743,9 +1798,23 @@ Value *ScalarExprEmitter::EmitScalarConversion(Value *Src, QualType SrcType,
}
}
- if (Opts.EmitImplicitIntegerTruncationChecks)
+ // Determine whether an overflow behavior of 'trap' has been specified for
+ // either the destination or the source types. If so, we can elide sanitizer
+ // capability checks as this overflow behavior kind is also capable of
+ // emitting traps without runtime sanitizer support.
+ // Also skip instrumentation if either source or destination has 'wrap'
+ // behavior - the user has explicitly indicated they accept wrapping semantics.
+ // Use non-canonical types to preserve OBT annotations.
+ const auto *DstOBT = NoncanonicalDstType->getAs<OverflowBehaviorType>();
+ const auto *SrcOBT = NoncanonicalSrcType->getAs<OverflowBehaviorType>();
+ bool OBTrapInvolved =
+ (DstOBT && DstOBT->isTrapKind()) || (SrcOBT && SrcOBT->isTrapKind());
+ bool OBWrapInvolved =
+ (DstOBT && DstOBT->isWrapKind()) || (SrcOBT && SrcOBT->isWrapKind());
+
+ if ((Opts.EmitImplicitIntegerTruncationChecks || OBTrapInvolved) && !OBWrapInvolved)
EmitIntegerTruncationCheck(Src, NoncanonicalSrcType, Res,
- NoncanonicalDstType, Loc);
+ NoncanonicalDstType, Loc, OBTrapInvolved);
if (Opts.EmitImplicitIntegerSignChangeChecks)
EmitIntegerSignChangeCheck(Src, NoncanonicalSrcType, Res,
@@ -3009,43 +3078,37 @@ llvm::Value *ScalarExprEmitter::EmitIncDecConsiderOverflowBehavior(
llvm::Value *Amount =
llvm::ConstantInt::get(InVal->getType(), IsInc ? 1 : -1, true);
StringRef Name = IsInc ? "inc" : "dec";
- switch (CGF.getLangOpts().getSignedOverflowBehavior()) {
- case LangOptions::SOB_Defined:
- if (!CGF.SanOpts.has(SanitizerKind::SignedIntegerOverflow))
+ QualType Ty = E->getType();
+ const bool isSigned = Ty->isSignedIntegerOrEnumerationType();
+ const bool hasSan =
+ isSigned ? CGF.SanOpts.has(SanitizerKind::SignedIntegerOverflow)
+ : CGF.SanOpts.has(SanitizerKind::UnsignedIntegerOverflow);
+
+ switch (getOverflowBehaviorConsideringType(CGF, Ty)) {
+ case LangOptions::OB_Wrap:
+ return Builder.CreateAdd(InVal, Amount, Name);
+ case LangOptions::OB_SignedAndDefined:
+ if (!hasSan)
return Builder.CreateAdd(InVal, Amount, Name);
[[fallthrough]];
- case LangOptions::SOB_Undefined:
- if (!CGF.SanOpts.has(SanitizerKind::SignedIntegerOverflow))
- return Builder.CreateNSWAdd(InVal, Amount, Name);
+ case LangOptions::OB_Unset:
+ if (!E->canOverflow())
+ return Builder.CreateAdd(InVal, Amount, Name);
+ if (!hasSan)
+ return isSigned ? Builder.CreateNSWAdd(InVal, Amount, Name)
+ : Builder.CreateAdd(InVal, Amount, Name);
[[fallthrough]];
- case LangOptions::SOB_Trapping:
+ case LangOptions::OB_Trap:
+ if (!Ty->getAs<OverflowBehaviorType>() && !E->canOverflow())
+ return Builder.CreateAdd(InVal, Amount, Name);
BinOpInfo Info = createBinOpInfoFromIncDec(
E, InVal, IsInc, E->getFPFeaturesInEffect(CGF.getLangOpts()));
- if (!E->canOverflow() || CanElideOverflowCheck(CGF.getContext(), Info))
- return Builder.CreateNSWAdd(InVal, Amount, Name);
+ if (CanElideOverflowCheck(CGF.getContext(), Info))
+ return isSigned ? Builder.CreateNSWAdd(InVal, Amount, Name)
+ : Builder.CreateAdd(InVal, Amount, Name);
return EmitOverflowCheckedBinOp(Info);
}
- llvm_unreachable("Unknown SignedOverflowBehaviorTy");
-}
-
-/// For the purposes of overflow pattern exclusion, does this match the
-/// "while(i--)" pattern?
-static bool matchesPostDecrInWhile(const UnaryOperator *UO, bool isInc,
- bool isPre, ASTContext &Ctx) {
- if (isInc || isPre)
- return false;
-
- // -fsanitize-undefined-ignore-overflow-pattern=unsigned-post-decr-while
- if (!Ctx.getLangOpts().isOverflowPatternExcluded(
- LangOptions::OverflowPatternExclusionKind::PostDecrInWhile))
- return false;
-
- // all Parents (usually just one) must be a WhileStmt
- for (const auto &Parent : Ctx.getParentMapContext().getParents(*UO))
- if (!Parent.get<WhileStmt>())
- return false;
-
- return true;
+ llvm_unreachable("Unknown OverflowBehaviorKind");
}
namespace {
@@ -3164,9 +3227,6 @@ ScalarExprEmitter::EmitScalarPrePostIncDec(const UnaryOperator *E, LValue LV,
QualType promotedType;
bool canPerformLossyDemotionCheck = false;
- bool excludeOverflowPattern =
- matchesPostDecrInWhile(E, isInc, isPre, CGF.getContext());
-
if (CGF.getContext().isPromotableIntegerType(type)) {
promotedType = CGF.getContext().getPromotedIntegerType(type);
assert(promotedType != type && "Shouldn't promote to the same type.");
@@ -3223,15 +3283,9 @@ ScalarExprEmitter::EmitScalarPrePostIncDec(const UnaryOperator *E, LValue LV,
// Note that signed integer inc/dec with width less than int can't
// overflow because of promotion rules; we're just eliding a few steps
// here.
- } else if (E->canOverflow() && type->isSignedIntegerOrEnumerationType()) {
+ } else if (type->isSignedIntegerOrEnumerationType() ||
+ type->isUnsignedIntegerType()) {
value = EmitIncDecConsiderOverflowBehavior(E, value, isInc);
- } else if (E->canOverflow() && type->isUnsignedIntegerType() &&
- CGF.SanOpts.has(SanitizerKind::UnsignedIntegerOverflow) &&
- !excludeOverflowPattern &&
- !CGF.getContext().isTypeIgnoredBySanitizer(
- SanitizerKind::UnsignedIntegerOverflow, E->getType())) {
- value = EmitOverflowCheckedBinOp(createBinOpInfoFromIncDec(
- E, value, isInc, E->getFPFeaturesInEffect(CGF.getLangOpts())));
} else {
llvm::Value *amt = llvm::ConstantInt::get(value->getType(), amount, true);
value = Builder.CreateAdd(value, amt, isInc ? "inc" : "dec");
@@ -4014,7 +4068,7 @@ void ScalarExprEmitter::EmitUndefinedBehaviorIntegerDivAndRemCheck(
if (CGF.SanOpts.has(SanitizerKind::SignedIntegerOverflow) &&
Ops.Ty->hasSignedIntegerRepresentation() &&
!IsWidenedIntegerOp(CGF.getContext(), BO->getLHS()) &&
- Ops.mayHaveIntegerOverflow()) {
+ Ops.mayHaveIntegerOverflow() && !Ops.Ty.isWrapType()) {
llvm::IntegerType *Ty = cast<llvm::IntegerType>(Zero->getType());
llvm::Value *IntMin =
@@ -4160,14 +4214,25 @@ Value *ScalarExprEmitter::EmitOverflowCheckedBinOp(const BinOpInfo &Ops) {
const std::string *handlerName =
&CGF.getLangOpts().OverflowHandler;
if (handlerName->empty()) {
- // If the signed-integer-overflow sanitizer is enabled, emit a call to its
- // runtime. Otherwise, this is a -ftrapv check, so just emit a trap.
- if (!isSigned || CGF.SanOpts.has(SanitizerKind::SignedIntegerOverflow)) {
- llvm::Value *NotOverflow = Builder.CreateNot(overflow);
- SanitizerKind::SanitizerOrdinal Ordinal =
- isSigned ? SanitizerKind::SO_SignedIntegerOverflow
- : SanitizerKind::SO_UnsignedIntegerOverflow;
- EmitBinOpCheck(std::make_pair(NotOverflow, Ordinal), Ops);
+ // If no -ftrapv handler has been specified, try to use sanitizer runtimes
+ // if available otherwise just emit a trap. It is possible for unsigned
+ // arithmetic to result in a trap due to the OverflowBehaviorType attribute
+ // which describes overflow behavior on a per-type basis.
+ if (isSigned) {
+ if (CGF.SanOpts.has(SanitizerKind::SignedIntegerOverflow)) {
+ llvm::Value *NotOf = Builder.CreateNot(overflow);
+ EmitBinOpCheck(
+ std::make_pair(NotOf, SanitizerKind::SO_SignedIntegerOverflow),
+ Ops);
+ } else
+ CGF.EmitTrapCheck(Builder.CreateNot(overflow), OverflowKind);
+ return result;
+ }
+ if (CGF.SanOpts.has(SanitizerKind::UnsignedIntegerOverflow)) {
+ llvm::Value *NotOf = Builder.CreateNot(overflow);
+ EmitBinOpCheck(
+ std::make_pair(NotOf, SanitizerKind::SO_UnsignedIntegerOverflow),
+ Ops);
} else
CGF.EmitTrapCheck(Builder.CreateNot(overflow), OverflowKind);
return result;
@@ -4491,19 +4556,28 @@ Value *ScalarExprEmitter::EmitAdd(const BinOpInfo &op) {
op.RHS->getType()->isPointerTy())
return emitPointerArithmetic(CGF, op, CodeGenFunction::NotSubtraction);
- if (op.Ty->isSignedIntegerOrEnumerationType()) {
- switch (CGF.getLangOpts().getSignedOverflowBehavior()) {
- case LangOptions::SOB_Defined:
- if (!CGF.SanOpts.has(SanitizerKind::SignedIntegerOverflow))
+ if (op.Ty->isSignedIntegerOrEnumerationType() ||
+ op.Ty->isUnsignedIntegerType()) {
+ const bool isSigned = op.Ty->isSignedIntegerOrEnumerationType();
+ const bool hasSan =
+ isSigned ? CGF.SanOpts.has(SanitizerKind::SignedIntegerOverflow)
+ : CGF.SanOpts.has(SanitizerKind::UnsignedIntegerOverflow);
+ switch (getOverflowBehaviorConsideringType(CGF, op.Ty)) {
+ case LangOptions::OB_Wrap:
+ return Builder.CreateAdd(op.LHS, op.RHS, "add");
+ case LangOptions::OB_SignedAndDefined:
+ if (!hasSan)
return Builder.CreateAdd(op.LHS, op.RHS, "add");
[[fallthrough]];
- case LangOptions::SOB_Undefined:
- if (!CGF.SanOpts.has(SanitizerKind::SignedIntegerOverflow))
- return Builder.CreateNSWAdd(op.LHS, op.RHS, "add");
+ case LangOptions::OB_Unset:
+ if (!hasSan)
+ return isSigned ? Builder.CreateNSWAdd(op.LHS, op.RHS, "add")
+ : Builder.CreateAdd(op.LHS, op.RHS, "add");
[[fallthrough]];
- case LangOptions::SOB_Trapping:
+ case LangOptions::OB_Trap:
if (CanElideOverflowCheck(CGF.getContext(), op))
- return Builder.CreateNSWAdd(op.LHS, op.RHS, "add");
+ return isSigned ? Builder.CreateNSWAdd(op.LHS, op.RHS, "add")
+ : Builder.CreateAdd(op.LHS, op.RHS, "add");
return EmitOverflowCheckedBinOp(op);
}
}
@@ -4522,11 +4596,6 @@ Value *ScalarExprEmitter::EmitAdd(const BinOpInfo &op) {
return MB.CreateAdd(op.LHS, op.RHS);
}
- if (op.Ty->isUnsignedIntegerType() &&
- CGF.SanOpts.has(SanitizerKind::UnsignedIntegerOverflow) &&
- !CanElideOverflowCheck(CGF.getContext(), op))
- return EmitOverflowCheckedBinOp(op);
-
if (op.LHS->getType()->isFPOrFPVectorTy()) {
CodeGenFunction::CGFPOptionsRAII FPOptsRAII(CGF, op.FPFeatures);
return Builder.CreateFAdd(op.LHS, op.RHS, "add");
@@ -4647,19 +4716,28 @@ Value *ScalarExprEmitter::EmitFixedPointBinOp(const BinOpInfo &op) {
Value *ScalarExprEmitter::EmitSub(const BinOpInfo &op) {
// The LHS is always a pointer if either side is.
if (!op.LHS->getType()->isPointerTy()) {
- if (op.Ty->isSignedIntegerOrEnumerationType()) {
- switch (CGF.getLangOpts().getSignedOverflowBehavior()) {
- case LangOptions::SOB_Defined:
- if (!CGF.SanOpts.has(SanitizerKind::SignedIntegerOverflow))
+ if (op.Ty->isSignedIntegerOrEnumerationType() ||
+ op.Ty->isUnsignedIntegerType()) {
+ const bool isSigned = op.Ty->isSignedIntegerOrEnumerationType();
+ const bool hasSan =
+ isSigned ? CGF.SanOpts.has(SanitizerKind::SignedIntegerOverflow)
+ : CGF.SanOpts.has(SanitizerKind::UnsignedIntegerOverflow);
+ switch (getOverflowBehaviorConsideringType(CGF, op.Ty)) {
+ case LangOptions::OB_Wrap:
+ return Builder.CreateSub(op.LHS, op.RHS, "sub");
+ case LangOptions::OB_SignedAndDefined:
+ if (!hasSan)
return Builder.CreateSub(op.LHS, op.RHS, "sub");
[[fallthrough]];
- case LangOptions::SOB_Undefined:
- if (!CGF.SanOpts.has(SanitizerKind::SignedIntegerOverflow))
- return Builder.CreateNSWSub(op.LHS, op.RHS, "sub");
+ case LangOptions::OB_Unset:
+ if (!hasSan)
+ return isSigned ? Builder.CreateNSWSub(op.LHS, op.RHS, "sub")
+ : Builder.CreateSub(op.LHS, op.RHS, "sub");
[[fallthrough]];
- case LangOptions::SOB_Trapping:
+ case LangOptions::OB_Trap:
if (CanElideOverflowCheck(CGF.getContext(), op))
- return Builder.CreateNSWSub(op.LHS, op.RHS, "sub");
+ return isSigned ? Builder.CreateNSWSub(op.LHS, op.RHS, "sub")
+ : Builder.CreateSub(op.LHS, op.RHS, "sub");
return EmitOverflowCheckedBinOp(op);
}
}
@@ -4678,11 +4756,6 @@ Value *ScalarExprEmitter::EmitSub(const BinOpInfo &op) {
return MB.CreateSub(op.LHS, op.RHS);
}
- if (op.Ty->isUnsignedIntegerType() &&
- CGF.SanOpts.has(SanitizerKind::UnsignedIntegerOverflow) &&
- !CanElideOverflowCheck(CGF.getContext(), op))
- return EmitOverflowCheckedBinOp(op);
-
if (op.LHS->getType()->isFPOrFPVectorTy()) {
CodeGenFunction::CGFPOptionsRAII FPOptsRAII(CGF, op.FPFeatures);
return Builder.CreateFSub(op.LHS, op.RHS, "sub");
diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp
index 64e594d09067b..ce3ffe7d3ec31 100644
--- a/clang/lib/CodeGen/CodeGenFunction.cpp
+++ b/clang/lib/CodeGen/CodeGenFunction.cpp
@@ -280,6 +280,7 @@ TypeEvaluationKind CodeGenFunction::getEvaluationKind(QualType type) {
case Type::BitInt:
case Type::HLSLAttributedResource:
case Type::HLSLInlineSpirv:
+ case Type::OverflowBehavior:
return TEK_Scalar;
// Complexes.
@@ -2574,6 +2575,7 @@ void CodeGenFunction::EmitVariablyModifiedType(QualType type) {
case Type::UnaryTransform:
case Type::Attributed:
case Type::BTFTagAttributed:
+ case Type::OverflowBehavior:
case Type::HLSLAttributedResource:
case Type::SubstTemplateTypeParm:
case Type::MacroQualified:
diff --git a/clang/lib/CodeGen/CodeGenTypes.cpp b/clang/lib/CodeGen/CodeGenTypes.cpp
index ea31195b7f92e..3f8f4a167f806 100644
--- a/clang/lib/CodeGen/CodeGenTypes.cpp
+++ b/clang/lib/CodeGen/CodeGenTypes.cpp
@@ -770,6 +770,10 @@ llvm::Type *CodeGenTypes::ConvertType(QualType T) {
case Type::HLSLInlineSpirv:
ResultType = CGM.getHLSLRuntime().convertHLSLSpecificType(Ty);
break;
+ case Type::OverflowBehavior:
+ ResultType =
+ ConvertType(dyn_cast<OverflowBehaviorType>(Ty)->getUnderlyingType());
+ break;
}
assert(ResultType && "Didn't convert a type?");
diff --git a/clang/lib/CodeGen/ItaniumCXXABI.cpp b/clang/lib/CodeGen/ItaniumCXXABI.cpp
index 65c47633bc5c4..dcfef4510f5ff 100644
--- a/clang/lib/CodeGen/ItaniumCXXABI.cpp
+++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp
@@ -3952,6 +3952,7 @@ void ItaniumRTTIBuilder::BuildVTablePointer(const Type *Ty,
case Type::Builtin:
case Type::BitInt:
+ case Type::OverflowBehavior:
// GCC treats vector and complex types as fundamental types.
case Type::Vector:
case Type::ExtVector:
@@ -4306,6 +4307,9 @@ llvm::Constant *ItaniumRTTIBuilder::BuildTypeInfo(
// No fields, at least for the moment.
break;
+ case Type::OverflowBehavior:
+ break;
+
case Type::HLSLAttributedResource:
case Type::HLSLInlineSpirv:
llvm_unreachable("HLSL doesn't support RTTI");
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index 0380568412e62..d9759229b3893 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -6269,6 +6269,9 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
Args.addOptInFlag(CmdArgs, options::OPT_ffixed_point,
options::OPT_fno_fixed_point);
+ Args.addOptInFlag(CmdArgs, options::OPT_foverflow_behavior_types,
+ options::OPT_fno_overflow_behavior_types);
+
if (Arg *A = Args.getLastArg(options::OPT_fcxx_abi_EQ))
A->render(Args, CmdArgs);
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 8688ccf41acb5..a7e9bce3c299a 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -4494,6 +4494,16 @@ void Parser::ParseDeclarationSpecifiers(
isInvalid = DS.SetTypeQual(DeclSpec::TQ_restrict, Loc, PrevSpec, DiagID,
getLangOpts());
break;
+ case tok::kw___ob_wrap:
+ isInvalid = DS.SetOverflowBehavior(
+ OverflowBehaviorType::OverflowBehaviorKind::Wrap, Loc, PrevSpec,
+ DiagID);
+ break;
+ case tok::kw___ob_trap:
+ isInvalid = DS.SetOverflowBehavior(
+ OverflowBehaviorType::OverflowBehaviorKind::Trap, Loc, PrevSpec,
+ DiagID);
+ break;
// C++ typename-specifier:
case tok::kw_typename:
@@ -5640,6 +5650,8 @@ bool Parser::isTypeSpecifierQualifier() {
case tok::kw_const:
case tok::kw_volatile:
case tok::kw_restrict:
+ case tok::kw___ob_wrap:
+ case tok::kw___ob_trap:
case tok::kw__Sat:
// Debugger support.
@@ -5853,6 +5865,8 @@ bool Parser::isDeclarationSpecifier(
case tok::kw_const:
case tok::kw_volatile:
case tok::kw_restrict:
+ case tok::kw___ob_wrap:
+ case tok::kw___ob_trap:
case tok::kw__Sat:
// function-specifier
@@ -6166,6 +6180,16 @@ void Parser::ParseTypeQualifierListOpt(
isInvalid = DS.SetTypeQual(DeclSpec::TQ_restrict, Loc, PrevSpec, DiagID,
getLangOpts());
break;
+ case tok::kw___ob_wrap:
+ isInvalid = DS.SetOverflowBehavior(
+ OverflowBehaviorType::OverflowBehaviorKind::Wrap, Loc, PrevSpec,
+ DiagID);
+ break;
+ case tok::kw___ob_trap:
+ isInvalid = DS.SetOverflowBehavior(
+ OverflowBehaviorType::OverflowBehaviorKind::Trap, Loc, PrevSpec,
+ DiagID);
+ break;
case tok::kw__Atomic:
if (!AtomicOrPtrauthAllowed)
goto DoneWithTypeQuals;
diff --git a/clang/lib/Parse/ParseTentative.cpp b/clang/lib/Parse/ParseTentative.cpp
index 82f2294ff5bb7..511cd834cf5f1 100644
--- a/clang/lib/Parse/ParseTentative.cpp
+++ b/clang/lib/Parse/ParseTentative.cpp
@@ -1231,6 +1231,11 @@ Parser::isCXXDeclarationSpecifier(ImplicitTypenameContext AllowImplicitTypename,
case tok::kw___auto_type:
return TPResult::True;
+ // OverflowBehaviorTypes
+ case tok::kw___ob_wrap:
+ case tok::kw___ob_trap:
+ return TPResult::True;
+
// Microsoft
case tok::kw___declspec:
case tok::kw___cdecl:
diff --git a/clang/lib/Sema/DeclSpec.cpp b/clang/lib/Sema/DeclSpec.cpp
index 9da3d0d2ef599..479a959e0aadc 100644
--- a/clang/lib/Sema/DeclSpec.cpp
+++ b/clang/lib/Sema/DeclSpec.cpp
@@ -616,6 +616,18 @@ const char *DeclSpec::getSpecifierName(TQ T) {
llvm_unreachable("Unknown typespec!");
}
+const char *DeclSpec::getSpecifierName(OverflowBehaviorState S) {
+ switch (S) {
+ case OverflowBehaviorState::Unspecified:
+ return "unspecified";
+ case OverflowBehaviorState::Wrap:
+ return "__ob_wrap";
+ case OverflowBehaviorState::Trap:
+ return "__ob_trap";
+ }
+ llvm_unreachable("Unknown overflow behavior state!");
+}
+
bool DeclSpec::SetStorageClassSpec(Sema &S, SCS SC, SourceLocation Loc,
const char *&PrevSpec,
unsigned &DiagID,
@@ -1003,6 +1015,25 @@ bool DeclSpec::SetTypeQual(TQ T, SourceLocation Loc) {
llvm_unreachable("Unknown type qualifier!");
}
+bool DeclSpec::SetOverflowBehavior(
+ OverflowBehaviorType::OverflowBehaviorKind Kind, SourceLocation Loc,
+ const char *&PrevSpec, unsigned &DiagID) {
+ OverflowBehaviorState NewState =
+ (Kind == OverflowBehaviorType::OverflowBehaviorKind::Wrap)
+ ? OverflowBehaviorState::Wrap
+ : OverflowBehaviorState::Trap;
+
+ OverflowBehaviorState CurrentState = getOverflowBehaviorState();
+
+ if (CurrentState != OverflowBehaviorState::Unspecified) {
+ return BadSpecifier(NewState, CurrentState, PrevSpec, DiagID);
+ }
+
+ OB_state = static_cast<unsigned>(NewState);
+ OB_Loc = Loc;
+ return false;
+}
+
bool DeclSpec::setFunctionSpecInline(SourceLocation Loc, const char *&PrevSpec,
unsigned &DiagID) {
// 'inline inline' is ok. However, since this is likely not what the user
diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp
index d32d7b960288d..80ca08ef6608d 100644
--- a/clang/lib/Sema/Sema.cpp
+++ b/clang/lib/Sema/Sema.cpp
@@ -842,6 +842,22 @@ ExprResult Sema::ImpCastExprToType(Expr *E, QualType Ty,
}
}
+ // Propagate overflow behavior type annotations through implicit casts If the
+ // source expression has an OBT annotation and we're doing an integral cast,
+ // preserve the OBT on the target type (unless target already has one).
+ bool IsExplicitCast = isa<CStyleCastExpr>(E) || isa<CXXStaticCastExpr>(E) ||
+ isa<CXXFunctionalCastExpr>(E);
+
+ if ((Kind == CK_IntegralCast || Kind == CK_IntegralToBoolean ||
+ (Kind == CK_NoOp && E->getType()->isIntegerType() && Ty->isIntegerType())) &&
+ IsExplicitCast) {
+ if (const auto *SourceOBT = E->getType()->getAs<OverflowBehaviorType>()) {
+ if (Ty->isIntegerType() && !Ty->isOverflowBehaviorType()) {
+ Ty = Context.getOverflowBehaviorType(SourceOBT->getBehaviorKind(), Ty);
+ }
+ }
+ }
+
return ImplicitCastExpr::Create(Context, Ty, Kind, E, BasePath, VK,
CurFPFeatureOverrides());
}
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 0ffb4854ba86d..49f578c0b6207 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -8618,6 +8618,10 @@ CheckPrintfHandler::checkFormatExpr(const analyze_printf::PrintfSpecifier &FS,
ExprTy = TET->getUnderlyingExpr()->getType();
}
+ if (const OverflowBehaviorType *OBT =
+ dyn_cast<OverflowBehaviorType>(ExprTy.getCanonicalType()))
+ ExprTy = OBT->getUnderlyingType();
+
// When using the format attribute in C++, you can receive a function or an
// array that will necessarily decay to a pointer when passed to the final
// format consumer. Apply decay before type comparison.
@@ -10872,6 +10876,8 @@ struct IntRange {
T = CT->getElementType().getTypePtr();
if (const auto *AT = dyn_cast<AtomicType>(T))
T = AT->getValueType().getTypePtr();
+ if (const OverflowBehaviorType *OBT = dyn_cast<OverflowBehaviorType>(T))
+ T = OBT->getUnderlyingType().getTypePtr();
if (!C.getLangOpts().CPlusPlus) {
// For enum types in C code, use the underlying datatype.
@@ -10921,6 +10927,8 @@ struct IntRange {
T = AT->getValueType().getTypePtr();
if (const auto *ED = T->getAsEnumDecl())
T = C.getCanonicalType(ED->getIntegerType()).getTypePtr();
+ if (const OverflowBehaviorType *OBT = dyn_cast<OverflowBehaviorType>(T))
+ T = OBT->getUnderlyingType().getTypePtr();
if (const auto *EIT = dyn_cast<BitIntType>(T))
return IntRange(EIT->getNumBits(), EIT->isUnsigned());
@@ -12054,6 +12062,11 @@ static void AnalyzeAssignment(Sema &S, BinaryOperator *E) {
}
}
+ // Set context flag for overflow behavior type assignment analysis, use RAII
+ // pattern to handle nested assignments.
+ llvm::SaveAndRestore OBTAssignmentContext(
+ S.InOverflowBehaviorAssignmentContext, true);
+
AnalyzeImplicitConversions(S, E->getRHS(), E->getOperatorLoc());
// Diagnose implicitly sequentially-consistent atomic assignment.
@@ -12528,6 +12541,8 @@ void Sema::CheckImplicitConversion(Expr *E, QualType T, SourceLocation CC,
}
}
+ CheckOverflowBehaviorTypeConversion(E, T, CC);
+
// If the we're converting a constant to an ObjC BOOL on a platform where BOOL
// is a typedef for signed char (macOS), then that constant value has to be 1
// or 0.
@@ -12860,6 +12875,23 @@ void Sema::CheckImplicitConversion(Expr *E, QualType T, SourceLocation CC,
IntRange TargetRange = IntRange::forTargetOfCanonicalType(Context, Target);
if (LikelySourceRange->Width > TargetRange.Width) {
+ // Check if target is a wrapping OBT - if so, don't warn about constant
+ // conversion as this type may be used intentionally with implicit
+ // truncation, especially during assignments.
+ if (const auto *TargetOBT = Target->getAs<OverflowBehaviorType>()) {
+ if (TargetOBT->isWrapKind()) {
+ return;
+ }
+ }
+
+ // Check if source expression has an explicit __ob_wrap cast because if so,
+ // wrapping was explicitly requested and we shouldn't warn
+ if (const auto *SourceOBT = E->getType()->getAs<OverflowBehaviorType>()) {
+ if (SourceOBT->isWrapKind()) {
+ return;
+ }
+ }
+
// If the source is a constant, use a default-on diagnostic.
// TODO: this should happen for bitfield stores, too.
Expr::EvalResult Result;
@@ -13544,6 +13576,40 @@ void Sema::DiagnoseAlwaysNonNullPointer(Expr *E,
<< FixItHint::CreateInsertion(getLocForEndOfToken(E->getEndLoc()), "()");
}
+bool Sema::CheckOverflowBehaviorTypeConversion(Expr *E, QualType T,
+ SourceLocation CC) {
+ QualType Source = E->getType();
+ QualType Target = T;
+
+ if (const auto *OBT = Source->getAs<OverflowBehaviorType>()) {
+ if (Target->isIntegerType() && !Target->isOverflowBehaviorType()) {
+ // Overflow behavior type is being stripped - issue warning
+ if (OBT->isUnsignedIntegerType() && OBT->isWrapKind() &&
+ Target->isUnsignedIntegerType()) {
+ // For unsigned wrap to unsigned conversions, use pedantic version
+ unsigned DiagId =
+ InOverflowBehaviorAssignmentContext
+ ? diag::warn_impcast_overflow_behavior_assignment_pedantic
+ : diag::warn_impcast_overflow_behavior_pedantic;
+ DiagnoseImpCast(*this, E, T, CC, DiagId);
+ } else {
+ unsigned DiagId = InOverflowBehaviorAssignmentContext
+ ? diag::warn_impcast_overflow_behavior_assignment
+ : diag::warn_impcast_overflow_behavior;
+ DiagnoseImpCast(*this, E, T, CC, DiagId);
+ }
+ }
+ }
+
+ if (const auto *TargetOBT = Target->getAs<OverflowBehaviorType>()) {
+ if (TargetOBT->isWrapKind()) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
void Sema::CheckImplicitConversions(Expr *E, SourceLocation CC) {
// Don't diagnose in unevaluated contexts.
if (isUnevaluatedContext())
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 7e7abcee40a56..6382528869e1c 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -14074,6 +14074,10 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init, bool DirectInit) {
// struct T { S a, b; } t = { Temp(), Temp() }
//
// we should destroy the first Temp before constructing the second.
+
+ // Set context flag for OverflowBehaviorType initialization analysis
+ llvm::SaveAndRestore OBTAssignmentContext(InOverflowBehaviorAssignmentContext,
+ true);
ExprResult Result =
ActOnFinishFullExpr(Init, VDecl->getLocation(),
/*DiscardedValue*/ false, VDecl->isConstexpr());
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index cfabd1b76c103..c432ec38ab1a7 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -1416,6 +1416,60 @@ static QualType handleComplexIntConversion(Sema &S, ExprResult &LHS,
return ComplexType;
}
+static QualType handleOverflowBehaviorTypeConversion(Sema &S, ExprResult &LHS,
+ ExprResult &RHS,
+ QualType LHSType,
+ QualType RHSType,
+ bool IsCompAssign) {
+
+ const auto *LhsOBT = LHSType->getAs<OverflowBehaviorType>();
+ const auto *RhsOBT = RHSType->getAs<OverflowBehaviorType>();
+
+ assert(LHSType->isIntegerType() && RHSType->isIntegerType() &&
+ "Non-integer type conversion not supported for OverflowBehaviorTypes");
+
+ bool LHSHasTrap =
+ LhsOBT && LhsOBT->getBehaviorKind() ==
+ OverflowBehaviorType::OverflowBehaviorKind::Trap;
+ bool RHSHasTrap =
+ RhsOBT && RhsOBT->getBehaviorKind() ==
+ OverflowBehaviorType::OverflowBehaviorKind::Trap;
+ bool LHSHasWrap =
+ LhsOBT && LhsOBT->getBehaviorKind() ==
+ OverflowBehaviorType::OverflowBehaviorKind::Wrap;
+ bool RHSHasWrap =
+ RhsOBT && RhsOBT->getBehaviorKind() ==
+ OverflowBehaviorType::OverflowBehaviorKind::Wrap;
+
+ QualType LHSUnderlyingType = LhsOBT ? LhsOBT->getUnderlyingType() : LHSType;
+ QualType RHSUnderlyingType = RhsOBT ? RhsOBT->getUnderlyingType() : RHSType;
+
+ std::optional<OverflowBehaviorType::OverflowBehaviorKind> DominantBehavior;
+ if (LHSHasTrap || RHSHasTrap)
+ DominantBehavior = OverflowBehaviorType::OverflowBehaviorKind::Trap;
+ else if (LHSHasWrap || RHSHasWrap)
+ DominantBehavior = OverflowBehaviorType::OverflowBehaviorKind::Wrap;
+
+ QualType LHSConvType = LHSUnderlyingType;
+ QualType RHSConvType = RHSUnderlyingType;
+ if (DominantBehavior) {
+ if (!LhsOBT || LhsOBT->getBehaviorKind() != *DominantBehavior)
+ LHSConvType = S.Context.getOverflowBehaviorType(*DominantBehavior,
+ LHSUnderlyingType);
+ else
+ LHSConvType = LHSType;
+
+ if (!RhsOBT || RhsOBT->getBehaviorKind() != *DominantBehavior)
+ RHSConvType = S.Context.getOverflowBehaviorType(*DominantBehavior,
+ RHSUnderlyingType);
+ else
+ RHSConvType = RHSType;
+ }
+
+ return handleIntegerConversion<doIntegralCast, doIntegralCast>(
+ S, LHS, RHS, LHSConvType, RHSConvType, IsCompAssign);
+}
+
/// Return the rank of a given fixed point or integer type. The value itself
/// doesn't matter, but the values must be increasing with proper increasing
/// rank as described in N1169 4.1.1.
@@ -1716,6 +1770,10 @@ QualType Sema::UsualArithmeticConversions(ExprResult &LHS, ExprResult &RHS,
if (LHSType->isFixedPointType() || RHSType->isFixedPointType())
return handleFixedPointConversion(*this, LHSType, RHSType);
+ if (LHSType->isOverflowBehaviorType() || RHSType->isOverflowBehaviorType())
+ return handleOverflowBehaviorTypeConversion(
+ *this, LHS, RHS, LHSType, RHSType, ACK == ArithConvKind::CompAssign);
+
// Finally, we have two differing integer types.
return handleIntegerConversion<doIntegralCast, doIntegralCast>(
*this, LHS, RHS, LHSType, RHSType, ACK == ArithConvKind::CompAssign);
@@ -1758,6 +1816,29 @@ ExprResult Sema::ActOnGenericSelectionExpr(
return ER;
}
+// Helper function to determine type compatibility for C _Generic expressions.
+// Multiple compatible types within the same _Generic expression is ambiguous
+// and not valid.
+static bool areTypesCompatibleForGeneric(ASTContext &Ctx, QualType T,
+ QualType U) {
+ // Try to handle special types like OverflowBehaviorTypes
+ const auto *TOBT = T->getAs<OverflowBehaviorType>();
+ const auto *UOBT = U.getCanonicalType()->getAs<OverflowBehaviorType>();
+
+ if (TOBT || UOBT) {
+ if (TOBT && UOBT) {
+ if (TOBT->getBehaviorKind() == UOBT->getBehaviorKind())
+ return Ctx.typesAreCompatible(TOBT->getUnderlyingType(),
+ UOBT->getUnderlyingType());
+ return false;
+ }
+ return false;
+ }
+
+ // We're dealing with types that don't require special handling.
+ return Ctx.typesAreCompatible(T, U);
+}
+
ExprResult Sema::CreateGenericSelectionExpr(
SourceLocation KeyLoc, SourceLocation DefaultLoc, SourceLocation RParenLoc,
bool PredicateIsExpr, void *ControllingExprOrType,
@@ -1882,8 +1963,8 @@ ExprResult Sema::CreateGenericSelectionExpr(
// selection shall specify compatible types."
for (unsigned j = i+1; j < NumAssocs; ++j)
if (Types[j] && !Types[j]->getType()->isDependentType() &&
- Context.typesAreCompatible(Types[i]->getType(),
- Types[j]->getType())) {
+ areTypesCompatibleForGeneric(Context, Types[i]->getType(),
+ Types[j]->getType())) {
Diag(Types[j]->getTypeLoc().getBeginLoc(),
diag::err_assoc_compatible_types)
<< Types[j]->getTypeLoc().getSourceRange()
@@ -1921,16 +2002,19 @@ ExprResult Sema::CreateGenericSelectionExpr(
for (unsigned i = 0; i < NumAssocs; ++i) {
if (!Types[i])
DefaultIndex = i;
- else if (ControllingExpr &&
- Context.typesAreCompatible(
- ControllingExpr->getType().getCanonicalType(),
- Types[i]->getType()))
- CompatIndices.push_back(i);
- else if (ControllingType &&
- Context.typesAreCompatible(
- ControllingType->getType().getCanonicalType(),
- Types[i]->getType()))
- CompatIndices.push_back(i);
+ else {
+ bool Compatible;
+ QualType ControllingQT =
+ ControllingExpr ? ControllingExpr->getType().getCanonicalType()
+ : ControllingType->getType().getCanonicalType();
+ QualType AssocQT = Types[i]->getType();
+
+ Compatible =
+ areTypesCompatibleForGeneric(Context, ControllingQT, AssocQT);
+
+ if (Compatible)
+ CompatIndices.push_back(i);
+ }
}
auto GetControllingRangeAndType = [](Expr *ControllingExpr,
@@ -4551,6 +4635,7 @@ static void captureVariablyModifiedType(ASTContext &Context, QualType T,
case Type::UnaryTransform:
case Type::Attributed:
case Type::BTFTagAttributed:
+ case Type::OverflowBehavior:
case Type::HLSLAttributedResource:
case Type::SubstTemplateTypeParm:
case Type::MacroQualified:
@@ -6092,6 +6177,18 @@ bool Sema::GatherArgumentsForCall(SourceLocation CallLoc, FunctionDecl *FDecl,
if (CFAudited)
Entity.setParameterCFAudited();
+ // Warn if argument has OBT but parameter doesn't, discarding OBTs at
+ // function boundaries is a common oversight.
+ if (const auto *OBT = Arg->getType()->getAs<OverflowBehaviorType>();
+ OBT && !ProtoArgType->isOverflowBehaviorType()) {
+ bool isPedantic =
+ OBT->isUnsignedIntegerOrEnumerationType() && OBT->isWrapKind();
+ Diag(Arg->getExprLoc(),
+ isPedantic ? diag::warn_obt_discarded_at_function_boundary_pedantic
+ : diag::warn_obt_discarded_at_function_boundary)
+ << Arg->getType() << ProtoArgType;
+ }
+
ExprResult ArgE = PerformCopyInitialization(
Entity, SourceLocation(), Arg, IsListInitialization, AllowExplicit);
if (ArgE.isInvalid())
@@ -9120,6 +9217,26 @@ static AssignConvertType checkPointerTypesForAssignment(Sema &S,
// C99 6.5.16.1p1 (constraint 3): both operands are pointers to qualified or
// unqualified versions of compatible types, ...
QualType ltrans = QualType(lhptee, 0), rtrans = QualType(rhptee, 0);
+
+ if (ltrans->isOverflowBehaviorType() || rtrans->isOverflowBehaviorType()) {
+ if (!S.Context.hasSameType(ltrans, rtrans)) {
+ QualType LUnderlying =
+ ltrans->isOverflowBehaviorType()
+ ? ltrans->castAs<OverflowBehaviorType>()->getUnderlyingType()
+ : ltrans;
+ QualType RUnderlying =
+ rtrans->isOverflowBehaviorType()
+ ? rtrans->castAs<OverflowBehaviorType>()->getUnderlyingType()
+ : rtrans;
+
+ if (S.Context.hasSameType(LUnderlying, RUnderlying))
+ return AssignConvertType::IncompatiblePointerDiscardsOverflowBehavior;
+
+ ltrans = LUnderlying;
+ rtrans = RUnderlying;
+ }
+ }
+
if (!S.Context.typesAreCompatible(ltrans, rtrans)) {
// Check if the pointee types are compatible ignoring the sign.
// We explicitly check for char so that we catch "char" vs
@@ -9373,6 +9490,31 @@ AssignConvertType Sema::CheckAssignmentConstraints(QualType LHSType,
}
}
+ auto OBTResult = Context.checkOBTAssignmentCompatibility(LHSType, RHSType);
+ switch (OBTResult) {
+ case ASTContext::OBTAssignResult::IncompatibleKinds:
+ Kind = CK_NoOp;
+ return AssignConvertType::IncompatibleOBTKinds;
+ case ASTContext::OBTAssignResult::Discards:
+ Kind = CK_IntegralCast;
+ return AssignConvertType::CompatibleOBTDiscards;
+ case ASTContext::OBTAssignResult::Compatible:
+ case ASTContext::OBTAssignResult::NotApplicable:
+ break;
+ }
+
+ // Check for incompatible OBT types in pointer pointee types
+ if (LHSType->isPointerType() && RHSType->isPointerType()) {
+ QualType LHSPointee = LHSType->getPointeeType();
+ QualType RHSPointee = RHSType->getPointeeType();
+ if ((LHSPointee->isOverflowBehaviorType() ||
+ RHSPointee->isOverflowBehaviorType()) &&
+ !Context.areCompatibleOverflowBehaviorTypes(LHSPointee, RHSPointee)) {
+ Kind = CK_NoOp;
+ return AssignConvertType::IncompatibleOBTKinds;
+ }
+ }
+
// If we have an atomic type, try a non-atomic assignment, then just add an
// atomic qualification step.
if (const AtomicType *AtomicTy = dyn_cast<AtomicType>(LHSType)) {
@@ -9849,6 +9991,15 @@ AssignConvertType Sema::CheckSingleAssignmentConstraints(QualType LHSType,
if (getLangOpts().allowsNonTrivialObjCLifetimeQualifiers() &&
!ObjC().CheckObjCARCUnavailableWeakConversion(LHSType, RHSType))
result = AssignConvertType::IncompatibleObjCWeakRef;
+
+ // Check if OBT is being discarded during assignment
+ // The RHS may have propagated OBT, but if LHS doesn't have it, warn
+ if (const auto *RHSOBT = RHSType->getAs<OverflowBehaviorType>()) {
+ if (!LHSType->isOverflowBehaviorType()) {
+ result = AssignConvertType::CompatibleOBTDiscards;
+ }
+ }
+
return result;
}
@@ -14366,6 +14517,8 @@ static QualType CheckIncrementDecrementOperand(Sema &S, Expr *Op,
// C99 6.5.2.4p2, 6.5.6p2
if (!checkArithmeticOpPointerOperand(S, OpLoc, Op))
return QualType();
+ } else if (ResType->isOverflowBehaviorType()) {
+ // OK!
} else if (ResType->isObjCObjectPointerType()) {
// On modern runtimes, ObjC pointer arithmetic is forbidden.
// Otherwise, we just need a complete type.
@@ -17250,6 +17403,12 @@ bool Sema::DiagnoseAssignmentResult(AssignConvertType ConvTy,
llvm_unreachable("unknown error case for discarding qualifiers!");
// fallthrough
}
+ case AssignConvertType::IncompatiblePointerDiscardsOverflowBehavior:
+ if (SrcType->isArrayType())
+ SrcType = Context.getArrayDecayedType(SrcType);
+
+ DiagKind = diag::ext_typecheck_convert_discards_overflow_behavior;
+ break;
case AssignConvertType::CompatiblePointerDiscardsQualifiers:
// If the qualifiers lost were because we were applying the
// (deprecated) C++ conversion from a string literal to a char*
@@ -17334,6 +17493,27 @@ bool Sema::DiagnoseAssignmentResult(AssignConvertType ConvTy,
DiagKind = diag::err_arc_weak_unavailable_assign;
isInvalid = true;
break;
+ case AssignConvertType::CompatibleOBTDiscards:
+ return false;
+ case AssignConvertType::IncompatibleOBTKinds: {
+ auto getOBTKindName = [](QualType Ty) -> StringRef {
+ if (Ty->isPointerType())
+ Ty = Ty->getPointeeType();
+ if (const auto *OBT = Ty->getAs<OverflowBehaviorType>()) {
+ return OBT->getBehaviorKind() ==
+ OverflowBehaviorType::OverflowBehaviorKind::Trap
+ ? "__ob_trap"
+ : "__ob_wrap";
+ }
+ llvm_unreachable("OBT kind unhandled");
+ };
+
+ Diag(Loc, diag::err_incompatible_obt_kinds_assignment)
+ << DstType << SrcType << getOBTKindName(DstType)
+ << getOBTKindName(SrcType);
+ isInvalid = true;
+ return true;
+ }
case AssignConvertType::Incompatible:
if (maybeDiagnoseAssignmentToFunction(*this, DstType, SrcExpr)) {
if (Complained)
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index d6f70e728be29..f5b8c2e4e128d 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -4685,6 +4685,30 @@ static QualType adjustVectorType(ASTContext &Context, QualType FromTy,
return Context.getExtVectorType(ElType, FromVec->getNumElements());
}
+/// Check if an integral conversion involves incompatible overflow behavior types.
+/// Returns true if the conversion is invalid.
+static bool checkIncompatibleOBTConversion(Sema &S, QualType FromType,
+ QualType ToType, Expr *From) {
+ const auto *FromOBT = FromType->getAs<OverflowBehaviorType>();
+ const auto *ToOBT = ToType->getAs<OverflowBehaviorType>();
+
+ if (FromOBT && ToOBT &&
+ FromOBT->getBehaviorKind() != ToOBT->getBehaviorKind()) {
+ S.Diag(From->getExprLoc(), diag::err_incompatible_obt_kinds_assignment)
+ << ToType << FromType
+ << (ToOBT->getBehaviorKind() ==
+ OverflowBehaviorType::OverflowBehaviorKind::Trap
+ ? "__ob_trap"
+ : "__ob_wrap")
+ << (FromOBT->getBehaviorKind() ==
+ OverflowBehaviorType::OverflowBehaviorKind::Trap
+ ? "__ob_trap"
+ : "__ob_wrap");
+ return true;
+ }
+ return false;
+}
+
ExprResult
Sema::PerformImplicitConversion(Expr *From, QualType ToType,
const StandardConversionSequence& SCS,
@@ -4844,6 +4868,11 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType,
QualType StepTy = ToType;
if (FromType->isVectorType() || ToType->isVectorType())
StepTy = adjustVectorType(Context, FromType, ToType, &ElTy);
+
+ // Check for incompatible OBT kinds before converting
+ if (checkIncompatibleOBTConversion(*this, FromType, StepTy, From))
+ return ExprError();
+
if (ElTy->isBooleanType()) {
assert(FromType->castAsEnumDecl()->isFixed() &&
SCS.Second == ICK_Integral_Promotion &&
diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp
index 5915d6e57d893..a14ebeeeec70c 100644
--- a/clang/lib/Sema/SemaLookup.cpp
+++ b/clang/lib/Sema/SemaLookup.cpp
@@ -3300,6 +3300,8 @@ addAssociatedClassesAndNamespaces(AssociatedLookup &Result, QualType Ty) {
// Inline SPIR-V types are treated as fundamental types.
case Type::HLSLInlineSpirv:
break;
+ case Type::OverflowBehavior:
+ T = cast<OverflowBehaviorType>(T)->getUnderlyingType().getTypePtr();
}
if (Queue.empty())
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index c12f92dfdab66..4b88349906888 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -117,6 +117,11 @@ CompareQualificationConversions(Sema &S,
const StandardConversionSequence& SCS1,
const StandardConversionSequence& SCS2);
+static ImplicitConversionSequence::CompareKind
+CompareOverflowBehaviorConversions(Sema &S,
+ const StandardConversionSequence &SCS1,
+ const StandardConversionSequence &SCS2);
+
static ImplicitConversionSequence::CompareKind
CompareDerivedToBaseConversions(Sema &S, SourceLocation Loc,
const StandardConversionSequence& SCS1,
@@ -2230,6 +2235,12 @@ static bool tryAtomicConversion(Sema &S, Expr *From, QualType ToType,
StandardConversionSequence &SCS,
bool CStyle);
+static bool tryOverflowBehaviorTypeConversion(Sema &S, Expr *From,
+ QualType ToType,
+ bool InOverloadResolution,
+ StandardConversionSequence &SCS,
+ bool CStyle);
+
/// IsStandardConversion - Determines whether there is a standard
/// conversion sequence (C++ [conv], C++ [over.ics.scs]) from the
/// expression From to the type ToType. Standard conversion sequences
@@ -2420,9 +2431,16 @@ static bool IsStandardConversion(Sema &S, Expr* From, QualType ToType,
// Complex promotion (Clang extension)
SCS.Second = ICK_Complex_Promotion;
FromType = ToType.getUnqualifiedType();
+ } else if (S.IsOverflowBehaviorTypePromotion(FromType, ToType)) {
+ // OverflowBehaviorType promotions
+ SCS.Second = ICK_Integral_Promotion;
+ FromType = ToType.getUnqualifiedType();
+ } else if (S.IsOverflowBehaviorTypeConversion(FromType, ToType)) {
+ // OverflowBehaviorType conversions
+ SCS.Second = ICK_Integral_Conversion;
+ FromType = ToType.getUnqualifiedType();
} else if (ToType->isBooleanType() &&
- (FromType->isArithmeticType() ||
- FromType->isAnyPointerType() ||
+ (FromType->isArithmeticType() || FromType->isAnyPointerType() ||
FromType->isBlockPointerType() ||
FromType->isMemberPointerType())) {
// Boolean conversions (C++ 4.12).
@@ -2488,6 +2506,9 @@ static bool IsStandardConversion(Sema &S, Expr* From, QualType ToType,
// tryAtomicConversion has updated the standard conversion sequence
// appropriately.
return true;
+ } else if (tryOverflowBehaviorTypeConversion(
+ S, From, ToType, InOverloadResolution, SCS, CStyle)) {
+ return true;
} else if (ToType->isEventT() &&
From->isIntegerConstantExpr(S.getASTContext()) &&
From->EvaluateKnownConstInt(S.getASTContext()) == 0) {
@@ -2835,6 +2856,41 @@ bool Sema::IsComplexPromotion(QualType FromType, QualType ToType) {
ToComplex->getElementType());
}
+bool Sema::IsOverflowBehaviorTypePromotion(QualType FromType, QualType ToType) {
+ if (!getLangOpts().OverflowBehaviorTypes)
+ return false;
+
+ if (!FromType->isOverflowBehaviorType() || !ToType->isOverflowBehaviorType())
+ return false;
+
+ return Context.getTypeSize(FromType) < Context.getTypeSize(ToType);
+}
+
+bool Sema::IsOverflowBehaviorTypeConversion(QualType FromType,
+ QualType ToType) {
+ if (!getLangOpts().OverflowBehaviorTypes)
+ return false;
+
+ if (FromType->isOverflowBehaviorType() && !ToType->isOverflowBehaviorType()) {
+ // Don't allow implicit conversion from OverflowBehaviorType to scoped enum
+ if (const EnumType *ToEnumType = ToType->getAs<EnumType>()) {
+ const EnumDecl *ToED =
+ ToEnumType->getDecl()->getDefinitionOrSelf();
+ if (ToED->isScoped())
+ return false;
+ }
+ return true;
+ }
+
+ if (!FromType->isOverflowBehaviorType() && ToType->isOverflowBehaviorType())
+ return true;
+
+ if (FromType->isOverflowBehaviorType() && ToType->isOverflowBehaviorType())
+ return Context.getTypeSize(FromType) > Context.getTypeSize(ToType);
+
+ return false;
+}
+
/// BuildSimilarlyQualifiedPointerType - In a pointer conversion from
/// the pointer type FromPtr to a pointer to type ToPointee, with the
/// same type qualifiers as FromPtr has on its pointee type. ToType,
@@ -3867,6 +3923,35 @@ static bool tryAtomicConversion(Sema &S, Expr *From, QualType ToType,
return true;
}
+static bool tryOverflowBehaviorTypeConversion(Sema &S, Expr *From,
+ QualType ToType,
+ bool InOverloadResolution,
+ StandardConversionSequence &SCS,
+ bool CStyle) {
+ const OverflowBehaviorType *ToOBT = ToType->getAs<OverflowBehaviorType>();
+ if (!ToOBT)
+ return false;
+
+ // Check for incompatible OBT kinds (e.g., trap vs wrap)
+ QualType FromType = From->getType();
+ if (!S.Context.areCompatibleOverflowBehaviorTypes(FromType, ToType))
+ return false;
+
+ StandardConversionSequence InnerSCS;
+ if (!IsStandardConversion(S, From, ToOBT->getUnderlyingType(),
+ InOverloadResolution, InnerSCS, CStyle,
+ /*AllowObjCWritebackConversion=*/false))
+ return false;
+
+ SCS.Second = InnerSCS.Second;
+ SCS.setToType(1, InnerSCS.getToType(1));
+ SCS.Third = InnerSCS.Third;
+ SCS.QualificationIncludesObjCLifetime =
+ InnerSCS.QualificationIncludesObjCLifetime;
+ SCS.setToType(2, InnerSCS.getToType(2));
+ return true;
+}
+
static bool isFirstArgumentCompatibleWithType(ASTContext &Context,
CXXConstructorDecl *Constructor,
QualType Type) {
@@ -4661,6 +4746,10 @@ CompareStandardConversionSequences(Sema &S, SourceLocation Loc,
= CompareQualificationConversions(S, SCS1, SCS2))
return QualCK;
+ if (ImplicitConversionSequence::CompareKind ObtCK =
+ CompareOverflowBehaviorConversions(S, SCS1, SCS2))
+ return ObtCK;
+
if (SCS1.ReferenceBinding && SCS2.ReferenceBinding) {
// C++ [over.ics.rank]p3b4:
// -- S1 and S2 are reference bindings (8.5.3), and the types to
@@ -4773,6 +4862,25 @@ CompareStandardConversionSequences(Sema &S, SourceLocation Loc,
return ImplicitConversionSequence::Indistinguishable;
}
+/// CompareOverflowBehaviorConversions - Compares two standard conversion
+/// sequences to determine whether they can be ranked based on their
+/// OverflowBehaviorType's underlying type.
+static ImplicitConversionSequence::CompareKind
+CompareOverflowBehaviorConversions(Sema &S,
+ const StandardConversionSequence &SCS1,
+ const StandardConversionSequence &SCS2) {
+
+ if (SCS1.getFromType()->isOverflowBehaviorType() &&
+ SCS1.getToType(2)->isOverflowBehaviorType())
+ return ImplicitConversionSequence::Better;
+
+ if (SCS2.getFromType()->isOverflowBehaviorType() &&
+ SCS2.getToType(2)->isOverflowBehaviorType())
+ return ImplicitConversionSequence::Worse;
+
+ return ImplicitConversionSequence::Indistinguishable;
+}
+
/// CompareQualificationConversions - Compares two standard conversion
/// sequences to determine whether they can be ranked based on their
/// qualification conversions (C++ 13.3.3.2p3 bullet 3).
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index ba91629783759..176cb79ac4fac 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -6761,6 +6761,11 @@ bool UnnamedLocalNoLinkageFinder::VisitAtomicType(const AtomicType* T) {
return Visit(T->getValueType());
}
+bool UnnamedLocalNoLinkageFinder::VisitOverflowBehaviorType(
+ const OverflowBehaviorType *T) {
+ return Visit(T->getUnderlyingType());
+}
+
bool UnnamedLocalNoLinkageFinder::VisitPipeType(const PipeType* T) {
return false;
}
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index a287319cc4f88..b6f7866d69ed6 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -2550,6 +2550,7 @@ static TemplateDeductionResult DeduceTemplateArgumentsByTypeMatch(
case Type::ArrayParameter:
case Type::HLSLAttributedResource:
case Type::HLSLInlineSpirv:
+ case Type::OverflowBehavior:
// No template argument deduction for these types
return TemplateDeductionResult::Success;
@@ -7077,6 +7078,7 @@ MarkUsedTemplateParameters(ASTContext &Ctx, QualType T,
case Type::Pipe:
case Type::BitInt:
case Type::HLSLInlineSpirv:
+ case Type::OverflowBehavior:
#define TYPE(Class, Base)
#define ABSTRACT_TYPE(Class, Base)
#define DEPENDENT_TYPE(Class, Base)
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index 9488a853ffab1..d92571e50ef47 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -301,6 +301,14 @@ namespace {
return sema.Context.getBTFTagAttributedType(BTFAttr, WrappedType);
}
+ /// Get a OverflowBehaviorType type for the overflow_behavior type
+ /// attribute.
+ QualType
+ getOverflowBehaviorType(OverflowBehaviorType::OverflowBehaviorKind Kind,
+ QualType UnderlyingType) {
+ return sema.Context.getOverflowBehaviorType(Kind, UnderlyingType);
+ }
+
/// Completely replace the \c auto in \p TypeWithAuto by
/// \p Replacement. Also replace \p TypeWithAuto in \c TypeAttrPair if
/// necessary.
@@ -1555,6 +1563,23 @@ static QualType ConvertDeclSpecToType(TypeProcessingState &state) {
Result = Qualified;
}
+ // Check for __ob_wrap and __ob_trap
+ if (DS.isOverflowBehaviorSpecified()) {
+ if (!Result->isIntegerType()) {
+ SourceLocation Loc = DS.getOverflowBehaviorLoc();
+ StringRef SpecifierName =
+ DeclSpec::getSpecifierName(DS.getOverflowBehaviorState());
+ S.Diag(Loc, diag::err_overflow_behavior_non_integer_type)
+ << SpecifierName << Result.getAsString() << 1;
+ } else {
+ OverflowBehaviorType::OverflowBehaviorKind Kind =
+ DS.isWrapSpecified()
+ ? OverflowBehaviorType::OverflowBehaviorKind::Wrap
+ : OverflowBehaviorType::OverflowBehaviorKind::Trap;
+ Result = state.getOverflowBehaviorType(Kind, Result);
+ }
+ }
+
if (S.getLangOpts().HLSL)
Result = S.HLSL().ProcessResourceTypeAttributes(Result);
@@ -5913,6 +5938,9 @@ namespace {
void VisitBTFTagAttributedTypeLoc(BTFTagAttributedTypeLoc TL) {
Visit(TL.getWrappedLoc());
}
+ void VisitOverflowBehaviorTypeLoc(OverflowBehaviorTypeLoc TL) {
+ Visit(TL.getWrappedLoc());
+ }
void VisitHLSLAttributedResourceTypeLoc(HLSLAttributedResourceTypeLoc TL) {
Visit(TL.getWrappedLoc());
fillHLSLAttributedResourceTypeLoc(TL, State);
@@ -6198,6 +6226,9 @@ namespace {
void VisitBTFTagAttributedTypeLoc(BTFTagAttributedTypeLoc TL) {
// nothing
}
+ void VisitOverflowBehaviorTypeLoc(OverflowBehaviorTypeLoc TL) {
+ // nothing
+ }
void VisitAdjustedTypeLoc(AdjustedTypeLoc TL) {
// nothing
}
@@ -6648,6 +6679,109 @@ static void HandleAddressSpaceTypeAttribute(QualType &Type,
}
}
+static void HandleOverflowBehaviorAttr(QualType &Type, const ParsedAttr &Attr,
+ TypeProcessingState &State) {
+ Sema &S = State.getSema();
+
+ // Check for -foverflow-behavior-types
+ if (!S.getLangOpts().OverflowBehaviorTypes) {
+ S.Diag(Attr.getLoc(), diag::warn_overflow_behavior_attribute_disabled)
+ << Attr << 1;
+ Attr.setInvalid();
+ return;
+ }
+
+ // Check the number of attribute arguments.
+ if (Attr.getNumArgs() != 1) {
+ S.Diag(Attr.getLoc(), diag::err_attribute_wrong_number_arguments)
+ << Attr << 1;
+ Attr.setInvalid();
+ return;
+ }
+
+ // Check that the underlying type is an integer type
+ if (!Type->isIntegerType()) {
+ S.Diag(Attr.getLoc(), diag::err_overflow_behavior_non_integer_type)
+ << Attr << Type.getAsString() << 0; // 0 for attribute
+ Attr.setInvalid();
+ return;
+ }
+
+ StringRef KindName = "";
+ IdentifierInfo *Ident = nullptr;
+
+ if (Attr.isArgIdent(0)) {
+ Ident = Attr.getArgAsIdent(0)->getIdentifierInfo();
+ KindName = Ident->getName();
+ }
+
+ // Support identifier or string argument types. Failure to provide one of
+ // these two types results in a diagnostic that hints towards using string
+ // arguments (either "wrap" or "trap") as this is the most common use
+ // pattern.
+ if (!Ident) {
+ auto *Str = dyn_cast<StringLiteral>(Attr.getArgAsExpr(0));
+ if (Str)
+ KindName = Str->getString();
+ else {
+ S.Diag(Attr.getLoc(), diag::err_attribute_argument_type)
+ << Attr << AANT_ArgumentString;
+ Attr.setInvalid();
+ return;
+ }
+ }
+
+ OverflowBehaviorType::OverflowBehaviorKind Kind;
+ if (KindName == "wrap") {
+ Kind = OverflowBehaviorType::OverflowBehaviorKind::Wrap;
+ } else if (KindName == "trap") {
+ Kind = OverflowBehaviorType::OverflowBehaviorKind::Trap;
+ } else {
+ S.Diag(Attr.getLoc(), diag::err_overflow_behavior_unknown_ident)
+ << KindName << Attr;
+ Attr.setInvalid();
+ return;
+ }
+
+ // Check for mixed specifier/attribute usage
+ const DeclSpec &DS = State.getDeclarator().getDeclSpec();
+ if (DS.isWrapSpecified() || DS.isTrapSpecified()) {
+ // We have both specifier and attribute on the same type. If
+ // OverflowBehaviorKinds are the same we can just warn.
+ OverflowBehaviorType::OverflowBehaviorKind SpecifierKind =
+ DS.isWrapSpecified() ? OverflowBehaviorType::OverflowBehaviorKind::Wrap
+ : OverflowBehaviorType::OverflowBehaviorKind::Trap;
+
+ if (SpecifierKind != Kind) {
+ StringRef SpecifierName = DS.isWrapSpecified() ? "wrap" : "trap";
+ S.Diag(Attr.getLoc(), diag::err_conflicting_overflow_behaviors)
+ << 1 << SpecifierName << KindName;
+ Attr.setInvalid();
+ return;
+ }
+ S.Diag(Attr.getLoc(), diag::warn_redundant_overflow_behaviors_mixed)
+ << KindName;
+ Attr.setInvalid();
+ return;
+ }
+
+ // Check for conflicting overflow behavior attributes
+ if (const auto *ExistingOBT = Type->getAs<OverflowBehaviorType>()) {
+ OverflowBehaviorType::OverflowBehaviorKind ExistingKind =
+ ExistingOBT->getBehaviorKind();
+ if (ExistingKind != Kind) {
+ S.Diag(Attr.getLoc(), diag::err_conflicting_overflow_behaviors) << 0;
+ if (Kind == OverflowBehaviorType::OverflowBehaviorKind::Trap) {
+ Type = State.getOverflowBehaviorType(Kind,
+ ExistingOBT->getUnderlyingType());
+ }
+ return;
+ }
+ } else {
+ Type = State.getOverflowBehaviorType(Kind, Type);
+ }
+}
+
/// handleObjCOwnershipTypeAttr - Process an objc_ownership
/// attribute on the specified type.
///
@@ -8978,6 +9112,10 @@ static void processTypeAttrs(TypeProcessingState &state, QualType &type,
if (TAL == TAL_DeclChunk)
HandleLifetimeCaptureByAttr(state, type, attr);
break;
+ case ParsedAttr::AT_OverflowBehavior:
+ HandleOverflowBehaviorAttr(type, attr, state);
+ attr.setUsedAsTypeAttr();
+ break;
case ParsedAttr::AT_NoDeref: {
// FIXME: `noderef` currently doesn't work correctly in [[]] syntax.
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 8e5dbeb792348..19766808e00e5 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -7712,6 +7712,27 @@ QualType TreeTransform<Derived>::TransformBTFTagAttributedType(
llvm_unreachable("Unexpected TreeTransform for BTFTagAttributedType");
}
+template <typename Derived>
+QualType TreeTransform<Derived>::TransformOverflowBehaviorType(
+ TypeLocBuilder &TLB, OverflowBehaviorTypeLoc TL) {
+ const OverflowBehaviorType *OldTy = TL.getTypePtr();
+ QualType InnerTy = getDerived().TransformType(TLB, TL.getWrappedLoc());
+ if (InnerTy.isNull())
+ return QualType();
+
+ QualType Result = TL.getType();
+ if (getDerived().AlwaysRebuild() || InnerTy != OldTy->getUnderlyingType()) {
+ Result = SemaRef.Context.getOverflowBehaviorType(OldTy->getBehaviorKind(),
+ InnerTy);
+ if (Result.isNull())
+ return QualType();
+ }
+
+ OverflowBehaviorTypeLoc NewTL = TLB.push<OverflowBehaviorTypeLoc>(Result);
+ NewTL.initializeLocal(SemaRef.Context, TL.getAttrLoc());
+ return Result;
+}
+
template <typename Derived>
QualType TreeTransform<Derived>::TransformHLSLAttributedResourceType(
TypeLocBuilder &TLB, HLSLAttributedResourceTypeLoc TL) {
diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp
index aec61322fb8be..700e29d6ac698 100644
--- a/clang/lib/Serialization/ASTReader.cpp
+++ b/clang/lib/Serialization/ASTReader.cpp
@@ -7498,6 +7498,10 @@ void TypeLocReader::VisitBTFTagAttributedTypeLoc(BTFTagAttributedTypeLoc TL) {
// Nothing to do.
}
+void TypeLocReader::VisitOverflowBehaviorTypeLoc(OverflowBehaviorTypeLoc TL) {
+ TL.setAttrLoc(readSourceLocation());
+}
+
void TypeLocReader::VisitHLSLAttributedResourceTypeLoc(
HLSLAttributedResourceTypeLoc TL) {
// Nothing to do.
diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp
index 667e04049dac8..7ad7f5589382f 100644
--- a/clang/lib/Serialization/ASTWriter.cpp
+++ b/clang/lib/Serialization/ASTWriter.cpp
@@ -604,6 +604,10 @@ void TypeLocWriter::VisitBTFTagAttributedTypeLoc(BTFTagAttributedTypeLoc TL) {
// Nothing to do.
}
+void TypeLocWriter::VisitOverflowBehaviorTypeLoc(OverflowBehaviorTypeLoc TL) {
+ addSourceLocation(TL.getAttrLoc());
+}
+
void TypeLocWriter::VisitHLSLAttributedResourceTypeLoc(
HLSLAttributedResourceTypeLoc TL) {
// Nothing to do.
diff --git a/clang/test/AST/ast-print-overflow-behavior.cpp b/clang/test/AST/ast-print-overflow-behavior.cpp
new file mode 100644
index 0000000000000..f66479951021f
--- /dev/null
+++ b/clang/test/AST/ast-print-overflow-behavior.cpp
@@ -0,0 +1,17 @@
+// RUN: %clang_cc1 -foverflow-behavior-types -std=c++11 -ast-print %s -o - | FileCheck %s --check-prefix=PRINT
+
+// RUN: %clang_cc1 -foverflow-behavior-types -std=c++11 -fsyntax-only %s
+// RUN: %clang_cc1 -foverflow-behavior-types -std=c++11 -ast-print %s -o %t.1.cpp
+// RUN: %clang_cc1 -foverflow-behavior-types -std=c++11 -ast-print %t.1.cpp -o %t.2.cpp
+// RUN: diff %t.1.cpp %t.2.cpp
+
+extern int __attribute__((overflow_behavior(trap))) a;
+extern int __attribute__((overflow_behavior(wrap))) b;
+
+extern int __ob_trap c;
+extern int __ob_wrap d;
+
+// PRINT: extern __ob_trap int a;
+// PRINT: extern __ob_wrap int b;
+// PRINT: extern __ob_trap int c;
+// PRINT: extern __ob_wrap int d;
diff --git a/clang/test/AST/overflow-behavior-keywords-ast.cpp b/clang/test/AST/overflow-behavior-keywords-ast.cpp
new file mode 100644
index 0000000000000..4269db7bdbfbb
--- /dev/null
+++ b/clang/test/AST/overflow-behavior-keywords-ast.cpp
@@ -0,0 +1,16 @@
+// RUN: %clang_cc1 -foverflow-behavior-types -ast-dump %s | FileCheck %s
+
+// Test that keyword and attribute syntax produce the same OverflowBehaviorType
+
+// Attribute syntax
+int __attribute__((overflow_behavior(wrap))) attr_wrap;
+int __attribute__((overflow_behavior(trap))) attr_trap;
+
+// Keyword syntax
+int __ob_wrap keyword_wrap;
+int __ob_trap keyword_trap;
+
+// CHECK: VarDecl {{.*}} attr_wrap '__ob_wrap int'
+// CHECK: VarDecl {{.*}} attr_trap '__ob_trap int'
+// CHECK: VarDecl {{.*}} keyword_wrap '__ob_wrap int'
+// CHECK: VarDecl {{.*}} keyword_trap '__ob_trap int'
diff --git a/clang/test/CodeGen/mangle-ms-overflow-behavior.cpp b/clang/test/CodeGen/mangle-ms-overflow-behavior.cpp
new file mode 100644
index 0000000000000..06af5b36f3eb1
--- /dev/null
+++ b/clang/test/CodeGen/mangle-ms-overflow-behavior.cpp
@@ -0,0 +1,12 @@
+// RUN: %clang_cc1 -emit-llvm %s -o - -fms-extensions -triple=x86_64-pc-win32 -foverflow-behavior-types | FileCheck %s
+
+#define __wrap __attribute__((overflow_behavior(wrap)))
+#define __trap __attribute__((overflow_behavior(trap)))
+
+typedef int __ob_wrap int_wrap;
+
+// CHECK: define dso_local void @"?test_wrap_int@@YAXU?$ObtWrap_ at H@__clang@@@Z"
+void test_wrap_int(int_wrap x) {}
+
+// CHECK: define dso_local void @"?test_trap_int@@YAXU?$ObtTrap_ at H@__clang@@@Z"
+void test_trap_int(int __ob_trap y) {}
diff --git a/clang/test/CodeGen/overflow-behavior-types-extensions.c b/clang/test/CodeGen/overflow-behavior-types-extensions.c
new file mode 100644
index 0000000000000..50e86c64571a1
--- /dev/null
+++ b/clang/test/CodeGen/overflow-behavior-types-extensions.c
@@ -0,0 +1,104 @@
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -foverflow-behavior-types -std=c2x %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK
+
+#define __wrap __attribute__((overflow_behavior(wrap)))
+#define __no_trap __attribute__((overflow_behavior(trap)))
+
+typedef int __ob_wrap w_int;
+typedef int __ob_trap no_trap_int;
+
+
+// CHECK-LABEL: define {{.*}} @generic_selection_test_nomatch
+int generic_selection_test_nomatch(int x) {
+ // CHECK: ret i32 3
+ return _Generic(x, w_int: 1, no_trap_int: 2, default: 3);
+}
+
+// CHECK-LABEL: define {{.*}} @generic_selection_test_obtmatch
+int generic_selection_test_obtmatch(w_int x) {
+ // CHECK: ret i32 1
+ return _Generic(x, w_int: 1, no_trap_int: 2, default: 3);
+}
+
+// CHECK-LABEL: define {{.*}} @generic_selection_test_obt_nomatch
+int generic_selection_test_obt_nomatch(w_int x) {
+ // CHECK: ret i32 3
+ return _Generic(x, int: 1, char: 2, default: 3);
+}
+
+// CHECK-LABEL: define {{.*}} @signed_bitint_test
+void signed_bitint_test(_BitInt(4) __ob_trap a, _BitInt(8) __ob_trap b, _BitInt(99) __ob_wrap c) {
+ // CHECK: call { i32, i1 } @llvm.sadd.with.overflow.i32(i32
+ (a + 1);
+
+ // CHECK: call { i32, i1 } @llvm.sadd.with.overflow.i32(i32
+ (b + 1);
+
+ // CHECK: add i99 {{.*}}, 1
+ (c + 1);
+}
+
+// CHECK-LABEL: define {{.*}} @unsigned_bitint_test
+void unsigned_bitint_test(unsigned _BitInt(4) __ob_trap a, unsigned _BitInt(8) __ob_trap b, unsigned _BitInt(99) __ob_wrap c) {
+ // CHECK: call { i32, i1 } @llvm.sadd.with.overflow.i32(i32
+ (a + 1);
+
+ // CHECK: call { i32, i1 } @llvm.sadd.with.overflow.i32(i32
+ (b + 1);
+
+ // CHECK: add i99 {{.*}}, 1
+ (c + 1);
+}
+
+// CHECK-LABEL: define {{.*}} @generic_obt_vs_underlying
+int generic_obt_vs_underlying(int plain, int __ob_wrap wrapped) {
+ // Regular int should match int case, store 1
+ // CHECK: store i32 1, ptr %plain_result
+ int plain_result = _Generic(plain,
+ int: 1,
+ int __ob_wrap: 2,
+ default: 3);
+
+ // Wrapped int should match __ob_wrap case, store 2
+ // CHECK: store i32 2, ptr %wrapped_result
+ int wrapped_result = _Generic(wrapped,
+ int: 1,
+ int __ob_wrap: 2,
+ default: 3);
+
+ // CHECK: add nsw i32
+ // CHECK: ret i32
+ return plain_result + wrapped_result; // Should return 1 + 2 = 3
+}
+
+// CHECK-LABEL: define {{.*}} @generic_comprehensive
+int generic_comprehensive(int __ob_wrap w, int __ob_trap t, int plain) {
+ // CHECK: store i32 111, ptr %w_val
+ int w_val = _Generic(w,
+ int: 100,
+ int __ob_wrap: 111,
+ int __ob_trap: 222,
+ char: 333,
+ default: 999);
+
+ // CHECK: store i32 222, ptr %t_val
+ int t_val = _Generic(t,
+ int: 100,
+ int __ob_wrap: 111,
+ int __ob_trap: 222,
+ char: 333,
+ default: 999);
+
+ // CHECK: store i32 100, ptr %p_val
+ int p_val = _Generic(plain,
+ int: 100,
+ int __ob_wrap: 111,
+ int __ob_trap: 222,
+ char: 333,
+ default: 999);
+
+ // CHECK: add nsw i32
+ // CHECK: add nsw i32
+ // CHECK: ret i32
+ return w_val + t_val + p_val; // Should return 111 + 222 + 100 = 433
+}
+
diff --git a/clang/test/CodeGen/overflow-behavior-types-operators.cpp b/clang/test/CodeGen/overflow-behavior-types-operators.cpp
new file mode 100644
index 0000000000000..22794cac4f3ad
--- /dev/null
+++ b/clang/test/CodeGen/overflow-behavior-types-operators.cpp
@@ -0,0 +1,70 @@
+// RUN: %clang_cc1 -triple x86_64-linux-gnu %s -foverflow-behavior-types \
+// RUN: -fsanitize=signed-integer-overflow -emit-llvm -o - -std=c++14 | FileCheck %s
+
+#define __wrap __attribute__((overflow_behavior(wrap)))
+#define __no_trap __attribute__((overflow_behavior(trap)))
+
+typedef int __ob_wrap wrap_int;
+typedef int __ob_trap no_trap_int;
+typedef unsigned int __ob_wrap u_wrap_int;
+typedef unsigned int __ob_trap u_no_trap_int;
+
+//===----------------------------------------------------------------------===//
+// Compound Assignment Operators
+//===----------------------------------------------------------------------===//
+
+// CHECK-LABEL: define {{.*}} @_Z28compound_assignment_operatorv
+void compound_assignment_operator() {
+ wrap_int a = 1;
+ // CHECK: add i32
+ a += 1;
+
+ no_trap_int b = 1;
+ // CHECK: llvm.sadd.with.overflow.i32
+ b += 1;
+
+ u_wrap_int c = 1;
+ // CHECK: sub i32
+ c -= 1;
+
+ u_no_trap_int d = 1;
+ // CHECK: llvm.usub.with.overflow.i32
+ d -= 1;
+
+ wrap_int e = 2;
+ // CHECK: mul i32
+ e *= 2;
+
+ no_trap_int f = 2;
+ // CHECK: llvm.smul.with.overflow.i32
+ f *= 2;
+}
+
+//===----------------------------------------------------------------------===//
+// Bitwise and Shift Operators
+//===----------------------------------------------------------------------===//
+
+// CHECK-LABEL: define {{.*}} @_Z27bitwise_and_shift_operatorsv
+void bitwise_and_shift_operators() {
+ wrap_int a = 1;
+ // CHECK: shl i32
+ // No overflow check for shifts
+ a <<= 1;
+
+ no_trap_int b = 1;
+ // CHECK: ashr i32
+ // No overflow check for shifts
+ b >>= 1;
+
+ wrap_int c = 1;
+ // CHECK: and i32
+ c &= 1;
+
+ no_trap_int d = 1;
+ // CHECK: xor i32
+ d ^= 1;
+
+ u_wrap_int e = 1;
+ // CHECK: or i32
+ e |= 1;
+}
diff --git a/clang/test/CodeGen/overflow-behavior-types-promotions.cpp b/clang/test/CodeGen/overflow-behavior-types-promotions.cpp
new file mode 100644
index 0000000000000..bebc2c3b7001f
--- /dev/null
+++ b/clang/test/CodeGen/overflow-behavior-types-promotions.cpp
@@ -0,0 +1,49 @@
+// RUN: %clang_cc1 -triple x86_64-linux-gnu %s -foverflow-behavior-types \
+// RUN: -fsanitize=signed-integer-overflow,unsigned-integer-overflow -emit-llvm -o - -std=c++14 | FileCheck %s
+
+#define __wrap __attribute__((overflow_behavior(wrap)))
+#define __no_trap __attribute__((overflow_behavior(trap)))
+
+typedef int __ob_wrap wrap_int;
+typedef char __ob_wrap wrap_char;
+typedef int __ob_trap no_trap_int;
+typedef unsigned int __ob_wrap u_wrap_int;
+typedef unsigned int __ob_trap u_no_trap_int;
+
+// CHECK-LABEL: define {{.*}} @_Z30conditional_operator_promotionbU8ObtWrap_cU8ObtTrap_ii
+void conditional_operator_promotion(bool cond, wrap_char w, no_trap_int nw, int i) {
+ // CHECK: cond.end:
+ // CHECK-NEXT: %cond1 = phi i32
+ // CHECK-NEXT: store i32 %cond1, ptr %r1
+ // CHECK-NEXT: %{{.*}} = load i32, ptr %r1
+ // CHECK-NEXT: add i32
+ auto r1 = cond ? w : i;
+ (void)(r1 + 2147483647);
+
+ // no_trap wins over wrap.
+ // CHECK: cond.end6:
+ // CHECK-NEXT: %cond7 = phi i32
+ // CHECK-NEXT: store i32 %cond7, ptr %r2
+ // CHECK-NEXT: %{{.*}} = load i32, ptr %r2
+ // CHECK-NEXT: call { i32, i1 } @llvm.sadd.with.overflow.i32
+ auto r2 = cond ? w : nw;
+ (void)(r2 + 2147483647);
+}
+
+// CHECK-LABEL: define {{.*}} @_Z20promotion_rules_testU8ObtWrap_iU8ObtTrap_iU8ObtWrap_jU8ObtTrap_j
+void promotion_rules_test(wrap_int sw, no_trap_int snw, u_wrap_int uw, u_no_trap_int unw) {
+ // Unsigned is favored over signed for same-behavior OBTs.
+ // CHECK: add i32
+ auto r1 = sw + uw;
+ (void)r1;
+
+ // trap is favored over wrap. Result is unsigned no_trap.
+ // CHECK: call { i32, i1 } @llvm.uadd.with.overflow.i32
+ auto r2 = sw + unw;
+ (void)r2;
+
+ // trap is favored over wrap. Result is unsigned trap (unsigned int + int → unsigned int in C).
+ // CHECK: call { i32, i1 } @llvm.uadd.with.overflow.i32
+ auto r3 = uw + snw;
+ (void)r3;
+}
diff --git a/clang/test/CodeGen/overflow-behavior-types-scl.c b/clang/test/CodeGen/overflow-behavior-types-scl.c
new file mode 100644
index 0000000000000..1bc1139bb72b0
--- /dev/null
+++ b/clang/test/CodeGen/overflow-behavior-types-scl.c
@@ -0,0 +1,43 @@
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+
+// RUN: %clang_cc1 -triple x86_64-linux-gnu %t/test.c -fsanitize-ignorelist=%t/sio.scl -foverflow-behavior-types -fsanitize=signed-integer-overflow -emit-llvm -o - | FileCheck %s --check-prefix=SIO
+// RUN: %clang_cc1 -triple x86_64-linux-gnu %t/test.c -fsanitize-ignorelist=%t/uio.scl -foverflow-behavior-types -fsanitize=unsigned-integer-overflow -emit-llvm -o - | FileCheck %s --check-prefix=UIO
+
+//--- sio.scl
+[signed-integer-overflow]
+# ignore signed-integer-overflow instrumentation across all types
+type:*
+
+//--- uio.scl
+[unsigned-integer-overflow]
+# ignore unsigned-integer-overflow instrumentation across all types
+type:*
+
+//--- test.c
+#define __wrap __attribute__((overflow_behavior("wrap")))
+#define __no_trap __attribute__((overflow_behavior("trap")))
+
+// SIO-LABEL: define {{.*}} @foo
+// UIO-LABEL: define {{.*}} @foo
+void foo(void) {
+ // SIO-LABEL: load volatile i32, ptr @a, align 4
+ volatile extern int a;
+ volatile extern char b;
+ volatile extern char __ob_trap c; // nowrap has precedence over scl entries
+
+ // SIO: add nsw i32
+ (a + 1);
+ // SIO: add nsw i32
+ (b + 1);
+ // SIO: @llvm.sadd.with.overflow.i32
+ (c + 1);
+
+ // UIO-LABEL: load volatile i32, ptr @d, align 4
+ volatile extern unsigned int d;
+ volatile extern unsigned short __ob_trap e;
+ // UIO: add i32
+ (d + 1);
+ // UIO: @llvm.sadd.with.overflow.i32
+ (e + 1);
+}
diff --git a/clang/test/CodeGen/overflow-behavior-types.c b/clang/test/CodeGen/overflow-behavior-types.c
new file mode 100644
index 0000000000000..abfdc7c639cab
--- /dev/null
+++ b/clang/test/CodeGen/overflow-behavior-types.c
@@ -0,0 +1,176 @@
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -foverflow-behavior-types %s \
+// RUN: -fsanitize=signed-integer-overflow,unsigned-integer-overflow,implicit-signed-integer-truncation,implicit-unsigned-integer-truncation \
+// RUN: -emit-llvm -o - | FileCheck %s --check-prefix=DEFAULT
+
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -foverflow-behavior-types %s -ftrapv \
+// RUN: -fsanitize=signed-integer-overflow,unsigned-integer-overflow,implicit-signed-integer-truncation,implicit-unsigned-integer-truncation \
+// RUN: -emit-llvm -o - | FileCheck %s --check-prefix=DEFAULT
+
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -foverflow-behavior-types %s \
+// RUN: -ftrapv -ftrapv-handler OVERFLOW_HANDLER \
+// RUN: -emit-llvm -o - | FileCheck %s --check-prefix=TRAPV-HANDLER
+
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -foverflow-behavior-types %s -fwrapv \
+// RUN: -fsanitize=signed-integer-overflow,unsigned-integer-overflow,implicit-signed-integer-truncation,implicit-unsigned-integer-truncation \
+// RUN: -emit-llvm -o - | FileCheck %s --check-prefix=DEFAULT
+
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -foverflow-behavior-types %s \
+// RUN: -fsanitize-undefined-ignore-overflow-pattern=all \
+// RUN: -fsanitize=signed-integer-overflow,unsigned-integer-overflow,implicit-signed-integer-truncation,implicit-unsigned-integer-truncation \
+// RUN: -emit-llvm -o - | FileCheck %s --check-prefix=EXCL
+
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -foverflow-behavior-types %s \
+// RUN: -emit-llvm -o - | FileCheck %s --check-prefix=NOSAN
+
+#define __wrap __attribute__((overflow_behavior("wrap")))
+#define __no_trap __attribute__((overflow_behavior("trap")))
+
+// DEFAULT-LABEL: define {{.*}} @test1
+// TRAPV-HANDLER-LABEL: define {{.*}} @test1
+// NOSAN-LABEL: define {{.*}} @test1
+void test1(int __ob_wrap a, int __ob_trap b) {
+ // DEFAULT: add i32
+ // TRAPV-HANDLER: add i32
+ // NOSAN: add i32
+ (a + 1);
+
+ // DEFAULT: llvm.sadd.with.overflow.i32
+ // TRAPV-HANDLER: %[[T0:.*]] = load i32, ptr %b
+ // TRAPV-HANDLER: call {{.*}} @OVERFLOW_HANDLER(i64 %[[T0]]
+ // NOSAN: %[[T0:.*]] = load i32, ptr %b
+ // NOSAN-NEXT: %[[T1:.*]] = call {{.*}} @llvm.sadd.with.overflow.i32(i32 %[[T0]]
+ // NOSAN: %[[OF:.*]] = extractvalue {{.*}} %[[T1]], 1
+ // NOSAN-NEXT: %[[XOR:.*]] = xor i1 %[[OF]]
+ // NOSAN-NEXT: br i1 %[[XOR]]{{.*}}cont, label %[[TRAP:.*]], !prof
+ // NOSAN: [[TRAP]]:
+ // NOSAN-NEXT: call void @llvm.ubsantrap
+ (b + 1);
+
+ // DEFAULT: sub i32 0
+ (-a);
+
+ // DEFAULT: llvm.ssub.with.overflow.i32
+ (-b);
+
+ // DEFAULT: add i32
+ a++;
+ // DEFAULT: llvm.sadd.with.overflow.i32
+ b++;
+
+ // DEFAULT: add i32
+ ++a;
+ // DEFAULT: llvm.sadd.with.overflow.i32
+ ++b;
+
+ volatile extern int divisor;
+ // DEFAULT: %[[T0:.*]] = load i32, ptr %a
+ // DEFAULT-NEXT: %[[T1:.*]] = load volatile i32, ptr @divisor
+ // DEFAULT-NOT: br {{.*}} %handler.divrem_overflow
+ // DEFAULT: sdiv i32 %[[T0]], %[[T1]]
+ a/divisor;
+
+ // DEFAULT: %[[T0:.*]] = load i32, ptr %b
+ // DEFAULT-NEXT: %[[T1:.*]] = load volatile i32, ptr @divisor
+ // DEFAULT: br {{.*}} %handler.divrem_overflow
+ b/divisor;
+}
+
+// DEFAULT-LABEL: define {{.*}} @test2
+void test2(unsigned char __ob_wrap a, unsigned char __ob_trap b) {
+ // DEFAULT: add i32
+ (a + 1);
+ // DEFAULT: llvm.sadd.with.overflow.i32
+ (b + 1);
+
+ // DEFAULT: %[[T0:.*]] = load volatile i64, ptr @big
+ // DEFAULT: trunc i64
+ // DEFAULT-NOT: icmp eq i64
+ // No truncation check for __ob_wrap destination
+ volatile extern unsigned long long big;
+ a = big;
+
+ // DEFAULT: %[[T1:.*]] = load volatile i64, ptr @big
+ // DEFAULT: %[[TRUNC2:.*]] = icmp eq i64 {{.*}} %[[T1]]
+ // DEFAULT: br i1 %[[TRUNC2]], {{.*}} %handler.implicit_conversion
+ b = big;
+}
+
+// DEFAULT-LABEL: define {{.*}} @test3
+void test3(void) {
+ volatile extern char __ob_wrap a;
+ volatile extern short __ob_wrap b;
+ // DEFAULT: add i32
+ (a + b);
+
+ // no_trap has precedence over wrap, regardless of bit widths
+ volatile extern unsigned long long __ob_wrap c;
+ volatile extern char __ob_trap d;
+
+ // DEFAULT: %[[T0:.*]] = load volatile i64, ptr @c
+ // DEFAULT: %[[T1:.*]] = load volatile i8, ptr @d
+ // DEFAULT: @llvm.uadd.with.overflow.i64
+ (c + d);
+
+ volatile extern int __ob_trap e;
+ volatile extern unsigned int __ob_wrap f;
+
+ // DEFAULT: @llvm.usub.with.overflow.i32
+ (e - f);
+}
+
+typedef int __attribute__((overflow_behavior(wrap))) wrap_int;
+typedef int __attribute__((overflow_behavior(trap))) no_trap_int;
+// DEFAULT-LABEL: define {{.*}} @typedefs
+void typedefs(no_trap_int a, wrap_int b) {
+ // DEFAULT: llvm.sadd.with.overflow.i32
+ (a + 100);
+
+ // DEFAULT: add i32
+ (b + 100);
+}
+
+// EXCL-LABEL: define {{.*}} @ignored_patterns
+void ignored_patterns(unsigned long __attribute__((overflow_behavior(trap))) a) {
+ // EXCL: %[[T0:.*]] = load i64, ptr %a.addr
+ // EXCL-NEXT: add i64 %[[T0]], -1
+ while (a--) { /*...*/ }
+
+ // EXCL: %[[T1:.*]] = load i64, ptr %a.addr
+ // EXCL: %[[T2:.*]] = load volatile i64, ptr %b
+ // EXCL-NEXT: add i64 %[[T1]], %[[T2]]
+ volatile unsigned long __attribute__((overflow_behavior(trap))) b;
+ if (a + b < a) { /*...*/ }
+}
+
+// NOSAN-LABEL: define {{.*}} @implicit_truncation_return
+int implicit_truncation_return(__ob_trap unsigned long long result) {
+ // NOSAN: trunc i64 {{.*}} to i32
+ // NOSAN: sext i32 {{.*}} to i64
+ // NOSAN: icmp eq i64
+ // NOSAN: br i1 {{.*}}, label %{{.*}}, label %trap
+ // NOSAN: trap:
+ // NOSAN: call void @llvm.ubsantrap(i8 7)
+ return result;
+}
+
+// NOSAN-LABEL: define {{.*}} @implicit_truncation_assignment
+void implicit_truncation_assignment(__ob_trap unsigned long long result) {
+ // NOSAN: trunc i64 {{.*}} to i32
+ // NOSAN: sext i32 {{.*}} to i64
+ // NOSAN: icmp eq i64
+ // NOSAN: br i1 {{.*}}, label %{{.*}}, label %trap
+ // NOSAN: trap:
+ // NOSAN: call void @llvm.ubsantrap(i8 7)
+ int a = result;
+}
+
+// NOSAN-LABEL: define {{.*}} @explicit_truncation_cast
+int explicit_truncation_cast(__ob_trap unsigned long long result) {
+ // NOSAN: trunc i64 {{.*}} to i32
+ // NOSAN: sext i32 {{.*}} to i64
+ // NOSAN: icmp eq i64
+ // NOSAN: br i1 {{.*}}, label %{{.*}}, label %trap
+ // NOSAN: trap:
+ // NOSAN: call void @llvm.ubsantrap(i8 7)
+ return (int)result;
+}
diff --git a/clang/test/CodeGen/overflow-behavior-types.cpp b/clang/test/CodeGen/overflow-behavior-types.cpp
new file mode 100644
index 0000000000000..25ca14bbd3b40
--- /dev/null
+++ b/clang/test/CodeGen/overflow-behavior-types.cpp
@@ -0,0 +1,51 @@
+// RUN: %clang_cc1 -triple x86_64-linux-gnu %s -foverflow-behavior-types \
+// RUN: -fsanitize=signed-integer-overflow,unsigned-integer-overflow,implicit-signed-integer-truncation,implicit-unsigned-integer-truncation \
+// RUN: -emit-llvm -o - | FileCheck %s --check-prefix=DEFAULT
+
+#define __wrap __attribute__((overflow_behavior(wrap)))
+#define __trap __attribute__((overflow_behavior(trap)))
+
+class Foo {
+public:
+ unsigned long other;
+ char __ob_wrap a;
+
+ Foo() = delete;
+ Foo(char _a) : a(_a) {}
+
+ decltype(a) getA() const { return a; }
+};
+
+// DEFAULT-LABEL: define {{.*}} @_Z12test_membersc
+void test_members(char some) {
+ Foo foo{some};
+
+ // DEFAULT: %[[A:.*]] = getelementptr inbounds nuw %class.Foo, ptr %foo, i32 0, i32 1
+ // DEFAULT-NEXT: %[[T1:.*]] = load i8, ptr %[[A]]
+ // DEFAULT-NEXT: %[[CONV:.*]] = sext i8 %[[T1]] to i32
+ // DEFAULT-NEXT: %inc{{\d*}} = add i32 %[[CONV]], 1
+ (++foo.a);
+
+ // DEFAULT: %[[CALL:.*]] = call noundef signext i8 @_ZNK3Foo4getAEv
+ // DEFAULT-NEXT: sext i8 %[[CALL]] to i32
+ // DEFAULT-NEXT: add i32 {{.*}}, 1
+ (void)(foo.getA() + 1);
+}
+
+// DEFAULT-LABEL: define {{.*}} @_Z9test_autoU8ObtWrap_c
+void test_auto(char __ob_wrap a) {
+ auto b = a;
+
+ // DEFAULT: %[[T1:.*]] = load i8, ptr %b
+ // DEFAULT: sub i32 {{.*}}, 1
+ (b - 1); // no instrumentation
+}
+
+
+int overloadme(__ob_trap int a) { return 0; }
+int overloadme(int a) { return 1; } // make sure we pick this one
+// DEFAULT-LABEL: define {{.*}}test_overload_set_exact_match
+int test_overload_set_exact_match(int a) {
+ // DEFAULT: call {{.*}} @_Z10overloadmei
+ return overloadme(a);
+}
diff --git a/clang/test/Misc/pragma-attribute-supported-attributes-list.test b/clang/test/Misc/pragma-attribute-supported-attributes-list.test
index 1e1d4a356f515..13a701dac82fa 100644
--- a/clang/test/Misc/pragma-attribute-supported-attributes-list.test
+++ b/clang/test/Misc/pragma-attribute-supported-attributes-list.test
@@ -172,6 +172,7 @@
// CHECK-NEXT: OpenCLIntelReqdSubGroupSize (SubjectMatchRule_function)
// CHECK-NEXT: OpenCLNoSVM (SubjectMatchRule_variable)
// CHECK-NEXT: OptimizeNone (SubjectMatchRule_function, SubjectMatchRule_objc_method)
+// CHECK-NEXT: OverflowBehavior (SubjectMatchRule_variable, SubjectMatchRule_type_alias, SubjectMatchRule_field)
// CHECK-NEXT: Overloadable (SubjectMatchRule_function)
// CHECK-NEXT: Owner (SubjectMatchRule_record_not_is_union)
// CHECK-NEXT: ParamTypestate (SubjectMatchRule_variable_is_parameter)
diff --git a/clang/test/Sema/attr-overflow-behavior-constexpr.cpp b/clang/test/Sema/attr-overflow-behavior-constexpr.cpp
new file mode 100644
index 0000000000000..0c9ed48a6fef4
--- /dev/null
+++ b/clang/test/Sema/attr-overflow-behavior-constexpr.cpp
@@ -0,0 +1,43 @@
+// RUN: %clang_cc1 -triple x86_64-linux-gnu %s -foverflow-behavior-types -verify -fsyntax-only -std=c++14
+
+#define __wrap __attribute__((overflow_behavior(wrap)))
+#define __no_trap __attribute__((overflow_behavior(trap)))
+
+typedef int __ob_wrap wrap_int;
+typedef int __ob_trap no_trap_int;
+
+constexpr wrap_int add(wrap_int a, wrap_int b) {
+ return a + b;
+}
+
+constexpr no_trap_int sub(no_trap_int a, no_trap_int b) {
+ return a - b; // expected-note {{-2147483649 is outside the range of representable values}}
+}
+
+void constexpr_test() {
+ constexpr wrap_int max = 2147483647;
+ constexpr wrap_int one = 1;
+ static_assert(add(max, one) == -2147483648, "constexpr wrapping failed");
+
+ constexpr no_trap_int min = -2147483648;
+ constexpr no_trap_int one_nw = 1;
+ constexpr no_trap_int res = sub(min, one_nw); // expected-error {{constexpr variable 'res' must be initialized by a constant expression}} expected-note {{in call to 'sub(-2147483648, 1)'}}
+}
+
+template <typename T>
+void check_deduction_wrap(T) {
+ static_assert(__is_same(T, wrap_int), "T should be deduced as wrap_int");
+}
+
+template <typename T>
+void check_deduction_no_trap(T) {
+ static_assert(__is_same(T, no_trap_int), "T should be deduced as no_trap_int");
+}
+
+void template_deduction_test() {
+ wrap_int w = 0;
+ check_deduction_wrap(w);
+
+ no_trap_int nw = 0;
+ check_deduction_no_trap(nw);
+}
diff --git a/clang/test/Sema/attr-overflow-behavior-format-strings.c b/clang/test/Sema/attr-overflow-behavior-format-strings.c
new file mode 100644
index 0000000000000..83f8c35824b6c
--- /dev/null
+++ b/clang/test/Sema/attr-overflow-behavior-format-strings.c
@@ -0,0 +1,93 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -Wformat -foverflow-behavior-types -isystem %S/Inputs %s
+
+// Test format string checking with overflow behavior types
+// This ensures that OverflowBehaviorTypes work seamlessly with printf/scanf
+// without spurious format warnings.
+
+int printf(const char *restrict, ...);
+int sprintf(char *restrict, const char *restrict, ...);
+int snprintf(char *restrict, __SIZE_TYPE__, const char *restrict, ...);
+
+int scanf(const char *restrict, ...);
+int sscanf(const char *restrict, const char *restrict, ...);
+
+#define __wrap __attribute__((overflow_behavior(wrap)))
+#define __trap __attribute__((overflow_behavior(trap)))
+
+typedef int __ob_wrap wrap_int;
+typedef int __ob_trap no_trap_int;
+typedef unsigned int __ob_wrap wrap_uint;
+typedef unsigned int __ob_trap no_trap_uint;
+typedef short __ob_wrap wrap_short;
+typedef long __ob_trap no_trap_long;
+
+void test_printf_compatibility() {
+ wrap_int wi = 42;
+ no_trap_int ni = 42;
+ wrap_uint wu = 42U;
+ no_trap_uint nu = 42U;
+ wrap_short ws = 42;
+ no_trap_long nl = 42L;
+
+ // These should all work without warnings - OBTs should be treated as their underlying types
+ printf("%d", wi);
+ printf("%d", ni);
+ printf("%u", wu);
+ printf("%u", nu);
+ printf("%hd", ws);
+ printf("%ld", nl);
+ printf("%x", wu);
+ printf("%o", nu);
+
+ printf("%s", wi); // expected-warning{{format specifies type 'char *' but the argument has type 'int'}}
+ printf("%f", wi); // expected-warning{{format specifies type 'double' but the argument has type 'int'}}
+ printf("%ld", wi); // expected-warning{{format specifies type 'long' but the argument has type 'int'}}
+}
+
+void test_scanf_compatibility() {
+ wrap_int wi;
+ no_trap_int ni;
+ wrap_uint wu;
+ no_trap_uint nu;
+ wrap_short ws;
+ no_trap_long nl;
+
+ // These should all work without warnings - pointers to OBTs should be treated as pointers to underlying types
+ scanf("%d", &wi);
+ scanf("%d", &ni);
+ scanf("%u", &wu);
+ scanf("%u", &nu);
+ scanf("%hd", &ws);
+ scanf("%ld", &nl);
+ scanf("%x", &wu);
+ scanf("%o", &nu);
+
+ scanf("%s", &wi); // expected-warning{{format specifies type 'char *' but the argument has type 'wrap_int *'}}
+ scanf("%f", &wi); // expected-warning{{format specifies type 'float *' but the argument has type 'wrap_int *'}}
+ scanf("%ld", &wi); // expected-warning{{format specifies type 'long *' but the argument has type 'wrap_int *'}}
+}
+
+void test_mixed_formats() {
+ wrap_int wi = 42;
+ int regular_int = 42;
+
+ printf("%d + %d = %d", wi, regular_int, wi + regular_int);
+ scanf("%d %d", &wi, ®ular_int);
+}
+
+typedef unsigned char __ob_wrap wrap_byte;
+typedef long long __ob_trap safe_longlong;
+
+void test_typedef_formats() {
+ wrap_byte wb = 255;
+ safe_longlong sll = 123456789LL;
+
+ printf("%hhu", wb); // OK: wrap_byte -> unsigned char for %hhu
+ printf("%lld", sll); // OK: safe_longlong -> long long for %lld
+
+ scanf("%hhu", &wb); // OK: &wb treated as unsigned char* for %hhu
+ scanf("%lld", &sll); // OK: &sll treated as long long* for %lld
+
+ printf("%d", wb); // OK: wb implicitly cast to int
+ printf("%d", sll); // expected-warning{{format specifies type 'int' but the argument has type 'long long'}}
+}
diff --git a/clang/test/Sema/attr-overflow-behavior-off.c b/clang/test/Sema/attr-overflow-behavior-off.c
new file mode 100644
index 0000000000000..2aac2a887e060
--- /dev/null
+++ b/clang/test/Sema/attr-overflow-behavior-off.c
@@ -0,0 +1,3 @@
+// RUN: %clang_cc1 %s -Winteger-overflow -Wno-unused-value -verify -fsyntax-only
+
+typedef int __attribute__((overflow_behavior(wrap))) wrap_int; // expected-warning {{'overflow_behavior' attribute is ignored because it is not enabled;}}
diff --git a/clang/test/Sema/attr-overflow-behavior-templates.cpp b/clang/test/Sema/attr-overflow-behavior-templates.cpp
new file mode 100644
index 0000000000000..6f7a064b83e14
--- /dev/null
+++ b/clang/test/Sema/attr-overflow-behavior-templates.cpp
@@ -0,0 +1,73 @@
+// RUN: %clang_cc1 %s -foverflow-behavior-types -verify -fsyntax-only -Wimplicit-overflow-behavior-conversion
+
+#define __wrap __attribute__((overflow_behavior(wrap)))
+#define __trap __attribute__((overflow_behavior(trap)))
+
+template<typename T>
+constexpr int template_overload_test(T) {
+ return 10;
+}
+
+constexpr int template_overload_test(int) {
+ return 20;
+}
+
+constexpr int template_overload_test(__ob_wrap int) {
+ return 30;
+}
+
+constexpr int template_overload_test(__ob_trap int) {
+ return 40;
+}
+
+void test_template_overload_resolution() {
+ static_assert(template_overload_test(42) == 20, "int should pick int overload");
+ static_assert(template_overload_test((__ob_wrap int)42) == 30, "__ob_wrap int should pick __ob_wrap int overload");
+ static_assert(template_overload_test((__ob_trap int)42) == 40, "__ob_trap int should pick __ob_trap int overload");
+}
+
+template<typename T>
+struct MultiSpecTester {
+ static constexpr int value = 0;
+};
+
+template<>
+struct MultiSpecTester<int> {
+ static constexpr int value = 1;
+};
+
+void test_choosing_generic_when_only_underlying_present() {
+ static_assert(MultiSpecTester<int>::value == 1, "int should match int specialization");
+ // OBTs don't choose template specialization based on underlying type, they should go with the generic
+ static_assert(MultiSpecTester<__ob_wrap int>::value == 0, "__ob_wrap int should match generic when there isn't a __ob_wrap specialization");
+ static_assert(MultiSpecTester<__ob_trap int>::value == 0, "__ob_trap int should match generic when there isn't a __ob_trap specialization");
+}
+
+template<typename T = int>
+constexpr int only_int_template(int value) {
+ return value + 100;
+}
+
+void test_template_conversion_fallback() {
+ static_assert(only_int_template<int>(42) == 142, "int direct match should work");
+ // expected-warning at +1 {{passing argument of type '__ob_wrap int' to parameter of type 'int' discards overflow behavior at function boundary}}
+ static_assert(only_int_template<int>((__ob_wrap int)42) == 142, "__ob_wrap int implicit conversion should work");
+ // expected-warning at +1 {{passing argument of type '__ob_trap int' to parameter of type 'int' discards overflow behavior at function boundary}}
+ static_assert(only_int_template<int>((__ob_trap int)42) == 142, "__ob_trap int implicit conversion should work");
+}
+
+void simple_overload_test(int);
+void simple_overload_test(__ob_wrap int);
+
+template<typename T>
+void simple_overload_test(T) {}
+
+void test_function_vs_template_overload() {
+ int regular = 42;
+ __ob_wrap int wrapped = 42;
+ __ob_trap int trap = 42;
+
+ simple_overload_test(regular);
+ simple_overload_test(wrapped);
+ simple_overload_test(trap);
+}
diff --git a/clang/test/Sema/attr-overflow-behavior.c b/clang/test/Sema/attr-overflow-behavior.c
new file mode 100644
index 0000000000000..720f2ea60477f
--- /dev/null
+++ b/clang/test/Sema/attr-overflow-behavior.c
@@ -0,0 +1,222 @@
+// RUN: %clang_cc1 %s -Winteger-overflow -Wno-unused-value -foverflow-behavior-types -Woverflow-behavior-conversion -Wconstant-conversion -verify -fsyntax-only -std=c11 -Wno-pointer-sign
+
+typedef int __attribute__((overflow_behavior)) bad_arg_count; // expected-error {{'overflow_behavior' attribute takes one argument}}
+typedef int __attribute__((overflow_behavior(not_real))) bad_arg_spec; // expected-error {{'not_real' is not a valid argument to attribute 'overflow_behavior'}}
+typedef int __attribute__((overflow_behavior("not_real"))) bad_arg_spec_str; // expected-error {{'not_real' is not a valid argument to attribute 'overflow_behavior'}}
+typedef char* __attribute__((overflow_behavior("wrap"))) bad_type; // expected-error {{'overflow_behavior' attribute cannot be applied to non-integer type 'char *'}}
+
+typedef int __attribute__((overflow_behavior(wrap))) ok_wrap; // OK
+typedef long __attribute__((overflow_behavior(trap))) ok_nowrap; // OK
+typedef unsigned long __attribute__((overflow_behavior("wrap"))) str_ok_wrap; // OK
+typedef char __attribute__((overflow_behavior("trap"))) str_ok_nowrap; // OK
+
+void foo() {
+ (2147483647 + 100); // expected-warning {{overflow in expression; result is }}
+ (ok_wrap)2147483647 + 100; // no warn
+}
+
+#define __wrap __attribute__((overflow_behavior(wrap)))
+#define __trap __attribute__((overflow_behavior(trap)))
+
+void ptr(int a) {
+ int __ob_trap *p = &a; // expected-warning {{initializing '__ob_trap int *' with an expression of type 'int *' discards overflow behavior}}
+}
+
+void ptr2(__ob_trap int a) {
+ int *p = &a; // expected-warning {{initializing 'int *' with an expression of type '__ob_trap int *' discards overflow behavior}}
+}
+
+
+// verify semantics of OBT at function boundaries
+void imp_disc_pedantic(unsigned a) {}
+void imp_disc(int a) {}
+void imp_disc_test(unsigned __attribute__((overflow_behavior(wrap))) a) {
+ imp_disc_pedantic(a); // expected-warning {{passing argument of type '__ob_wrap unsigned int' to parameter of type 'unsigned int' discards overflow behavior at function boundary}}
+ // expected-warning at -1 {{implicit conversion from '__ob_wrap unsigned int' to 'unsigned int' discards overflow behavior}}
+ imp_disc(a); // expected-warning {{passing argument of type '__ob_wrap unsigned int' to parameter of type 'int' discards overflow behavior at function boundary}}
+ // expected-warning at -1 {{implicit conversion from '__ob_wrap unsigned int' to 'int' discards overflow behavior}}
+}
+
+void assignment_disc(unsigned __attribute__((overflow_behavior(wrap))) a) {
+ int b = a; // expected-warning {{implicit conversion from '__ob_wrap unsigned int' to 'int' during assignment discards overflow behavior}}
+ int c = (int)a; // OK
+}
+
+void constant_conversion() {
+ short x1 = (int __ob_wrap)100000;
+ short __ob_wrap x2 = (int)100000; // No warning expected
+ // expected-warning at +1 {{implicit conversion from 'int' to '__ob_trap short' changes value from 100000 to -31072}}
+ short __ob_trap x3 = (int)100000;
+ // OBT now propagates through implicit casts, so x4 gets __ob_trap from the explicit cast
+ // expected-warning at +1 {{implicit conversion from '__ob_trap int' to '__ob_trap short' changes value from 100000 to -31072}}
+ short x4 = (int __ob_trap)100000;
+
+ unsigned short __ob_wrap ux1 = (unsigned int)100000; // No warning - wrapping expected
+ unsigned short ux2 = (unsigned int __ob_wrap)100000;
+ unsigned short __ob_trap ux3 = (unsigned int)100000; // expected-warning {{implicit conversion from 'unsigned int' to '__ob_trap unsigned short' changes value from 100000 to 34464}}
+ unsigned short __ob_trap ux4 = (unsigned int __ob_trap)100000; // expected-warning {{implicit conversion from '__ob_trap unsigned int' to '__ob_trap unsigned short' changes value from 100000 to 34464}}
+ unsigned short __ob_trap ux5 = (unsigned int __ob_wrap)100000; // expected-error {{assigning to '__ob_trap unsigned short' from '__ob_wrap unsigned int' with incompatible overflow behavior types ('__ob_trap' and '__ob_wrap')}}
+}
+
+typedef long s64_typedef1;
+typedef s64_typedef1 __attribute__((overflow_behavior(trap))) nw_s64_typedef2;
+nw_s64_typedef2 global_var;
+void test_nested_typedef_control_flow() {
+ // We had a crash during Sema with nested typedefs and control flow, make
+ // sure we don't crash and just warn.
+ if (global_var) {} // expected-warning {{implicit conversion from 'nw_s64_typedef2'}}
+}
+
+int test_discard_on_return(unsigned long __ob_trap a) {
+ return a; // expected-warning {{implicit conversion from '__ob_trap unsigned long' to 'int' discards overflow behavior}}
+}
+
+// Test OBT pointer compatibility
+void test_obt_pointer_compatibility() {
+ unsigned long x = 42;
+ unsigned long __ob_trap y = 42;
+ unsigned long __ob_wrap z = 42;
+
+ unsigned long *px = &x;
+ unsigned long __ob_trap *py = &y;
+ unsigned long __ob_wrap *pz = &z;
+
+ // Same types - should not warn
+ px = &x; // OK
+ py = &y; // OK
+ pz = &z; // OK
+
+ // Different OBT annotations - should warn but allow
+ // expected-warning at +1 {{assigning to 'unsigned long *' from '__ob_trap unsigned long *' discards overflow behavior}}
+ px = py;
+ // expected-warning at +1 {{assigning to 'unsigned long *' from '__ob_wrap unsigned long *' discards overflow behavior}}
+ px = pz;
+ // expected-warning at +1 {{assigning to '__ob_trap unsigned long *' from 'unsigned long *' discards overflow behavior}}
+ py = px;
+ // expected-warning at +1 {{assigning to '__ob_wrap unsigned long *' from 'unsigned long *' discards overflow behavior}}
+ pz = px;
+ // expected-error at +1 {{assigning to '__ob_trap unsigned long *' from '__ob_wrap unsigned long *' with incompatible overflow behavior types ('__ob_trap' and '__ob_wrap')}}
+ py = pz;
+ // expected-error at +1 {{assigning to '__ob_wrap unsigned long *' from '__ob_trap unsigned long *' with incompatible overflow behavior types ('__ob_wrap' and '__ob_trap')}}
+ pz = py;
+}
+
+// Test function parameter passing
+// expected-note at +2 {{passing argument to parameter 'p' here}}
+// expected-note at +1 {{passing argument to parameter 'p' here}}
+void func_takes_regular_ptr(unsigned long *p) {}
+// expected-note at +1 {{passing argument to parameter 'p' here}}
+void func_takes_trap_ptr(unsigned long __ob_trap *p) {}
+ // expected-note at +1 {{passing argument to parameter 'p' here}}
+void func_takes_wrap_ptr(unsigned long __ob_wrap *p) {}
+
+void test_function_parameters() {
+ unsigned long x = 42;
+ unsigned long __ob_trap y = 42;
+ unsigned long __ob_wrap z = 42;
+
+ unsigned long *px = &x;
+ unsigned long __ob_trap *py = &y;
+ unsigned long __ob_wrap *pz = &z;
+
+ // Same types - should not warn
+ func_takes_regular_ptr(px); // OK
+ func_takes_trap_ptr(py); // OK
+ func_takes_wrap_ptr(pz); // OK
+
+ // Different OBT annotations - should warn but allow
+ // expected-warning at +1 {{passing '__ob_trap unsigned long *' to parameter of type 'unsigned long *' discards overflow behavior}}
+ func_takes_regular_ptr(py);
+ // expected-warning at +1 {{passing '__ob_wrap unsigned long *' to parameter of type 'unsigned long *' discards overflow behavior}}
+ func_takes_regular_ptr(pz);
+ // expected-warning at +1 {{passing 'unsigned long *' to parameter of type '__ob_trap unsigned long *' discards overflow behavior}}
+ func_takes_trap_ptr(px);
+ // expected-warning at +1 {{passing 'unsigned long *' to parameter of type '__ob_wrap unsigned long *' discards overflow behavior}}
+ func_takes_wrap_ptr(px);
+}
+
+void test_different_underlying_types_for_pointers() {
+ int x = 42;
+ unsigned long __ob_trap y = 42;
+
+ int *px = &x;
+ unsigned long __ob_trap *py = &y;
+
+ px = py; // expected-error {{incompatible pointer types assigning to 'int *' from '__ob_trap unsigned long *'}}
+}
+
+typedef unsigned long __ob_trap nw_ul;
+typedef signed long sl;
+
+void qux(nw_ul *ptr) {}
+
+void test_signed_unsigned_pointer_compatibility() {
+ sl a;
+ qux(&a);
+}
+
+// expected-error at +1 {{conflicting 'overflow_behavior' attributes on the same type}}
+typedef int __attribute__((overflow_behavior(wrap))) __attribute__((overflow_behavior(trap))) conflicting_trap_wins;
+// expected-error at +1 {{conflicting 'overflow_behavior' attributes on the same type}}
+typedef int __attribute__((overflow_behavior(trap))) __attribute__((overflow_behavior(wrap))) conflicting_wrap_ignored;
+
+void test_conflicting_behavior_kinds() {
+ conflicting_trap_wins x = 42;
+ conflicting_wrap_ignored y = 42;
+
+ int __ob_trap *px = &x;
+ int __ob_trap *py = &y;
+}
+
+typedef int __attribute__((overflow_behavior(wrap))) __attribute__((overflow_behavior(wrap))) duplicate_wrap; // no warn
+typedef int __attribute__((overflow_behavior(trap))) __attribute__((overflow_behavior(trap))) duplicate_trap; // no warn
+
+// Test type merging behavior for OBTs on top of typedefs
+typedef int pid_t;
+typedef int clockid_t;
+
+void test_obt_type_merging() {
+ pid_t __ob_wrap a = 1;
+ clockid_t __ob_wrap b = 2;
+ pid_t __ob_wrap c = 4;
+ _Static_assert(_Generic((a + b), int __ob_wrap: 1, default: 0), "a + b should be __ob_wrap int");
+ _Static_assert(_Generic((a + c), pid_t __ob_wrap: 1, default: 0), "a + c should be __ob_wrap pid_t");
+}
+
+typedef unsigned long __ob_trap test_size_t;
+typedef int __ob_wrap test_wrap_int;
+
+void test_pointer_arithmetic_crash_fix() {
+ int a = 42;
+ test_size_t offset = 10;
+ test_wrap_int w_offset = 5;
+
+ int *ptr1 = &a + offset;
+ int *ptr2 = &a + w_offset;
+ int *ptr3 = offset + &a;
+
+ test_size_t bad1 = &a + offset; // expected-error {{incompatible pointer to integer conversion}}
+ test_wrap_int bad2 = &a + w_offset; // expected-error {{incompatible pointer to integer conversion}}
+ test_size_t b = &a + b; // expected-error {{incompatible pointer to integer conversion}}
+ int arr[10];
+ test_size_t diff = &arr[5] - &arr[0]; // OK: pointer difference assigned to OBT
+}
+
+void test_mixed_specifier_attribute() {
+ int __ob_wrap __attribute__((overflow_behavior(wrap))) a; // expected-warning {{redundant overflow behavior specification; both specifier and attribute specify 'wrap'}}
+ int __ob_trap __attribute__((overflow_behavior(trap))) b; // expected-warning {{redundant overflow behavior specification; both specifier and attribute specify 'trap'}}
+
+ int __ob_wrap __attribute__((overflow_behavior(trap))) c; // expected-error {{conflicting overflow behavior specification; specifier specifies 'wrap' but attribute specifies 'trap'}}
+ int __ob_trap __attribute__((overflow_behavior(wrap))) d; // expected-error {{conflicting overflow behavior specification; specifier specifies 'trap' but attribute specifies 'wrap'}}
+
+ int __ob_wrap e; // OK
+ int __attribute__((overflow_behavior(trap))) f; // OK
+}
+
+void test_incompatible_obt_assignment() {
+ int __ob_trap trap_var;
+ int __ob_wrap wrap_var;
+
+ trap_var = wrap_var; // expected-error {{assigning to '__ob_trap int' from '__ob_wrap int' with incompatible overflow behavior types ('__ob_trap' and '__ob_wrap')}}
+ wrap_var = trap_var; // expected-error {{assigning to '__ob_wrap int' from '__ob_trap int' with incompatible overflow behavior types ('__ob_wrap' and '__ob_trap')}}
+}
diff --git a/clang/test/Sema/attr-overflow-behavior.cpp b/clang/test/Sema/attr-overflow-behavior.cpp
new file mode 100644
index 0000000000000..15e4dcbfce297
--- /dev/null
+++ b/clang/test/Sema/attr-overflow-behavior.cpp
@@ -0,0 +1,178 @@
+// RUN: %clang_cc1 %s -Winteger-overflow -Wno-unused-value -foverflow-behavior-types -Wconstant-conversion -Woverflow-behavior-conversion -verify -fsyntax-only
+
+typedef int __attribute__((overflow_behavior)) bad_arg_count; // expected-error {{'overflow_behavior' attribute takes one argument}}
+typedef int __attribute__((overflow_behavior(not_real))) bad_arg_spec; // expected-error {{'not_real' is not a valid argument to attribute 'overflow_behavior'}}
+typedef int __attribute__((overflow_behavior("not_real"))) bad_arg_spec_str; // expected-error {{'not_real' is not a valid argument to attribute 'overflow_behavior'}}
+typedef char* __attribute__((overflow_behavior("wrap"))) bad_type; // expected-error {{'overflow_behavior' attribute cannot be applied to non-integer type 'char *'}}
+
+typedef int __attribute__((overflow_behavior(wrap))) ok_wrap; // OK
+typedef long __attribute__((overflow_behavior(trap))) ok_nowrap; // OK
+typedef unsigned long __attribute__((overflow_behavior("wrap"))) str_ok_wrap; // OK
+typedef char __attribute__((overflow_behavior("trap"))) str_ok_nowrap; // OK
+
+#define __wrap __attribute__((overflow_behavior(wrap)))
+#define __trap __attribute__((overflow_behavior(trap)))
+
+struct struct_not_allowed {
+ int i;
+} __attribute__((overflow_behavior(wrap))); // expected-warning {{'overflow_behavior' attribute only applies to variables, typedefs, and non-static data members}}
+
+void foo() {
+ (2147483647 + 100); // expected-warning {{overflow in expression; result is }}
+ (ok_wrap)2147483647 + 100; // no warn
+}
+
+// C++ stuff expects no warns
+typedef int __attribute__((overflow_behavior(wrap))) wrap_int;
+
+template <typename T>
+T bar(T a) {
+ return 1UL;
+}
+
+void f() {
+ wrap_int a = 4;
+ bar(a);
+}
+
+class TestOverload {
+ public:
+ void operator<<(int other); // expected-note {{candidate function}}
+ void operator<<(char other); // expected-note {{candidate function}}
+};
+
+void test_overload1() {
+ wrap_int a = 4;
+ TestOverload TO;
+ TO << a; // expected-error {{use of overloaded operator '<<' is ambiguous}}
+}
+
+// expected-note at +1 {{candidate function}}
+int add_one(long a) { // expected-note {{candidate function}}
+ return (a + 1);
+}
+
+// expected-note at +1 {{candidate function}}
+int add_one(char a) { // expected-note {{candidate function}}
+ return (a + 1);
+}
+
+// expected-note at +1 {{candidate function}}
+int add_one(int a) { // expected-note {{candidate function}}
+ return (a + 1);
+}
+
+void test_overload2(wrap_int a) {
+ // to be clear, this is the same ambiguity expected when using a non-OBT int type.
+ add_one(a); // expected-error {{call to 'add_one' is ambiguous}}
+ long __attribute__((overflow_behavior(trap))) b; // don't consider underlying type an exact match.
+ add_one(b); // expected-error {{call to 'add_one' is ambiguous}}
+}
+
+void func(__ob_trap int i);
+void func(int i); // Overload, not invalid redeclaration
+
+template <typename Ty>
+void func2(__ob_trap Ty i) {} // expected-error {{__ob_trap specifier cannot be applied to non-integer type 'Ty'}}
+
+template <typename Ty>
+struct S {};
+
+template <>
+struct S<__ob_trap int> {};
+
+template <>
+struct S<int> {};
+
+void ptr(int a) {
+ int __ob_trap *p = &a; // expected-error-re {{cannot initialize a variable of type '__ob_trap int *' {{.*}}with an rvalue of type 'int *'}}
+}
+
+void ptr2(__ob_trap int a) {
+ int *p = &a; // expected-error-re {{cannot initialize a variable of type 'int *' {{.*}}with an rvalue of type '__ob_trap int *'}}
+}
+
+void overloadme(__ob_trap int a); // expected-note {{candidate function}}
+void overloadme(short a); // expected-note {{candidate function}}
+
+void test_overload_ambiguity() {
+ int a;
+ overloadme(a); // expected-error {{call to 'overloadme' is ambiguous}}
+}
+
+void f(void) __attribute__((overflow_behavior(wrap))); // expected-error {{'overflow_behavior' attribute cannot be applied to non-integer type 'void (void)'}}
+
+typedef float __attribute__((overflow_behavior(wrap))) wrap_float; // expected-error {{'overflow_behavior' attribute cannot be applied to non-integer type 'float'}}
+
+void pointer_compatibility(int* i_ptr) {
+ __ob_trap int* nowrap_ptr;
+
+ // static_cast should fail.
+ nowrap_ptr = static_cast<__ob_trap int*>(i_ptr); // expected-error {{static_cast from 'int *' to '__ob_trap int *' is not allowed}}
+
+ // reinterpret_cast should succeed.
+ nowrap_ptr = reinterpret_cast<__ob_trap int*>(i_ptr);
+ (void)nowrap_ptr;
+}
+
+void cpp_constexpr_bracket_initialization() {
+ constexpr short cx1 = {(int __ob_wrap)100000}; // expected-error {{constant expression evaluates to 100000 which cannot be narrowed to type 'short'}}
+ // expected-note at -1 {{insert an explicit cast to silence this issue}}
+
+ constexpr short __ob_wrap cx2 = {100000}; // expected-error {{constant expression evaluates to 100000 which cannot be narrowed to type '__ob_wrap short'}}
+
+ constexpr short __ob_trap cx3 = {(int)100000}; // expected-error {{constant expression evaluates to 100000 which cannot be narrowed to type '__ob_trap short'}}
+ // expected-warning at -1 {{implicit conversion from 'int' to '__ob_trap short const' changes value from 100000 to -31072}}
+
+ constexpr short cx4 = {(int __ob_trap)100000}; // expected-error {{constant expression evaluates to 100000 which cannot be narrowed to type 'short'}}
+ // expected-warning at -1 {{implicit conversion from '__ob_trap int' to '__ob_trap const short' changes value from 100000 to -31072}}
+ // expected-note at -2 {{insert an explicit cast to silence this issue}}
+}
+
+// ensure that all qualifier placements result in the same canonical type
+void test_qualifier_placements() {
+ using ConstInt = const int;
+ using WrapConstInt1 = ConstInt __attribute__((overflow_behavior(wrap)));
+ using WrapConstInt2 = const int __attribute__((overflow_behavior(wrap)));
+ typedef const int __ob_wrap const_int_wrap;
+ typedef int __ob_wrap const int_wrap_const;
+ typedef int __ob_trap const int_trap_const;
+
+ static_assert(__is_same(WrapConstInt1, WrapConstInt2));
+ static_assert(__is_same(const_int_wrap, int_wrap_const));
+ static_assert(!__is_same(const_int_wrap, int_trap_const));
+}
+
+void test_mixed_specifier_attribute() {
+ int __ob_wrap __attribute__((overflow_behavior(wrap))) a; // expected-warning {{redundant overflow behavior specification; both specifier and attribute specify 'wrap'}}
+ int __ob_trap __attribute__((overflow_behavior(trap))) b; // expected-warning {{redundant overflow behavior specification; both specifier and attribute specify 'trap'}}
+
+ int __ob_wrap __attribute__((overflow_behavior(trap))) c; // expected-error {{conflicting overflow behavior specification; specifier specifies 'wrap' but attribute specifies 'trap'}}
+ int __ob_trap __attribute__((overflow_behavior(wrap))) d; // expected-error {{conflicting overflow behavior specification; specifier specifies 'trap' but attribute specifies 'wrap'}}
+
+ int __ob_wrap e; // OK
+ int __attribute__((overflow_behavior(trap))) f; // OK
+}
+
+void test_incompatible_obt_initialization() {
+ int __ob_trap a = 10;
+ int __ob_wrap b = 20;
+
+ int __ob_wrap c = a; // expected-error {{cannot initialize a variable of type '__ob_wrap int' with an lvalue of type '__ob_trap int'}}
+ int __ob_trap d = b; // expected-error {{cannot initialize a variable of type '__ob_trap int' with an lvalue of type '__ob_wrap int'}}
+}
+
+void constant_conversion() {
+ short x1 = (int __ob_wrap)100000;
+ short __ob_wrap x2 = (int)100000; // No warning expected
+ // expected-warning at +1 {{implicit conversion from 'int' to '__ob_trap short' changes value from 100000 to -31072}}
+ short __ob_trap x3 = (int)100000;
+ // expected-warning at +1 {{implicit conversion from '__ob_trap int' to '__ob_trap short' changes value from 100000 to -31072}}
+ short x4 = (int __ob_trap)100000;
+
+ unsigned short __ob_wrap ux1 = (unsigned int)100000; // No warning - wrapping expected
+ unsigned short ux2 = (unsigned int __ob_wrap)100000;
+ unsigned short __ob_trap ux3 = (unsigned int)100000; // expected-warning {{implicit conversion from 'unsigned int' to '__ob_trap unsigned short' changes value from 100000 to 34464}}
+ unsigned short __ob_trap ux4 = (unsigned int __ob_trap)100000; // expected-warning {{implicit conversion from '__ob_trap unsigned int' to '__ob_trap unsigned short' changes value from 100000 to 34464}}
+ unsigned short __ob_trap ux5 = (unsigned int __ob_wrap)100000; // expected-error {{assigning to '__ob_trap unsigned short' from '__ob_wrap unsigned int' with incompatible overflow behavior types ('__ob_trap' and '__ob_wrap')}}
+}
diff --git a/clang/test/Sema/overflow-behavior-assignment-pedantic.c b/clang/test/Sema/overflow-behavior-assignment-pedantic.c
new file mode 100644
index 0000000000000..a6433c9b2982a
--- /dev/null
+++ b/clang/test/Sema/overflow-behavior-assignment-pedantic.c
@@ -0,0 +1,17 @@
+// RUN: %clang_cc1 %s -foverflow-behavior-types -Wimplicit-overflow-behavior-conversion-assignment-pedantic -verify -fsyntax-only -std=c11
+
+// Test that -Wimplicit-overflow-behavior-conversion-assignment-pedantic only warns
+// for unsigned wrap to unsigned conversions during assignment
+
+void test_assignment_pedantic() {
+ unsigned int __ob_wrap uwrap = 42;
+ unsigned int ureg = uwrap; // expected-warning {{implicit conversion from '__ob_wrap unsigned int' to 'unsigned int' during assignment discards overflow behavior}}
+
+ int __ob_wrap swrap = 42;
+ int sreg = swrap;
+
+ unsigned int __ob_trap utrap = 42;
+ unsigned int ureg2 = utrap;
+
+ unsigned int ureg3 = (unsigned int)uwrap;
+}
diff --git a/clang/test/Sema/overflow-behavior-function-boundary-default.c b/clang/test/Sema/overflow-behavior-function-boundary-default.c
new file mode 100644
index 0000000000000..66aa44aa9078d
--- /dev/null
+++ b/clang/test/Sema/overflow-behavior-function-boundary-default.c
@@ -0,0 +1,16 @@
+// RUN: %clang_cc1 %s -foverflow-behavior-types -fsyntax-only -verify
+
+// Test that function boundary warnings are off by default
+// expected-no-diagnostics
+
+void func_no_obt(int x) {}
+void func_no_obt_unsigned(unsigned int x) {}
+
+void test_function_boundary_default() {
+ int __ob_wrap w = 42;
+ unsigned int __ob_wrap uw = 42;
+
+ // These should NOT warn by default
+ func_no_obt(w);
+ func_no_obt_unsigned(uw);
+}
diff --git a/clang/test/Sema/overflow-behavior-keywords.c b/clang/test/Sema/overflow-behavior-keywords.c
new file mode 100644
index 0000000000000..a9e0e2f79d622
--- /dev/null
+++ b/clang/test/Sema/overflow-behavior-keywords.c
@@ -0,0 +1,66 @@
+// RUN: %clang_cc1 -fsyntax-only -foverflow-behavior-types -verify %s
+
+int __ob_wrap a;
+int __ob_trap b;
+
+const int __ob_wrap c;
+volatile int __ob_trap d;
+const volatile int __ob_wrap e;
+
+int __attribute__((overflow_behavior(wrap))) attr_style_var;
+int __ob_wrap keyword_style_var;
+
+int __ob_wrap __ob_trap conflicting_var; // expected-error{{cannot combine with previous '__ob_wrap' declaration specifier}}
+
+// Test duplicate qualifiers
+int __ob_wrap __ob_wrap duplicate_wrap; // expected-warning{{duplicate '__ob_wrap' declaration specifier}}
+int __ob_trap __ob_trap duplicate_trap; // expected-warning{{duplicate '__ob_trap' declaration specifier}}
+
+int __attribute__((overflow_behavior(wrap))) __attribute__((overflow_behavior(trap))) attr_conflict; // expected-error{{conflicting 'overflow_behavior' attributes on the same type}}
+
+// Test duplicate attributes - no warning, less problematic than duplicate decl specifiers
+int __attribute__((overflow_behavior(wrap))) __attribute__((overflow_behavior(wrap))) duplicate_attr;
+
+const volatile int __ob_wrap __ob_trap __ob_wrap complex_conflict; // expected-error{{cannot combine with previous '__ob_wrap' declaration specifier}} expected-warning{{duplicate '__ob_wrap' declaration specifier}}
+
+extern int __ob_wrap __ob_trap extern_conflict; // expected-error{{cannot combine with previous '__ob_wrap' declaration specifier}}
+static int __ob_wrap __ob_trap static_conflict; // expected-error{{cannot combine with previous '__ob_wrap' declaration specifier}}
+
+void test_storage_class(void) {
+ register int __ob_wrap __ob_trap register_conflict; // expected-error{{cannot combine with previous '__ob_wrap' declaration specifier}}
+}
+
+int __ob_wrap __ob_trap *ptr_conflict; // expected-error{{cannot combine with previous '__ob_wrap' declaration specifier}}
+int __ob_wrap __ob_trap arr_conflict[5]; // expected-error{{cannot combine with previous '__ob_wrap' declaration specifier}}
+
+int __ob_wrap __ob_trap (*func_ptr_conflict)(void); // expected-error{{cannot combine with previous '__ob_wrap' declaration specifier}}
+
+void param_test(int __ob_wrap __ob_trap param); // expected-error{{cannot combine with previous '__ob_wrap' declaration specifier}}
+
+struct conflict_struct {
+ int __ob_wrap __ob_trap member; // expected-error{{cannot combine with previous '__ob_wrap' declaration specifier}}
+ int __ob_wrap __ob_wrap dup_member; // expected-warning{{duplicate '__ob_wrap' declaration specifier}}
+};
+
+typedef int __ob_wrap __ob_trap conflict_typedef; // expected-error{{cannot combine with previous '__ob_wrap' declaration specifier}}
+typedef int __ob_wrap __ob_wrap dup_typedef; // expected-warning{{duplicate '__ob_wrap' declaration specifier}}
+
+int __ob_wrap *ptr_to_wrap;
+int __ob_trap arr[10];
+int __ob_wrap (*func_ptr)(int);
+
+void test_function(int __ob_wrap param1, int __ob_trap param2);
+
+struct test_struct {
+ int __ob_wrap member1;
+ int __ob_trap member2;
+};
+
+typedef int __ob_wrap wrap_int_t;
+typedef int __ob_trap trap_int_t;
+
+typedef float __ob_wrap float_wrap; // expected-error{{__ob_wrap specifier cannot be applied to non-integer type 'float'}}
+typedef double __ob_trap double_trap; // expected-error{{__ob_trap specifier cannot be applied to non-integer type 'double'}}
+
+struct S { int i; };
+typedef struct S __ob_wrap struct_wrap; // expected-error{{__ob_wrap specifier cannot be applied to non-integer type 'struct S'}}
diff --git a/clang/test/SemaCXX/sugar-common-types.cpp b/clang/test/SemaCXX/sugar-common-types.cpp
index 4db0d2ac2f2ae..b0271569dfffa 100644
--- a/clang/test/SemaCXX/sugar-common-types.cpp
+++ b/clang/test/SemaCXX/sugar-common-types.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++20 -x objective-c++ -fobjc-arc -fenable-matrix -triple i686-pc-win32
+// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++20 -x objective-c++ -fobjc-arc -fenable-matrix -foverflow-behavior-types -triple i686-pc-win32
enum class N {};
@@ -188,6 +188,45 @@ namespace arrays {
} // namespace balanced_qualifiers
} // namespace arrays
+namespace overflow_behavior_types {
+ namespace same_canonical {
+ using WrapB1 = B1 __attribute__((overflow_behavior(wrap)));
+ using WrapB1_2 = B1 __attribute__((overflow_behavior(wrap)));
+ WrapB1 a = 0;
+ WrapB1_2 b = 0;
+ N ta = a;
+ // expected-error at -1 {{cannot initialize a variable of type 'N' with an lvalue of type 'WrapB1' (aka '__ob_wrap B1')}}
+ N tb = b;
+ // expected-error at -1 {{cannot initialize a variable of type 'N' with an lvalue of type 'WrapB1_2' (aka '__ob_wrap B1')}}
+ N tc = 0 ? a : b;
+ // expected-error at -1 {{cannot initialize a variable of type 'N' with an lvalue of type '__ob_wrap B1'}}
+ } // namespace same_canonical
+ namespace same_underlying {
+ using WrapX1 = X1 __attribute__((overflow_behavior(wrap)));
+ using WrapY1 = Y1 __attribute__((overflow_behavior(wrap)));
+ WrapX1 a = 0;
+ WrapY1 b = 0;
+ N ta = a;
+ // expected-error at -1 {{cannot initialize a variable of type 'N' with an lvalue of type 'WrapX1' (aka '__ob_wrap X1')}}
+ N tb = b;
+ // expected-error at -1 {{cannot initialize a variable of type 'N' with an lvalue of type 'WrapY1' (aka '__ob_wrap Y1')}}
+ N tc = 0 ? a : b;
+ // expected-error at -1 {{cannot initialize a variable of type 'N' with an lvalue of type '__ob_wrap B1'}}
+ } // namespace same_underlying
+ namespace balanced_qualifiers {
+ using ConstWrapX1 = const volatile X1 __attribute__((overflow_behavior(wrap)));
+ using WrapY1 = volatile Y1 __attribute__((overflow_behavior(wrap)));
+ volatile ConstWrapX1 a = 0;
+ const volatile WrapY1 b = 0;
+ N ta = a;
+ // expected-error at -1 {{cannot initialize a variable of type 'N' with an lvalue of type 'volatile ConstWrapX1' (aka '__ob_wrap X1 const volatile')}}
+ N tb = b;
+ // expected-error at -1 {{cannot initialize a variable of type 'N' with an lvalue of type 'const volatile WrapY1' (aka '__ob_wrap Y1 const volatile')}}
+ N tc = 0 ? a : b;
+ // expected-error at -1 {{cannot initialize a variable of type 'N' with an lvalue of type '__ob_wrap B1 const volatile'}}
+ } // namespace balanced_qualifiers
+} // namespace overflow_behavior_types
+
namespace member_pointers {
template <class T> struct W {
X1 a;
diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp
index 32e84248c1b27..ab9b4cb7aa665 100644
--- a/clang/tools/libclang/CIndex.cpp
+++ b/clang/tools/libclang/CIndex.cpp
@@ -1748,6 +1748,10 @@ bool CursorVisitor::VisitBTFTagAttributedTypeLoc(BTFTagAttributedTypeLoc TL) {
return Visit(TL.getWrappedLoc());
}
+bool CursorVisitor::VisitOverflowBehaviorTypeLoc(OverflowBehaviorTypeLoc TL) {
+ return Visit(TL.getWrappedLoc());
+}
+
bool CursorVisitor::VisitHLSLAttributedResourceTypeLoc(
HLSLAttributedResourceTypeLoc TL) {
return Visit(TL.getWrappedLoc());
diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
index aa8d309fbc730..137794f658981 100644
--- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
+++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
@@ -4219,6 +4219,7 @@ TypeSystemClang::GetTypeClass(lldb::opaque_compiler_type_t type) {
// Ext-Int is just an integer type.
case clang::Type::BitInt:
case clang::Type::DependentBitInt:
+ case clang::Type::OverflowBehavior:
return lldb::eTypeClassBuiltin;
case clang::Type::ObjCObjectPointer:
return lldb::eTypeClassObjCObjectPointer;
@@ -4917,6 +4918,7 @@ lldb::Encoding TypeSystemClang::GetEncoding(lldb::opaque_compiler_type_t type) {
case clang::Type::BitInt:
case clang::Type::DependentBitInt:
+ case clang::Type::OverflowBehavior:
return qual_type->isUnsignedIntegerType() ? lldb::eEncodingUint
: lldb::eEncodingSint;
@@ -5217,6 +5219,7 @@ lldb::Format TypeSystemClang::GetFormat(lldb::opaque_compiler_type_t type) {
case clang::Type::BitInt:
case clang::Type::DependentBitInt:
+ case clang::Type::OverflowBehavior:
return qual_type->isUnsignedIntegerType() ? lldb::eFormatUnsigned
: lldb::eFormatDecimal;
>From 3fcb27cbfd420c3ea228518f142e6ea95e2f247d Mon Sep 17 00:00:00 2001
From: Justin Stitt <justinstitt at google.com>
Date: Tue, 2 Dec 2025 13:50:00 -0800
Subject: [PATCH 2/4] change flag to driver-only and make it experimental
Signed-off-by: Justin Stitt <justinstitt at google.com>
---
clang/docs/OverflowBehaviorTypes.rst | 27 +++++++++++--------
clang/docs/ReleaseNotes.rst | 2 +-
.../clang/Basic/DiagnosticSemaKinds.td | 2 +-
clang/include/clang/Options/Options.td | 13 ++++-----
clang/lib/Driver/ToolChains/Clang.cpp | 4 +--
clang/lib/Sema/SemaType.cpp | 2 +-
.../test/AST/ast-print-overflow-behavior.cpp | 8 +++---
.../AST/overflow-behavior-keywords-ast.cpp | 2 +-
.../CodeGen/mangle-ms-overflow-behavior.cpp | 2 +-
.../overflow-behavior-types-extensions.c | 2 +-
.../overflow-behavior-types-operators.cpp | 2 +-
.../overflow-behavior-types-promotions.cpp | 2 +-
.../CodeGen/overflow-behavior-types-scl.c | 4 +--
clang/test/CodeGen/overflow-behavior-types.c | 12 ++++-----
.../test/CodeGen/overflow-behavior-types.cpp | 2 +-
.../Sema/attr-overflow-behavior-constexpr.cpp | 2 +-
.../attr-overflow-behavior-format-strings.c | 2 +-
.../Sema/attr-overflow-behavior-templates.cpp | 2 +-
clang/test/Sema/attr-overflow-behavior.c | 2 +-
clang/test/Sema/attr-overflow-behavior.cpp | 2 +-
.../overflow-behavior-assignment-pedantic.c | 2 +-
...rflow-behavior-function-boundary-default.c | 2 +-
clang/test/Sema/overflow-behavior-keywords.c | 2 +-
clang/test/SemaCXX/sugar-common-types.cpp | 2 +-
24 files changed, 55 insertions(+), 49 deletions(-)
diff --git a/clang/docs/OverflowBehaviorTypes.rst b/clang/docs/OverflowBehaviorTypes.rst
index 7492d7819cb51..0bedbc9a1a39c 100644
--- a/clang/docs/OverflowBehaviorTypes.rst
+++ b/clang/docs/OverflowBehaviorTypes.rst
@@ -16,8 +16,13 @@ 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 compiler option
-``-foverflow-behavior-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:
@@ -226,8 +231,8 @@ represent fundamentally different behavioral contracts.
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 below details the exact diagnostics Clang provides for
-overflow behavior types.
+The Diagnostics section further below details the exact diagnostics Clang
+provides for overflow behavior types.
Truncation Semantics
--------------------
@@ -267,10 +272,10 @@ truncation, instrumentation checks behave as follows:
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 the wrapping behavior that ``wrap`` types are
-designed to handle predictably.
+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
------------------------------
@@ -512,14 +517,14 @@ redundant but allowed:
**Feature Not Enabled:**
When overflow behavior types are used without enabling the feature via
-``-foverflow-behavior-types``, a warning is issued and the attribute is ignored:
+``-fexperimental-overflow-behavior-types``, a warning is issued and the attribute is ignored:
.. code-block:: c++
- // Without -foverflow-behavior-types
+ // Without -fexperimental-overflow-behavior-types
int __attribute__((overflow_behavior(wrap))) x;
// warning: 'overflow_behavior' attribute is ignored because it is
- // not enabled; pass -foverflow-behavior-types
+ // not enabled; pass -fexperimental-overflow-behavior-types
-Woverflow-behavior-conversion
------------------------------
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 6d9738cdffb5f..24e78b21c1931 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -331,7 +331,7 @@ New Compiler Flags
- New option ``-fsanitize-debug-trap-reasons=`` added to control emitting trap reasons into the debug info when compiling with trapping UBSan (e.g. ``-fsanitize-trap=undefined``).
- New options for enabling allocation token instrumentation: ``-fsanitize=alloc-token``, ``-falloc-token-max=``, ``-fsanitize-alloc-token-fast-abi``, ``-fsanitize-alloc-token-extended``.
- The ``-resource-dir`` option is now displayed in the list of options shown by ``--help``.
-- New option ``-foverflow-behavior-types`` added to enable parsing of the ``overflow_behavior`` type attribute and type specifiers.
+- New ``-cc1`` option ``-fexperimental-overflow-behavior-types`` added to enable parsing of the experimental ``overflow_behavior`` type attribute and type specifiers.
Lanai Support
^^^^^^^^^^^^^^
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index c0e380ce38e8d..7fa55379182cf 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -4095,7 +4095,7 @@ def err_incompatible_obt_kinds_assignment
"('%2' and '%3')">;
def warn_overflow_behavior_attribute_disabled
: Warning<"%0 attribute is ignored because it is not enabled; pass "
- "-foverflow-behavior-types">,
+ "-Xclang -fexperimental-overflow-behavior-types">,
InGroup<OverflowBehaviorAttributeIgnored>;
def warn_impcast_overflow_behavior
: Warning<"implicit conversion from %0 to %1 discards overflow behavior">,
diff --git a/clang/include/clang/Options/Options.td b/clang/include/clang/Options/Options.td
index 38661b0d8a8b2..58ced52997b4f 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -2349,12 +2349,13 @@ defm fixed_point : BoolFOption<"fixed-point",
PosFlag<SetTrue, [], [ClangOption, CC1Option], "Enable">,
NegFlag<SetFalse, [], [ClangOption], "Disable">,
BothFlags<[], [ClangOption], " fixed point types">>;
-defm overflow_behavior_types
- : BoolFOption<"overflow-behavior-types", LangOpts<"OverflowBehaviorTypes">,
- DefaultFalse,
- PosFlag<SetTrue, [], [ClangOption, CC1Option], "Enable">,
- NegFlag<SetFalse, [], [ClangOption], "Disable">,
- BothFlags<[], [ClangOption], " overflow behavior types">>;
+defm experimental_overflow_behavior_types
+ : BoolFOption<"experimental-overflow-behavior-types",
+ LangOpts<"OverflowBehaviorTypes">, DefaultFalse,
+ PosFlag<SetTrue, [], [CC1Option], "Enable">,
+ NegFlag<SetFalse, [], [CC1Option], "Disable">,
+ BothFlags<[], [CC1Option],
+ " the experimental overflow behavior types">>;
def cxx_static_destructors_EQ : Joined<["-"], "fc++-static-destructors=">, Group<f_Group>,
HelpText<"Controls which variables C++ static destructors are registered for">,
Values<"all,thread-local,none">,
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index d9759229b3893..fa48528a5c401 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -6269,8 +6269,8 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
Args.addOptInFlag(CmdArgs, options::OPT_ffixed_point,
options::OPT_fno_fixed_point);
- Args.addOptInFlag(CmdArgs, options::OPT_foverflow_behavior_types,
- options::OPT_fno_overflow_behavior_types);
+ Args.addOptInFlag(CmdArgs, options::OPT_fexperimental_overflow_behavior_types,
+ options::OPT_fno_experimental_overflow_behavior_types);
if (Arg *A = Args.getLastArg(options::OPT_fcxx_abi_EQ))
A->render(Args, CmdArgs);
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index d92571e50ef47..a00437f5c7924 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -6683,7 +6683,7 @@ static void HandleOverflowBehaviorAttr(QualType &Type, const ParsedAttr &Attr,
TypeProcessingState &State) {
Sema &S = State.getSema();
- // Check for -foverflow-behavior-types
+ // Check for -fexperimental-overflow-behavior-types
if (!S.getLangOpts().OverflowBehaviorTypes) {
S.Diag(Attr.getLoc(), diag::warn_overflow_behavior_attribute_disabled)
<< Attr << 1;
diff --git a/clang/test/AST/ast-print-overflow-behavior.cpp b/clang/test/AST/ast-print-overflow-behavior.cpp
index f66479951021f..46c8511cddc9e 100644
--- a/clang/test/AST/ast-print-overflow-behavior.cpp
+++ b/clang/test/AST/ast-print-overflow-behavior.cpp
@@ -1,8 +1,8 @@
-// RUN: %clang_cc1 -foverflow-behavior-types -std=c++11 -ast-print %s -o - | FileCheck %s --check-prefix=PRINT
+// RUN: %clang_cc1 -fexperimental-overflow-behavior-types -std=c++11 -ast-print %s -o - | FileCheck %s --check-prefix=PRINT
-// RUN: %clang_cc1 -foverflow-behavior-types -std=c++11 -fsyntax-only %s
-// RUN: %clang_cc1 -foverflow-behavior-types -std=c++11 -ast-print %s -o %t.1.cpp
-// RUN: %clang_cc1 -foverflow-behavior-types -std=c++11 -ast-print %t.1.cpp -o %t.2.cpp
+// RUN: %clang_cc1 -fexperimental-overflow-behavior-types -std=c++11 -fsyntax-only %s
+// RUN: %clang_cc1 -fexperimental-overflow-behavior-types -std=c++11 -ast-print %s -o %t.1.cpp
+// RUN: %clang_cc1 -fexperimental-overflow-behavior-types -std=c++11 -ast-print %t.1.cpp -o %t.2.cpp
// RUN: diff %t.1.cpp %t.2.cpp
extern int __attribute__((overflow_behavior(trap))) a;
diff --git a/clang/test/AST/overflow-behavior-keywords-ast.cpp b/clang/test/AST/overflow-behavior-keywords-ast.cpp
index 4269db7bdbfbb..2e459ed803d4d 100644
--- a/clang/test/AST/overflow-behavior-keywords-ast.cpp
+++ b/clang/test/AST/overflow-behavior-keywords-ast.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -foverflow-behavior-types -ast-dump %s | FileCheck %s
+// RUN: %clang_cc1 -fexperimental-overflow-behavior-types -ast-dump %s | FileCheck %s
// Test that keyword and attribute syntax produce the same OverflowBehaviorType
diff --git a/clang/test/CodeGen/mangle-ms-overflow-behavior.cpp b/clang/test/CodeGen/mangle-ms-overflow-behavior.cpp
index 06af5b36f3eb1..431424724b370 100644
--- a/clang/test/CodeGen/mangle-ms-overflow-behavior.cpp
+++ b/clang/test/CodeGen/mangle-ms-overflow-behavior.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -emit-llvm %s -o - -fms-extensions -triple=x86_64-pc-win32 -foverflow-behavior-types | FileCheck %s
+// RUN: %clang_cc1 -emit-llvm %s -o - -fms-extensions -triple=x86_64-pc-win32 -fexperimental-overflow-behavior-types | FileCheck %s
#define __wrap __attribute__((overflow_behavior(wrap)))
#define __trap __attribute__((overflow_behavior(trap)))
diff --git a/clang/test/CodeGen/overflow-behavior-types-extensions.c b/clang/test/CodeGen/overflow-behavior-types-extensions.c
index 50e86c64571a1..2f3a79f860ac3 100644
--- a/clang/test/CodeGen/overflow-behavior-types-extensions.c
+++ b/clang/test/CodeGen/overflow-behavior-types-extensions.c
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -triple x86_64-linux-gnu -foverflow-behavior-types -std=c2x %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -fexperimental-overflow-behavior-types -std=c2x %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK
#define __wrap __attribute__((overflow_behavior(wrap)))
#define __no_trap __attribute__((overflow_behavior(trap)))
diff --git a/clang/test/CodeGen/overflow-behavior-types-operators.cpp b/clang/test/CodeGen/overflow-behavior-types-operators.cpp
index 22794cac4f3ad..c0cce80035b34 100644
--- a/clang/test/CodeGen/overflow-behavior-types-operators.cpp
+++ b/clang/test/CodeGen/overflow-behavior-types-operators.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -triple x86_64-linux-gnu %s -foverflow-behavior-types \
+// RUN: %clang_cc1 -triple x86_64-linux-gnu %s -fexperimental-overflow-behavior-types \
// RUN: -fsanitize=signed-integer-overflow -emit-llvm -o - -std=c++14 | FileCheck %s
#define __wrap __attribute__((overflow_behavior(wrap)))
diff --git a/clang/test/CodeGen/overflow-behavior-types-promotions.cpp b/clang/test/CodeGen/overflow-behavior-types-promotions.cpp
index bebc2c3b7001f..a3eb25805100e 100644
--- a/clang/test/CodeGen/overflow-behavior-types-promotions.cpp
+++ b/clang/test/CodeGen/overflow-behavior-types-promotions.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -triple x86_64-linux-gnu %s -foverflow-behavior-types \
+// RUN: %clang_cc1 -triple x86_64-linux-gnu %s -fexperimental-overflow-behavior-types \
// RUN: -fsanitize=signed-integer-overflow,unsigned-integer-overflow -emit-llvm -o - -std=c++14 | FileCheck %s
#define __wrap __attribute__((overflow_behavior(wrap)))
diff --git a/clang/test/CodeGen/overflow-behavior-types-scl.c b/clang/test/CodeGen/overflow-behavior-types-scl.c
index 1bc1139bb72b0..3ed5eaea6e28c 100644
--- a/clang/test/CodeGen/overflow-behavior-types-scl.c
+++ b/clang/test/CodeGen/overflow-behavior-types-scl.c
@@ -1,8 +1,8 @@
// RUN: rm -rf %t
// RUN: split-file %s %t
-// RUN: %clang_cc1 -triple x86_64-linux-gnu %t/test.c -fsanitize-ignorelist=%t/sio.scl -foverflow-behavior-types -fsanitize=signed-integer-overflow -emit-llvm -o - | FileCheck %s --check-prefix=SIO
-// RUN: %clang_cc1 -triple x86_64-linux-gnu %t/test.c -fsanitize-ignorelist=%t/uio.scl -foverflow-behavior-types -fsanitize=unsigned-integer-overflow -emit-llvm -o - | FileCheck %s --check-prefix=UIO
+// RUN: %clang_cc1 -triple x86_64-linux-gnu %t/test.c -fsanitize-ignorelist=%t/sio.scl -fexperimental-overflow-behavior-types -fsanitize=signed-integer-overflow -emit-llvm -o - | FileCheck %s --check-prefix=SIO
+// RUN: %clang_cc1 -triple x86_64-linux-gnu %t/test.c -fsanitize-ignorelist=%t/uio.scl -fexperimental-overflow-behavior-types -fsanitize=unsigned-integer-overflow -emit-llvm -o - | FileCheck %s --check-prefix=UIO
//--- sio.scl
[signed-integer-overflow]
diff --git a/clang/test/CodeGen/overflow-behavior-types.c b/clang/test/CodeGen/overflow-behavior-types.c
index abfdc7c639cab..4638f2e7fb1a7 100644
--- a/clang/test/CodeGen/overflow-behavior-types.c
+++ b/clang/test/CodeGen/overflow-behavior-types.c
@@ -1,25 +1,25 @@
-// RUN: %clang_cc1 -triple x86_64-linux-gnu -foverflow-behavior-types %s \
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -fexperimental-overflow-behavior-types %s \
// RUN: -fsanitize=signed-integer-overflow,unsigned-integer-overflow,implicit-signed-integer-truncation,implicit-unsigned-integer-truncation \
// RUN: -emit-llvm -o - | FileCheck %s --check-prefix=DEFAULT
-// RUN: %clang_cc1 -triple x86_64-linux-gnu -foverflow-behavior-types %s -ftrapv \
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -fexperimental-overflow-behavior-types %s -ftrapv \
// RUN: -fsanitize=signed-integer-overflow,unsigned-integer-overflow,implicit-signed-integer-truncation,implicit-unsigned-integer-truncation \
// RUN: -emit-llvm -o - | FileCheck %s --check-prefix=DEFAULT
-// RUN: %clang_cc1 -triple x86_64-linux-gnu -foverflow-behavior-types %s \
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -fexperimental-overflow-behavior-types %s \
// RUN: -ftrapv -ftrapv-handler OVERFLOW_HANDLER \
// RUN: -emit-llvm -o - | FileCheck %s --check-prefix=TRAPV-HANDLER
-// RUN: %clang_cc1 -triple x86_64-linux-gnu -foverflow-behavior-types %s -fwrapv \
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -fexperimental-overflow-behavior-types %s -fwrapv \
// RUN: -fsanitize=signed-integer-overflow,unsigned-integer-overflow,implicit-signed-integer-truncation,implicit-unsigned-integer-truncation \
// RUN: -emit-llvm -o - | FileCheck %s --check-prefix=DEFAULT
-// RUN: %clang_cc1 -triple x86_64-linux-gnu -foverflow-behavior-types %s \
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -fexperimental-overflow-behavior-types %s \
// RUN: -fsanitize-undefined-ignore-overflow-pattern=all \
// RUN: -fsanitize=signed-integer-overflow,unsigned-integer-overflow,implicit-signed-integer-truncation,implicit-unsigned-integer-truncation \
// RUN: -emit-llvm -o - | FileCheck %s --check-prefix=EXCL
-// RUN: %clang_cc1 -triple x86_64-linux-gnu -foverflow-behavior-types %s \
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -fexperimental-overflow-behavior-types %s \
// RUN: -emit-llvm -o - | FileCheck %s --check-prefix=NOSAN
#define __wrap __attribute__((overflow_behavior("wrap")))
diff --git a/clang/test/CodeGen/overflow-behavior-types.cpp b/clang/test/CodeGen/overflow-behavior-types.cpp
index 25ca14bbd3b40..89d9d5b24cf09 100644
--- a/clang/test/CodeGen/overflow-behavior-types.cpp
+++ b/clang/test/CodeGen/overflow-behavior-types.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -triple x86_64-linux-gnu %s -foverflow-behavior-types \
+// RUN: %clang_cc1 -triple x86_64-linux-gnu %s -fexperimental-overflow-behavior-types \
// RUN: -fsanitize=signed-integer-overflow,unsigned-integer-overflow,implicit-signed-integer-truncation,implicit-unsigned-integer-truncation \
// RUN: -emit-llvm -o - | FileCheck %s --check-prefix=DEFAULT
diff --git a/clang/test/Sema/attr-overflow-behavior-constexpr.cpp b/clang/test/Sema/attr-overflow-behavior-constexpr.cpp
index 0c9ed48a6fef4..ea5bc7fdb9fbe 100644
--- a/clang/test/Sema/attr-overflow-behavior-constexpr.cpp
+++ b/clang/test/Sema/attr-overflow-behavior-constexpr.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -triple x86_64-linux-gnu %s -foverflow-behavior-types -verify -fsyntax-only -std=c++14
+// RUN: %clang_cc1 -triple x86_64-linux-gnu %s -fexperimental-overflow-behavior-types -verify -fsyntax-only -std=c++14
#define __wrap __attribute__((overflow_behavior(wrap)))
#define __no_trap __attribute__((overflow_behavior(trap)))
diff --git a/clang/test/Sema/attr-overflow-behavior-format-strings.c b/clang/test/Sema/attr-overflow-behavior-format-strings.c
index 83f8c35824b6c..b1ff90f27e158 100644
--- a/clang/test/Sema/attr-overflow-behavior-format-strings.c
+++ b/clang/test/Sema/attr-overflow-behavior-format-strings.c
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -fsyntax-only -verify -Wformat -foverflow-behavior-types -isystem %S/Inputs %s
+// RUN: %clang_cc1 -fsyntax-only -verify -Wformat -fexperimental-overflow-behavior-types -isystem %S/Inputs %s
// Test format string checking with overflow behavior types
// This ensures that OverflowBehaviorTypes work seamlessly with printf/scanf
diff --git a/clang/test/Sema/attr-overflow-behavior-templates.cpp b/clang/test/Sema/attr-overflow-behavior-templates.cpp
index 6f7a064b83e14..14f0131bb4368 100644
--- a/clang/test/Sema/attr-overflow-behavior-templates.cpp
+++ b/clang/test/Sema/attr-overflow-behavior-templates.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 %s -foverflow-behavior-types -verify -fsyntax-only -Wimplicit-overflow-behavior-conversion
+// RUN: %clang_cc1 %s -fexperimental-overflow-behavior-types -verify -fsyntax-only -Wimplicit-overflow-behavior-conversion
#define __wrap __attribute__((overflow_behavior(wrap)))
#define __trap __attribute__((overflow_behavior(trap)))
diff --git a/clang/test/Sema/attr-overflow-behavior.c b/clang/test/Sema/attr-overflow-behavior.c
index 720f2ea60477f..125ed860a91b1 100644
--- a/clang/test/Sema/attr-overflow-behavior.c
+++ b/clang/test/Sema/attr-overflow-behavior.c
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 %s -Winteger-overflow -Wno-unused-value -foverflow-behavior-types -Woverflow-behavior-conversion -Wconstant-conversion -verify -fsyntax-only -std=c11 -Wno-pointer-sign
+// RUN: %clang_cc1 %s -Winteger-overflow -Wno-unused-value -fexperimental-overflow-behavior-types -Woverflow-behavior-conversion -Wconstant-conversion -verify -fsyntax-only -std=c11 -Wno-pointer-sign
typedef int __attribute__((overflow_behavior)) bad_arg_count; // expected-error {{'overflow_behavior' attribute takes one argument}}
typedef int __attribute__((overflow_behavior(not_real))) bad_arg_spec; // expected-error {{'not_real' is not a valid argument to attribute 'overflow_behavior'}}
diff --git a/clang/test/Sema/attr-overflow-behavior.cpp b/clang/test/Sema/attr-overflow-behavior.cpp
index 15e4dcbfce297..7a8b9109ef6da 100644
--- a/clang/test/Sema/attr-overflow-behavior.cpp
+++ b/clang/test/Sema/attr-overflow-behavior.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 %s -Winteger-overflow -Wno-unused-value -foverflow-behavior-types -Wconstant-conversion -Woverflow-behavior-conversion -verify -fsyntax-only
+// RUN: %clang_cc1 %s -Winteger-overflow -Wno-unused-value -fexperimental-overflow-behavior-types -Wconstant-conversion -Woverflow-behavior-conversion -verify -fsyntax-only
typedef int __attribute__((overflow_behavior)) bad_arg_count; // expected-error {{'overflow_behavior' attribute takes one argument}}
typedef int __attribute__((overflow_behavior(not_real))) bad_arg_spec; // expected-error {{'not_real' is not a valid argument to attribute 'overflow_behavior'}}
diff --git a/clang/test/Sema/overflow-behavior-assignment-pedantic.c b/clang/test/Sema/overflow-behavior-assignment-pedantic.c
index a6433c9b2982a..ac59d1c934d8a 100644
--- a/clang/test/Sema/overflow-behavior-assignment-pedantic.c
+++ b/clang/test/Sema/overflow-behavior-assignment-pedantic.c
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 %s -foverflow-behavior-types -Wimplicit-overflow-behavior-conversion-assignment-pedantic -verify -fsyntax-only -std=c11
+// RUN: %clang_cc1 %s -fexperimental-overflow-behavior-types -Wimplicit-overflow-behavior-conversion-assignment-pedantic -verify -fsyntax-only -std=c11
// Test that -Wimplicit-overflow-behavior-conversion-assignment-pedantic only warns
// for unsigned wrap to unsigned conversions during assignment
diff --git a/clang/test/Sema/overflow-behavior-function-boundary-default.c b/clang/test/Sema/overflow-behavior-function-boundary-default.c
index 66aa44aa9078d..7501efd24a4b4 100644
--- a/clang/test/Sema/overflow-behavior-function-boundary-default.c
+++ b/clang/test/Sema/overflow-behavior-function-boundary-default.c
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 %s -foverflow-behavior-types -fsyntax-only -verify
+// RUN: %clang_cc1 %s -fexperimental-overflow-behavior-types -fsyntax-only -verify
// Test that function boundary warnings are off by default
// expected-no-diagnostics
diff --git a/clang/test/Sema/overflow-behavior-keywords.c b/clang/test/Sema/overflow-behavior-keywords.c
index a9e0e2f79d622..85c4076f48f1a 100644
--- a/clang/test/Sema/overflow-behavior-keywords.c
+++ b/clang/test/Sema/overflow-behavior-keywords.c
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -fsyntax-only -foverflow-behavior-types -verify %s
+// RUN: %clang_cc1 -fsyntax-only -fexperimental-overflow-behavior-types -verify %s
int __ob_wrap a;
int __ob_trap b;
diff --git a/clang/test/SemaCXX/sugar-common-types.cpp b/clang/test/SemaCXX/sugar-common-types.cpp
index b0271569dfffa..4c704db195c54 100644
--- a/clang/test/SemaCXX/sugar-common-types.cpp
+++ b/clang/test/SemaCXX/sugar-common-types.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++20 -x objective-c++ -fobjc-arc -fenable-matrix -foverflow-behavior-types -triple i686-pc-win32
+// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++20 -x objective-c++ -fobjc-arc -fenable-matrix -fexperimental-overflow-behavior-types -triple i686-pc-win32
enum class N {};
>From 1769e03ba6d5984a3c03373cdabe3dfa10d378d6 Mon Sep 17 00:00:00 2001
From: Justin Stitt <justinstitt at google.com>
Date: Tue, 2 Dec 2025 14:30:06 -0800
Subject: [PATCH 3/4] run formatter
Signed-off-by: Justin Stitt <justinstitt at google.com>
---
clang/include/clang/AST/ASTContext.h | 3 +-
clang/include/clang/Basic/DiagnosticGroups.td | 3 +-
.../clang/Basic/DiagnosticSemaKinds.td | 29 ++++++++++---------
clang/lib/CodeGen/CGExprScalar.cpp | 7 +++--
clang/lib/Sema/Sema.cpp | 3 +-
clang/lib/Sema/SemaExprCXX.cpp | 4 +--
clang/lib/Sema/SemaOverload.cpp | 3 +-
7 files changed, 28 insertions(+), 24 deletions(-)
diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index 2b52a73a0cebc..230239cf6967c 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -2659,7 +2659,8 @@ class ASTContext : public RefCountedBase<ASTContext> {
};
/// Check overflow behavior type compatibility for assignments.
- /// Returns detailed information about OBT compatibility for assignment checking.
+ /// Returns detailed information about OBT compatibility for assignment
+ /// checking.
OBTAssignResult checkOBTAssignmentCompatibility(QualType LHS, QualType RHS);
/// Return true if the given types are an RISC-V vector builtin type and a
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index b72f97aae64f5..9f633e00be187 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -138,7 +138,8 @@ def ImplicitOverflowBehaviorConversionAssignmentPedantic
def ImplicitOverflowBehaviorConversionAtFunctionBoundary
: DiagGroup<"implicit-overflow-behavior-conversion-function-boundary">;
def ImplicitOverflowBehaviorConversionAtFunctionBoundaryPedantic
- : DiagGroup<"implicit-overflow-behavior-conversion-function-boundary-pedantic">;
+ : DiagGroup<
+ "implicit-overflow-behavior-conversion-function-boundary-pedantic">;
def ImplicitOverflowBehaviorConversionPedantic
: DiagGroup<"implicit-overflow-behavior-conversion-pedantic">;
def ImplicitOverflowBehaviorConversion
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 7fa55379182cf..9b00045031fd2 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -9211,20 +9211,21 @@ def ext_typecheck_convert_discards_qualifiers : ExtWarn<
"|%diff{casting $ to type $|casting between types}0,1}2"
" discards qualifiers">,
InGroup<IncompatiblePointerTypesDiscardsQualifiers>;
-def ext_typecheck_convert_discards_overflow_behavior : ExtWarn<
- "%select{%diff{assigning to $ from $|assigning to different types}0,1"
- "|%diff{passing $ to parameter of type $|"
- "passing to parameter of different type}0,1"
- "|%diff{returning $ from a function with result type $|"
- "returning from function with different return type}0,1"
- "|%diff{converting $ to type $|converting between types}0,1"
- "|%diff{initializing $ with an expression of type $|"
- "initializing with expression of different type}0,1"
- "|%diff{sending $ to parameter of type $|"
- "sending to parameter of different type}0,1"
- "|%diff{casting $ to type $|casting between types}0,1}2"
- " discards overflow behavior">,
- InGroup<IncompatiblePointerTypesDiscardsOverflowBehavior>;
+def ext_typecheck_convert_discards_overflow_behavior
+ : ExtWarn<
+ "%select{%diff{assigning to $ from $|assigning to different types}0,1"
+ "|%diff{passing $ to parameter of type $|"
+ "passing to parameter of different type}0,1"
+ "|%diff{returning $ from a function with result type $|"
+ "returning from function with different return type}0,1"
+ "|%diff{converting $ to type $|converting between types}0,1"
+ "|%diff{initializing $ with an expression of type $|"
+ "initializing with expression of different type}0,1"
+ "|%diff{sending $ to parameter of type $|"
+ "sending to parameter of different type}0,1"
+ "|%diff{casting $ to type $|casting between types}0,1}2"
+ " discards overflow behavior">,
+ InGroup<IncompatiblePointerTypesDiscardsOverflowBehavior>;
def err_typecheck_convert_discards_qualifiers : Error<
"%select{%diff{assigning to $ from $|assigning to different types}0,1"
"|%diff{passing $ to parameter of type $|"
diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp
index f00a55aaaafae..e5c00a2dfd454 100644
--- a/clang/lib/CodeGen/CGExprScalar.cpp
+++ b/clang/lib/CodeGen/CGExprScalar.cpp
@@ -1803,8 +1803,8 @@ Value *ScalarExprEmitter::EmitScalarConversion(Value *Src, QualType SrcType,
// capability checks as this overflow behavior kind is also capable of
// emitting traps without runtime sanitizer support.
// Also skip instrumentation if either source or destination has 'wrap'
- // behavior - the user has explicitly indicated they accept wrapping semantics.
- // Use non-canonical types to preserve OBT annotations.
+ // behavior - the user has explicitly indicated they accept wrapping
+ // semantics. Use non-canonical types to preserve OBT annotations.
const auto *DstOBT = NoncanonicalDstType->getAs<OverflowBehaviorType>();
const auto *SrcOBT = NoncanonicalSrcType->getAs<OverflowBehaviorType>();
bool OBTrapInvolved =
@@ -1812,7 +1812,8 @@ Value *ScalarExprEmitter::EmitScalarConversion(Value *Src, QualType SrcType,
bool OBWrapInvolved =
(DstOBT && DstOBT->isWrapKind()) || (SrcOBT && SrcOBT->isWrapKind());
- if ((Opts.EmitImplicitIntegerTruncationChecks || OBTrapInvolved) && !OBWrapInvolved)
+ if ((Opts.EmitImplicitIntegerTruncationChecks || OBTrapInvolved) &&
+ !OBWrapInvolved)
EmitIntegerTruncationCheck(Src, NoncanonicalSrcType, Res,
NoncanonicalDstType, Loc, OBTrapInvolved);
diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp
index 80ca08ef6608d..c5a673dec55fe 100644
--- a/clang/lib/Sema/Sema.cpp
+++ b/clang/lib/Sema/Sema.cpp
@@ -849,7 +849,8 @@ ExprResult Sema::ImpCastExprToType(Expr *E, QualType Ty,
isa<CXXFunctionalCastExpr>(E);
if ((Kind == CK_IntegralCast || Kind == CK_IntegralToBoolean ||
- (Kind == CK_NoOp && E->getType()->isIntegerType() && Ty->isIntegerType())) &&
+ (Kind == CK_NoOp && E->getType()->isIntegerType() &&
+ Ty->isIntegerType())) &&
IsExplicitCast) {
if (const auto *SourceOBT = E->getType()->getAs<OverflowBehaviorType>()) {
if (Ty->isIntegerType() && !Ty->isOverflowBehaviorType()) {
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index f5b8c2e4e128d..f86a35b1b4013 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -4685,8 +4685,8 @@ static QualType adjustVectorType(ASTContext &Context, QualType FromTy,
return Context.getExtVectorType(ElType, FromVec->getNumElements());
}
-/// Check if an integral conversion involves incompatible overflow behavior types.
-/// Returns true if the conversion is invalid.
+/// Check if an integral conversion involves incompatible overflow behavior
+/// types. Returns true if the conversion is invalid.
static bool checkIncompatibleOBTConversion(Sema &S, QualType FromType,
QualType ToType, Expr *From) {
const auto *FromOBT = FromType->getAs<OverflowBehaviorType>();
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 4b88349906888..5f775e8a4f9f3 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -2874,8 +2874,7 @@ bool Sema::IsOverflowBehaviorTypeConversion(QualType FromType,
if (FromType->isOverflowBehaviorType() && !ToType->isOverflowBehaviorType()) {
// Don't allow implicit conversion from OverflowBehaviorType to scoped enum
if (const EnumType *ToEnumType = ToType->getAs<EnumType>()) {
- const EnumDecl *ToED =
- ToEnumType->getDecl()->getDefinitionOrSelf();
+ const EnumDecl *ToED = ToEnumType->getDecl()->getDefinitionOrSelf();
if (ToED->isScoped())
return false;
}
>From b31e2d296ea5a47ef577e142e663794c0e757640 Mon Sep 17 00:00:00 2001
From: Justin Stitt <justinstitt at google.com>
Date: Tue, 2 Dec 2025 15:20:06 -0800
Subject: [PATCH 4/4] remove unused variable to fix test
Signed-off-by: Justin Stitt <justinstitt at google.com>
---
clang/lib/Sema/SemaExpr.cpp | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index c432ec38ab1a7..77e571170337e 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -9994,10 +9994,9 @@ AssignConvertType Sema::CheckSingleAssignmentConstraints(QualType LHSType,
// Check if OBT is being discarded during assignment
// The RHS may have propagated OBT, but if LHS doesn't have it, warn
- if (const auto *RHSOBT = RHSType->getAs<OverflowBehaviorType>()) {
- if (!LHSType->isOverflowBehaviorType()) {
- result = AssignConvertType::CompatibleOBTDiscards;
- }
+ if (RHSType->isOverflowBehaviorType() &&
+ !LHSType->isOverflowBehaviorType()) {
+ result = AssignConvertType::CompatibleOBTDiscards;
}
return result;
More information about the lldb-commits
mailing list