[Lldb-commits] [clang] [lldb] [Clang] Introduce OverflowBehaviorType for fine-grained overflow control (PR #148914)

Justin Stitt via lldb-commits lldb-commits at lists.llvm.org
Tue Dec 2 15:20:26 PST 2025


https://github.com/JustinStitt updated https://github.com/llvm/llvm-project/pull/148914

>From 31330361fc7d4c00faf1bfb97d0d1c90d8cd2e70 Mon Sep 17 00:00:00 2001
From: Justin Stitt <justinstitt at google.com>
Date: Mon, 14 Jul 2025 17:30:14 -0700
Subject: [PATCH 1/4] [Clang] implement OverflowBehaviorTypes

Signed-off-by: Justin Stitt <justinstitt at google.com>
---
 clang/docs/OverflowBehaviorTypes.rst          | 827 ++++++++++++++++++
 clang/docs/ReleaseNotes.rst                   |   6 +
 clang/docs/SanitizerSpecialCaseList.rst       |  34 +
 clang/docs/UndefinedBehaviorSanitizer.rst     |  36 +
 clang/docs/index.rst                          |   1 +
 clang/include/clang/AST/ASTContext.h          |  36 +
 clang/include/clang/AST/ASTNodeTraverser.h    |   3 +
 clang/include/clang/AST/PropertiesBase.td     |   2 +
 clang/include/clang/AST/RecursiveASTVisitor.h |   6 +
 clang/include/clang/AST/TypeBase.h            |  58 +-
 clang/include/clang/AST/TypeLoc.h             |  28 +
 clang/include/clang/AST/TypeProperties.td     |  13 +
 clang/include/clang/Basic/Attr.td             |   7 +
 clang/include/clang/Basic/DiagnosticGroups.td |  64 +-
 .../clang/Basic/DiagnosticSemaKinds.td        |  66 ++
 clang/include/clang/Basic/LangOptions.def     |   2 +
 clang/include/clang/Basic/LangOptions.h       |  19 +
 clang/include/clang/Basic/TokenKinds.def      |   4 +
 clang/include/clang/Basic/TypeNodes.td        |   1 +
 clang/include/clang/Options/Options.td        |   6 +
 clang/include/clang/Sema/DeclSpec.h           |  45 +-
 clang/include/clang/Sema/Sema.h               |  33 +
 .../clang/Serialization/TypeBitCodes.def      |   1 +
 clang/lib/AST/ASTContext.cpp                  | 227 ++++-
 clang/lib/AST/ASTImporter.cpp                 |  12 +
 clang/lib/AST/ASTStructuralEquivalence.cpp    |   7 +
 clang/lib/AST/ExprConstant.cpp                |   6 +-
 clang/lib/AST/FormatString.cpp                |   3 +
 clang/lib/AST/ItaniumMangle.cpp               |  12 +
 clang/lib/AST/MicrosoftMangle.cpp             |  18 +
 clang/lib/AST/Type.cpp                        |  77 +-
 clang/lib/AST/TypeLoc.cpp                     |   8 +
 clang/lib/AST/TypePrinter.cpp                 |  20 +
 clang/lib/CodeGen/CGDebugInfo.cpp             |   7 +
 clang/lib/CodeGen/CGDebugInfo.h               |   1 +
 clang/lib/CodeGen/CGExprScalar.cpp            | 299 ++++---
 clang/lib/CodeGen/CodeGenFunction.cpp         |   2 +
 clang/lib/CodeGen/CodeGenTypes.cpp            |   4 +
 clang/lib/CodeGen/ItaniumCXXABI.cpp           |   4 +
 clang/lib/Driver/ToolChains/Clang.cpp         |   3 +
 clang/lib/Parse/ParseDecl.cpp                 |  24 +
 clang/lib/Parse/ParseTentative.cpp            |   5 +
 clang/lib/Sema/DeclSpec.cpp                   |  31 +
 clang/lib/Sema/Sema.cpp                       |  16 +
 clang/lib/Sema/SemaChecking.cpp               |  66 ++
 clang/lib/Sema/SemaDecl.cpp                   |   4 +
 clang/lib/Sema/SemaExpr.cpp                   | 204 ++++-
 clang/lib/Sema/SemaExprCXX.cpp                |  29 +
 clang/lib/Sema/SemaLookup.cpp                 |   2 +
 clang/lib/Sema/SemaOverload.cpp               | 112 ++-
 clang/lib/Sema/SemaTemplate.cpp               |   5 +
 clang/lib/Sema/SemaTemplateDeduction.cpp      |   2 +
 clang/lib/Sema/SemaType.cpp                   | 138 +++
 clang/lib/Sema/TreeTransform.h                |  21 +
 clang/lib/Serialization/ASTReader.cpp         |   4 +
 clang/lib/Serialization/ASTWriter.cpp         |   4 +
 .../test/AST/ast-print-overflow-behavior.cpp  |  17 +
 .../AST/overflow-behavior-keywords-ast.cpp    |  16 +
 .../CodeGen/mangle-ms-overflow-behavior.cpp   |  12 +
 .../overflow-behavior-types-extensions.c      | 104 +++
 .../overflow-behavior-types-operators.cpp     |  70 ++
 .../overflow-behavior-types-promotions.cpp    |  49 ++
 .../CodeGen/overflow-behavior-types-scl.c     |  43 +
 clang/test/CodeGen/overflow-behavior-types.c  | 176 ++++
 .../test/CodeGen/overflow-behavior-types.cpp  |  51 ++
 ...a-attribute-supported-attributes-list.test |   1 +
 .../Sema/attr-overflow-behavior-constexpr.cpp |  43 +
 .../attr-overflow-behavior-format-strings.c   |  93 ++
 clang/test/Sema/attr-overflow-behavior-off.c  |   3 +
 .../Sema/attr-overflow-behavior-templates.cpp |  73 ++
 clang/test/Sema/attr-overflow-behavior.c      | 222 +++++
 clang/test/Sema/attr-overflow-behavior.cpp    | 178 ++++
 .../overflow-behavior-assignment-pedantic.c   |  17 +
 ...rflow-behavior-function-boundary-default.c |  16 +
 clang/test/Sema/overflow-behavior-keywords.c  |  66 ++
 clang/test/SemaCXX/sugar-common-types.cpp     |  41 +-
 clang/tools/libclang/CIndex.cpp               |   4 +
 .../TypeSystem/Clang/TypeSystemClang.cpp      |   3 +
 78 files changed, 3805 insertions(+), 168 deletions(-)
 create mode 100644 clang/docs/OverflowBehaviorTypes.rst
 create mode 100644 clang/test/AST/ast-print-overflow-behavior.cpp
 create mode 100644 clang/test/AST/overflow-behavior-keywords-ast.cpp
 create mode 100644 clang/test/CodeGen/mangle-ms-overflow-behavior.cpp
 create mode 100644 clang/test/CodeGen/overflow-behavior-types-extensions.c
 create mode 100644 clang/test/CodeGen/overflow-behavior-types-operators.cpp
 create mode 100644 clang/test/CodeGen/overflow-behavior-types-promotions.cpp
 create mode 100644 clang/test/CodeGen/overflow-behavior-types-scl.c
 create mode 100644 clang/test/CodeGen/overflow-behavior-types.c
 create mode 100644 clang/test/CodeGen/overflow-behavior-types.cpp
 create mode 100644 clang/test/Sema/attr-overflow-behavior-constexpr.cpp
 create mode 100644 clang/test/Sema/attr-overflow-behavior-format-strings.c
 create mode 100644 clang/test/Sema/attr-overflow-behavior-off.c
 create mode 100644 clang/test/Sema/attr-overflow-behavior-templates.cpp
 create mode 100644 clang/test/Sema/attr-overflow-behavior.c
 create mode 100644 clang/test/Sema/attr-overflow-behavior.cpp
 create mode 100644 clang/test/Sema/overflow-behavior-assignment-pedantic.c
 create mode 100644 clang/test/Sema/overflow-behavior-function-boundary-default.c
 create mode 100644 clang/test/Sema/overflow-behavior-keywords.c

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

>From 3fcb27cbfd420c3ea228518f142e6ea95e2f247d Mon Sep 17 00:00:00 2001
From: Justin Stitt <justinstitt at google.com>
Date: Tue, 2 Dec 2025 13:50:00 -0800
Subject: [PATCH 2/4] change flag to driver-only and make it experimental

Signed-off-by: Justin Stitt <justinstitt at google.com>
---
 clang/docs/OverflowBehaviorTypes.rst          | 27 +++++++++++--------
 clang/docs/ReleaseNotes.rst                   |  2 +-
 .../clang/Basic/DiagnosticSemaKinds.td        |  2 +-
 clang/include/clang/Options/Options.td        | 13 ++++-----
 clang/lib/Driver/ToolChains/Clang.cpp         |  4 +--
 clang/lib/Sema/SemaType.cpp                   |  2 +-
 .../test/AST/ast-print-overflow-behavior.cpp  |  8 +++---
 .../AST/overflow-behavior-keywords-ast.cpp    |  2 +-
 .../CodeGen/mangle-ms-overflow-behavior.cpp   |  2 +-
 .../overflow-behavior-types-extensions.c      |  2 +-
 .../overflow-behavior-types-operators.cpp     |  2 +-
 .../overflow-behavior-types-promotions.cpp    |  2 +-
 .../CodeGen/overflow-behavior-types-scl.c     |  4 +--
 clang/test/CodeGen/overflow-behavior-types.c  | 12 ++++-----
 .../test/CodeGen/overflow-behavior-types.cpp  |  2 +-
 .../Sema/attr-overflow-behavior-constexpr.cpp |  2 +-
 .../attr-overflow-behavior-format-strings.c   |  2 +-
 .../Sema/attr-overflow-behavior-templates.cpp |  2 +-
 clang/test/Sema/attr-overflow-behavior.c      |  2 +-
 clang/test/Sema/attr-overflow-behavior.cpp    |  2 +-
 .../overflow-behavior-assignment-pedantic.c   |  2 +-
 ...rflow-behavior-function-boundary-default.c |  2 +-
 clang/test/Sema/overflow-behavior-keywords.c  |  2 +-
 clang/test/SemaCXX/sugar-common-types.cpp     |  2 +-
 24 files changed, 55 insertions(+), 49 deletions(-)

diff --git a/clang/docs/OverflowBehaviorTypes.rst b/clang/docs/OverflowBehaviorTypes.rst
index 7492d7819cb51..0bedbc9a1a39c 100644
--- a/clang/docs/OverflowBehaviorTypes.rst
+++ b/clang/docs/OverflowBehaviorTypes.rst
@@ -16,8 +16,13 @@ overflow. This is particularly useful for projects that need to balance
 performance and safety, allowing developers to enable or disable overflow
 checks for specific types.
 
-Overflow behavior types can be enabled using the compiler option
-``-foverflow-behavior-types``.
+Overflow behavior types can be enabled using the ``-cc1`` compiler option
+``-fexperimental-overflow-behavior-types``.
+
+.. note::
+
+   This feature is experimental. The flag spelling may change in future
+   releases as the feature matures.
 
 There are two syntax options for specifying overflow behavior:
 
@@ -226,8 +231,8 @@ represent fundamentally different behavioral contracts.
   a = b; // error: assigning to '__ob_trap int' from '__ob_wrap int' with
          // incompatible overflow behavior types ('__ob_trap' and '__ob_wrap')
 
-The Diagnostics section below details the exact diagnostics Clang provides for
-overflow behavior types.
+The Diagnostics section further below details the exact diagnostics Clang
+provides for overflow behavior types.
 
 Truncation Semantics
 --------------------
@@ -267,10 +272,10 @@ truncation, instrumentation checks behave as follows:
     a = b; // sanitizer truncation checks disallowed
   }
 
-Note that truncation itself is a form of overflow behavior - when a value
-is too large to fit in the destination type, the high-order bits are
-discarded, which is the wrapping behavior that ``wrap`` types are
-designed to handle predictably.
+Note that truncation itself is a form of overflow behavior - when a value is
+too large to fit in the destination type, the high-order bits are discarded,
+which is a wrapping behavior that ``wrap`` types are designed to handle
+predictably.
 
 Constant Conversion Semantics
 ------------------------------
@@ -512,14 +517,14 @@ redundant but allowed:
 **Feature Not Enabled:**
 
 When overflow behavior types are used without enabling the feature via
-``-foverflow-behavior-types``, a warning is issued and the attribute is ignored:
+``-fexperimental-overflow-behavior-types``, a warning is issued and the attribute is ignored:
 
 .. code-block:: c++
 
-  // Without -foverflow-behavior-types
+  // Without -fexperimental-overflow-behavior-types
   int __attribute__((overflow_behavior(wrap))) x;
   // warning: 'overflow_behavior' attribute is ignored because it is
-  // not enabled; pass -foverflow-behavior-types
+  // not enabled; pass -fexperimental-overflow-behavior-types
 
 -Woverflow-behavior-conversion
 ------------------------------
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 6d9738cdffb5f..24e78b21c1931 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -331,7 +331,7 @@ New Compiler Flags
 - New option ``-fsanitize-debug-trap-reasons=`` added to control emitting trap reasons into the debug info when compiling with trapping UBSan (e.g. ``-fsanitize-trap=undefined``).
 - New options for enabling allocation token instrumentation: ``-fsanitize=alloc-token``, ``-falloc-token-max=``, ``-fsanitize-alloc-token-fast-abi``, ``-fsanitize-alloc-token-extended``.
 - The ``-resource-dir`` option is now displayed in the list of options shown by ``--help``.
-- New option ``-foverflow-behavior-types`` added to enable parsing of the ``overflow_behavior`` type attribute and type specifiers.
+- New ``-cc1`` option ``-fexperimental-overflow-behavior-types`` added to enable parsing of the experimental ``overflow_behavior`` type attribute and type specifiers.
 
 Lanai Support
 ^^^^^^^^^^^^^^
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index c0e380ce38e8d..7fa55379182cf 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -4095,7 +4095,7 @@ def err_incompatible_obt_kinds_assignment
             "('%2' and '%3')">;
 def warn_overflow_behavior_attribute_disabled
     : Warning<"%0 attribute is ignored because it is not enabled; pass "
-              "-foverflow-behavior-types">,
+              "-Xclang -fexperimental-overflow-behavior-types">,
       InGroup<OverflowBehaviorAttributeIgnored>;
 def warn_impcast_overflow_behavior
     : Warning<"implicit conversion from %0 to %1 discards overflow behavior">,
diff --git a/clang/include/clang/Options/Options.td b/clang/include/clang/Options/Options.td
index 38661b0d8a8b2..58ced52997b4f 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -2349,12 +2349,13 @@ defm fixed_point : BoolFOption<"fixed-point",
   PosFlag<SetTrue, [], [ClangOption, CC1Option], "Enable">,
   NegFlag<SetFalse, [], [ClangOption], "Disable">,
   BothFlags<[], [ClangOption], " fixed point types">>;
-defm overflow_behavior_types
-    : BoolFOption<"overflow-behavior-types", LangOpts<"OverflowBehaviorTypes">,
-                  DefaultFalse,
-                  PosFlag<SetTrue, [], [ClangOption, CC1Option], "Enable">,
-                  NegFlag<SetFalse, [], [ClangOption], "Disable">,
-                  BothFlags<[], [ClangOption], " overflow behavior types">>;
+defm experimental_overflow_behavior_types
+    : BoolFOption<"experimental-overflow-behavior-types",
+                  LangOpts<"OverflowBehaviorTypes">, DefaultFalse,
+                  PosFlag<SetTrue, [], [CC1Option], "Enable">,
+                  NegFlag<SetFalse, [], [CC1Option], "Disable">,
+                  BothFlags<[], [CC1Option],
+                            " the experimental overflow behavior types">>;
 def cxx_static_destructors_EQ : Joined<["-"], "fc++-static-destructors=">, Group<f_Group>,
   HelpText<"Controls which variables C++ static destructors are registered for">,
   Values<"all,thread-local,none">,
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index d9759229b3893..fa48528a5c401 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -6269,8 +6269,8 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
   Args.addOptInFlag(CmdArgs, options::OPT_ffixed_point,
                     options::OPT_fno_fixed_point);
 
-  Args.addOptInFlag(CmdArgs, options::OPT_foverflow_behavior_types,
-                    options::OPT_fno_overflow_behavior_types);
+  Args.addOptInFlag(CmdArgs, options::OPT_fexperimental_overflow_behavior_types,
+                    options::OPT_fno_experimental_overflow_behavior_types);
 
   if (Arg *A = Args.getLastArg(options::OPT_fcxx_abi_EQ))
     A->render(Args, CmdArgs);
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index d92571e50ef47..a00437f5c7924 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -6683,7 +6683,7 @@ static void HandleOverflowBehaviorAttr(QualType &Type, const ParsedAttr &Attr,
                                        TypeProcessingState &State) {
   Sema &S = State.getSema();
 
-  // Check for -foverflow-behavior-types
+  // Check for -fexperimental-overflow-behavior-types
   if (!S.getLangOpts().OverflowBehaviorTypes) {
     S.Diag(Attr.getLoc(), diag::warn_overflow_behavior_attribute_disabled)
         << Attr << 1;
diff --git a/clang/test/AST/ast-print-overflow-behavior.cpp b/clang/test/AST/ast-print-overflow-behavior.cpp
index f66479951021f..46c8511cddc9e 100644
--- a/clang/test/AST/ast-print-overflow-behavior.cpp
+++ b/clang/test/AST/ast-print-overflow-behavior.cpp
@@ -1,8 +1,8 @@
-// RUN: %clang_cc1 -foverflow-behavior-types -std=c++11 -ast-print %s -o - | FileCheck %s --check-prefix=PRINT
+// RUN: %clang_cc1 -fexperimental-overflow-behavior-types -std=c++11 -ast-print %s -o - | FileCheck %s --check-prefix=PRINT
 
-// RUN: %clang_cc1 -foverflow-behavior-types -std=c++11 -fsyntax-only %s
-// RUN: %clang_cc1 -foverflow-behavior-types -std=c++11 -ast-print %s -o %t.1.cpp
-// RUN: %clang_cc1 -foverflow-behavior-types -std=c++11 -ast-print %t.1.cpp -o %t.2.cpp
+// RUN: %clang_cc1 -fexperimental-overflow-behavior-types -std=c++11 -fsyntax-only %s
+// RUN: %clang_cc1 -fexperimental-overflow-behavior-types -std=c++11 -ast-print %s -o %t.1.cpp
+// RUN: %clang_cc1 -fexperimental-overflow-behavior-types -std=c++11 -ast-print %t.1.cpp -o %t.2.cpp
 // RUN: diff %t.1.cpp %t.2.cpp
 
 extern int __attribute__((overflow_behavior(trap))) a;
diff --git a/clang/test/AST/overflow-behavior-keywords-ast.cpp b/clang/test/AST/overflow-behavior-keywords-ast.cpp
index 4269db7bdbfbb..2e459ed803d4d 100644
--- a/clang/test/AST/overflow-behavior-keywords-ast.cpp
+++ b/clang/test/AST/overflow-behavior-keywords-ast.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -foverflow-behavior-types -ast-dump %s | FileCheck %s
+// RUN: %clang_cc1 -fexperimental-overflow-behavior-types -ast-dump %s | FileCheck %s
 
 // Test that keyword and attribute syntax produce the same OverflowBehaviorType
 
diff --git a/clang/test/CodeGen/mangle-ms-overflow-behavior.cpp b/clang/test/CodeGen/mangle-ms-overflow-behavior.cpp
index 06af5b36f3eb1..431424724b370 100644
--- a/clang/test/CodeGen/mangle-ms-overflow-behavior.cpp
+++ b/clang/test/CodeGen/mangle-ms-overflow-behavior.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -emit-llvm %s -o - -fms-extensions -triple=x86_64-pc-win32 -foverflow-behavior-types | FileCheck %s
+// RUN: %clang_cc1 -emit-llvm %s -o - -fms-extensions -triple=x86_64-pc-win32 -fexperimental-overflow-behavior-types | FileCheck %s
 
 #define __wrap __attribute__((overflow_behavior(wrap)))
 #define __trap __attribute__((overflow_behavior(trap)))
diff --git a/clang/test/CodeGen/overflow-behavior-types-extensions.c b/clang/test/CodeGen/overflow-behavior-types-extensions.c
index 50e86c64571a1..2f3a79f860ac3 100644
--- a/clang/test/CodeGen/overflow-behavior-types-extensions.c
+++ b/clang/test/CodeGen/overflow-behavior-types-extensions.c
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -triple x86_64-linux-gnu -foverflow-behavior-types -std=c2x %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -fexperimental-overflow-behavior-types -std=c2x %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK
 
 #define __wrap __attribute__((overflow_behavior(wrap)))
 #define __no_trap __attribute__((overflow_behavior(trap)))
diff --git a/clang/test/CodeGen/overflow-behavior-types-operators.cpp b/clang/test/CodeGen/overflow-behavior-types-operators.cpp
index 22794cac4f3ad..c0cce80035b34 100644
--- a/clang/test/CodeGen/overflow-behavior-types-operators.cpp
+++ b/clang/test/CodeGen/overflow-behavior-types-operators.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -triple x86_64-linux-gnu %s -foverflow-behavior-types \
+// RUN: %clang_cc1 -triple x86_64-linux-gnu %s -fexperimental-overflow-behavior-types \
 // RUN: -fsanitize=signed-integer-overflow -emit-llvm -o - -std=c++14 | FileCheck %s
 
 #define __wrap __attribute__((overflow_behavior(wrap)))
diff --git a/clang/test/CodeGen/overflow-behavior-types-promotions.cpp b/clang/test/CodeGen/overflow-behavior-types-promotions.cpp
index bebc2c3b7001f..a3eb25805100e 100644
--- a/clang/test/CodeGen/overflow-behavior-types-promotions.cpp
+++ b/clang/test/CodeGen/overflow-behavior-types-promotions.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -triple x86_64-linux-gnu %s -foverflow-behavior-types \
+// RUN: %clang_cc1 -triple x86_64-linux-gnu %s -fexperimental-overflow-behavior-types \
 // RUN: -fsanitize=signed-integer-overflow,unsigned-integer-overflow -emit-llvm -o - -std=c++14 | FileCheck %s
 
 #define __wrap __attribute__((overflow_behavior(wrap)))
diff --git a/clang/test/CodeGen/overflow-behavior-types-scl.c b/clang/test/CodeGen/overflow-behavior-types-scl.c
index 1bc1139bb72b0..3ed5eaea6e28c 100644
--- a/clang/test/CodeGen/overflow-behavior-types-scl.c
+++ b/clang/test/CodeGen/overflow-behavior-types-scl.c
@@ -1,8 +1,8 @@
 // RUN: rm -rf %t
 // RUN: split-file %s %t
 
-// RUN: %clang_cc1 -triple x86_64-linux-gnu %t/test.c -fsanitize-ignorelist=%t/sio.scl -foverflow-behavior-types -fsanitize=signed-integer-overflow -emit-llvm -o - | FileCheck %s --check-prefix=SIO
-// RUN: %clang_cc1 -triple x86_64-linux-gnu %t/test.c -fsanitize-ignorelist=%t/uio.scl -foverflow-behavior-types -fsanitize=unsigned-integer-overflow -emit-llvm -o - | FileCheck %s --check-prefix=UIO
+// RUN: %clang_cc1 -triple x86_64-linux-gnu %t/test.c -fsanitize-ignorelist=%t/sio.scl -fexperimental-overflow-behavior-types -fsanitize=signed-integer-overflow -emit-llvm -o - | FileCheck %s --check-prefix=SIO
+// RUN: %clang_cc1 -triple x86_64-linux-gnu %t/test.c -fsanitize-ignorelist=%t/uio.scl -fexperimental-overflow-behavior-types -fsanitize=unsigned-integer-overflow -emit-llvm -o - | FileCheck %s --check-prefix=UIO
 
 //--- sio.scl
 [signed-integer-overflow]
diff --git a/clang/test/CodeGen/overflow-behavior-types.c b/clang/test/CodeGen/overflow-behavior-types.c
index abfdc7c639cab..4638f2e7fb1a7 100644
--- a/clang/test/CodeGen/overflow-behavior-types.c
+++ b/clang/test/CodeGen/overflow-behavior-types.c
@@ -1,25 +1,25 @@
-// RUN: %clang_cc1 -triple x86_64-linux-gnu -foverflow-behavior-types %s \
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -fexperimental-overflow-behavior-types %s \
 // RUN: -fsanitize=signed-integer-overflow,unsigned-integer-overflow,implicit-signed-integer-truncation,implicit-unsigned-integer-truncation \
 // RUN: -emit-llvm -o - | FileCheck %s --check-prefix=DEFAULT
 
-// RUN: %clang_cc1 -triple x86_64-linux-gnu -foverflow-behavior-types %s -ftrapv \
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -fexperimental-overflow-behavior-types %s -ftrapv \
 // RUN: -fsanitize=signed-integer-overflow,unsigned-integer-overflow,implicit-signed-integer-truncation,implicit-unsigned-integer-truncation \
 // RUN: -emit-llvm -o - | FileCheck %s --check-prefix=DEFAULT
 
-// RUN: %clang_cc1 -triple x86_64-linux-gnu -foverflow-behavior-types %s \
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -fexperimental-overflow-behavior-types %s \
 // RUN: -ftrapv -ftrapv-handler OVERFLOW_HANDLER \
 // RUN: -emit-llvm -o - | FileCheck %s --check-prefix=TRAPV-HANDLER
 
-// RUN: %clang_cc1 -triple x86_64-linux-gnu -foverflow-behavior-types %s -fwrapv \
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -fexperimental-overflow-behavior-types %s -fwrapv \
 // RUN: -fsanitize=signed-integer-overflow,unsigned-integer-overflow,implicit-signed-integer-truncation,implicit-unsigned-integer-truncation \
 // RUN: -emit-llvm -o - | FileCheck %s --check-prefix=DEFAULT
 
-// RUN: %clang_cc1 -triple x86_64-linux-gnu -foverflow-behavior-types %s \
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -fexperimental-overflow-behavior-types %s \
 // RUN: -fsanitize-undefined-ignore-overflow-pattern=all \
 // RUN: -fsanitize=signed-integer-overflow,unsigned-integer-overflow,implicit-signed-integer-truncation,implicit-unsigned-integer-truncation \
 // RUN: -emit-llvm -o - | FileCheck %s --check-prefix=EXCL
 
-// RUN: %clang_cc1 -triple x86_64-linux-gnu -foverflow-behavior-types %s \
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -fexperimental-overflow-behavior-types %s \
 // RUN: -emit-llvm -o - | FileCheck %s --check-prefix=NOSAN
 
 #define __wrap __attribute__((overflow_behavior("wrap")))
diff --git a/clang/test/CodeGen/overflow-behavior-types.cpp b/clang/test/CodeGen/overflow-behavior-types.cpp
index 25ca14bbd3b40..89d9d5b24cf09 100644
--- a/clang/test/CodeGen/overflow-behavior-types.cpp
+++ b/clang/test/CodeGen/overflow-behavior-types.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -triple x86_64-linux-gnu %s -foverflow-behavior-types \
+// RUN: %clang_cc1 -triple x86_64-linux-gnu %s -fexperimental-overflow-behavior-types \
 // RUN: -fsanitize=signed-integer-overflow,unsigned-integer-overflow,implicit-signed-integer-truncation,implicit-unsigned-integer-truncation \
 // RUN: -emit-llvm -o - | FileCheck %s --check-prefix=DEFAULT
 
diff --git a/clang/test/Sema/attr-overflow-behavior-constexpr.cpp b/clang/test/Sema/attr-overflow-behavior-constexpr.cpp
index 0c9ed48a6fef4..ea5bc7fdb9fbe 100644
--- a/clang/test/Sema/attr-overflow-behavior-constexpr.cpp
+++ b/clang/test/Sema/attr-overflow-behavior-constexpr.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -triple x86_64-linux-gnu %s -foverflow-behavior-types -verify -fsyntax-only -std=c++14
+// RUN: %clang_cc1 -triple x86_64-linux-gnu %s -fexperimental-overflow-behavior-types -verify -fsyntax-only -std=c++14
 
 #define __wrap __attribute__((overflow_behavior(wrap)))
 #define __no_trap __attribute__((overflow_behavior(trap)))
diff --git a/clang/test/Sema/attr-overflow-behavior-format-strings.c b/clang/test/Sema/attr-overflow-behavior-format-strings.c
index 83f8c35824b6c..b1ff90f27e158 100644
--- a/clang/test/Sema/attr-overflow-behavior-format-strings.c
+++ b/clang/test/Sema/attr-overflow-behavior-format-strings.c
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -fsyntax-only -verify -Wformat -foverflow-behavior-types -isystem %S/Inputs %s
+// RUN: %clang_cc1 -fsyntax-only -verify -Wformat -fexperimental-overflow-behavior-types -isystem %S/Inputs %s
 
 // Test format string checking with overflow behavior types
 // This ensures that OverflowBehaviorTypes work seamlessly with printf/scanf
diff --git a/clang/test/Sema/attr-overflow-behavior-templates.cpp b/clang/test/Sema/attr-overflow-behavior-templates.cpp
index 6f7a064b83e14..14f0131bb4368 100644
--- a/clang/test/Sema/attr-overflow-behavior-templates.cpp
+++ b/clang/test/Sema/attr-overflow-behavior-templates.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 %s -foverflow-behavior-types -verify -fsyntax-only -Wimplicit-overflow-behavior-conversion
+// RUN: %clang_cc1 %s -fexperimental-overflow-behavior-types -verify -fsyntax-only -Wimplicit-overflow-behavior-conversion
 
 #define __wrap __attribute__((overflow_behavior(wrap)))
 #define __trap __attribute__((overflow_behavior(trap)))
diff --git a/clang/test/Sema/attr-overflow-behavior.c b/clang/test/Sema/attr-overflow-behavior.c
index 720f2ea60477f..125ed860a91b1 100644
--- a/clang/test/Sema/attr-overflow-behavior.c
+++ b/clang/test/Sema/attr-overflow-behavior.c
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 %s -Winteger-overflow -Wno-unused-value -foverflow-behavior-types -Woverflow-behavior-conversion -Wconstant-conversion -verify -fsyntax-only -std=c11 -Wno-pointer-sign
+// RUN: %clang_cc1 %s -Winteger-overflow -Wno-unused-value -fexperimental-overflow-behavior-types -Woverflow-behavior-conversion -Wconstant-conversion -verify -fsyntax-only -std=c11 -Wno-pointer-sign
 
 typedef int __attribute__((overflow_behavior)) bad_arg_count; // expected-error {{'overflow_behavior' attribute takes one argument}}
 typedef int __attribute__((overflow_behavior(not_real))) bad_arg_spec; // expected-error {{'not_real' is not a valid argument to attribute 'overflow_behavior'}}
diff --git a/clang/test/Sema/attr-overflow-behavior.cpp b/clang/test/Sema/attr-overflow-behavior.cpp
index 15e4dcbfce297..7a8b9109ef6da 100644
--- a/clang/test/Sema/attr-overflow-behavior.cpp
+++ b/clang/test/Sema/attr-overflow-behavior.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 %s -Winteger-overflow -Wno-unused-value -foverflow-behavior-types -Wconstant-conversion -Woverflow-behavior-conversion -verify -fsyntax-only
+// RUN: %clang_cc1 %s -Winteger-overflow -Wno-unused-value -fexperimental-overflow-behavior-types -Wconstant-conversion -Woverflow-behavior-conversion -verify -fsyntax-only
 
 typedef int __attribute__((overflow_behavior)) bad_arg_count; // expected-error {{'overflow_behavior' attribute takes one argument}}
 typedef int __attribute__((overflow_behavior(not_real))) bad_arg_spec; // expected-error {{'not_real' is not a valid argument to attribute 'overflow_behavior'}}
diff --git a/clang/test/Sema/overflow-behavior-assignment-pedantic.c b/clang/test/Sema/overflow-behavior-assignment-pedantic.c
index a6433c9b2982a..ac59d1c934d8a 100644
--- a/clang/test/Sema/overflow-behavior-assignment-pedantic.c
+++ b/clang/test/Sema/overflow-behavior-assignment-pedantic.c
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 %s -foverflow-behavior-types -Wimplicit-overflow-behavior-conversion-assignment-pedantic -verify -fsyntax-only -std=c11
+// RUN: %clang_cc1 %s -fexperimental-overflow-behavior-types -Wimplicit-overflow-behavior-conversion-assignment-pedantic -verify -fsyntax-only -std=c11
 
 // Test that -Wimplicit-overflow-behavior-conversion-assignment-pedantic only warns
 // for unsigned wrap to unsigned conversions during assignment
diff --git a/clang/test/Sema/overflow-behavior-function-boundary-default.c b/clang/test/Sema/overflow-behavior-function-boundary-default.c
index 66aa44aa9078d..7501efd24a4b4 100644
--- a/clang/test/Sema/overflow-behavior-function-boundary-default.c
+++ b/clang/test/Sema/overflow-behavior-function-boundary-default.c
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 %s -foverflow-behavior-types -fsyntax-only -verify
+// RUN: %clang_cc1 %s -fexperimental-overflow-behavior-types -fsyntax-only -verify
 
 // Test that function boundary warnings are off by default
 // expected-no-diagnostics
diff --git a/clang/test/Sema/overflow-behavior-keywords.c b/clang/test/Sema/overflow-behavior-keywords.c
index a9e0e2f79d622..85c4076f48f1a 100644
--- a/clang/test/Sema/overflow-behavior-keywords.c
+++ b/clang/test/Sema/overflow-behavior-keywords.c
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -fsyntax-only -foverflow-behavior-types -verify %s
+// RUN: %clang_cc1 -fsyntax-only -fexperimental-overflow-behavior-types -verify %s
 
 int __ob_wrap a;
 int __ob_trap b;
diff --git a/clang/test/SemaCXX/sugar-common-types.cpp b/clang/test/SemaCXX/sugar-common-types.cpp
index b0271569dfffa..4c704db195c54 100644
--- a/clang/test/SemaCXX/sugar-common-types.cpp
+++ b/clang/test/SemaCXX/sugar-common-types.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++20 -x objective-c++ -fobjc-arc -fenable-matrix -foverflow-behavior-types -triple i686-pc-win32
+// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++20 -x objective-c++ -fobjc-arc -fenable-matrix -fexperimental-overflow-behavior-types -triple i686-pc-win32
 
 enum class N {};
 

>From 1769e03ba6d5984a3c03373cdabe3dfa10d378d6 Mon Sep 17 00:00:00 2001
From: Justin Stitt <justinstitt at google.com>
Date: Tue, 2 Dec 2025 14:30:06 -0800
Subject: [PATCH 3/4] run formatter

Signed-off-by: Justin Stitt <justinstitt at google.com>
---
 clang/include/clang/AST/ASTContext.h          |  3 +-
 clang/include/clang/Basic/DiagnosticGroups.td |  3 +-
 .../clang/Basic/DiagnosticSemaKinds.td        | 29 ++++++++++---------
 clang/lib/CodeGen/CGExprScalar.cpp            |  7 +++--
 clang/lib/Sema/Sema.cpp                       |  3 +-
 clang/lib/Sema/SemaExprCXX.cpp                |  4 +--
 clang/lib/Sema/SemaOverload.cpp               |  3 +-
 7 files changed, 28 insertions(+), 24 deletions(-)

diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index 2b52a73a0cebc..230239cf6967c 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -2659,7 +2659,8 @@ class ASTContext : public RefCountedBase<ASTContext> {
   };
 
   /// Check overflow behavior type compatibility for assignments.
-  /// Returns detailed information about OBT compatibility for assignment checking.
+  /// Returns detailed information about OBT compatibility for assignment
+  /// checking.
   OBTAssignResult checkOBTAssignmentCompatibility(QualType LHS, QualType RHS);
 
   /// Return true if the given types are an RISC-V vector builtin type and a
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index b72f97aae64f5..9f633e00be187 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -138,7 +138,8 @@ def ImplicitOverflowBehaviorConversionAssignmentPedantic
 def ImplicitOverflowBehaviorConversionAtFunctionBoundary
     : DiagGroup<"implicit-overflow-behavior-conversion-function-boundary">;
 def ImplicitOverflowBehaviorConversionAtFunctionBoundaryPedantic
-    : DiagGroup<"implicit-overflow-behavior-conversion-function-boundary-pedantic">;
+    : DiagGroup<
+          "implicit-overflow-behavior-conversion-function-boundary-pedantic">;
 def ImplicitOverflowBehaviorConversionPedantic
     : DiagGroup<"implicit-overflow-behavior-conversion-pedantic">;
 def ImplicitOverflowBehaviorConversion
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 7fa55379182cf..9b00045031fd2 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -9211,20 +9211,21 @@ def ext_typecheck_convert_discards_qualifiers : ExtWarn<
   "|%diff{casting $ to type $|casting between types}0,1}2"
   " discards qualifiers">,
   InGroup<IncompatiblePointerTypesDiscardsQualifiers>;
-def ext_typecheck_convert_discards_overflow_behavior : ExtWarn<
-  "%select{%diff{assigning to $ from $|assigning to different types}0,1"
-  "|%diff{passing $ to parameter of type $|"
-  "passing to parameter of different type}0,1"
-  "|%diff{returning $ from a function with result type $|"
-  "returning from function with different return type}0,1"
-  "|%diff{converting $ to type $|converting between types}0,1"
-  "|%diff{initializing $ with an expression of type $|"
-  "initializing with expression of different type}0,1"
-  "|%diff{sending $ to parameter of type $|"
-  "sending to parameter of different type}0,1"
-  "|%diff{casting $ to type $|casting between types}0,1}2"
-  " discards overflow behavior">,
-  InGroup<IncompatiblePointerTypesDiscardsOverflowBehavior>;
+def ext_typecheck_convert_discards_overflow_behavior
+    : ExtWarn<
+          "%select{%diff{assigning to $ from $|assigning to different types}0,1"
+          "|%diff{passing $ to parameter of type $|"
+          "passing to parameter of different type}0,1"
+          "|%diff{returning $ from a function with result type $|"
+          "returning from function with different return type}0,1"
+          "|%diff{converting $ to type $|converting between types}0,1"
+          "|%diff{initializing $ with an expression of type $|"
+          "initializing with expression of different type}0,1"
+          "|%diff{sending $ to parameter of type $|"
+          "sending to parameter of different type}0,1"
+          "|%diff{casting $ to type $|casting between types}0,1}2"
+          " discards overflow behavior">,
+      InGroup<IncompatiblePointerTypesDiscardsOverflowBehavior>;
 def err_typecheck_convert_discards_qualifiers : Error<
   "%select{%diff{assigning to $ from $|assigning to different types}0,1"
   "|%diff{passing $ to parameter of type $|"
diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp
index f00a55aaaafae..e5c00a2dfd454 100644
--- a/clang/lib/CodeGen/CGExprScalar.cpp
+++ b/clang/lib/CodeGen/CGExprScalar.cpp
@@ -1803,8 +1803,8 @@ Value *ScalarExprEmitter::EmitScalarConversion(Value *Src, QualType SrcType,
   // capability checks as this overflow behavior kind is also capable of
   // emitting traps without runtime sanitizer support.
   // Also skip instrumentation if either source or destination has 'wrap'
-  // behavior - the user has explicitly indicated they accept wrapping semantics.
-  // Use non-canonical types to preserve OBT annotations.
+  // behavior - the user has explicitly indicated they accept wrapping
+  // semantics. Use non-canonical types to preserve OBT annotations.
   const auto *DstOBT = NoncanonicalDstType->getAs<OverflowBehaviorType>();
   const auto *SrcOBT = NoncanonicalSrcType->getAs<OverflowBehaviorType>();
   bool OBTrapInvolved =
@@ -1812,7 +1812,8 @@ Value *ScalarExprEmitter::EmitScalarConversion(Value *Src, QualType SrcType,
   bool OBWrapInvolved =
       (DstOBT && DstOBT->isWrapKind()) || (SrcOBT && SrcOBT->isWrapKind());
 
-  if ((Opts.EmitImplicitIntegerTruncationChecks || OBTrapInvolved) && !OBWrapInvolved)
+  if ((Opts.EmitImplicitIntegerTruncationChecks || OBTrapInvolved) &&
+      !OBWrapInvolved)
     EmitIntegerTruncationCheck(Src, NoncanonicalSrcType, Res,
                                NoncanonicalDstType, Loc, OBTrapInvolved);
 
diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp
index 80ca08ef6608d..c5a673dec55fe 100644
--- a/clang/lib/Sema/Sema.cpp
+++ b/clang/lib/Sema/Sema.cpp
@@ -849,7 +849,8 @@ ExprResult Sema::ImpCastExprToType(Expr *E, QualType Ty,
                         isa<CXXFunctionalCastExpr>(E);
 
   if ((Kind == CK_IntegralCast || Kind == CK_IntegralToBoolean ||
-       (Kind == CK_NoOp && E->getType()->isIntegerType() && Ty->isIntegerType())) &&
+       (Kind == CK_NoOp && E->getType()->isIntegerType() &&
+        Ty->isIntegerType())) &&
       IsExplicitCast) {
     if (const auto *SourceOBT = E->getType()->getAs<OverflowBehaviorType>()) {
       if (Ty->isIntegerType() && !Ty->isOverflowBehaviorType()) {
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index f5b8c2e4e128d..f86a35b1b4013 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -4685,8 +4685,8 @@ static QualType adjustVectorType(ASTContext &Context, QualType FromTy,
   return Context.getExtVectorType(ElType, FromVec->getNumElements());
 }
 
-/// Check if an integral conversion involves incompatible overflow behavior types.
-/// Returns true if the conversion is invalid.
+/// Check if an integral conversion involves incompatible overflow behavior
+/// types. Returns true if the conversion is invalid.
 static bool checkIncompatibleOBTConversion(Sema &S, QualType FromType,
                                            QualType ToType, Expr *From) {
   const auto *FromOBT = FromType->getAs<OverflowBehaviorType>();
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 4b88349906888..5f775e8a4f9f3 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -2874,8 +2874,7 @@ bool Sema::IsOverflowBehaviorTypeConversion(QualType FromType,
   if (FromType->isOverflowBehaviorType() && !ToType->isOverflowBehaviorType()) {
     // Don't allow implicit conversion from OverflowBehaviorType to scoped enum
     if (const EnumType *ToEnumType = ToType->getAs<EnumType>()) {
-      const EnumDecl *ToED =
-          ToEnumType->getDecl()->getDefinitionOrSelf();
+      const EnumDecl *ToED = ToEnumType->getDecl()->getDefinitionOrSelf();
       if (ToED->isScoped())
         return false;
     }

>From b31e2d296ea5a47ef577e142e663794c0e757640 Mon Sep 17 00:00:00 2001
From: Justin Stitt <justinstitt at google.com>
Date: Tue, 2 Dec 2025 15:20:06 -0800
Subject: [PATCH 4/4] remove unused variable to fix test

Signed-off-by: Justin Stitt <justinstitt at google.com>
---
 clang/lib/Sema/SemaExpr.cpp | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index c432ec38ab1a7..77e571170337e 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -9994,10 +9994,9 @@ AssignConvertType Sema::CheckSingleAssignmentConstraints(QualType LHSType,
 
       // Check if OBT is being discarded during assignment
       // The RHS may have propagated OBT, but if LHS doesn't have it, warn
-      if (const auto *RHSOBT = RHSType->getAs<OverflowBehaviorType>()) {
-        if (!LHSType->isOverflowBehaviorType()) {
-          result = AssignConvertType::CompatibleOBTDiscards;
-        }
+      if (RHSType->isOverflowBehaviorType() &&
+          !LHSType->isOverflowBehaviorType()) {
+        result = AssignConvertType::CompatibleOBTDiscards;
       }
 
       return result;



More information about the lldb-commits mailing list