[clang] 7f631bb - [Clang] Introduce OverflowBehaviorType for fine-grained overflow control (#148914)

via cfe-commits cfe-commits at lists.llvm.org
Thu Feb 19 13:54:39 PST 2026


Author: Justin Stitt
Date: 2026-02-19T13:54:33-08:00
New Revision: 7f631bb523a34d3b95a6614006468c609e60fcf5

URL: https://github.com/llvm/llvm-project/commit/7f631bb523a34d3b95a6614006468c609e60fcf5
DIFF: https://github.com/llvm/llvm-project/commit/7f631bb523a34d3b95a6614006468c609e60fcf5.diff

LOG: [Clang] Introduce OverflowBehaviorType for fine-grained overflow control (#148914)

Introduce `OverflowBehaviorType` (OBT), a new type attribute in Clang
that provides developers with fine-grained control over the overflow
behavior of integer types. This feature allows for a more nuanced
approach to integer safety, achieving better granularity than global
compiler flags like `-fwrapv` and `-ftrapv`. Type specifiers are also
available as keywords `__ob_wrap` and `__ob_trap`.

These can be applied to integer types (both signed and unsigned) as well
as typedef declarations, where the behavior is one of the following:

* `wrap`: Guarantees that arithmetic operations on the type will wrap on
overflow, similar to `-fwrapv`. This suppresses UBSan's integer overflow
checks for the attributed type and prevents eager compiler
optimizations.
* `trap`: Enforces overflow checking for the type, even when global
flags like `-fwrapv` would otherwise suppress it.

A key aspect of this feature is its interaction with existing
mechanisms. `OverflowBehaviorType` takes precedence over global flags
and, notably, over entries in the Sanitizer Special Case List (SSCL).
This allows developers to "allowlist" critical types for overflow
instrumentation, even if they are disabled by a broad rule in an SSCL.


Signed-off-by: Justin Stitt <justinstitt at google.com>

Added: 
    clang/docs/OverflowBehaviorTypes.rst
    clang/test/AST/ast-print-overflow-behavior.cpp
    clang/test/AST/overflow-behavior-keywords-ast.cpp
    clang/test/CodeGen/mangle-ms-overflow-behavior.cpp
    clang/test/CodeGen/overflow-behavior-types-extensions.c
    clang/test/CodeGen/overflow-behavior-types-operators.cpp
    clang/test/CodeGen/overflow-behavior-types-promotions.cpp
    clang/test/CodeGen/overflow-behavior-types-scl.c
    clang/test/CodeGen/overflow-behavior-types.c
    clang/test/CodeGen/overflow-behavior-types.cpp
    clang/test/Sema/attr-overflow-behavior-constexpr.cpp
    clang/test/Sema/attr-overflow-behavior-format-strings.c
    clang/test/Sema/attr-overflow-behavior-off.c
    clang/test/Sema/attr-overflow-behavior-templates.cpp
    clang/test/Sema/attr-overflow-behavior.c
    clang/test/Sema/attr-overflow-behavior.cpp
    clang/test/Sema/overflow-behavior-assignment-pedantic.c
    clang/test/Sema/overflow-behavior-function-boundary-default.c
    clang/test/Sema/overflow-behavior-keywords-off.c
    clang/test/Sema/overflow-behavior-keywords.c

Modified: 
    clang/docs/LanguageExtensions.rst
    clang/docs/ReleaseNotes.rst
    clang/docs/SanitizerSpecialCaseList.rst
    clang/docs/UndefinedBehaviorSanitizer.rst
    clang/docs/index.rst
    clang/include/clang/AST/ASTContext.h
    clang/include/clang/AST/ASTNodeTraverser.h
    clang/include/clang/AST/PropertiesBase.td
    clang/include/clang/AST/RecursiveASTVisitor.h
    clang/include/clang/AST/TypeBase.h
    clang/include/clang/AST/TypeLoc.h
    clang/include/clang/AST/TypeProperties.td
    clang/include/clang/Basic/Attr.td
    clang/include/clang/Basic/DiagnosticGroups.td
    clang/include/clang/Basic/DiagnosticSemaKinds.td
    clang/include/clang/Basic/Features.def
    clang/include/clang/Basic/LangOptions.def
    clang/include/clang/Basic/LangOptions.h
    clang/include/clang/Basic/TokenKinds.def
    clang/include/clang/Basic/TypeNodes.td
    clang/include/clang/Options/Options.td
    clang/include/clang/Sema/DeclSpec.h
    clang/include/clang/Sema/Sema.h
    clang/include/clang/Serialization/TypeBitCodes.def
    clang/lib/AST/ASTContext.cpp
    clang/lib/AST/ASTImporter.cpp
    clang/lib/AST/ASTStructuralEquivalence.cpp
    clang/lib/AST/ExprConstant.cpp
    clang/lib/AST/FormatString.cpp
    clang/lib/AST/ItaniumMangle.cpp
    clang/lib/AST/MicrosoftMangle.cpp
    clang/lib/AST/Type.cpp
    clang/lib/AST/TypeLoc.cpp
    clang/lib/AST/TypePrinter.cpp
    clang/lib/CodeGen/CGCall.cpp
    clang/lib/CodeGen/CGDebugInfo.cpp
    clang/lib/CodeGen/CGDebugInfo.h
    clang/lib/CodeGen/CGExprScalar.cpp
    clang/lib/CodeGen/CodeGenFunction.cpp
    clang/lib/CodeGen/CodeGenTypes.cpp
    clang/lib/CodeGen/ItaniumCXXABI.cpp
    clang/lib/Driver/ToolChains/Clang.cpp
    clang/lib/Parse/ParseDecl.cpp
    clang/lib/Parse/ParseTentative.cpp
    clang/lib/Sema/DeclSpec.cpp
    clang/lib/Sema/Sema.cpp
    clang/lib/Sema/SemaChecking.cpp
    clang/lib/Sema/SemaDecl.cpp
    clang/lib/Sema/SemaExpr.cpp
    clang/lib/Sema/SemaExprCXX.cpp
    clang/lib/Sema/SemaLookup.cpp
    clang/lib/Sema/SemaOverload.cpp
    clang/lib/Sema/SemaTemplate.cpp
    clang/lib/Sema/SemaTemplateDeduction.cpp
    clang/lib/Sema/SemaType.cpp
    clang/lib/Sema/TreeTransform.h
    clang/lib/Serialization/ASTReader.cpp
    clang/lib/Serialization/ASTWriter.cpp
    clang/test/Misc/pragma-attribute-supported-attributes-list.test
    clang/test/SemaCXX/sugar-common-types.cpp
    clang/tools/libclang/CIndex.cpp
    lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp

Removed: 
    


################################################################################
diff  --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index 29328355c3e6f..5ac15dd80760b 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -1237,6 +1237,133 @@ generated saying the pragma didn't resolve to a declaration.  For example:
   #pragma export(func)
   int func(double) { return 0; } // warning: failed to resolve '#pragma export' to a declaration
 
+Overflow behavior types
+=======================
+
+Clang provides an extension that allows developers to annotate integer types
+with explicit overflow behavior. This enables fine-grained control over whether
+arithmetic operations should wrap on overflow (with two's complement semantics)
+or be checked for overflow (trapping or reporting via sanitizers).
+
+This feature is experimental and must be enabled with the ``-cc1`` option
+``-fexperimental-overflow-behavior-types``.
+
+Query for this feature with ``__has_extension(overflow_behavior_types)``.
+
+Syntax
+------
+
+Overflow behavior can be specified using either attribute syntax or keyword
+syntax:
+
+**Attribute syntax:**
+
+.. code-block:: c
+
+  typedef int __attribute__((overflow_behavior(wrap))) wrapping_int;
+  typedef int __attribute__((overflow_behavior(trap))) checked_int;
+
+**Keyword syntax:**
+
+.. code-block:: c
+
+  typedef int __ob_wrap wrapping_int;
+  typedef int __ob_trap checked_int;
+
+The annotation can also be applied directly to variable declarations:
+
+.. code-block:: c
+
+  int __ob_wrap counter = 0;
+  unsigned __ob_trap safe_index = 0;
+
+Semantics
+---------
+
+**wrap behavior:**
+
+When an integer type is annotated with ``wrap`` (or ``__ob_wrap``), arithmetic
+operations on values of that type use two's complement wrapping semantics on
+overflow. This behavior is well-defined regardless of signedness. Compilers
+must not optimize based on the assumption that overflow does not occur.
+
+**trap behavior:**
+
+When an integer type is annotated with ``trap`` (or ``__ob_trap``), arithmetic
+operations on values of that type are checked for overflow. If overflow occurs,
+the program traps or reports via sanitizers (depending on compiler settings).
+
+Integer Promotions and Conversions
+----------------------------------
+
+Overflow behavior types follow standard C integer promotion and conversion
+rules. The overflow behavior annotation is preserved through implicit
+promotions and conversions.
+
+**Standard promotion rules apply:**
+
+Integer literals without a suffix have type ``int`` (or a larger type if the
+value requires it), following normal C rules. When such a literal is used in
+an operation with an overflow behavior type, standard promotion rules determine
+the result type, and the overflow behavior is propagated:
+
+.. code-block:: c
+
+  typedef int __ob_wrap wrap_int;
+  wrap_int x = 100;
+  wrap_int y = x + 1;  // 1 is promoted; result is __ob_wrap int
+
+**Combining 
diff erent overflow behaviors:**
+
+When operands have 
diff erent overflow behaviors, ``trap`` takes precedence
+over ``wrap``:
+
+.. code-block:: c
+
+  typedef int __ob_wrap wrap_int;
+  typedef int __ob_trap trap_int;
+  wrap_int a = 1;
+  trap_int b = 2;
+  auto c = a + b;  // Result is __ob_trap int (trap dominates)
+
+**Conversion to standard types:**
+
+When an overflow behavior type is converted to a standard integer type (without
+an overflow behavior annotation), the overflow behavior is discarded. Compilers
+may warn about this:
+
+.. code-block:: c
+
+  typedef int __ob_wrap wrap_int;
+  wrap_int w = 42;
+  int i = w;  // Warning: discards overflow behavior
+
+Interaction with Compiler Flags
+-------------------------------
+
+Overflow behavior annotations take precedence over global compiler flags:
+
+- A ``wrap`` type wraps on overflow even when ``-ftrapv`` is enabled.
+- A ``trap`` type is checked for overflow even when ``-fwrapv`` is enabled.
+
+This allows mixing 
diff erent overflow behaviors within the same program,
+enabling developers to selectively apply wrapping or checking to specific
+types while using 
diff erent defaults elsewhere.
+
+Incompatible Assignments
+------------------------
+
+Direct assignment between types with 
diff erent overflow behaviors (``wrap``
+vs ``trap``) is an error:
+
+.. code-block:: c
+
+  int __ob_wrap w;
+  int __ob_trap t;
+  w = t;  // Error: incompatible overflow behaviors
+
+For more detailed documentation, see :doc:`OverflowBehaviorTypes`.
+
 Messages on ``deprecated`` and ``unavailable`` Attributes
 =========================================================
 

diff  --git a/clang/docs/OverflowBehaviorTypes.rst b/clang/docs/OverflowBehaviorTypes.rst
new file mode 100644
index 0000000000000..0bedbc9a1a39c
--- /dev/null
+++ b/clang/docs/OverflowBehaviorTypes.rst
@@ -0,0 +1,832 @@
+=====================
+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 ``-cc1`` compiler option
+``-fexperimental-overflow-behavior-types``.
+
+.. note::
+
+   This feature is experimental. The flag spelling may change in future
+   releases as the feature matures.
+
+There are two syntax options for specifying overflow behavior:
+
+**Attribute syntax:**
+
+.. 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 
diff erent 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 
diff erent 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 further below details the exact diagnostics Clang
+provides for overflow behavior types.
+
+Truncation Semantics
+--------------------
+
+Truncation and overflow are related and are both often desirable in some
+contexts and undesirable in others. To provide control over these behaviors,
+overflow behavior types may also be used to control truncation instrumentation
+at the type level.
+
+Note that implicit integer truncation is not an undefined behavior in C. Due to
+this, overflow behavior types make no special guarantees about whether implicit
+integer truncation is defined or not -- the behavior is simply always defined.
+Use of overflow behavior types in these contexts only control instrumentation
+related to truncation.
+
+When an overflow behavior type is involved as the source or destination type of
+truncation, instrumentation checks behave as follows:
+
+* **One or more types is 'trap'**: Truncation checks are inserted and may issue
+  either a trap or sanitizer warning based on compiler settings.
+
+* **One or more types is 'wrap'**: No truncation checks are added regardless of
+  compiler flags because truncation with wrapping behavior well-defined. If any
+  of the types is ``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 a wrapping behavior that ``wrap`` types are designed to handle
+predictably.
+
+Constant Conversion Semantics
+------------------------------
+
+When converting constant values to overflow behavior types, the behavior
+depends on the overflow behavior annotation and whether the conversion would
+change the value:
+
+**With 'wrap' types:**
+
+Constant conversions that would overflow are accepted without warning when
+assigning to or explicitly casting to ``wrap`` types, as wrapping is the
+intended behavior:
+
+.. 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 
diff ering
+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,
+
diff erent 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 
diff ering 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
+``-fexperimental-overflow-behavior-types``, a warning is issued and the attribute is ignored:
+
+.. code-block:: c++
+
+  // Without -fexperimental-overflow-behavior-types
+  int __attribute__((overflow_behavior(wrap))) x;
+  // warning: 'overflow_behavior' attribute is ignored because it is
+  // not enabled; pass -fexperimental-overflow-behavior-types
+
+-Woverflow-behavior-conversion
+------------------------------
+
+This warning group is issued when an overflow behavior type is implicitly
+converted to a standard integer type, which may lead to the loss of the
+specified overflow behavior.
+
+.. 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 
diff er 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 6e9e5baea2921..b2302cbdb3f54 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -154,6 +154,10 @@ New Compiler Flags
   can only generate the reduced BMI as a by-product, e.g, an object files or
   a full BMI.
 
+- New ``-cc1`` option ``-fexperimental-overflow-behavior-types`` added to
+  enable parsing of the experimental ``overflow_behavior`` type attribute and
+  type specifiers.
+
 Deprecated Compiler Flags
 -------------------------
 
@@ -177,6 +181,11 @@ Attribute Changes in Clang
 - Added a new attribute, ``[[clang::no_outline]]`` to suppress outlining from
   annotated functions. This uses the LLVM `nooutline` attribute.
 
+- 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
 -----------------------------------
 - Added ``-Wlifetime-safety`` to enable lifetime safety analysis,

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 60c619daecfc3..bb0dc00609688 100644
--- a/clang/docs/UndefinedBehaviorSanitizer.rst
+++ b/clang/docs/UndefinedBehaviorSanitizer.rst
@@ -394,6 +394,42 @@ There are (at least) two exceptions:
 * combining `__attribute((no_sanitize("local-bounds")))` with
   `__attribute((always_inline))` is not supported
 
+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 5cdbd52a2398d..83aed67959fef 100644
--- a/clang/docs/index.rst
+++ b/clang/docs/index.rst
@@ -42,6 +42,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 6b819de2fb36d..32f3f0736fa82 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -25,6 +25,7 @@
 #include "clang/AST/RawCommentList.h"
 #include "clang/AST/SYCLKernelInfo.h"
 #include "clang/AST/TemplateName.h"
+#include "clang/AST/Type.h"
 #include "clang/AST/TypeOrdering.h"
 #include "clang/Basic/LLVM.h"
 #include "clang/Basic/PartialDiagnostic.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;
 
@@ -956,6 +958,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;
   }
@@ -1056,6 +1060,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.
@@ -1943,6 +1956,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);
@@ -2640,6 +2660,23 @@ 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 cfee62a362b1e..f97b54276cbee 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 2bec5131dc0d2..9402469f5e12b 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.
 
@@ -2644,6 +2650,7 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase {
   bool isSubscriptableVectorType() const;
   bool isMatrixType() const;                    // Matrix type.
   bool isConstantMatrixType() const;            // Constant matrix type.
+  bool isOverflowBehaviorType() const;          // Overflow behavior type.
   bool isDependentAddressSpaceType() const;     // value-dependent address space qualifier
   bool isObjCObjectPointerType() const;         // pointer to ObjC object
   bool isObjCRetainableType() const;            // ObjC object or block pointer
@@ -6693,6 +6700,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 {
@@ -8709,6 +8754,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);
 }
@@ -8953,6 +9002,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();
 }
 
@@ -9015,7 +9068,7 @@ inline bool Type::isScalarType() const {
          isa<MemberPointerType>(CanonicalType) ||
          isa<ComplexType>(CanonicalType) ||
          isa<ObjCObjectPointerType>(CanonicalType) ||
-         isBitIntType();
+         isOverflowBehaviorType() || isBitIntType();
 }
 
 inline bool Type::isIntegralOrEnumerationType() const {
@@ -9027,6 +9080,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 0f8eea43d0972..24df18dbaace4 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 8c1e9f209e5ad..350b748b804a3 100644
--- a/clang/include/clang/AST/TypeProperties.td
+++ b/clang/include/clang/AST/TypeProperties.td
@@ -655,6 +655,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 584182fc2b5db..9c6594a1c9ff0 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -5371,3 +5371,11 @@ def ModularFormat : InheritableAttr {
   let Subjects = SubjectList<[Function]>;
   let Documentation = [ModularFormatDocs];
 }
+
+def OverflowBehavior : TypeAttr {
+  let Spellings = [Clang<"overflow_behavior">];
+  let Args = [IdentifierArgument<"BehaviorKind">];
+  let Subjects = SubjectList<[Var, TypedefName, Field], WarnDiag,
+                             "variables, typedefs, and data members">;
+  let Documentation = [Undocumented];
+}

diff  --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index 0372cf062ec67..aaf8275a78707 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,27 @@ 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">;
@@ -608,12 +630,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">;
@@ -1196,23 +1221,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,
@@ -1721,6 +1739,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 85a023435ba23..615fdba99c301 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -4109,6 +4109,63 @@ 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 "
+              "-Xclang -fexperimental-overflow-behavior-types">,
+      InGroup<OverflowBehaviorAttributeIgnored>;
+def warn_overflow_behavior_keyword_disabled
+    : Warning<"'%0' specifier is ignored because overflow behavior types are "
+              "not enabled; pass -Xclang "
+              "-fexperimental-overflow-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>;
@@ -9216,6 +9273,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 
diff erent types}0,1"
+          "|%
diff {passing $ to parameter of type $|"
+          "passing to parameter of 
diff erent type}0,1"
+          "|%
diff {returning $ from a function with result type $|"
+          "returning from function with 
diff erent return type}0,1"
+          "|%
diff {converting $ to type $|converting between types}0,1"
+          "|%
diff {initializing $ with an expression of type $|"
+          "initializing with expression of 
diff erent type}0,1"
+          "|%
diff {sending $ to parameter of type $|"
+          "sending to parameter of 
diff erent 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 
diff erent types}0,1"
   "|%
diff {passing $ to parameter of type $|"

diff  --git a/clang/include/clang/Basic/Features.def b/clang/include/clang/Basic/Features.def
index ea99a1d5dbbbf..ea5198a079254 100644
--- a/clang/include/clang/Basic/Features.def
+++ b/clang/include/clang/Basic/Features.def
@@ -387,5 +387,8 @@ EXTENSION(cuda_implicit_host_device_templates, LangOpts.CUDA && LangOpts.Offload
 // C++2d type-aware allocators
 EXTENSION(cxx_type_aware_allocators, true)
 
+// Overflow behavior types
+EXTENSION(overflow_behavior_types, LangOpts.OverflowBehaviorTypes)
+
 #undef EXTENSION
 #undef FEATURE

diff  --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def
index 75543e0d78f9c..f39dbe241ccbc 100644
--- a/clang/include/clang/Basic/LangOptions.def
+++ b/clang/include/clang/Basic/LangOptions.def
@@ -432,6 +432,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 ebd0436fa154b..79ddeebaa7fba 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 84339c8b64db9..8b9f613037718 100644
--- a/clang/include/clang/Basic/TokenKinds.def
+++ b/clang/include/clang/Basic/TokenKinds.def
@@ -357,6 +357,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 e3724cda55ff8..d9a1a2f78f227 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -2387,6 +2387,13 @@ defm fixed_point : BoolFOption<"fixed-point",
   PosFlag<SetTrue, [], [ClangOption, CC1Option], "Enable">,
   NegFlag<SetFalse, [], [ClangOption], "Disable">,
   BothFlags<[], [ClangOption], " fixed point 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/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h
index 43a48c92fc305..6e5421c7072c7 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
@@ -1258,6 +1293,14 @@ struct DeclaratorChunk {
     /// The location of the __unaligned-qualifier, if any.
     SourceLocation UnalignedQualLoc;
 
+    /// The location of an __ob_wrap or __ob_trap qualifier, if any.
+    SourceLocation OverflowBehaviorLoc;
+
+    /// Whether the overflow behavior qualifier is wrap (true) or trap (false).
+    /// Only meaningful if OverflowBehaviorLoc is valid.
+    LLVM_PREFERRED_TYPE(bool)
+    unsigned OverflowBehaviorIsWrap : 1;
+
     void destroy() {
     }
   };
@@ -1639,7 +1682,9 @@ struct DeclaratorChunk {
                                     SourceLocation VolatileQualLoc,
                                     SourceLocation RestrictQualLoc,
                                     SourceLocation AtomicQualLoc,
-                                    SourceLocation UnalignedQualLoc) {
+                                    SourceLocation UnalignedQualLoc,
+                                    SourceLocation OverflowBehaviorLoc = {},
+                                    bool OverflowBehaviorIsWrap = false) {
     DeclaratorChunk I;
     I.Kind                = Pointer;
     I.Loc                 = Loc;
@@ -1650,6 +1695,8 @@ struct DeclaratorChunk {
     I.Ptr.RestrictQualLoc = RestrictQualLoc;
     I.Ptr.AtomicQualLoc   = AtomicQualLoc;
     I.Ptr.UnalignedQualLoc = UnalignedQualLoc;
+    I.Ptr.OverflowBehaviorLoc = OverflowBehaviorLoc;
+    I.Ptr.OverflowBehaviorIsWrap = OverflowBehaviorIsWrap;
     return I;
   }
 

diff  --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 9424b80d5cdb6..41e5218f501e3 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -737,6 +737,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
@@ -770,6 +775,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
@@ -1348,6 +1360,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 {
@@ -2898,6 +2914,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 =
@@ -10171,6 +10192,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 3f63420cae91e..8c59adba53f9f 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,53 @@ QualType ASTContext::getBTFTagAttributedType(const BTFTypeTagAttr *BTFAttr,
   return QualType(Ty, 0);
 }
 
+QualType ASTContext::getOverflowBehaviorType(const OverflowBehaviorAttr *Attr,
+                                             QualType Underlying) const {
+  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 {
+  assert(!Underlying->isOverflowBehaviorType() &&
+         "Cannot have underlying types that are themselves OBTs");
+  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 +8201,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 +8320,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 +9691,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:
@@ -10545,6 +10659,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");
@@ -11613,6 +11758,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 
diff erent 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) {
@@ -11633,6 +11820,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();
@@ -11755,6 +11947,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:
@@ -12164,6 +12357,12 @@ QualType ASTContext::getCorrespondingUnsignedType(QualType T) const {
   if (const auto *EITy = T->getAs<BitIntType>())
     return getBitIntType(/*Unsigned=*/true, EITy->getNumBits());
 
+  // For the overflow behavior types, construct a new unsigned variant
+  if (const auto *OBT = T->getAs<OverflowBehaviorType>())
+    return getOverflowBehaviorType(
+        OBT->getBehaviorKind(),
+        getCorrespondingUnsignedType(OBT->getUnderlyingType()));
+
   // For enums, get the underlying integer type of the enum, and let the general
   // integer type signchanging code handle it.
   if (const auto *ED = T->getAsEnumDecl())
@@ -13903,23 +14102,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 
diff erent, 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) {
@@ -14320,6 +14526,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);
@@ -14393,6 +14608,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)
@@ -14653,7 +14869,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 
diff er. Build a common canonical node out of the two,

diff  --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index 101ab2c40973b..8d8cc5426146d 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 3d1ef2f6c701d..9d970651a9e65 100644
--- a/clang/lib/AST/ASTStructuralEquivalence.cpp
+++ b/clang/lib/AST/ASTStructuralEquivalence.cpp
@@ -1204,6 +1204,13 @@ bool ASTStructuralEquivalence::isEquivalent(
       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 2c13befec02f2..06a957966ba43 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -2774,7 +2774,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)
@@ -15386,6 +15386,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;
@@ -18595,7 +18596,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 36c5f57671631..6eb93598033f6 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 70acc8a78ed52..df00760fa911b 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:
@@ -4586,6 +4587,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 551aa7bf3321c..dda39cff10baf 100644
--- a/clang/lib/AST/MicrosoftMangle.cpp
+++ b/clang/lib/AST/MicrosoftMangle.cpp
@@ -3789,6 +3789,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 dcdbb62f9d62b..a85f08753a132 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -1154,6 +1154,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())
@@ -2043,6 +2055,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());
   }
@@ -2106,10 +2122,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();
 }
 
@@ -2117,6 +2138,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;
 
@@ -2220,6 +2244,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;
 }
 
@@ -2238,6 +2265,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;
 }
 
@@ -2268,6 +2298,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;
 }
 
@@ -2286,6 +2319,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;
 }
 
@@ -2349,6 +2385,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();
 }
 
@@ -2395,6 +2436,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");
@@ -2740,6 +2783,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:
@@ -2951,6 +2995,22 @@ bool QualType::isWebAssemblyFuncrefType() const {
           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 =
@@ -3049,6 +3109,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()))
@@ -3979,6 +4042,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) {}
@@ -4884,6 +4953,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");
@@ -4979,6 +5050,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()
@@ -5173,6 +5247,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 3a2f6a1486ddf..de7b429a8e589 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;
@@ -2025,6 +2026,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:
@@ -2102,6 +2104,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/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp
index 224b2997b7db4..93176eabb9280 100644
--- a/clang/lib/CodeGen/CGCall.cpp
+++ b/clang/lib/CodeGen/CGCall.cpp
@@ -4729,14 +4729,17 @@ void CodeGenFunction::EmitCallArgs(
     CallExpr::const_arg_iterator Arg = ArgRange.begin();
     for (QualType Ty : ArgTypes) {
       assert(Arg != ArgRange.end() && "Running over edge of argument list!");
-      assert(
-          (isGenericMethod || Ty->isVariablyModifiedType() ||
-           Ty.getNonReferenceType()->isObjCRetainableType() ||
-           getContext()
-                   .getCanonicalType(Ty.getNonReferenceType())
-                   .getTypePtr() ==
-               getContext().getCanonicalType((*Arg)->getType()).getTypePtr()) &&
-          "type mismatch in call argument!");
+      QualType ParamTy = Ty.getNonReferenceType();
+      QualType ArgTy = (*Arg)->getType();
+      if (const auto *OBT = ParamTy->getAs<OverflowBehaviorType>())
+        ParamTy = OBT->getUnderlyingType();
+      if (const auto *OBT = ArgTy->getAs<OverflowBehaviorType>())
+        ArgTy = OBT->getUnderlyingType();
+      assert((isGenericMethod || Ty->isVariablyModifiedType() ||
+              ParamTy->isObjCRetainableType() ||
+              getContext().getCanonicalType(ParamTy).getTypePtr() ==
+                  getContext().getCanonicalType(ArgTy).getTypePtr()) &&
+             "type mismatch in call argument!");
       ++Arg;
     }
 

diff  --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp
index 5e452245ee627..2b123631c526c 100644
--- a/clang/lib/CodeGen/CGDebugInfo.cpp
+++ b/clang/lib/CodeGen/CGDebugInfo.cpp
@@ -1198,6 +1198,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;
@@ -4201,6 +4206,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 31b40f06f09d0..b36a597a80ede 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 ed4691c8efabb..3ef9da7806606 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.
@@ -787,19 +823,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);
       }
     }
@@ -821,11 +866,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);
@@ -1122,8 +1162,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.
@@ -1169,14 +1211,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 && !OBTrapInvolved)
     return;
 
   llvm::Constant *StaticArgs[] = {
@@ -1753,9 +1808,24 @@ 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,
@@ -3109,46 +3179,42 @@ static BinOpInfo createBinOpInfoFromIncDec(const UnaryOperator *E,
 
 llvm::Value *ScalarExprEmitter::EmitIncDecConsiderOverflowBehavior(
     const UnaryOperator *E, llvm::Value *InVal, bool IsInc) {
+  // Treat positive amount as unsigned to support inc of i1 (needed for
+  // unsigned _BitInt(1)).
   llvm::Value *Amount =
-      llvm::ConstantInt::get(InVal->getType(), IsInc ? 1 : -1, true);
+      llvm::ConstantInt::get(InVal->getType(), IsInc ? 1 : -1, !IsInc);
   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 {
@@ -3267,9 +3333,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.");
@@ -3326,15 +3389,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 {
       // Treat positive amount as unsigned to support inc of i1 (needed for
       // unsigned _BitInt(1)).
@@ -4114,7 +4171,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 =
@@ -4260,14 +4317,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;
@@ -4591,19 +4659,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);
     }
   }
@@ -4622,11 +4699,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");
@@ -4747,19 +4819,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);
       }
     }
@@ -4778,11 +4859,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 2d3507447804a..73232ae86b734 100644
--- a/clang/lib/CodeGen/CodeGenFunction.cpp
+++ b/clang/lib/CodeGen/CodeGenFunction.cpp
@@ -277,6 +277,7 @@ TypeEvaluationKind CodeGenFunction::getEvaluationKind(QualType type) {
     case Type::BitInt:
     case Type::HLSLAttributedResource:
     case Type::HLSLInlineSpirv:
+    case Type::OverflowBehavior:
       return TEK_Scalar;
 
     // Complexes.
@@ -2609,6 +2610,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 f54921434986c..6bd79056e599a 100644
--- a/clang/lib/CodeGen/CodeGenTypes.cpp
+++ b/clang/lib/CodeGen/CodeGenTypes.cpp
@@ -794,6 +794,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 c62bc4998c324..7b267f0ff8b37 100644
--- a/clang/lib/CodeGen/ItaniumCXXABI.cpp
+++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp
@@ -3972,6 +3972,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:
@@ -4325,6 +4326,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 8aa2c595e2dea..63ee1ed51d6b4 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -6366,6 +6366,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_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/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index df9e3878bffc0..d0d006a78274e 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -4493,6 +4493,26 @@ void Parser::ParseDeclarationSpecifiers(
       isInvalid = DS.SetTypeQual(DeclSpec::TQ_restrict, Loc, PrevSpec, DiagID,
                                  getLangOpts());
       break;
+    case tok::kw___ob_wrap:
+      if (!getLangOpts().OverflowBehaviorTypes) {
+        Diag(Loc, diag::warn_overflow_behavior_keyword_disabled)
+            << tok::getKeywordSpelling(Tok.getKind());
+        break;
+      }
+      isInvalid = DS.SetOverflowBehavior(
+          OverflowBehaviorType::OverflowBehaviorKind::Wrap, Loc, PrevSpec,
+          DiagID);
+      break;
+    case tok::kw___ob_trap:
+      if (!getLangOpts().OverflowBehaviorTypes) {
+        Diag(Loc, diag::warn_overflow_behavior_keyword_disabled)
+            << tok::getKeywordSpelling(Tok.getKind());
+        break;
+      }
+      isInvalid = DS.SetOverflowBehavior(
+          OverflowBehaviorType::OverflowBehaviorKind::Trap, Loc, PrevSpec,
+          DiagID);
+      break;
 
     // C++ typename-specifier:
     case tok::kw_typename:
@@ -5643,6 +5663,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.
@@ -5856,6 +5878,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
@@ -6169,6 +6193,26 @@ void Parser::ParseTypeQualifierListOpt(
       isInvalid = DS.SetTypeQual(DeclSpec::TQ_restrict, Loc, PrevSpec, DiagID,
                                  getLangOpts());
       break;
+    case tok::kw___ob_wrap:
+      if (!getLangOpts().OverflowBehaviorTypes) {
+        Diag(Loc, diag::warn_overflow_behavior_keyword_disabled)
+            << tok::getKeywordSpelling(Tok.getKind());
+        break;
+      }
+      isInvalid = DS.SetOverflowBehavior(
+          OverflowBehaviorType::OverflowBehaviorKind::Wrap, Loc, PrevSpec,
+          DiagID);
+      break;
+    case tok::kw___ob_trap:
+      if (!getLangOpts().OverflowBehaviorTypes) {
+        Diag(Loc, diag::warn_overflow_behavior_keyword_disabled)
+            << tok::getKeywordSpelling(Tok.getKind());
+        break;
+      }
+      isInvalid = DS.SetOverflowBehavior(
+          OverflowBehaviorType::OverflowBehaviorKind::Trap, Loc, PrevSpec,
+          DiagID);
+      break;
     case tok::kw__Atomic:
       if (!AtomicOrPtrauthAllowed)
         goto DoneWithTypeQuals;
@@ -6467,7 +6511,8 @@ void Parser::ParseDeclaratorInternal(Declarator &D,
       D.AddTypeInfo(DeclaratorChunk::getPointer(
                         DS.getTypeQualifiers(), Loc, DS.getConstSpecLoc(),
                         DS.getVolatileSpecLoc(), DS.getRestrictSpecLoc(),
-                        DS.getAtomicSpecLoc(), DS.getUnalignedSpecLoc()),
+                        DS.getAtomicSpecLoc(), DS.getUnalignedSpecLoc(),
+                        DS.getOverflowBehaviorLoc(), DS.isWrapSpecified()),
                     std::move(DS.getAttributes()), SourceLocation());
     else
       // Remember that we parsed a Block type, and remember the type-quals.

diff  --git a/clang/lib/Parse/ParseTentative.cpp b/clang/lib/Parse/ParseTentative.cpp
index c08546816d35a..3af20ce66a5d1 100644
--- a/clang/lib/Parse/ParseTentative.cpp
+++ b/clang/lib/Parse/ParseTentative.cpp
@@ -1252,6 +1252,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 d53527af38653..3065b5e1e66d3 100644
--- a/clang/lib/Sema/Sema.cpp
+++ b/clang/lib/Sema/Sema.cpp
@@ -842,6 +842,20 @@ ExprResult Sema::ImpCastExprToType(Expr *E, QualType Ty,
     }
   }
 
+  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 45006bfc11644..1d139878bd34d 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -8969,6 +8969,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.
@@ -11223,6 +11227,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.
@@ -11272,6 +11278,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());
@@ -12405,6 +12413,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.
@@ -12879,6 +12892,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.
@@ -13225,6 +13240,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;
@@ -13909,6 +13941,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 4dfde4bf8cedf..3db91b00f9d80 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -14134,6 +14134,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 6f4a2219c666a..82da5dc032237 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -1417,6 +1417,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.
@@ -1717,6 +1771,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 
diff ering integer types.
   return handleIntegerConversion<doIntegralCast, doIntegralCast>(
       *this, LHS, RHS, LHSType, RHSType, ACK == ArithConvKind::CompAssign);
@@ -1759,6 +1817,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,
@@ -1883,8 +1964,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()
@@ -1922,16 +2003,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,
@@ -4553,6 +4637,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:
@@ -6150,6 +6235,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())
@@ -9233,6 +9330,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
@@ -9486,6 +9603,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 = LHSType->isBooleanType() ? CK_IntegralToBoolean : 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)) {
@@ -9968,6 +10110,14 @@ 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 (RHSType->isOverflowBehaviorType() &&
+          !LHSType->isOverflowBehaviorType()) {
+        result = AssignConvertType::CompatibleOBTDiscards;
+      }
+
       return result;
     }
 
@@ -14511,6 +14661,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.
@@ -17395,6 +17547,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*
@@ -17479,6 +17637,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 366491fb4b2fc..3db8d3a10252e 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -4703,6 +4703,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,
@@ -4862,6 +4886,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 b9fac5a4a1153..4dd0fa41588b5 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 81edf966de9e7..c513e72db1094 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,
@@ -2295,6 +2300,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
@@ -2485,9 +2496,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).
@@ -2558,6 +2576,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) {
@@ -2905,6 +2926,40 @@ 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,
@@ -3937,6 +3992,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) {
@@ -4731,6 +4815,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
@@ -4843,6 +4931,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 3497ff7856eed..b71dc371508b9 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -6439,6 +6439,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 c0c0ab7a09c72..c082dd85f345f 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -299,6 +299,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.
@@ -1562,6 +1570,24 @@ static QualType ConvertDeclSpecToType(TypeProcessingState &state) {
       Result = Qualified;
   }
 
+  // Check for __ob_wrap and __ob_trap
+  if (DS.isOverflowBehaviorSpecified() &&
+      S.getLangOpts().OverflowBehaviorTypes) {
+    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);
 
@@ -4739,6 +4765,15 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
       T = S.BuildPointerType(T, DeclType.Loc, Name);
       if (DeclType.Ptr.TypeQuals)
         T = S.BuildQualifiedType(T, DeclType.Loc, DeclType.Ptr.TypeQuals);
+      if (DeclType.Ptr.OverflowBehaviorLoc.isValid()) {
+        auto OBState = DeclType.Ptr.OverflowBehaviorIsWrap
+                           ? DeclSpec::OverflowBehaviorState::Wrap
+                           : DeclSpec::OverflowBehaviorState::Trap;
+        S.Diag(DeclType.Ptr.OverflowBehaviorLoc,
+               diag::err_overflow_behavior_non_integer_type)
+            << DeclSpec::getSpecifierName(OBState) << T.getAsString() << 1;
+        D.setInvalidType(true);
+      }
       break;
     case DeclaratorChunk::Reference: {
       // Verify that we're not building a reference to pointer to function with
@@ -5921,6 +5956,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);
@@ -6206,6 +6244,9 @@ namespace {
     void VisitBTFTagAttributedTypeLoc(BTFTagAttributedTypeLoc TL) {
       // nothing
     }
+    void VisitOverflowBehaviorTypeLoc(OverflowBehaviorTypeLoc TL) {
+      // nothing
+    }
     void VisitAdjustedTypeLoc(AdjustedTypeLoc TL) {
       // nothing
     }
@@ -6656,6 +6697,109 @@ static void HandleAddressSpaceTypeAttribute(QualType &Type,
   }
 }
 
+static void HandleOverflowBehaviorAttr(QualType &Type, const ParsedAttr &Attr,
+                                       TypeProcessingState &State) {
+  Sema &S = State.getSema();
+
+  // Check for -fexperimental-overflow-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.
 ///
@@ -9012,6 +9156,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 6a4d88b28c614..1a050bd6a8737 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -7756,6 +7756,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 194f546ad5b29..ff888fdcb0e64 100644
--- a/clang/lib/Serialization/ASTReader.cpp
+++ b/clang/lib/Serialization/ASTReader.cpp
@@ -7559,6 +7559,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 0368fc2d6e3cc..9427b54d6645e 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..46c8511cddc9e
--- /dev/null
+++ b/clang/test/AST/ast-print-overflow-behavior.cpp
@@ -0,0 +1,17 @@
+// RUN: %clang_cc1 -fexperimental-overflow-behavior-types -std=c++11 -ast-print %s -o - | FileCheck %s --check-prefix=PRINT
+
+// 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;
+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..2e459ed803d4d
--- /dev/null
+++ b/clang/test/AST/overflow-behavior-keywords-ast.cpp
@@ -0,0 +1,16 @@
+// RUN: %clang_cc1 -fexperimental-overflow-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..431424724b370
--- /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 -fexperimental-overflow-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..2f3a79f860ac3
--- /dev/null
+++ b/clang/test/CodeGen/overflow-behavior-types-extensions.c
@@ -0,0 +1,104 @@
+// 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)))
+
+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..c0cce80035b34
--- /dev/null
+++ b/clang/test/CodeGen/overflow-behavior-types-operators.cpp
@@ -0,0 +1,70 @@
+// 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)))
+#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..a3eb25805100e
--- /dev/null
+++ b/clang/test/CodeGen/overflow-behavior-types-promotions.cpp
@@ -0,0 +1,49 @@
+// 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)))
+#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..19022f083f7eb
--- /dev/null
+++ b/clang/test/CodeGen/overflow-behavior-types-scl.c
@@ -0,0 +1,64 @@
+// 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 \
+// RUN: -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 \
+// RUN: -fexperimental-overflow-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/trunc.scl \
+// RUN: -fexperimental-overflow-behavior-types -fsanitize=implicit-unsigned-integer-truncation,implicit-signed-integer-truncation \
+// RUN: -emit-llvm -o - | FileCheck %s --check-prefix=TRUNC
+
+//--- 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:*
+
+//--- trunc.scl
+[{implicit-unsigned-integer-truncation,implicit-signed-integer-truncation}]
+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);
+}
+
+// TRUNC-LABEL: define {{.*}} @bar
+void bar(int value) {
+  // TRUNC: %[[V0:.*]] = load i32, ptr %value.addr
+  // TRUNC-NEXT: %[[CONV:.*]] = trunc i32 %[[V0]] to i8
+  // TRUNC-NEXT: %[[ANYEXT:.*]] = zext i8 %[[CONV]] to i32
+  // TRUNC-NEXT: %[[TCHECK:.*]] = icmp eq i32 %[[ANYEXT]], %[[V0]]
+  // TRUNC-NEXT: br i1 %[[TCHECK]], {{.*}}%handler.implicit_conversion
+  unsigned char __ob_trap a = value;
+}

diff  --git a/clang/test/CodeGen/overflow-behavior-types.c b/clang/test/CodeGen/overflow-behavior-types.c
new file mode 100644
index 0000000000000..4638f2e7fb1a7
--- /dev/null
+++ b/clang/test/CodeGen/overflow-behavior-types.c
@@ -0,0 +1,176 @@
+// 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 -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 -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 -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 -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 -fexperimental-overflow-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..89d9d5b24cf09
--- /dev/null
+++ b/clang/test/CodeGen/overflow-behavior-types.cpp
@@ -0,0 +1,51 @@
+// 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
+
+#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 626a0743238d5..cff9073f91618 100644
--- a/clang/test/Misc/pragma-attribute-supported-attributes-list.test
+++ b/clang/test/Misc/pragma-attribute-supported-attributes-list.test
@@ -176,6 +176,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..ea5bc7fdb9fbe
--- /dev/null
+++ b/clang/test/Sema/attr-overflow-behavior-constexpr.cpp
@@ -0,0 +1,43 @@
+// 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)))
+
+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..b1ff90f27e158
--- /dev/null
+++ b/clang/test/Sema/attr-overflow-behavior-format-strings.c
@@ -0,0 +1,93 @@
+// 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
+// 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, &regular_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..14f0131bb4368
--- /dev/null
+++ b/clang/test/Sema/attr-overflow-behavior-templates.cpp
@@ -0,0 +1,73 @@
+// 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)))
+
+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..57d1075b06804
--- /dev/null
+++ b/clang/test/Sema/attr-overflow-behavior.c
@@ -0,0 +1,254 @@
+// 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'}}
+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_
diff erent_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 
diff erence 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')}}
+}
+
+void test_signedness_
diff erences(long long __ob_trap a,
+                                 unsigned long __ob_trap b) {
+  (void)(a + b); // OK
+}
+
+typedef unsigned long __ob_trap size_t_trap;
+void takes_three_ints(int, int, int);
+void test_explicit_cast_to_function_arg(void) {
+  // expected-warning at +1 {{passing argument of type 'size_t_trap' (aka '__ob_trap unsigned long') to parameter of type 'int' discards overflow behavior at function boundary}}
+  takes_three_ints(0, 0, (size_t_trap)0);
+}
+
+_Bool test_obt_to_bool_conversion(size_t_trap a) {
+  return a; // expected-warning {{implicit conversion from 'size_t_trap' (aka '__ob_trap unsigned long') to '_Bool' discards overflow behavior}}
+}
+
+void takes_obt_param(size_t_trap);
+void test_int_literal_to_obt_param(void) {
+  takes_obt_param(0);
+  takes_obt_param(42);
+}
+
+// OBT specifiers cannot qualify pointers
+void (* __ob_wrap bad_fp)(int); // expected-error {{__ob_wrap specifier cannot be applied to non-integer type 'void (*)(int)'}}
+int (* __ob_wrap bad_arr_ptr)[10]; // expected-error {{__ob_wrap specifier cannot be applied to non-integer type 'int (*)[10]'}}
+int * __ob_wrap bad_ptr; // expected-error {{__ob_wrap specifier cannot be applied to non-integer type 'int *'}}
+int * __ob_trap bad_ptr2; // expected-error {{__ob_trap specifier cannot be applied to non-integer type 'int *'}}
+
+// OBT specifiers on the base integer type (before *) are fine
+int __ob_wrap *good_ptr;
+int __ob_trap *good_ptr2;

diff  --git a/clang/test/Sema/attr-overflow-behavior.cpp b/clang/test/Sema/attr-overflow-behavior.cpp
new file mode 100644
index 0000000000000..ad06ced7748b9
--- /dev/null
+++ b/clang/test/Sema/attr-overflow-behavior.cpp
@@ -0,0 +1,190 @@
+// 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'}}
+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 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')}}
+}
+
+// OBT on data members (both static and non-static)
+struct DataMembers {
+  __ob_wrap int nonstatic_specifier;
+  int __attribute__((overflow_behavior(wrap))) nonstatic_attr;
+  static __ob_trap int static_specifier;
+  static int __ob_wrap static_specifier2;
+  static int __attribute__((overflow_behavior(trap))) static_attr;
+};
+__ob_trap int DataMembers::static_specifier = 0;
+int __ob_wrap DataMembers::static_specifier2 = 0;
+int __attribute__((overflow_behavior(trap))) DataMembers::static_attr = 0;

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..ac59d1c934d8a
--- /dev/null
+++ b/clang/test/Sema/overflow-behavior-assignment-pedantic.c
@@ -0,0 +1,17 @@
+// 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
+
+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..7501efd24a4b4
--- /dev/null
+++ b/clang/test/Sema/overflow-behavior-function-boundary-default.c
@@ -0,0 +1,16 @@
+// RUN: %clang_cc1 %s -fexperimental-overflow-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-off.c b/clang/test/Sema/overflow-behavior-keywords-off.c
new file mode 100644
index 0000000000000..7d4b4304d3b47
--- /dev/null
+++ b/clang/test/Sema/overflow-behavior-keywords-off.c
@@ -0,0 +1,26 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+// Verify that __ob_wrap and __ob_trap are ignored when
+// -Xclang -fexperimental-overflow-behavior-types is not passed.
+
+typedef int __ob_wrap wrap_int; // expected-warning {{'__ob_wrap' specifier is ignored because overflow behavior types are not enabled;}}
+typedef int __ob_trap trap_int; // expected-warning {{'__ob_trap' specifier is ignored because overflow behavior types are not enabled;}}
+
+int __ob_wrap a; // expected-warning {{'__ob_wrap' specifier is ignored}}
+int __ob_trap b; // expected-warning {{'__ob_trap' specifier is ignored}}
+
+// The type should just be plain int when the flag is off.
+int *p_a = &a;
+int *p_b = &b;
+
+// Test with pointer type qualifiers
+int * __ob_wrap ptr_w; // expected-warning {{'__ob_wrap' specifier is ignored}}
+int * __ob_trap ptr_t; // expected-warning {{'__ob_trap' specifier is ignored}}
+
+void test_params(int __ob_wrap x, int __ob_trap y) { // expected-warning {{'__ob_wrap' specifier is ignored}} expected-warning {{'__ob_trap' specifier is ignored}}
+  (void)x;
+  (void)y;
+}
+
+struct S {
+  int __ob_wrap member; // expected-warning {{'__ob_wrap' specifier is ignored}}
+};

diff  --git a/clang/test/Sema/overflow-behavior-keywords.c b/clang/test/Sema/overflow-behavior-keywords.c
new file mode 100644
index 0000000000000..5bd742953c8d0
--- /dev/null
+++ b/clang/test/Sema/overflow-behavior-keywords.c
@@ -0,0 +1,70 @@
+// RUN: %clang_cc1 -fsyntax-only -fexperimental-overflow-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'}}
+
+__ob_trap struct S2 { // expected-error{{__ob_trap specifier cannot be applied to non-integer type 'struct S2'}}
+  int a;
+} s2;

diff  --git a/clang/test/SemaCXX/sugar-common-types.cpp b/clang/test/SemaCXX/sugar-common-types.cpp
index 4db0d2ac2f2ae..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 -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 {};
 
@@ -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 15eec87652451..d99af266ea0f1 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 c9192938fca80..1347bf264cf9c 100644
--- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
+++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
@@ -4177,6 +4177,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;
@@ -4875,6 +4876,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;
 
@@ -5175,6 +5177,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;
 


        


More information about the cfe-commits mailing list