[clang] [Clang] Introduce OverflowBehaviorType for fine-grained overflow control (PR #148914)
Justin Stitt via cfe-commits
cfe-commits at lists.llvm.org
Wed Aug 27 15:47:27 PDT 2025
https://github.com/JustinStitt updated https://github.com/llvm/llvm-project/pull/148914
>From 020adafe31a57b737cc1c3ffabf9a8e109a292a1 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 01/21] [Clang] implement OverflowBehaviorTypes
Signed-off-by: Justin Stitt <justinstitt at google.com>
---
clang/docs/OverflowBehaviorTypes.rst | 269 ++++++++++++++++
clang/docs/ReleaseNotes.rst | 20 ++
clang/docs/SanitizerSpecialCaseList.rst | 34 ++
clang/docs/UndefinedBehaviorSanitizer.rst | 22 ++
clang/docs/index.rst | 1 +
clang/include/clang/AST/ASTContext.h | 20 ++
clang/include/clang/AST/ASTNodeTraverser.h | 3 +
clang/include/clang/AST/Expr.h | 8 +
clang/include/clang/AST/PropertiesBase.td | 1 +
clang/include/clang/AST/RecursiveASTVisitor.h | 6 +
clang/include/clang/AST/Stmt.h | 3 +
clang/include/clang/AST/Type.h | 77 +++++
clang/include/clang/AST/TypeLoc.h | 21 ++
clang/include/clang/AST/TypeProperties.td | 13 +
clang/include/clang/Basic/Attr.td | 7 +
clang/include/clang/Basic/DiagnosticGroups.td | 11 +
.../clang/Basic/DiagnosticSemaKinds.td | 21 ++
clang/include/clang/Basic/LangOptions.def | 2 +
clang/include/clang/Basic/LangOptions.h | 20 ++
clang/include/clang/Basic/TypeNodes.td | 1 +
clang/include/clang/Driver/Options.td | 6 +
clang/include/clang/Sema/Sema.h | 12 +
.../clang/Serialization/ASTRecordReader.h | 4 +
.../clang/Serialization/ASTRecordWriter.h | 2 +
.../clang/Serialization/TypeBitCodes.def | 1 +
clang/lib/AST/ASTContext.cpp | 158 ++++++++++
clang/lib/AST/ASTDiagnostic.cpp | 7 +
clang/lib/AST/ASTImporter.cpp | 12 +
clang/lib/AST/ASTStructuralEquivalence.cpp | 7 +
clang/lib/AST/ExprConstant.cpp | 6 +-
clang/lib/AST/FormatString.cpp | 4 +
clang/lib/AST/ItaniumMangle.cpp | 6 +
clang/lib/AST/MicrosoftMangle.cpp | 5 +
clang/lib/AST/Type.cpp | 82 ++++-
clang/lib/AST/TypeLoc.cpp | 8 +
clang/lib/AST/TypePrinter.cpp | 22 ++
clang/lib/CodeGen/CGDebugInfo.cpp | 6 +
clang/lib/CodeGen/CGDebugInfo.h | 1 +
clang/lib/CodeGen/CGExprScalar.cpp | 292 +++++++++++-------
clang/lib/CodeGen/CodeGenFunction.cpp | 2 +
clang/lib/CodeGen/CodeGenTypes.cpp | 4 +
clang/lib/CodeGen/ItaniumCXXABI.cpp | 6 +
clang/lib/Driver/ToolChains/Clang.cpp | 3 +
clang/lib/Sema/SemaChecking.cpp | 32 ++
clang/lib/Sema/SemaDecl.cpp | 7 +
clang/lib/Sema/SemaExpr.cpp | 85 ++++-
clang/lib/Sema/SemaLookup.cpp | 3 +
clang/lib/Sema/SemaOverload.cpp | 99 +++++-
clang/lib/Sema/SemaTemplate.cpp | 5 +
clang/lib/Sema/SemaTemplateDeduction.cpp | 2 +
clang/lib/Sema/SemaType.cpp | 84 +++++
clang/lib/Sema/TreeTransform.h | 7 +
clang/lib/Serialization/ASTReader.cpp | 5 +
clang/lib/Serialization/ASTWriter.cpp | 5 +
.../overflow-behavior-types-extensions.c | 44 +++
.../overflow-behavior-types-operators.cpp | 70 +++++
.../overflow-behavior-types-promotions.cpp | 50 +++
.../CodeGen/overflow-behavior-types-scl.c | 43 +++
clang/test/CodeGen/overflow-behavior-types.c | 148 +++++++++
.../test/CodeGen/overflow-behavior-types.cpp | 68 ++++
...a-attribute-supported-attributes-list.test | 1 +
.../Sema/attr-overflow-behavior-constexpr.cpp | 44 +++
clang/test/Sema/attr-overflow-behavior-off.c | 3 +
clang/test/Sema/attr-overflow-behavior.c | 41 +++
clang/test/Sema/attr-overflow-behavior.cpp | 115 +++++++
clang/tools/libclang/CIndex.cpp | 5 +
66 files changed, 2052 insertions(+), 130 deletions(-)
create mode 100644 clang/docs/OverflowBehaviorTypes.rst
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-off.c
create mode 100644 clang/test/Sema/attr-overflow-behavior.c
create mode 100644 clang/test/Sema/attr-overflow-behavior.cpp
diff --git a/clang/docs/OverflowBehaviorTypes.rst b/clang/docs/OverflowBehaviorTypes.rst
new file mode 100644
index 0000000000000..18e337e181e8a
--- /dev/null
+++ b/clang/docs/OverflowBehaviorTypes.rst
@@ -0,0 +1,269 @@
+=====================
+OverflowBehaviorTypes
+=====================
+
+.. contents::
+ :local:
+
+Introduction
+============
+
+Clang provides a type attribute that allows developers to have fine-grained control
+over the overflow behavior of integer types. The ``overflow_behavior``
+attribute can be used to specify 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.
+
+The attribute can be enabled using the compiler option
+``-foverflow-behavior-types``.
+
+The attribute syntax is as follows:
+
+.. 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.
+
+* ``no_wrap``: 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.
+
+This attribute can be applied to ``typedef`` declarations and to integer types directly.
+
+Examples
+========
+
+Here is an example of how to use the ``overflow_behavior`` attribute with a ``typedef``:
+
+.. code-block:: c++
+
+ typedef unsigned int __attribute__((overflow_behavior(no_wrap))) non_wrapping_uint;
+
+ non_wrapping_uint add_one(non_wrapping_uint a) {
+ return a + 1; // Overflow is checked for this operation.
+ }
+
+Here is an example of how to use the ``overflow_behavior`` attribute 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
+ }
+
+"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).
+
+Overflow behavior types are implicitly convertible to and from built-in
+integral types.
+
+Note that C++ overload set formation rules treat promotions to and from
+overflow behavior types the same as normal integral promotions and conversions.
+
+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
+ - No trap
+ - Wraps
+ - No report
+ - Overrides SSCL
+ * - ``overflow_behavior(no_wrap)``
+ - 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(no_wrap)``
+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 instrumentation.
+
+Promotion Rules
+===============
+
+The promotion rules for overflow behavior types are designed to preserve the
+specified overflow behavior throughout an arithmetic expression. They differ
+from standard C/C++ integer promotions but in a predictable way, similar to
+how ``_Complex`` and ``_BitInt`` have their own promotion rules.
+
+* **OBT and Standard Integer Type**: In an operation involving an overflow
+ behavior type (OBT) and a standard integer type, the result will have the
+ type of the OBT, including its overflow behavior, sign, and bit-width. The
+ standard integer type is implicitly converted to match the OBT.
+
+ .. code-block:: c++
+
+ typedef char __attribute__((overflow_behavior(no_wrap))) no_wrap_char;
+ // The result of this expression is no_wrap_char.
+ no_wrap_char c;
+ unsigned long ul;
+ auto result = c + ul;
+
+* **Two OBTs of the Same Kind**: When an operation involves two OBTs of the
+ same kind (e.g., both ``wrap``), the result will have the larger of the two
+ bit-widths. If the bit-widths are the same, an unsigned type is favored over
+ a signed one.
+
+ .. code-block:: c++
+
+ typedef unsigned char __attribute__((overflow_behavior(wrap))) u8_wrap;
+ typedef unsigned short __attribute__((overflow_behavior(wrap))) u16_wrap;
+ // The result of this expression is u16_wrap.
+ u8_wrap a;
+ u16_wrap b;
+ auto result = a + b;
+
+* **Two OBTs of Different Kinds**: In an operation between a ``wrap`` and a
+ ``no_wrap`` type, a ``no_wrap`` is produced. It is recommended to avoid such
+ operations, as Clang may emit a warning for such cases in the future.
+ Regardless, the resulting type matches the bit-width, sign and behavior of
+ the ``no_wrap`` type.
+
+Diagnostics
+===========
+
+Clang provides diagnostics to help developers manage overflow behavior types.
+
+-Wimplicitly-discarded-overflow-behavior
+----------------------------------------
+
+This warning 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 __attribute__((overflow_behavior(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 __attribute__((overflow_behavior(wrap))) wrapping_int;
+
+ void some_function(int);
+
+ void another_function(wrapping_int w) {
+ some_function(static_cast<int>(w)); // OK
+ }
+
+This warning acts as a group that includes
+``-Wimplicitly-discarded-overflow-behavior-pedantic`` and
+``-Wimplicitly-discarded-overflow-behavior-assignment``.
+
+-Wimplicitly-discarded-overflow-behavior-pedantic
+-------------------------------------------------
+
+A less severe version of the warning, ``-Wimplicitly-discarded-overflow-behavior-pedantic``,
+is issued for implicit conversions from an unsigned wrapping type to a standard
+unsigned integer type. This is considered less problematic because both types
+have well-defined wrapping behavior, but the conversion still discards the
+explicit ``overflow_behavior`` attribute.
+
+.. code-block:: c++
+
+ typedef unsigned int __attribute__((overflow_behavior(wrap))) wrapping_uint;
+
+ void some_function(unsigned int);
+
+ void another_function(wrapping_uint w) {
+ some_function(w); // warning: implicit conversion from 'wrapping_uint' to
+ // 'unsigned int' discards overflow behavior
+ // [-Wimplicitly-discarded-overflow-behavior-pedantic]
+ }
+
+-Wimplicitly-discarded-overflow-behavior-assignment
+---------------------------------------------------
+
+This warning is issued when an overflow behavior type is implicitly converted
+to a standard integer type as part of an assignment, which may lead to the
+loss of the specified overflow behavior. This is a more specific version of
+the ``-Wimplicitly-discarded-overflow-behavior`` warning, and it is off by
+default.
+
+.. code-block:: c++
+
+ typedef int __attribute__((overflow_behavior(wrap))) wrapping_int;
+
+ void some_function() {
+ wrapping_int w = 1;
+ int i = w; // warning: implicit conversion from 'wrapping_int' to 'int'
+ // discards overflow behavior
+ // [-Wimplicitly-discarded-overflow-behavior-assignment]
+ }
+
+To fix this, you can explicitly cast the overflow behavior type to a standard
+integer type.
+
+.. code-block:: c++
+
+ typedef int __attribute__((overflow_behavior(wrap))) wrapping_int;
+
+ void some_function() {
+ wrapping_int w = 1;
+ int i = static_cast<int>(w); // OK
+ int j = (int)w; // C-style OK
+ }
+
+
+-Woverflow-behavior-attribute-ignored
+-------------------------------------
+
+This warning is issued when the ``overflow_behavior`` attribute is applied to
+a type that is not an integer type.
+
+.. code-block:: c++
+
+ typedef float __attribute__((overflow_behavior(wrap))) wrapping_float;
+ // warning: 'overflow_behavior' attribute only applies to integer types;
+ // attribute is ignored [-Woverflow-behavior-attribute-ignored]
+
+ typedef struct S { int i; } __attribute__((overflow_behavior(wrap))) S_t;
+ // warning: 'overflow_behavior' attribute only applies to integer types;
+ // attribute is ignored [-Woverflow-behavior-attribute-ignored]
+
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 9ea9fcdf889df..2740078208f1c 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -141,6 +141,22 @@ New Compiler Flags
------------------
- New option ``-fno-sanitize-annotate-debug-info-traps`` added to disable emitting trap reasons into the debug info when compiling with trapping UBSan (e.g. ``-fsanitize-trap=undefined``).
+- New option ``-Wundef-true`` added and enabled by default to warn when `true` is used in the C preprocessor without being defined before C23.
+
+- New option ``-fprofile-continuous`` added to enable continuous profile syncing to file (#GH124353, `docs <https://clang.llvm.org/docs/UsersManual.html#cmdoption-fprofile-continuous>`_).
+ The feature has `existed <https://clang.llvm.org/docs/SourceBasedCodeCoverage.html#running-the-instrumented-program>`_)
+ for a while and this is just a user facing option.
+
+- New option ``-ftime-report-json`` added which outputs the same timing data as ``-ftime-report`` but formatted as JSON.
+
+- New option ``-Wnrvo`` added and disabled by default to warn about missed NRVO opportunities.
+
+- New option ``-ignore-pch`` added to disable precompiled headers. It overrides ``-emit-pch`` and ``-include-pch``. (#GH142409, `PCHDocs <https://clang.llvm.org/docs/UsersManual.html#ignoring-a-pch-file>`_).
+
+- New options ``-g[no-]key-instructions`` added, disabled by default. Reduces jumpiness of debug stepping for optimized code in some debuggers (not LLDB at this time). Not recommended for use without optimizations. DWARF only. Note both the positive and negative flags imply ``-g``.
+
+- New option ``-foverflow-behavior-types`` added to enable parsing of the ``overflow_behavior`` type attribute.
+
Deprecated Compiler Flags
-------------------------
@@ -153,6 +169,10 @@ Removed Compiler Flags
Attribute Changes in Clang
--------------------------
+- Introduced a new type attribute ``__attribute__((overflow_behavior))`` which
+ currently accepts either ``wrap`` or ``no_wrap`` as an argument, enabling
+ type-level control over overflow behavior.
+
Improvements to Clang's diagnostics
-----------------------------------
- Added a separate diagnostic group ``-Wfunction-effect-redeclarations``, for the more pedantic
diff --git a/clang/docs/SanitizerSpecialCaseList.rst b/clang/docs/SanitizerSpecialCaseList.rst
index 194f2fc5a7825..61e41113ab366 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(no_wrap))) critical_type;
+
+ void foo(int x) {
+ critical_type a = x;
+ a++; // Overflow is checked here due to the 'no_wrap' 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..f98eda1e9399c 100644
--- a/clang/docs/UndefinedBehaviorSanitizer.rst
+++ b/clang/docs/UndefinedBehaviorSanitizer.rst
@@ -380,6 +380,28 @@ 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.
+
+For more information, see :doc:`OverflowBehaviorTypes`.
+
+Enforcing Overflow Instrumentation with ``__attribute__((overflow_behavior(no_wrap)))``
+---------------------------------------------------------------------------------------
+
+Conversely, you can use ``__attribute__((overflow_behavior(no_wrap)))`` 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 542bfc94cd576..09c46692a16bc 100644
--- a/clang/docs/index.rst
+++ b/clang/docs/index.rst
@@ -40,6 +40,7 @@ Using Clang as a Compiler
SanitizerCoverage
SanitizerStats
SanitizerSpecialCaseList
+ OverflowBehaviorTypes
BoundsSafety
BoundsSafetyAdoptionGuide
BoundsSafetyImplPlans
diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index 7c2566a09665d..447ec8475511e 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"
@@ -260,6 +261,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;
@@ -905,6 +907,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;
}
@@ -1005,6 +1009,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.
@@ -1880,6 +1893,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);
diff --git a/clang/include/clang/AST/ASTNodeTraverser.h b/clang/include/clang/AST/ASTNodeTraverser.h
index d9dc8290b0e49..c5992ac83647b 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/Expr.h b/clang/include/clang/AST/Expr.h
index 77d0912d1f41b..d4c5e730e6ea7 100644
--- a/clang/include/clang/AST/Expr.h
+++ b/clang/include/clang/AST/Expr.h
@@ -1480,6 +1480,14 @@ class DeclRefExpr final
return DeclRefExprBits.IsImmediateEscalating;
}
+ bool isOverflowBehaviorDiscarded() const {
+ return DeclRefExprBits.IsOverflwBehaviorDiscarded;
+ }
+
+ void setOverflowBehaviorDiscarded(bool Set) {
+ DeclRefExprBits.IsOverflwBehaviorDiscarded = Set;
+ }
+
void setIsImmediateEscalating(bool Set) {
DeclRefExprBits.IsImmediateEscalating = Set;
}
diff --git a/clang/include/clang/AST/PropertiesBase.td b/clang/include/clang/AST/PropertiesBase.td
index 5b10127526e4e..d51a94d5a313a 100644
--- a/clang/include/clang/AST/PropertiesBase.td
+++ b/clang/include/clang/AST/PropertiesBase.td
@@ -81,6 +81,7 @@ 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 248b89200eace..669cd3d38c07d 100644
--- a/clang/include/clang/AST/RecursiveASTVisitor.h
+++ b/clang/include/clang/AST/RecursiveASTVisitor.h
@@ -1154,6 +1154,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())); })
@@ -1499,6 +1502,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/Stmt.h b/clang/include/clang/AST/Stmt.h
index a5b0d5053003f..42469c43cd845 100644
--- a/clang/include/clang/AST/Stmt.h
+++ b/clang/include/clang/AST/Stmt.h
@@ -450,6 +450,9 @@ class alignas(void *) Stmt {
LLVM_PREFERRED_TYPE(bool)
unsigned IsImmediateEscalating : 1;
+ LLVM_PREFERRED_TYPE(bool)
+ unsigned IsOverflwBehaviorDiscarded : 1;
+
/// The location of the declaration name itself.
SourceLocation Loc;
};
diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h
index cfbb9de4f2a06..29358f3b9dfc1 100644
--- a/clang/include/clang/AST/Type.h
+++ b/clang/include/clang/AST/Type.h
@@ -70,6 +70,7 @@ class TagDecl;
class TemplateParameterList;
class Type;
class Attr;
+class NoSanitizeAttr;
enum {
TypeAlignmentInBits = 4,
@@ -1149,6 +1150,13 @@ 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 NoWrap kind.
+ bool isNoWrapType() const;
+
// Don't promise in the API that anything besides 'const' can be
// easily added.
@@ -2657,6 +2665,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
@@ -3131,6 +3140,10 @@ template <> const BoundsAttributedType *Type::getAs() const;
/// sugar until it reaches an CountAttributedType or a non-sugared type.
template <> const CountAttributedType *Type::getAs() const;
+/// This will check for a OverflowBehaviorType by removing any existing
+/// sugar until it reaches an OverflowBehaviorType or a non-sugared type.
+template <> const OverflowBehaviorType *Type::getAs() const;
+
// We can do canonical leaf types faster, because we don't have to
// worry about preserving child type decoration.
#define TYPE(Class, Base)
@@ -6660,6 +6673,53 @@ class BTFTagAttributedType : public Type, public llvm::FoldingSetNode {
}
};
+class OverflowBehaviorType : public Type, public llvm::FoldingSetNode {
+public:
+ enum OverflowBehaviorKind {
+ Wrap,
+ NoWrap
+ };
+
+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 isNoWrapKind() const {
+ return BehaviorKind == OverflowBehaviorKind::NoWrap;
+ }
+
+ OverflowBehaviorKind setBehaviorKind(OverflowBehaviorKind Kind) {
+ BehaviorKind = Kind;
+ return BehaviorKind;
+ }
+
+ 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 {
@@ -8670,6 +8730,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);
}
@@ -8914,6 +8978,12 @@ inline bool Type::isIntegerType() const {
return IsEnumDeclComplete(ET->getOriginalDecl()) &&
!IsEnumDeclScoped(ET->getOriginalDecl());
}
+
+ if (isOverflowBehaviorType())
+ return cast<OverflowBehaviorType>(CanonicalType)
+ ->getUnderlyingType()
+ ->isIntegerType();
+
return isBitIntType();
}
@@ -8976,6 +9046,7 @@ inline bool Type::isScalarType() const {
isa<MemberPointerType>(CanonicalType) ||
isa<ComplexType>(CanonicalType) ||
isa<ObjCObjectPointerType>(CanonicalType) ||
+ isa<OverflowBehaviorType>(CanonicalType) ||
isBitIntType();
}
@@ -8988,6 +9059,10 @@ inline bool Type::isIntegralOrEnumerationType() const {
if (const auto *ET = dyn_cast<EnumType>(CanonicalType))
return IsEnumDeclComplete(ET->getOriginalDecl());
+ if (const OverflowBehaviorType *OBT =
+ dyn_cast<OverflowBehaviorType>(CanonicalType))
+ return OBT->getUnderlyingType()->isIntegralOrEnumerationType();
+
return isBitIntType();
}
@@ -9120,6 +9195,8 @@ template <typename T> const T *Type::getAsAdjusted() const {
Ty = A->getModifiedType().getTypePtr();
else if (const auto *A = dyn_cast<BTFTagAttributedType>(Ty))
Ty = A->getWrappedType().getTypePtr();
+ // else if (const auto *A = dyn_cast<OverflowBehaviorType>(Ty))
+ // Ty = A->getWrappedType().getTypePtr();
else if (const auto *A = dyn_cast<HLSLAttributedResourceType>(Ty))
Ty = A->getWrappedType().getTypePtr();
else if (const auto *P = dyn_cast<ParenType>(Ty))
diff --git a/clang/include/clang/AST/TypeLoc.h b/clang/include/clang/AST/TypeLoc.h
index 6389bdea6d122..c97f4cb9ed6e8 100644
--- a/clang/include/clang/AST/TypeLoc.h
+++ b/clang/include/clang/AST/TypeLoc.h
@@ -1072,6 +1072,25 @@ class BTFTagAttributedTypeLoc
QualType getInnerType() const { return getTypePtr()->getWrappedType(); }
};
+struct OverflowBehaviorLocInfo {}; // Nothing.
+
+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) {}
+
+ QualType getInnerType() const { return getTypePtr()->getUnderlyingType(); }
+};
+
struct HLSLAttributedResourceLocInfo {
SourceRange Range;
TypeSourceInfo *ContainedTyInfo;
@@ -2844,6 +2863,8 @@ inline T TypeLoc::getAsAdjusted() const {
Cur = ATL.getModifiedLoc();
else if (auto ATL = Cur.getAs<BTFTagAttributedTypeLoc>())
Cur = ATL.getWrappedLoc();
+ // else if (auto ATL = Cur.getAs<OverflowBehaviorTypeLoc>())
+ // Cur = ATL.getWrappedLoc();
else if (auto ATL = Cur.getAs<HLSLAttributedResourceTypeLoc>())
Cur = ATL.getWrappedLoc();
else if (auto ATL = Cur.getAs<AdjustedTypeLoc>())
diff --git a/clang/include/clang/AST/TypeProperties.td b/clang/include/clang/AST/TypeProperties.td
index 388f6dda4a6f0..9e8e3e25ea29e 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 8c8e0b3bca46c..89c3b281fb491 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -5233,3 +5233,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 2edf4da435366..b0cddb0d9c681 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -121,6 +121,14 @@ def ImplicitIntConversion : DiagGroup<"implicit-int-conversion",
[Shorten64To32,
ObjCSignedCharBoolImplicitIntConversion,
ImplicitIntConversionOnNegation]>;
+def ImplicitlyDiscardedOverflowBehaviorPedantic :
+ DiagGroup<"implicitly-discarded-overflow-behavior-pedantic">;
+def ImplicitlyDiscardedOverflowBehaviorAssignment :
+ DiagGroup<"implicitly-discarded-overflow-behavior-assignment">;
+def ImplicitlyDiscardedOverflowBehavior :
+ DiagGroup<"implicitly-discarded-overflow-behavior",
+ [ImplicitlyDiscardedOverflowBehaviorPedantic,
+ ImplicitlyDiscardedOverflowBehaviorAssignment]>;
def ImplicitConstIntFloatConversion : DiagGroup<"implicit-const-int-float-conversion">;
def ImplicitIntFloatConversion : DiagGroup<"implicit-int-float-conversion",
[ImplicitConstIntFloatConversion]>;
@@ -1664,6 +1672,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 c733e8823cea6..d7cc21bfe8739 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -4030,6 +4030,27 @@ 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 'no_wrap' "
+ "are supported">;
+def warn_overflow_behavior_non_integer_type
+ : Warning<"%0 attribute cannot be applied to non-integer type '%1'; "
+ "attribute ignored">,
+ InGroup<OverflowBehaviorAttributeIgnored>;
+def warn_overflow_behavior_attribute_disabled
+ : Warning<"%0 attribute is ignored because it is not enabled; pass "
+ "-foverflow-behavior-types">,
+ InGroup<OverflowBehaviorAttributeIgnored>;
+def warn_implicitly_discarded_overflow_behavior : Warning<
+ "implicit conversion from %0 to %1 discards overflow behavior">,
+ InGroup<ImplicitlyDiscardedOverflowBehavior>;
+def warn_implicitly_discarded_overflow_behavior_pedantic : Warning<
+ "implicit conversion from %0 to %1 discards overflow behavior">,
+ InGroup<ImplicitlyDiscardedOverflowBehaviorPedantic>;
+def warn_implicitly_discarded_overflow_behavior_assignment : Warning<
+ "implicit conversion from %0 to %1 during assignment discards overflow behavior">,
+ InGroup<ImplicitlyDiscardedOverflowBehaviorAssignment>, DefaultIgnore;
// Availability attribute
def warn_availability_unknown_platform : Warning<
"unknown platform %0 in availability macro">, InGroup<Availability>;
diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def
index f094ba112988f..895005a9b7c9d 100644
--- a/clang/include/clang/Basic/LangOptions.def
+++ b/clang/include/clang/Basic/LangOptions.def
@@ -424,6 +424,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 569584bcc2297..09d7c3d814cf6 100644
--- a/clang/include/clang/Basic/LangOptions.h
+++ b/clang/include/clang/Basic/LangOptions.h
@@ -112,6 +112,26 @@ 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("no_wrap")))
+ OB_NoWrap,
+
+ // 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_FWrapv
+ };
+
+
// FIXME: Unify with TUKind.
enum CompilingModuleKind {
/// Not compiling a module interface at all.
diff --git a/clang/include/clang/Basic/TypeNodes.td b/clang/include/clang/Basic/TypeNodes.td
index e4960ec660b90..8a323f9c1dc53 100644
--- a/clang/include/clang/Basic/TypeNodes.td
+++ b/clang/include/clang/Basic/TypeNodes.td
@@ -117,3 +117,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/Driver/Options.td b/clang/include/clang/Driver/Options.td
index 6a2f4575459b2..e06b1671025b7 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -2317,6 +2317,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/Sema.h b/clang/include/clang/Sema/Sema.h
index da9070842694e..8dd88dcad5940 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -10081,6 +10081,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/ASTRecordReader.h b/clang/include/clang/Serialization/ASTRecordReader.h
index aed1b7d309001..d2285c1f94981 100644
--- a/clang/include/clang/Serialization/ASTRecordReader.h
+++ b/clang/include/clang/Serialization/ASTRecordReader.h
@@ -350,6 +350,10 @@ class ASTRecordReader
return cast<BTFTypeTagAttr>(readAttr());
}
+ NoSanitizeAttr *readNoSanitizeAttr() {
+ return cast<NoSanitizeAttr>(readAttr());
+ }
+
/// Reads a token out of a record, advancing Idx.
Token readToken() {
return Reader->ReadToken(*F, Record, Idx);
diff --git a/clang/include/clang/Serialization/ASTRecordWriter.h b/clang/include/clang/Serialization/ASTRecordWriter.h
index 9849ea6b395ab..299a7a8dd29f1 100644
--- a/clang/include/clang/Serialization/ASTRecordWriter.h
+++ b/clang/include/clang/Serialization/ASTRecordWriter.h
@@ -141,6 +141,8 @@ class ASTRecordWriter
/// Write an BTFTypeTagAttr object.
void writeBTFTypeTagAttr(const BTFTypeTagAttr *A) { AddAttr(A); }
+ void writeNoSanitizeAttr(const NoSanitizeAttr *A) { AddAttr(A); }
+
/// Add a definition for the given function to the queue of statements
/// to emit.
void AddFunctionDefinition(const FunctionDecl *FD);
diff --git a/clang/include/clang/Serialization/TypeBitCodes.def b/clang/include/clang/Serialization/TypeBitCodes.def
index 8af32db4c0b39..4fbd3972739ea 100644
--- a/clang/include/clang/Serialization/TypeBitCodes.def
+++ b/clang/include/clang/Serialization/TypeBitCodes.def
@@ -69,5 +69,6 @@ TYPE_BIT_CODE(ArrayParameter, ARRAY_PARAMETER, 58)
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(OverflowBehavior, OVERFLOWBEHAVIOR, 62)
#undef TYPE_BIT_CODE
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 2f2685495a8f1..62092774f60bb 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -873,6 +873,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,
@@ -2557,6 +2593,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());
@@ -3603,6 +3643,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:
@@ -3748,6 +3791,18 @@ 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::Elaborated: {
+ const auto *ET = cast<ElaboratedType>(Orig);
+ return getElaboratedType(ET->getKeyword(), ET->getQualifier(),
+ adjustType(ET->getNamedType(), Adjust));
+ }
+
case Type::Paren:
return getParenType(
adjustType(cast<ParenType>(Orig)->getInnerType(), Adjust));
@@ -4315,6 +4370,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
@@ -5776,6 +5832,49 @@ 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 == "no_wrap") {
+ Kind = OverflowBehaviorType::OverflowBehaviorKind::NoWrap;
+ } 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()) {
+ Canonical = getOverflowBehaviorType(Kind, getCanonicalType(Underlying));
+ OverflowBehaviorType *NewOBT = OverflowBehaviorTypes.FindNodeOrInsertPos(ID, InsertPos);
+ assert(!NewOBT && "Shouldn't be in the map!");
+ (void)NewOBT;
+ }
+
+ 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) {
@@ -8221,6 +8320,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:
@@ -9701,6 +9803,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:
@@ -11710,6 +11813,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) {
+ // Both are OverflowBehaviorTypes.
+ if (LHSOBT->getBehaviorKind() != RHSOBT->getBehaviorKind())
+ return QualType(); // Incompatible if behaviors differ.
+
+ QualType MergedUnderlying = mergeTypes(
+ LHSOBT->getUnderlyingType(), RHSOBT->getUnderlyingType(),
+ OfBlockPointer, Unqualified, BlockReturnType, IsConditionalOperator);
+
+ if (MergedUnderlying.isNull())
+ return QualType();
+
+ // If the merged underlying type is the same as one of the original
+ // underlying types, we can return the original OBT to preserve typedefs.
+ if (getCanonicalType(MergedUnderlying) ==
+ getCanonicalType(LHSOBT->getUnderlyingType()))
+ return LHS;
+ if (getCanonicalType(MergedUnderlying) ==
+ getCanonicalType(RHSOBT->getUnderlyingType()))
+ return RHS;
+
+ 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) {
@@ -11730,6 +11875,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();
@@ -11852,6 +12002,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:
@@ -14308,6 +14459,12 @@ 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: {
+ // FIXME: Should we consider both types?
+ const auto *NX = cast<OverflowBehaviorType>(X);
+ return Ctx.getOverflowBehaviorType(NX->getBehaviorKind(),
+ NX->getUnderlyingType());
+ }
case Type::DependentTemplateSpecialization: {
const auto *TX = cast<DependentTemplateSpecializationType>(X),
*TY = cast<DependentTemplateSpecializationType>(Y);
@@ -14396,6 +14553,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)
diff --git a/clang/lib/AST/ASTDiagnostic.cpp b/clang/lib/AST/ASTDiagnostic.cpp
index d7fd411ab464c..40db5d2dc6fb6 100644
--- a/clang/lib/AST/ASTDiagnostic.cpp
+++ b/clang/lib/AST/ASTDiagnostic.cpp
@@ -74,6 +74,13 @@ QualType clang::desugarForDiagnostic(ASTContext &Context, QualType QT,
QT = AT->desugar();
continue;
}
+ // ... or an overflow behavior type.
+ if (const OverflowBehaviorType *OBT = dyn_cast<OverflowBehaviorType>(Ty)) {
+ if (!OBT->isSugared())
+ break;
+ QT = OBT->desugar();
+ continue;
+ }
// Desugar FunctionType if return type or any parameter type should be
// desugared. Preserve nullability attribute on desugared types.
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index 315ead9ef0105..b152f412c208d 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -1987,6 +1987,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 096bc6c8ca5b3..d16515d933499 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 40c56501b0c14..728684d184ff3 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -2929,7 +2929,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)
@@ -12750,6 +12750,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;
@@ -15393,7 +15394,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 502a3e6b145e3..89072f044032d 100644
--- a/clang/lib/AST/FormatString.cpp
+++ b/clang/lib/AST/FormatString.cpp
@@ -405,6 +405,10 @@ ArgType::matchesType(ASTContext &C, QualType argTy) const {
argTy = PT->getPointeeType();
}
+ if (const OverflowBehaviorType *OBT =
+ dyn_cast<OverflowBehaviorType>(argTy.getCanonicalType()))
+ 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 a7380a19e3607..a7696dd6d4554 100644
--- a/clang/lib/AST/ItaniumMangle.cpp
+++ b/clang/lib/AST/ItaniumMangle.cpp
@@ -2427,6 +2427,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:
@@ -4607,6 +4608,11 @@ void CXXNameMangler::mangleType(const PipeType *T) {
Out << "8ocl_pipe";
}
+void CXXNameMangler::mangleType(const OverflowBehaviorType *T) {
+ Out << "U9Obt_";
+ 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 241c7c35fcc83..9391879fbe1bc 100644
--- a/clang/lib/AST/MicrosoftMangle.cpp
+++ b/clang/lib/AST/MicrosoftMangle.cpp
@@ -3780,6 +3780,11 @@ void MicrosoftCXXNameMangler::mangleType(const HLSLInlineSpirvType *T,
llvm_unreachable("HLSL uses Itanium name mangling");
}
+void MicrosoftCXXNameMangler::mangleType(const OverflowBehaviorType *T,
+ Qualifiers, SourceRange Range) {
+ llvm_unreachable("OverflowBehaviorType uses Itanium name mangling");
+}
+
// <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 5fbf1999ed725..7ea4ebcf06763 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -649,6 +649,10 @@ template <> const CountAttributedType *Type::getAs() const {
return getAsSugar<CountAttributedType>(this);
}
+template <> const OverflowBehaviorType *Type::getAs() const {
+ return getAsSugar<OverflowBehaviorType>(this);
+}
+
/// getUnqualifiedDesugaredType - Pull any qualifiers and syntactic
/// sugar off the given type. This should produce an object of the
/// same dynamic type as the canonical type.
@@ -1159,6 +1163,17 @@ 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())
@@ -2082,6 +2097,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());
}
@@ -2145,10 +2164,16 @@ 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->getOriginalDecl());
+ if (const OverflowBehaviorType *OBT =
+ dyn_cast<OverflowBehaviorType>(CanonicalType))
+ return OBT->getUnderlyingType()->isIntegralOrEnumerationType();
+ }
+
+
return isBitIntType();
}
@@ -2156,6 +2181,10 @@ bool Type::isIntegralOrUnscopedEnumerationType() const {
if (const auto *BT = dyn_cast<BuiltinType>(CanonicalType))
return BT->isInteger();
+ if (const OverflowBehaviorType *OBT =
+ dyn_cast<OverflowBehaviorType>(CanonicalType))
+ return OBT->getUnderlyingType()->isIntegralOrUnscopedEnumerationType();
+
if (isBitIntType())
return true;
@@ -2260,6 +2289,9 @@ bool Type::isSignedIntegerType() const {
if (const auto *IT = dyn_cast<DependentBitIntType>(CanonicalType))
return IT->isSigned();
+ if (const auto *NSAT = dyn_cast<OverflowBehaviorType>(CanonicalType))
+ return NSAT->getUnderlyingType()->isSignedIntegerType();
+
return false;
}
@@ -2279,6 +2311,9 @@ bool Type::isSignedIntegerOrEnumerationType() const {
if (const auto *IT = dyn_cast<DependentBitIntType>(CanonicalType))
return IT->isSigned();
+ if (const auto *NSAT = dyn_cast<OverflowBehaviorType>(CanonicalType))
+ return NSAT->getUnderlyingType()->isSignedIntegerOrEnumerationType();
+
return false;
}
@@ -2310,6 +2345,9 @@ bool Type::isUnsignedIntegerType() const {
if (const auto *IT = dyn_cast<DependentBitIntType>(CanonicalType))
return IT->isUnsigned();
+ if (const auto *NSAT = dyn_cast<OverflowBehaviorType>(CanonicalType))
+ return NSAT->getUnderlyingType()->isUnsignedIntegerType();
+
return false;
}
@@ -2329,6 +2367,9 @@ bool Type::isUnsignedIntegerOrEnumerationType() const {
if (const auto *IT = dyn_cast<DependentBitIntType>(CanonicalType))
return IT->isUnsigned();
+ if (const auto *NSAT = dyn_cast<OverflowBehaviorType>(CanonicalType))
+ return NSAT->getUnderlyingType()->isUnsignedIntegerOrEnumerationType();
+
return false;
}
@@ -2392,6 +2433,11 @@ bool Type::isArithmeticType() const {
const auto *ED = ET->getOriginalDecl();
return !ED->isScoped() && ED->getDefinitionOrSelf()->isComplete();
}
+
+ if (isOverflowBehaviorType() &&
+ getAs<OverflowBehaviorType>()->getUnderlyingType()->isArithmeticType())
+ return true;
+
return isa<ComplexType>(CanonicalType) || isBitIntType();
}
@@ -2443,6 +2489,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");
@@ -2789,6 +2837,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:
@@ -2994,6 +3043,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::isNoWrapType() const {
+ if (const auto *OBT = getCanonicalType()->getAs<OverflowBehaviorType>())
+ return OBT->getBehaviorKind() ==
+ OverflowBehaviorType::OverflowBehaviorKind::NoWrap;
+
+ return false;
+}
+
QualType::PrimitiveDefaultInitializeKind
QualType::isNonTrivialToPrimitiveDefaultInitialize() const {
if (const auto *RT =
@@ -3096,6 +3161,9 @@ bool Type::isLiteralType(const ASTContext &Ctx) const {
if (const auto *AT = BaseTy->getAs<AtomicType>())
return AT->getValueType()->isLiteralType(Ctx);
+ if (const auto *NSAT = BaseTy->getAs<OverflowBehaviorType>())
+ return NSAT->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()))
@@ -4055,6 +4123,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) {}
@@ -4934,6 +5008,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");
@@ -5029,6 +5105,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()
@@ -5223,6 +5302,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 fbe8772924465..e5fb62bdc1f7b 100644
--- a/clang/lib/AST/TypeLoc.cpp
+++ b/clang/lib/AST/TypeLoc.cpp
@@ -647,6 +647,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>
@@ -921,6 +925,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 85242b69f0679..6aecfbff16964 100644
--- a/clang/lib/AST/TypePrinter.cpp
+++ b/clang/lib/AST/TypePrinter.cpp
@@ -288,6 +288,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;
@@ -2092,6 +2093,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:
@@ -2172,6 +2174,26 @@ void TypePrinter::printBTFTagAttributedAfter(const BTFTagAttributedType *T,
printAfter(T->getWrappedType(), OS);
}
+void TypePrinter::printOverflowBehaviorBefore(
+ const OverflowBehaviorType *T, raw_ostream &OS) {
+ StringRef KindName;
+ switch (T->getBehaviorKind()) {
+ case clang::OverflowBehaviorType::OverflowBehaviorKind::Wrap:
+ KindName = "__wrap";
+ break;
+ case clang::OverflowBehaviorType::OverflowBehaviorKind::NoWrap:
+ KindName = "__no_wrap";
+ break;
+ }
+ OS << KindName << " ";
+ 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 994bdbdae860f..dc952c903f488 100644
--- a/clang/lib/CodeGen/CGDebugInfo.cpp
+++ b/clang/lib/CodeGen/CGDebugInfo.cpp
@@ -1115,6 +1115,10 @@ llvm::DIType *CGDebugInfo::CreateType(const BitIntType *Ty) {
Encoding);
}
+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;
@@ -4037,6 +4041,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 497d3a6ab17b1..c3c0adec076ae 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 155b80df36715..46b6986904782 100644
--- a/clang/lib/CodeGen/CGExprScalar.cpp
+++ b/clang/lib/CodeGen/CGExprScalar.cpp
@@ -189,8 +189,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::NoWrap:
+ return LangOptions::OverflowBehaviorKind::OB_NoWrap;
+ }
+ 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_FWrapv;
+ case LangOptions::SignedOverflowBehaviorTy::SOB_Undefined:
+ return LangOptions::OverflowBehaviorKind::OB_Unset;
+ case LangOptions::SignedOverflowBehaviorTy::SOB_Trapping:
+ return LangOptions::OverflowBehaviorKind::OB_NoWrap;
+ }
+}
+
/// 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");
@@ -199,6 +233,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.isNoWrapType())
+ return false;
+
if (Op.Ty->isSignedIntegerType() &&
Ctx.isTypeIgnoredBySanitizer(SanitizerKind::SignedIntegerOverflow,
Op.Ty)) {
@@ -211,24 +258,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;
@@ -774,19 +809,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_FWrapv:
+ 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_NoWrap:
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);
}
}
@@ -808,11 +852,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);
@@ -1162,8 +1201,16 @@ void ScalarExprEmitter::EmitIntegerTruncationCheck(Value *Src, QualType SrcType,
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->getBehaviorKind() !=
+ OverflowBehaviorType::OverflowBehaviorKind::NoWrap)
+ return;
+ } else if (ignoredBySanitizer)
return;
llvm::Constant *StaticArgs[] = {
@@ -2975,43 +3022,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);
- [[fallthrough]];
- case LangOptions::SOB_Undefined:
- if (!CGF.SanOpts.has(SanitizerKind::SignedIntegerOverflow))
- return Builder.CreateNSWAdd(InVal, Amount, Name);
- [[fallthrough]];
- case LangOptions::SOB_Trapping:
- BinOpInfo Info = createBinOpInfoFromIncDec(
- E, InVal, IsInc, E->getFPFeaturesInEffect(CGF.getLangOpts()));
- if (!E->canOverflow() || CanElideOverflowCheck(CGF.getContext(), Info))
- return Builder.CreateNSWAdd(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;
+ case LangOptions::OB_FWrapv:
+ if (!hasSan)
+ return Builder.CreateAdd(InVal, Amount, Name);
+ [[fallthrough]];
+ 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::OB_NoWrap:
+ if (!Ty->getAs<OverflowBehaviorType>() && !E->canOverflow())
+ return Builder.CreateAdd(InVal, Amount, Name);
+ BinOpInfo Info = createBinOpInfoFromIncDec(
+ E, InVal, IsInc, E->getFPFeaturesInEffect(CGF.getLangOpts()));
+ if (CanElideOverflowCheck(CGF.getContext(), Info))
+ return isSigned ? Builder.CreateNSWAdd(InVal, Amount, Name)
+ : Builder.CreateAdd(InVal, Amount, Name);
+ return EmitOverflowCheckedBinOp(Info);
+ }
+ llvm_unreachable("Unknown OverflowBehaviorKind");
}
namespace {
@@ -3130,9 +3171,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.");
@@ -3189,16 +3227,21 @@ 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 {
+ }
+ // else if (E->canOverflow() && type->isSignedIntegerOrEnumerationType()) {
+ // 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");
}
@@ -3979,7 +4022,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 =
@@ -4125,14 +4168,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;
@@ -4454,19 +4508,30 @@ 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))
+ // FIXME: maybe we want to emit NUWAdd for unsigned non-wrapping types which
+ // should depend on -fwrapv (-fno-strict-overflow)
+ 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_FWrapv:
+ 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_NoWrap:
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);
}
}
@@ -4485,11 +4550,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");
@@ -4610,19 +4670,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_FWrapv:
+ 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_NoWrap:
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);
}
}
@@ -4641,11 +4710,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 652fe672f15e3..2c5d7cc75dcb1 100644
--- a/clang/lib/CodeGen/CodeGenFunction.cpp
+++ b/clang/lib/CodeGen/CodeGenFunction.cpp
@@ -285,6 +285,7 @@ TypeEvaluationKind CodeGenFunction::getEvaluationKind(QualType type) {
case Type::BitInt:
case Type::HLSLAttributedResource:
case Type::HLSLInlineSpirv:
+ case Type::OverflowBehavior:
return TEK_Scalar;
// Complexes.
@@ -2582,6 +2583,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 f2a0a649a88fd..fd8d84a364dd0 100644
--- a/clang/lib/CodeGen/CodeGenTypes.cpp
+++ b/clang/lib/CodeGen/CodeGenTypes.cpp
@@ -771,6 +771,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 4ed3775f156c9..16fcdddd7bcfa 100644
--- a/clang/lib/CodeGen/ItaniumCXXABI.cpp
+++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp
@@ -4002,8 +4002,11 @@ void ItaniumRTTIBuilder::BuildVTablePointer(const Type *Ty,
case Type::HLSLAttributedResource:
case Type::HLSLInlineSpirv:
llvm_unreachable("HLSL doesn't support virtual functions");
+ case Type::OverflowBehavior:
+ llvm_unreachable("NoSanitize not supported for virtual functions");
}
+
llvm::Constant *VTable = nullptr;
// Check if the alias exists. If it doesn't, then get or create the global.
@@ -4277,6 +4280,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 29b7180df5cb5..6eef93b3ea218 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -6331,6 +6331,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/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 2dc4ee74dc9df..11346433b67e8 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -8231,6 +8231,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.
@@ -10492,6 +10496,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.
@@ -10548,6 +10554,8 @@ struct IntRange {
T = C.getCanonicalType(
ET->getOriginalDecl()->getDefinitionOrSelf()->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());
@@ -12172,6 +12180,30 @@ void Sema::CheckImplicitConversion(Expr *E, QualType T, SourceLocation CC,
}
}
+ // Diagnose potentially problematic implicit casts from an overflow behavior
+ // type to an integer type.
+ if (const auto *OBT = Source->getAs<OverflowBehaviorType>()) {
+ bool DiscardedDuringAssignment = false;
+ if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E))
+ DiscardedDuringAssignment = DRE->isOverflowBehaviorDiscarded();
+
+ if (Target->isIntegerType() && !Target->isOverflowBehaviorType()) {
+ // Implicit casts from unsigned wrap types to unsigned types are less
+ // problematic but still warrant some diagnostic.
+ if (DiscardedDuringAssignment)
+ return DiagnoseImpCast(
+ *this, E, T, CC,
+ diag::warn_implicitly_discarded_overflow_behavior_assignment);
+ if (OBT->isUnsignedIntegerType() && OBT->isWrapKind() &&
+ Target->isUnsignedIntegerType())
+ return DiagnoseImpCast(
+ *this, E, T, CC,
+ diag::warn_implicitly_discarded_overflow_behavior_pedantic);
+ return DiagnoseImpCast(*this, E, T, CC,
+ diag::warn_implicitly_discarded_overflow_behavior);
+ }
+ }
+
// 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.
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 8ddbaf34a7f47..a22bccdc0180b 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -13720,6 +13720,13 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init, bool DirectInit) {
return;
}
+ if (!VDecl->getType()->isOverflowBehaviorType() &&
+ Init->getType()->isOverflowBehaviorType()) {
+ if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Init)) {
+ DRE->setOverflowBehaviorDiscarded(true);
+ }
+ }
+
// C++11 [decl.spec.auto]p6. Deduce the type which 'auto' stands in for.
if (VDecl->getType()->isUndeducedType()) {
if (Init->containsErrors()) {
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 237c068f59283..a2f598b663d45 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -1409,6 +1409,37 @@ static QualType handleComplexIntConversion(Sema &S, ExprResult &LHS,
return ComplexType;
}
+static QualType handleOverflowBehaviorTypeConversion(Sema &S, ExprResult &LHS,
+ ExprResult &RHS,
+ bool IsCompAssign) {
+ QualType LHSType = LHS.get()->getType().getUnqualifiedType();
+ QualType RHSType = RHS.get()->getType().getUnqualifiedType();
+
+ const OverflowBehaviorType *LhsOBT = LHSType->getAs<OverflowBehaviorType>();
+ const OverflowBehaviorType *RhsOBT = RHSType->getAs<OverflowBehaviorType>();
+
+ assert(LHSType->isIntegerType() && RHSType->isIntegerType() &&
+ "Non-integer type conversion not supported for OverflowBehaviorTypes");
+
+ if (LhsOBT && RhsOBT) {
+ if (LhsOBT->getBehaviorKind() == RhsOBT->getBehaviorKind())
+ return handleIntegerConversion<doIntegralCast, doIntegralCast>(
+ S, LHS, RHS, LHSType, RHSType, IsCompAssign);
+ }
+
+ // NoWrap has precedence over Wrap; eagerly cast Wrap types to NoWrap types
+ if ((LhsOBT && !RhsOBT) ||
+ (LhsOBT && RhsOBT && !RhsOBT->isNoWrapKind())) {
+ RHS = doIntegralCast(S, RHS.get(), LHSType);
+ return LHSType;
+ }
+
+ if (!IsCompAssign)
+ LHS = doIntegralCast(S, LHS.get(), RHSType);
+
+ return RHSType;
+}
+
/// 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.
@@ -1662,8 +1693,9 @@ QualType Sema::UsualArithmeticConversions(ExprResult &LHS, ExprResult &RHS,
LHSType = AtomicLHS->getValueType();
// If both types are identical, no conversion is needed.
- if (Context.hasSameType(LHSType, RHSType))
+ if (Context.hasSameType(LHSType, RHSType)) {
return Context.getCommonSugaredType(LHSType, RHSType);
+ }
// If either side is a non-arithmetic type (e.g. a pointer), we are done.
// The caller can deal with this (e.g. pointer + int).
@@ -1709,6 +1741,11 @@ 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, ACK == ArithConvKind::CompAssign);
+ }
+
// Finally, we have two differing integer types.
return handleIntegerConversion<doIntegralCast, doIntegralCast>(
*this, LHS, RHS, LHSType, RHSType, ACK == ArithConvKind::CompAssign);
@@ -1914,16 +1951,33 @@ 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();
+
+ const auto *ControllingOBT =
+ ControllingQT->getAs<OverflowBehaviorType>();
+ const auto *AssocOBT = AssocQT.getCanonicalType()->getAs<OverflowBehaviorType>();
+
+ if (ControllingOBT || AssocOBT) {
+ if (ControllingOBT && AssocOBT) {
+ if (ControllingOBT->getBehaviorKind() == AssocOBT->getBehaviorKind())
+ Compatible = Context.typesAreCompatible(
+ ControllingOBT->getUnderlyingType(),
+ AssocOBT->getUnderlyingType());
+ else
+ Compatible = false;
+ } else
+ Compatible = false;
+ } else
+ Compatible = Context.typesAreCompatible(ControllingQT, AssocQT);
+
+ if (Compatible)
+ CompatIndices.push_back(i);
+ }
}
auto GetControllingRangeAndType = [](Expr *ControllingExpr,
@@ -4540,6 +4594,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:
@@ -9086,6 +9141,12 @@ 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))
+ return AssignConvertType::IncompatiblePointer;
+ }
+
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
@@ -14229,6 +14290,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.
diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp
index e28492b579564..6d1c677465317 100644
--- a/clang/lib/Sema/SemaLookup.cpp
+++ b/clang/lib/Sema/SemaLookup.cpp
@@ -3309,8 +3309,11 @@ 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())
break;
T = Queue.pop_back_val();
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 7c4405b414c47..e9a8dd5d433a5 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,
@@ -2227,6 +2232,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
@@ -2417,9 +2428,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).
@@ -2485,6 +2503,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) {
@@ -2837,6 +2858,33 @@ 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())
+ 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,
@@ -3869,6 +3917,30 @@ 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;
+
+ 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) {
@@ -4656,6 +4728,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
@@ -4768,6 +4844,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 72d98ca391f4b..078c8e1292a57 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -6334,6 +6334,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 477f8c7541939..fb7ce6e6ee8b4 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -2539,6 +2539,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;
@@ -7120,6 +7121,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 d745cdbf0526f..84dfb1567016e 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -300,6 +300,13 @@ 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.
@@ -5893,6 +5900,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);
@@ -6187,6 +6197,9 @@ namespace {
void VisitBTFTagAttributedTypeLoc(BTFTagAttributedTypeLoc TL) {
// nothing
}
+ void VisitOverflowBehaviorTypeLoc(OverflowBehaviorTypeLoc TL) {
+ // nothing
+ }
void VisitAdjustedTypeLoc(AdjustedTypeLoc TL) {
// nothing
}
@@ -6637,6 +6650,73 @@ 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::warn_overflow_behavior_non_integer_type)
+ << Attr << Type.getAsString();
+ 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 "no_wrap") 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 == "no_wrap") {
+ Kind = OverflowBehaviorType::OverflowBehaviorKind::NoWrap;
+ } else {
+ S.Diag(Attr.getLoc(), diag::err_overflow_behavior_unknown_ident)
+ << KindName << Attr;
+ Attr.setInvalid();
+ return;
+ }
+
+ Type = State.getOverflowBehaviorType(Kind, Type);
+}
+
/// handleObjCOwnershipTypeAttr - Process an objc_ownership
/// attribute on the specified type.
///
@@ -8979,6 +9059,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 055d3cd1a8609..6ea3d72ecc681 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -7538,6 +7538,13 @@ QualType TreeTransform<Derived>::TransformBTFTagAttributedType(
llvm_unreachable("Unexpected TreeTransform for BTFTagAttributedType");
}
+template <typename Derived>
+QualType TreeTransform<Derived>::TransformOverflowBehaviorType(
+ TypeLocBuilder &TLB, OverflowBehaviorTypeLoc TL) {
+ // The OverflowBehaviorType is available for C only.
+ llvm_unreachable("Unexpected TreeTransform for OverflowBehaviorType");
+}
+
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 e8dddda584a9b..4a3c9f02624e3 100644
--- a/clang/lib/Serialization/ASTReader.cpp
+++ b/clang/lib/Serialization/ASTReader.cpp
@@ -7477,6 +7477,11 @@ void TypeLocReader::VisitBTFTagAttributedTypeLoc(BTFTagAttributedTypeLoc TL) {
// Nothing to do.
}
+void TypeLocReader::VisitOverflowBehaviorTypeLoc(
+ OverflowBehaviorTypeLoc TL) {
+ // Nothing to do.
+}
+
void TypeLocReader::VisitHLSLAttributedResourceTypeLoc(
HLSLAttributedResourceTypeLoc TL) {
// Nothing to do.
diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp
index 9faf107f64751..bfac2e8d8756e 100644
--- a/clang/lib/Serialization/ASTWriter.cpp
+++ b/clang/lib/Serialization/ASTWriter.cpp
@@ -604,6 +604,11 @@ void TypeLocWriter::VisitBTFTagAttributedTypeLoc(BTFTagAttributedTypeLoc TL) {
// Nothing to do.
}
+void TypeLocWriter::VisitOverflowBehaviorTypeLoc(
+ OverflowBehaviorTypeLoc TL) {
+ // Nothing to do.
+}
+
void TypeLocWriter::VisitHLSLAttributedResourceTypeLoc(
HLSLAttributedResourceTypeLoc TL) {
// Nothing to do.
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..56ef12329d905
--- /dev/null
+++ b/clang/test/CodeGen/overflow-behavior-types-extensions.c
@@ -0,0 +1,44 @@
+// 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 __nowrap __attribute__((overflow_behavior(no_wrap)))
+
+typedef int __wrap w_int;
+typedef int __nowrap nw_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, nw_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, nw_int: 2, default: 3);
+}
+
+// CHECK-LABEL: define {{.*}} @signed_bitint_test
+void signed_bitint_test(_BitInt(4) __nowrap a, _BitInt(8) __nowrap b, _BitInt(99) __wrap c) {
+ // CHECK: call { i4, i1 } @llvm.sadd.with.overflow.i4(i4
+ (a + 1);
+
+ // CHECK: call { i8, i1 } @llvm.sadd.with.overflow.i8(i8
+ (b + 1);
+
+ // CHECK: add i99 {{.*}}, 1
+ (c + 1);
+}
+
+// CHECK-LABEL: define {{.*}} @unsigned_bitint_test
+void unsigned_bitint_test(unsigned _BitInt(4) __nowrap a, unsigned _BitInt(8) __nowrap b, unsigned _BitInt(99) __wrap c) {
+ // CHECK: call { i4, i1 } @llvm.uadd.with.overflow.i4(i4
+ (a + 1);
+
+ // CHECK: call { i8, i1 } @llvm.uadd.with.overflow.i8(i8
+ (b + 1);
+
+ // CHECK: add i99 {{.*}}, 1
+ (c + 1);
+}
+
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..8049ece3af696
--- /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 __nowrap __attribute__((overflow_behavior(no_wrap)))
+
+typedef int __wrap wrap_int;
+typedef int __nowrap nowrap_int;
+typedef unsigned int __wrap u_wrap_int;
+typedef unsigned int __nowrap u_nowrap_int;
+
+//===----------------------------------------------------------------------===//
+// Compound Assignment Operators
+//===----------------------------------------------------------------------===//
+
+// CHECK-LABEL: define {{.*}} @_Z28compound_assignment_operatorv
+void compound_assignment_operator() {
+ wrap_int a = 1;
+ // CHECK: add i32
+ a += 1;
+
+ nowrap_int b = 1;
+ // CHECK: llvm.sadd.with.overflow.i32
+ b += 1;
+
+ u_wrap_int c = 1;
+ // CHECK: sub i32
+ c -= 1;
+
+ u_nowrap_int d = 1;
+ // CHECK: llvm.usub.with.overflow.i32
+ d -= 1;
+
+ wrap_int e = 2;
+ // CHECK: mul i32
+ e *= 2;
+
+ nowrap_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;
+
+ nowrap_int b = 1;
+ // CHECK: ashr i32
+ // No overflow check for shifts
+ b >>= 1;
+
+ wrap_int c = 1;
+ // CHECK: and i32
+ c &= 1;
+
+ nowrap_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..fd8622e88b44f
--- /dev/null
+++ b/clang/test/CodeGen/overflow-behavior-types-promotions.cpp
@@ -0,0 +1,50 @@
+// 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 __nowrap __attribute__((overflow_behavior(no_wrap)))
+
+typedef int __wrap wrap_int;
+typedef char __wrap wrap_char;
+typedef int __nowrap nowrap_int;
+typedef unsigned int __wrap u_wrap_int;
+typedef unsigned int __nowrap u_nowrap_int;
+
+// CHECK-LABEL: define {{.*}} @_Z30conditional_operator_promotionbU9Obt_cU9Obt_ii
+void conditional_operator_promotion(bool cond, wrap_char w, nowrap_int nw, int i) {
+ // OBT wins over regular integer.
+ // CHECK: cond.end:
+ // CHECK-NEXT: %cond1 = phi i8
+ // CHECK-NEXT: store i8 %cond1, ptr %r1
+ // CHECK-NEXT: %{{.*}} = load i8, ptr %r1
+ // CHECK-NEXT: add i8
+ auto r1 = cond ? w : i;
+ (void)(r1 + 2147483647);
+
+ // nowrap 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_testU9Obt_iU9Obt_iU9Obt_jU9Obt_j
+void promotion_rules_test(wrap_int sw, nowrap_int snw, u_wrap_int uw, u_nowrap_int unw) {
+ // Unsigned is favored over signed for same-behavior OBTs.
+ // CHECK: add i32
+ auto r1 = sw + uw;
+ (void)r1;
+
+ // nowrap is favored over wrap. Result is unsigned nowrap.
+ // CHECK: call { i32, i1 } @llvm.uadd.with.overflow.i32
+ auto r2 = sw + unw;
+ (void)r2;
+
+ // nowrap is favored over wrap. Result is signed nowrap.
+ // CHECK: call { i32, i1 } @llvm.sadd.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..2abf0cb2fbbb7
--- /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 __nowrap __attribute__((overflow_behavior("no_wrap")))
+
+// 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 __nowrap c; // nowrap has precedence over scl entries
+
+ // SIO: add nsw i32
+ (a + 1);
+ // SIO: add nsw i32
+ (b + 1);
+ // SIO: @llvm.sadd.with.overflow.i8
+ (c + 1);
+
+ // UIO-LABEL: load volatile i32, ptr @d, align 4
+ volatile extern unsigned int d;
+ volatile extern unsigned short __nowrap e;
+ // UIO: add i32
+ (d + 1);
+ // UIO: @llvm.uadd.with.overflow.i16
+ (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..bc01f4184fb8d
--- /dev/null
+++ b/clang/test/CodeGen/overflow-behavior-types.c
@@ -0,0 +1,148 @@
+// 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 __nowrap __attribute__((overflow_behavior("no_wrap")))
+
+// DEFAULT-LABEL: define {{.*}} @test1
+// TRAPV-HANDLER-LABEL: define {{.*}} @test1
+// NOSAN-LABEL: define {{.*}} @test1
+void test1(int __wrap a, int __nowrap 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 __wrap a, unsigned char __nowrap b) {
+ // DEFAULT: add i8
+ (a + 1);
+ // DEFAULT: llvm.uadd.with.overflow.i8
+ (b + 1);
+
+ // DEFAULT: %[[T0:.*]] = load volatile i64, ptr @big
+ // DEFAULT: %[[TRUNC1:.*]] = icmp eq i64 {{.*}} %[[T0]]
+ // DEFAULT-NOT: br i1 %[[TRUNC1]], {{.*}} %handler.implicit_conversion
+ 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 __wrap a;
+ volatile extern short __wrap b;
+ // less-than-int arithmetic is possible when one or more wrapping types are
+ // present. When both operands are wrapping types, the larger of the two
+ // types should be used as the result of the arithmetic.
+
+ // DEFAULT: add i16
+ (a + b);
+
+ // nowrap has precedence over wrap, regardless of bit widths
+ volatile extern unsigned long long __wrap c;
+ volatile extern char __nowrap d;
+
+ // DEFAULT: %[[T0:.*]] = load volatile i64, ptr @c
+ // DEFAULT: %[[TRUNC1:.*]] = icmp eq i64 {{.*}} %[[T0]]
+ // DEFAULT: br i1 %[[TRUNC1]]
+ // DEFAULT: %[[T1:.*]] = load volatile i8, ptr @d
+ // DEFAULT-NEXT: @llvm.sadd.with.overflow.i8
+ (c + d);
+
+ volatile extern int __nowrap e;
+ volatile extern unsigned int __wrap f;
+
+ // DEFAULT: @llvm.ssub.with.overflow.i32
+ (e - f);
+}
+
+typedef int __attribute__((overflow_behavior(wrap))) wrap_int;
+typedef int __attribute__((overflow_behavior(no_wrap))) nowrap_int;
+// DEFAULT-LABEL: define {{.*}} @typedefs
+void typedefs(nowrap_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(no_wrap))) 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(no_wrap))) b;
+ if (a + b < a) { /*...*/ }
+}
diff --git a/clang/test/CodeGen/overflow-behavior-types.cpp b/clang/test/CodeGen/overflow-behavior-types.cpp
new file mode 100644
index 0000000000000..e552a20a91d17
--- /dev/null
+++ b/clang/test/CodeGen/overflow-behavior-types.cpp
@@ -0,0 +1,68 @@
+// 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
+
+
+// Test the __attribute__((overflow_behavior())) for C++
+
+#define __wrap __attribute__((overflow_behavior(wrap)))
+#define __no_wrap __attribute__((overflow_behavior(no_wrap)))
+
+class Foo {
+public:
+ unsigned long other;
+ char __wrap a;
+
+ Foo() = delete;
+ Foo(char _a) : a(_a) {}
+
+ decltype(a) getA() const { return a; }
+};
+/* define dso_local void @_Z12test_membersc(i8 noundef signext %some) #0 {
+entry:
+ %some.addr = alloca i8, align 1
+ %foo = alloca %class.Foo, align 8
+ store i8 %some, ptr %some.addr, align 1
+ %0 = load i8, ptr %some.addr, align 1
+ call void @_ZN3FooC2Ec(ptr noundef nonnull align 8 dereferenceable(9) %foo, i8 noundef signext %0)
+ %a = getelementptr inbounds nuw %class.Foo, ptr %foo, i32 0, i32 1
+ %1 = load i8, ptr %a, align 8
+ %inc = add i8 %1, 1
+ store i8 %inc, ptr %a, align 8
+ %call = call noundef i8 @_ZNK3Foo4getAEv(ptr noundef nonnull align 8 dereferenceable(9) %foo)
+ %add = add i8 %call, 1
+ ret void
+}
+*/
+
+// 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]], align 8
+ // DEFAULT-NEXT: %inc{{\d*}} = add i8 %[[T1]], 1
+ (++foo.a);
+
+ // DEFAULT: %[[CALL:.*]] = call noundef i8 @_ZNK3Foo4getAEv
+ // DEFAULT-NEXT: add i8 %[[CALL]], 1
+ (void)(foo.getA() + 1);
+}
+
+// DEFAULT-LABEL: define {{.*}} @_Z9test_autoU9Obt_c
+void test_auto(char __wrap a) {
+ auto b = a;
+
+ // DEFAULT: %[[T1:.*]] = load i8, ptr %b
+ // DEFAULT-NEXT: sub i8 %[[T1]], 1
+ (b - 1); // no instrumentation
+}
+
+
+int overloadme(__no_wrap 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 b9cf7cf9462fe..4097492f82aeb 100644
--- a/clang/test/Misc/pragma-attribute-supported-attributes-list.test
+++ b/clang/test/Misc/pragma-attribute-supported-attributes-list.test
@@ -166,6 +166,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..a1ce02a581bb6
--- /dev/null
+++ b/clang/test/Sema/attr-overflow-behavior-constexpr.cpp
@@ -0,0 +1,44 @@
+// 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 __nowrap __attribute__((overflow_behavior(no_wrap)))
+
+typedef int __wrap wrap_int;
+typedef int __nowrap nowrap_int;
+
+constexpr wrap_int add(wrap_int a, wrap_int b) {
+ return a + b;
+}
+
+constexpr nowrap_int sub(nowrap_int a, nowrap_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 nowrap_int min = -2147483648;
+ constexpr nowrap_int one_nw = 1;
+ // This should fail to compile because of overflow.
+ constexpr nowrap_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_nowrap(T) {
+ static_assert(__is_same(T, nowrap_int), "T should be deduced as nowrap_int");
+}
+
+void template_deduction_test() {
+ wrap_int w = 0;
+ check_deduction_wrap(w);
+
+ nowrap_int nw = 0;
+ check_deduction_nowrap(nw);
+}
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.c b/clang/test/Sema/attr-overflow-behavior.c
new file mode 100644
index 0000000000000..53f7bb51b6c7a
--- /dev/null
+++ b/clang/test/Sema/attr-overflow-behavior.c
@@ -0,0 +1,41 @@
+// RUN: %clang_cc1 %s -Winteger-overflow -Wno-unused-value -foverflow-behavior-types -Wimplicitly-discarded-overflow-behavior -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-warning {{'overflow_behavior' attribute cannot be applied to non-integer type 'char *'; attribute ignored}}
+
+typedef int __attribute__((overflow_behavior(wrap))) ok_wrap; // OK
+typedef long __attribute__((overflow_behavior(no_wrap))) ok_nowrap; // OK
+typedef unsigned long __attribute__((overflow_behavior("wrap"))) str_ok_wrap; // OK
+typedef char __attribute__((overflow_behavior("no_wrap"))) str_ok_nowrap; // OK
+
+void foo() {
+ (2147483647 + 100); // expected-warning {{overflow in expression; result is }}
+ (ok_wrap)2147483647 + 100; // no warn
+}
+
+#define __no_wrap __attribute__((overflow_behavior(no_wrap)))
+
+void ptr(int a) {
+ int __no_wrap *p = &a; // expected-warning-re {{incompatible pointer types initializing '__no_wrap int *' {{.*}}of type 'int *'}}
+}
+
+void ptr2(__no_wrap int a) {
+ int *p = &a; // expected-warning-re {{incompatible pointer types initializing 'int *' {{.*}}of type '__no_wrap int *'}}
+}
+
+
+// verify semantics of -Wimplicitly-discarded-overflow-behavior{,-pedantic}
+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 {{implicit conversion from '__wrap unsigned int' to 'unsigned int' discards overflow behavior}}
+ imp_disc(a); // expected-warning {{implicit conversion from '__wrap unsigned int' to 'int' discards overflow behavior}}
+}
+
+// -Wimplicitly-discarded-overflow-behavior-assignment
+void assignment_disc_test(unsigned __attribute__((overflow_behavior(wrap))) a) {
+ int b = a; // expected-warning {{implicit conversion from '__wrap unsigned int' to 'int' during assignment discards overflow behavior}}
+ int c = (int)a; // OK
+}
diff --git a/clang/test/Sema/attr-overflow-behavior.cpp b/clang/test/Sema/attr-overflow-behavior.cpp
new file mode 100644
index 0000000000000..1eedd736a6b91
--- /dev/null
+++ b/clang/test/Sema/attr-overflow-behavior.cpp
@@ -0,0 +1,115 @@
+// RUN: %clang_cc1 %s -Winteger-overflow -Wno-unused-value -foverflow-behavior-types -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-warning {{'overflow_behavior' attribute cannot be applied to non-integer type 'char *'; attribute ignored}}
+
+typedef int __attribute__((overflow_behavior(wrap))) ok_wrap; // OK
+typedef long __attribute__((overflow_behavior(no_wrap))) ok_nowrap; // OK
+typedef unsigned long __attribute__((overflow_behavior("wrap"))) str_ok_wrap; // OK
+typedef char __attribute__((overflow_behavior("no_wrap"))) str_ok_nowrap; // OK
+
+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(no_wrap))) b; // don't consider underlying type an exact match.
+ add_one(b); // expected-error {{call to 'add_one' is ambiguous}}
+}
+
+#define __no_wrap __attribute__((overflow_behavior(no_wrap)))
+void func(__no_wrap int i);
+void func(int i); // Overload, not invalid redeclaration
+
+// TODO: make this diagnostic message more descriptive
+template <typename Ty>
+void func2(__no_wrap Ty i) {} // expected-warning {{'overflow_behavior' attribute cannot be applied to non-integer type 'Ty'; attribute ignored}}
+
+template <typename Ty>
+struct S {};
+
+template <>
+struct S<__no_wrap int> {};
+
+template <>
+struct S<int> {};
+
+void ptr(int a) {
+ int __no_wrap *p = &a; // expected-error-re {{cannot initialize a variable of type '__no_wrap int *' {{.*}}with an rvalue of type 'int *'}}
+}
+
+void ptr2(__no_wrap int a) {
+ int *p = &a; // expected-error-re {{cannot initialize a variable of type 'int *' {{.*}}with an rvalue of type '__no_wrap int *'}}
+}
+
+void overloadme(__no_wrap 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-warning {{'overflow_behavior' attribute cannot be applied to non-integer type 'void (void)'; attribute ignored}}
+
+typedef float __attribute__((overflow_behavior(wrap))) wrap_float; // expected-warning {{'overflow_behavior' attribute cannot be applied to non-integer type 'float'; attribute ignored}}
+
+void pointer_compatibility_test(int* i_ptr) {
+ __no_wrap int* nowrap_ptr;
+
+ // static_cast should fail.
+ nowrap_ptr = static_cast<__no_wrap int*>(i_ptr); // expected-error {{static_cast from 'int *' to '__no_wrap int *' is not allowed}}
+
+ // reinterpret_cast should succeed.
+ nowrap_ptr = reinterpret_cast<__no_wrap int*>(i_ptr);
+ (void)nowrap_ptr;
+}
diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp
index c446bb0331990..9849cc3b98990 100644
--- a/clang/tools/libclang/CIndex.cpp
+++ b/clang/tools/libclang/CIndex.cpp
@@ -1747,6 +1747,11 @@ 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());
>From 49d2d6e8dda361cc9af5138c8c4adb70677a39cb Mon Sep 17 00:00:00 2001
From: Justin Stitt <justinstitt at google.com>
Date: Tue, 15 Jul 2025 09:57:21 -0700
Subject: [PATCH 02/21] run clang format
Signed-off-by: Justin Stitt <justinstitt at google.com>
---
clang/include/clang/AST/PropertiesBase.td | 3 +-
clang/include/clang/AST/Type.h | 16 +++----
clang/include/clang/AST/TypeLoc.h | 7 ++--
clang/include/clang/Basic/DiagnosticGroups.td | 16 +++----
.../clang/Basic/DiagnosticSemaKinds.td | 27 ++++++------
clang/include/clang/Basic/LangOptions.h | 1 -
clang/lib/AST/ASTContext.cpp | 24 ++++++-----
clang/lib/AST/Type.cpp | 4 +-
clang/lib/AST/TypePrinter.cpp | 20 ++++-----
clang/lib/CodeGen/CGDebugInfo.cpp | 3 +-
clang/lib/CodeGen/CGExprScalar.cpp | 42 +++++++++----------
clang/lib/CodeGen/ItaniumCXXABI.cpp | 1 -
clang/lib/Sema/SemaDecl.cpp | 2 +-
clang/lib/Sema/SemaExpr.cpp | 17 ++++----
clang/lib/Sema/SemaLookup.cpp | 1 -
clang/lib/Sema/SemaType.cpp | 3 +-
clang/lib/Serialization/ASTReader.cpp | 3 +-
clang/lib/Serialization/ASTWriter.cpp | 3 +-
clang/tools/libclang/CIndex.cpp | 3 +-
19 files changed, 97 insertions(+), 99 deletions(-)
diff --git a/clang/include/clang/AST/PropertiesBase.td b/clang/include/clang/AST/PropertiesBase.td
index d51a94d5a313a..4581e55c28027 100644
--- a/clang/include/clang/AST/PropertiesBase.td
+++ b/clang/include/clang/AST/PropertiesBase.td
@@ -81,7 +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 OverflowBehaviorKind
+ : EnumPropertyType<"OverflowBehaviorType::OverflowBehaviorKind">;
def CallingConv : EnumPropertyType;
def DeclarationName : PropertyType;
def DeclarationNameKind : EnumPropertyType<"DeclarationName::NameKind">;
diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h
index 29358f3b9dfc1..257ec30358e78 100644
--- a/clang/include/clang/AST/Type.h
+++ b/clang/include/clang/AST/Type.h
@@ -1150,7 +1150,6 @@ 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;
@@ -2665,7 +2664,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 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
@@ -6675,10 +6674,7 @@ class BTFTagAttributedType : public Type, public llvm::FoldingSetNode {
class OverflowBehaviorType : public Type, public llvm::FoldingSetNode {
public:
- enum OverflowBehaviorKind {
- Wrap,
- NoWrap
- };
+ enum OverflowBehaviorKind { Wrap, NoWrap };
private:
friend class ASTContext; // ASTContext creates these
@@ -6686,9 +6682,10 @@ class OverflowBehaviorType : public Type, public llvm::FoldingSetNode {
QualType UnderlyingType;
OverflowBehaviorKind BehaviorKind;
- OverflowBehaviorType(QualType Canon, QualType Underlying, OverflowBehaviorKind Kind);
-public:
+ OverflowBehaviorType(QualType Canon, QualType Underlying,
+ OverflowBehaviorKind Kind);
+public:
QualType getUnderlyingType() const { return UnderlyingType; }
OverflowBehaviorKind getBehaviorKind() const { return BehaviorKind; }
@@ -9046,8 +9043,7 @@ inline bool Type::isScalarType() const {
isa<MemberPointerType>(CanonicalType) ||
isa<ComplexType>(CanonicalType) ||
isa<ObjCObjectPointerType>(CanonicalType) ||
- isa<OverflowBehaviorType>(CanonicalType) ||
- isBitIntType();
+ isa<OverflowBehaviorType>(CanonicalType) || isBitIntType();
}
inline bool Type::isIntegralOrEnumerationType() const {
diff --git a/clang/include/clang/AST/TypeLoc.h b/clang/include/clang/AST/TypeLoc.h
index c97f4cb9ed6e8..ee9da8f24ed38 100644
--- a/clang/include/clang/AST/TypeLoc.h
+++ b/clang/include/clang/AST/TypeLoc.h
@@ -1076,13 +1076,14 @@ struct OverflowBehaviorLocInfo {}; // Nothing.
class OverflowBehaviorTypeLoc
: public ConcreteTypeLoc<UnqualTypeLoc, OverflowBehaviorTypeLoc,
- OverflowBehaviorType,
- OverflowBehaviorLocInfo> {
+ OverflowBehaviorType, OverflowBehaviorLocInfo> {
public:
TypeLoc getWrappedLoc() const { return getInnerTypeLoc(); }
/// The no_sanitize type attribute.
- OverflowBehaviorType::OverflowBehaviorKind getBehaviorKind() const { return getTypePtr()->getBehaviorKind(); }
+ OverflowBehaviorType::OverflowBehaviorKind getBehaviorKind() const {
+ return getTypePtr()->getBehaviorKind();
+ }
SourceRange getLocalSourceRange() const;
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index b0cddb0d9c681..a8124bd6cb2b4 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -121,14 +121,14 @@ def ImplicitIntConversion : DiagGroup<"implicit-int-conversion",
[Shorten64To32,
ObjCSignedCharBoolImplicitIntConversion,
ImplicitIntConversionOnNegation]>;
-def ImplicitlyDiscardedOverflowBehaviorPedantic :
- DiagGroup<"implicitly-discarded-overflow-behavior-pedantic">;
-def ImplicitlyDiscardedOverflowBehaviorAssignment :
- DiagGroup<"implicitly-discarded-overflow-behavior-assignment">;
-def ImplicitlyDiscardedOverflowBehavior :
- DiagGroup<"implicitly-discarded-overflow-behavior",
- [ImplicitlyDiscardedOverflowBehaviorPedantic,
- ImplicitlyDiscardedOverflowBehaviorAssignment]>;
+def ImplicitlyDiscardedOverflowBehaviorPedantic
+ : DiagGroup<"implicitly-discarded-overflow-behavior-pedantic">;
+def ImplicitlyDiscardedOverflowBehaviorAssignment
+ : DiagGroup<"implicitly-discarded-overflow-behavior-assignment">;
+def ImplicitlyDiscardedOverflowBehavior
+ : DiagGroup<"implicitly-discarded-overflow-behavior",
+ [ImplicitlyDiscardedOverflowBehaviorPedantic,
+ ImplicitlyDiscardedOverflowBehaviorAssignment]>;
def ImplicitConstIntFloatConversion : DiagGroup<"implicit-const-int-float-conversion">;
def ImplicitIntFloatConversion : DiagGroup<"implicit-int-float-conversion",
[ImplicitConstIntFloatConversion]>;
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index d7cc21bfe8739..94fdcbe7ae378 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -4031,9 +4031,10 @@ def note_cannot_use_trivial_abi_reason : Note<
"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 'no_wrap' "
- "are supported">;
+def err_overflow_behavior_unknown_ident
+ : Error<"'%0' is not a valid argument to attribute %1, only 'wrap' and "
+ "'no_wrap' "
+ "are supported">;
def warn_overflow_behavior_non_integer_type
: Warning<"%0 attribute cannot be applied to non-integer type '%1'; "
"attribute ignored">,
@@ -4042,15 +4043,17 @@ def warn_overflow_behavior_attribute_disabled
: Warning<"%0 attribute is ignored because it is not enabled; pass "
"-foverflow-behavior-types">,
InGroup<OverflowBehaviorAttributeIgnored>;
-def warn_implicitly_discarded_overflow_behavior : Warning<
- "implicit conversion from %0 to %1 discards overflow behavior">,
- InGroup<ImplicitlyDiscardedOverflowBehavior>;
-def warn_implicitly_discarded_overflow_behavior_pedantic : Warning<
- "implicit conversion from %0 to %1 discards overflow behavior">,
- InGroup<ImplicitlyDiscardedOverflowBehaviorPedantic>;
-def warn_implicitly_discarded_overflow_behavior_assignment : Warning<
- "implicit conversion from %0 to %1 during assignment discards overflow behavior">,
- InGroup<ImplicitlyDiscardedOverflowBehaviorAssignment>, DefaultIgnore;
+def warn_implicitly_discarded_overflow_behavior
+ : Warning<"implicit conversion from %0 to %1 discards overflow behavior">,
+ InGroup<ImplicitlyDiscardedOverflowBehavior>;
+def warn_implicitly_discarded_overflow_behavior_pedantic
+ : Warning<"implicit conversion from %0 to %1 discards overflow behavior">,
+ InGroup<ImplicitlyDiscardedOverflowBehaviorPedantic>;
+def warn_implicitly_discarded_overflow_behavior_assignment
+ : Warning<"implicit conversion from %0 to %1 during assignment discards "
+ "overflow behavior">,
+ InGroup<ImplicitlyDiscardedOverflowBehaviorAssignment>,
+ DefaultIgnore;
// Availability attribute
def warn_availability_unknown_platform : Warning<
"unknown platform %0 in availability macro">, InGroup<Availability>;
diff --git a/clang/include/clang/Basic/LangOptions.h b/clang/include/clang/Basic/LangOptions.h
index 09d7c3d814cf6..58817f818db10 100644
--- a/clang/include/clang/Basic/LangOptions.h
+++ b/clang/include/clang/Basic/LangOptions.h
@@ -131,7 +131,6 @@ class LangOptionsBase {
OB_FWrapv
};
-
// FIXME: Unify with TUKind.
enum CompilingModuleKind {
/// Not compiling a module interface at all.
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 62092774f60bb..eb80ceed3d1a3 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -3793,8 +3793,8 @@ ASTContext::adjustType(QualType Orig,
case Type::OverflowBehavior: {
const auto *OB = dyn_cast<OverflowBehaviorType>(Orig);
- return getOverflowBehaviorType(
- OB->getBehaviorKind(), adjustType(OB->getUnderlyingType(), Adjust));
+ return getOverflowBehaviorType(OB->getBehaviorKind(),
+ adjustType(OB->getUnderlyingType(), Adjust));
}
case Type::Elaborated: {
@@ -5832,8 +5832,8 @@ QualType ASTContext::getBTFTagAttributedType(const BTFTypeTagAttr *BTFAttr,
return QualType(Ty, 0);
}
-QualType
-ASTContext::getOverflowBehaviorType(const OverflowBehaviorAttr *Attr, QualType Underlying) const {
+QualType ASTContext::getOverflowBehaviorType(const OverflowBehaviorAttr *Attr,
+ QualType Underlying) const {
IdentifierInfo *II = Attr->getBehaviorKind();
StringRef IdentName = II->getName();
OverflowBehaviorType::OverflowBehaviorKind Kind;
@@ -5848,8 +5848,9 @@ ASTContext::getOverflowBehaviorType(const OverflowBehaviorAttr *Attr, QualType U
return getOverflowBehaviorType(Kind, Underlying);
}
-QualType
-ASTContext::getOverflowBehaviorType(OverflowBehaviorType::OverflowBehaviorKind Kind, QualType Underlying) const {
+QualType ASTContext::getOverflowBehaviorType(
+ OverflowBehaviorType::OverflowBehaviorKind Kind,
+ QualType Underlying) const {
llvm::FoldingSetNodeID ID;
OverflowBehaviorType::Profile(ID, Underlying, Kind);
void *InsertPos = nullptr;
@@ -5862,13 +5863,14 @@ ASTContext::getOverflowBehaviorType(OverflowBehaviorType::OverflowBehaviorKind K
QualType Canonical;
if (!Underlying.isCanonical()) {
Canonical = getOverflowBehaviorType(Kind, getCanonicalType(Underlying));
- OverflowBehaviorType *NewOBT = OverflowBehaviorTypes.FindNodeOrInsertPos(ID, InsertPos);
+ OverflowBehaviorType *NewOBT =
+ OverflowBehaviorTypes.FindNodeOrInsertPos(ID, InsertPos);
assert(!NewOBT && "Shouldn't be in the map!");
(void)NewOBT;
}
OverflowBehaviorType *Ty = new (*this, alignof(OverflowBehaviorType))
- OverflowBehaviorType(Canonical, Underlying, Kind);
+ OverflowBehaviorType(Canonical, Underlying, Kind);
Types.push_back(Ty);
OverflowBehaviorTypes.InsertNode(Ty, InsertPos);
@@ -11875,9 +11877,9 @@ 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))
+ if (std::optional<QualType> MergedOBT =
+ tryMergeOverflowBehaviorTypes(LHS, RHS, OfBlockPointer, Unqualified,
+ BlockReturnType, IsConditionalOperator))
return *MergedOBT;
if (Unqualified) {
diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index 7ea4ebcf06763..5eb14ff4605d6 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -1168,7 +1168,8 @@ struct SimpleTransformVisitor : public TypeVisitor<Derived, QualType> {
if (UnderlyingType.isNull())
return {};
- if (UnderlyingType.getAsOpaquePtr() == T->getUnderlyingType().getAsOpaquePtr())
+ if (UnderlyingType.getAsOpaquePtr() ==
+ T->getUnderlyingType().getAsOpaquePtr())
return QualType(T, 0);
return Ctx.getOverflowBehaviorType(T->getBehaviorKind(), UnderlyingType);
@@ -2173,7 +2174,6 @@ bool Type::isIntegralType(const ASTContext &Ctx) const {
return OBT->getUnderlyingType()->isIntegralOrEnumerationType();
}
-
return isBitIntType();
}
diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp
index 6aecfbff16964..449894f826e33 100644
--- a/clang/lib/AST/TypePrinter.cpp
+++ b/clang/lib/AST/TypePrinter.cpp
@@ -2174,23 +2174,23 @@ void TypePrinter::printBTFTagAttributedAfter(const BTFTagAttributedType *T,
printAfter(T->getWrappedType(), OS);
}
-void TypePrinter::printOverflowBehaviorBefore(
- const OverflowBehaviorType *T, raw_ostream &OS) {
+void TypePrinter::printOverflowBehaviorBefore(const OverflowBehaviorType *T,
+ raw_ostream &OS) {
StringRef KindName;
switch (T->getBehaviorKind()) {
- case clang::OverflowBehaviorType::OverflowBehaviorKind::Wrap:
- KindName = "__wrap";
- break;
- case clang::OverflowBehaviorType::OverflowBehaviorKind::NoWrap:
- KindName = "__no_wrap";
- break;
+ case clang::OverflowBehaviorType::OverflowBehaviorKind::Wrap:
+ KindName = "__wrap";
+ break;
+ case clang::OverflowBehaviorType::OverflowBehaviorKind::NoWrap:
+ KindName = "__no_wrap";
+ break;
}
OS << KindName << " ";
printBefore(T->getUnderlyingType(), OS);
}
-void TypePrinter::printOverflowBehaviorAfter(
- const OverflowBehaviorType *T, raw_ostream &OS) {
+void TypePrinter::printOverflowBehaviorAfter(const OverflowBehaviorType *T,
+ raw_ostream &OS) {
printAfter(T->getUnderlyingType(), OS);
}
diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp
index dc952c903f488..2b2d6244be88b 100644
--- a/clang/lib/CodeGen/CGDebugInfo.cpp
+++ b/clang/lib/CodeGen/CGDebugInfo.cpp
@@ -1115,7 +1115,8 @@ llvm::DIType *CGDebugInfo::CreateType(const BitIntType *Ty) {
Encoding);
}
-llvm::DIType *CGDebugInfo::CreateType(const OverflowBehaviorType *Ty, llvm::DIFile *U) {
+llvm::DIType *CGDebugInfo::CreateType(const OverflowBehaviorType *Ty,
+ llvm::DIFile *U) {
return getOrCreateType(Ty->getUnderlyingType(), U);
}
diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp
index 46b6986904782..ae51517c62bd2 100644
--- a/clang/lib/CodeGen/CGExprScalar.cpp
+++ b/clang/lib/CodeGen/CGExprScalar.cpp
@@ -3029,28 +3029,28 @@ llvm::Value *ScalarExprEmitter::EmitIncDecConsiderOverflowBehavior(
: CGF.SanOpts.has(SanitizerKind::UnsignedIntegerOverflow);
switch (getOverflowBehaviorConsideringType(CGF, Ty)) {
- case LangOptions::OB_Wrap:
+ case LangOptions::OB_Wrap:
+ return Builder.CreateAdd(InVal, Amount, Name);
+ case LangOptions::OB_FWrapv:
+ if (!hasSan)
return Builder.CreateAdd(InVal, Amount, Name);
- case LangOptions::OB_FWrapv:
- if (!hasSan)
- return Builder.CreateAdd(InVal, Amount, Name);
- [[fallthrough]];
- 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::OB_NoWrap:
- if (!Ty->getAs<OverflowBehaviorType>() && !E->canOverflow())
- return Builder.CreateAdd(InVal, Amount, Name);
- BinOpInfo Info = createBinOpInfoFromIncDec(
- E, InVal, IsInc, E->getFPFeaturesInEffect(CGF.getLangOpts()));
- if (CanElideOverflowCheck(CGF.getContext(), Info))
- return isSigned ? Builder.CreateNSWAdd(InVal, Amount, Name)
- : Builder.CreateAdd(InVal, Amount, Name);
- return EmitOverflowCheckedBinOp(Info);
+ [[fallthrough]];
+ 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::OB_NoWrap:
+ if (!Ty->getAs<OverflowBehaviorType>() && !E->canOverflow())
+ return Builder.CreateAdd(InVal, Amount, Name);
+ BinOpInfo Info = createBinOpInfoFromIncDec(
+ E, InVal, IsInc, E->getFPFeaturesInEffect(CGF.getLangOpts()));
+ if (CanElideOverflowCheck(CGF.getContext(), Info))
+ return isSigned ? Builder.CreateNSWAdd(InVal, Amount, Name)
+ : Builder.CreateAdd(InVal, Amount, Name);
+ return EmitOverflowCheckedBinOp(Info);
}
llvm_unreachable("Unknown OverflowBehaviorKind");
}
diff --git a/clang/lib/CodeGen/ItaniumCXXABI.cpp b/clang/lib/CodeGen/ItaniumCXXABI.cpp
index 16fcdddd7bcfa..74728734f6b0e 100644
--- a/clang/lib/CodeGen/ItaniumCXXABI.cpp
+++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp
@@ -4006,7 +4006,6 @@ void ItaniumRTTIBuilder::BuildVTablePointer(const Type *Ty,
llvm_unreachable("NoSanitize not supported for virtual functions");
}
-
llvm::Constant *VTable = nullptr;
// Check if the alias exists. If it doesn't, then get or create the global.
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index a22bccdc0180b..6513fc6260af6 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -13721,7 +13721,7 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init, bool DirectInit) {
}
if (!VDecl->getType()->isOverflowBehaviorType() &&
- Init->getType()->isOverflowBehaviorType()) {
+ !Init->getType().isNull() && Init->getType()->isOverflowBehaviorType()) {
if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Init)) {
DRE->setOverflowBehaviorDiscarded(true);
}
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index a2f598b663d45..b32a4c6a70445 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -1428,8 +1428,7 @@ static QualType handleOverflowBehaviorTypeConversion(Sema &S, ExprResult &LHS,
}
// NoWrap has precedence over Wrap; eagerly cast Wrap types to NoWrap types
- if ((LhsOBT && !RhsOBT) ||
- (LhsOBT && RhsOBT && !RhsOBT->isNoWrapKind())) {
+ if ((LhsOBT && !RhsOBT) || (LhsOBT && RhsOBT && !RhsOBT->isNoWrapKind())) {
RHS = doIntegralCast(S, RHS.get(), LHSType);
return LHSType;
}
@@ -1958,16 +1957,16 @@ ExprResult Sema::CreateGenericSelectionExpr(
: ControllingType->getType().getCanonicalType();
QualType AssocQT = Types[i]->getType();
- const auto *ControllingOBT =
- ControllingQT->getAs<OverflowBehaviorType>();
- const auto *AssocOBT = AssocQT.getCanonicalType()->getAs<OverflowBehaviorType>();
+ const auto *ControllingOBT = ControllingQT->getAs<OverflowBehaviorType>();
+ const auto *AssocOBT =
+ AssocQT.getCanonicalType()->getAs<OverflowBehaviorType>();
if (ControllingOBT || AssocOBT) {
if (ControllingOBT && AssocOBT) {
if (ControllingOBT->getBehaviorKind() == AssocOBT->getBehaviorKind())
- Compatible = Context.typesAreCompatible(
- ControllingOBT->getUnderlyingType(),
- AssocOBT->getUnderlyingType());
+ Compatible =
+ Context.typesAreCompatible(ControllingOBT->getUnderlyingType(),
+ AssocOBT->getUnderlyingType());
else
Compatible = false;
} else
@@ -9144,7 +9143,7 @@ static AssignConvertType checkPointerTypesForAssignment(Sema &S,
if (ltrans->isOverflowBehaviorType() || rtrans->isOverflowBehaviorType()) {
if (!S.Context.hasSameType(ltrans, rtrans))
- return AssignConvertType::IncompatiblePointer;
+ return AssignConvertType::IncompatiblePointer;
}
if (!S.Context.typesAreCompatible(ltrans, rtrans)) {
diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp
index 6d1c677465317..d274727dfb9c9 100644
--- a/clang/lib/Sema/SemaLookup.cpp
+++ b/clang/lib/Sema/SemaLookup.cpp
@@ -3313,7 +3313,6 @@ addAssociatedClassesAndNamespaces(AssociatedLookup &Result, QualType Ty) {
T = cast<OverflowBehaviorType>(T)->getUnderlyingType().getTypePtr();
}
-
if (Queue.empty())
break;
T = Queue.pop_back_val();
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index 84dfb1567016e..ff28507cf40cd 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -300,7 +300,8 @@ namespace {
return sema.Context.getBTFTagAttributedType(BTFAttr, WrappedType);
}
- /// Get a OverflowBehaviorType type for the overflow_behavior type attribute.
+ /// Get a OverflowBehaviorType type for the overflow_behavior type
+ /// attribute.
QualType
getOverflowBehaviorType(OverflowBehaviorType::OverflowBehaviorKind Kind,
QualType UnderlyingType) {
diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp
index 4a3c9f02624e3..ebbbfd96430b2 100644
--- a/clang/lib/Serialization/ASTReader.cpp
+++ b/clang/lib/Serialization/ASTReader.cpp
@@ -7477,8 +7477,7 @@ void TypeLocReader::VisitBTFTagAttributedTypeLoc(BTFTagAttributedTypeLoc TL) {
// Nothing to do.
}
-void TypeLocReader::VisitOverflowBehaviorTypeLoc(
- OverflowBehaviorTypeLoc TL) {
+void TypeLocReader::VisitOverflowBehaviorTypeLoc(OverflowBehaviorTypeLoc TL) {
// Nothing to do.
}
diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp
index bfac2e8d8756e..db7c05c33b90e 100644
--- a/clang/lib/Serialization/ASTWriter.cpp
+++ b/clang/lib/Serialization/ASTWriter.cpp
@@ -604,8 +604,7 @@ void TypeLocWriter::VisitBTFTagAttributedTypeLoc(BTFTagAttributedTypeLoc TL) {
// Nothing to do.
}
-void TypeLocWriter::VisitOverflowBehaviorTypeLoc(
- OverflowBehaviorTypeLoc TL) {
+void TypeLocWriter::VisitOverflowBehaviorTypeLoc(OverflowBehaviorTypeLoc TL) {
// Nothing to do.
}
diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp
index 9849cc3b98990..90c3dcde56885 100644
--- a/clang/tools/libclang/CIndex.cpp
+++ b/clang/tools/libclang/CIndex.cpp
@@ -1747,8 +1747,7 @@ bool CursorVisitor::VisitBTFTagAttributedTypeLoc(BTFTagAttributedTypeLoc TL) {
return Visit(TL.getWrappedLoc());
}
-bool CursorVisitor::VisitOverflowBehaviorTypeLoc(
- OverflowBehaviorTypeLoc TL) {
+bool CursorVisitor::VisitOverflowBehaviorTypeLoc(OverflowBehaviorTypeLoc TL) {
return Visit(TL.getWrappedLoc());
}
>From 628f2919fb804d39a1f7e5b1b14377cd5dcbf117 Mon Sep 17 00:00:00 2001
From: Justin Stitt <justinstitt at google.com>
Date: Thu, 17 Jul 2025 17:19:31 -0700
Subject: [PATCH 03/21] rename NSAT to OBT
These used to make sense when this feature set was called
"NoSanitizeAttributedType". They are now called OverflowBehaviorTypes.
Signed-off-by: Justin Stitt <justinstitt at google.com>
---
clang/lib/AST/Type.cpp | 20 ++++++++++----------
1 file changed, 10 insertions(+), 10 deletions(-)
diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index 5eb14ff4605d6..2977e3db6dbd3 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -2289,8 +2289,8 @@ bool Type::isSignedIntegerType() const {
if (const auto *IT = dyn_cast<DependentBitIntType>(CanonicalType))
return IT->isSigned();
- if (const auto *NSAT = dyn_cast<OverflowBehaviorType>(CanonicalType))
- return NSAT->getUnderlyingType()->isSignedIntegerType();
+ if (const auto *OBT = dyn_cast<OverflowBehaviorType>(CanonicalType))
+ return OBT->getUnderlyingType()->isSignedIntegerType();
return false;
}
@@ -2311,8 +2311,8 @@ bool Type::isSignedIntegerOrEnumerationType() const {
if (const auto *IT = dyn_cast<DependentBitIntType>(CanonicalType))
return IT->isSigned();
- if (const auto *NSAT = dyn_cast<OverflowBehaviorType>(CanonicalType))
- return NSAT->getUnderlyingType()->isSignedIntegerOrEnumerationType();
+ if (const auto *OBT = dyn_cast<OverflowBehaviorType>(CanonicalType))
+ return OBT->getUnderlyingType()->isSignedIntegerOrEnumerationType();
return false;
}
@@ -2345,8 +2345,8 @@ bool Type::isUnsignedIntegerType() const {
if (const auto *IT = dyn_cast<DependentBitIntType>(CanonicalType))
return IT->isUnsigned();
- if (const auto *NSAT = dyn_cast<OverflowBehaviorType>(CanonicalType))
- return NSAT->getUnderlyingType()->isUnsignedIntegerType();
+ if (const auto *OBT = dyn_cast<OverflowBehaviorType>(CanonicalType))
+ return OBT->getUnderlyingType()->isUnsignedIntegerType();
return false;
}
@@ -2367,8 +2367,8 @@ bool Type::isUnsignedIntegerOrEnumerationType() const {
if (const auto *IT = dyn_cast<DependentBitIntType>(CanonicalType))
return IT->isUnsigned();
- if (const auto *NSAT = dyn_cast<OverflowBehaviorType>(CanonicalType))
- return NSAT->getUnderlyingType()->isUnsignedIntegerOrEnumerationType();
+ if (const auto *OBT = dyn_cast<OverflowBehaviorType>(CanonicalType))
+ return OBT->getUnderlyingType()->isUnsignedIntegerOrEnumerationType();
return false;
}
@@ -3161,8 +3161,8 @@ bool Type::isLiteralType(const ASTContext &Ctx) const {
if (const auto *AT = BaseTy->getAs<AtomicType>())
return AT->getValueType()->isLiteralType(Ctx);
- if (const auto *NSAT = BaseTy->getAs<OverflowBehaviorType>())
- return NSAT->getUnderlyingType()->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.
>From 8250a3900aab8e8ffaba5ec2c156b266a38d775e Mon Sep 17 00:00:00 2001
From: Justin Stitt <justinstitt at google.com>
Date: Tue, 22 Jul 2025 17:33:40 -0700
Subject: [PATCH 04/21] refactor OBT diagnostic groups and naming
Use -Wconversion diagnostic group patterns instead of hand-rolled ones.
Signed-off-by: Justin Stitt <justinstitt at google.com>
---
clang/docs/OverflowBehaviorTypes.rst | 93 ++++++++++++-------
clang/include/clang/Basic/DiagnosticGroups.td | 48 +++++-----
.../clang/Basic/DiagnosticSemaKinds.td | 18 ++--
clang/lib/Sema/SemaChecking.cpp | 14 ++-
clang/test/Sema/attr-overflow-behavior.c | 4 +-
5 files changed, 98 insertions(+), 79 deletions(-)
diff --git a/clang/docs/OverflowBehaviorTypes.rst b/clang/docs/OverflowBehaviorTypes.rst
index 18e337e181e8a..3a0873f070b3f 100644
--- a/clang/docs/OverflowBehaviorTypes.rst
+++ b/clang/docs/OverflowBehaviorTypes.rst
@@ -161,10 +161,10 @@ Diagnostics
Clang provides diagnostics to help developers manage overflow behavior types.
--Wimplicitly-discarded-overflow-behavior
-----------------------------------------
+-Woverflow-behavior-conversion
+------------------------------
-This warning is issued when an overflow behavior type is implicitly converted
+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.
@@ -192,39 +192,20 @@ integer type.
some_function(static_cast<int>(w)); // OK
}
-This warning acts as a group that includes
-``-Wimplicitly-discarded-overflow-behavior-pedantic`` and
-``-Wimplicitly-discarded-overflow-behavior-assignment``.
-
--Wimplicitly-discarded-overflow-behavior-pedantic
--------------------------------------------------
-
-A less severe version of the warning, ``-Wimplicitly-discarded-overflow-behavior-pedantic``,
-is issued for implicit conversions from an unsigned wrapping type to a standard
-unsigned integer type. This is considered less problematic because both types
-have well-defined wrapping behavior, but the conversion still discards the
-explicit ``overflow_behavior`` attribute.
-
-.. code-block:: c++
-
- typedef unsigned int __attribute__((overflow_behavior(wrap))) wrapping_uint;
-
- void some_function(unsigned int);
+This warning group includes
+``-Wimplicit-overflow-behavior-conversion`` and
+``-Wimplicit-overflow-behavior-conversion-pedantic``.
- void another_function(wrapping_uint w) {
- some_function(w); // warning: implicit conversion from 'wrapping_uint' to
- // 'unsigned int' discards overflow behavior
- // [-Wimplicitly-discarded-overflow-behavior-pedantic]
- }
+.. note::
+ ``-Woverflow-behavior-conversion`` is implied by ``-Wconversion``.
--Wimplicitly-discarded-overflow-behavior-assignment
----------------------------------------------------
+-Wimplicit-overflow-behavior-conversion
+---------------------------------------
This warning is issued when an overflow behavior type is implicitly converted
-to a standard integer type as part of an assignment, which may lead to the
-loss of the specified overflow behavior. This is a more specific version of
-the ``-Wimplicitly-discarded-overflow-behavior`` warning, and it is off by
-default.
+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++
@@ -233,8 +214,23 @@ default.
void some_function() {
wrapping_int w = 1;
int i = w; // warning: implicit conversion from 'wrapping_int' to 'int'
- // discards overflow behavior
- // [-Wimplicitly-discarded-overflow-behavior-assignment]
+ // during assignment discards overflow behavior
+ // [-Wimplicit-overflow-behavior-conversion]
+ }
+
+Here's another example showing function parameter conversion with a ``no_wrap`` type:
+
+.. code-block:: c++
+
+ typedef int __attribute__((overflow_behavior(no_wrap))) 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
@@ -243,6 +239,7 @@ integer type.
.. code-block:: c++
typedef int __attribute__((overflow_behavior(wrap))) wrapping_int;
+ typedef int __attribute__((overflow_behavior(no_wrap))) safe_int;
void some_function() {
wrapping_int w = 1;
@@ -250,6 +247,34 @@ integer type.
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-pedantic
+------------------------------------------------
+
+A less severe version of the warning, ``-Wimplicit-overflow-behavior-conversion-pedantic``,
+is issued for implicit conversions from an unsigned wrapping type to a standard
+unsigned integer type. This is considered less problematic because both types
+have well-defined wrapping behavior, but the conversion still discards the
+explicit ``overflow_behavior`` attribute.
+
+.. code-block:: c++
+
+ typedef unsigned int __attribute__((overflow_behavior(wrap))) wrapping_uint;
+
+ void some_function(unsigned int);
+
+ void another_function(wrapping_uint w) {
+ some_function(w); // warning: implicit conversion from 'wrapping_uint' to
+ // 'unsigned int' discards overflow behavior
+ // [-Wimplicit-overflow-behavior-conversion-pedantic]
+ }
-Woverflow-behavior-attribute-ignored
-------------------------------------
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index a8124bd6cb2b4..a3f6bf29dadd8 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -117,18 +117,11 @@ 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 ImplicitlyDiscardedOverflowBehaviorPedantic
- : DiagGroup<"implicitly-discarded-overflow-behavior-pedantic">;
-def ImplicitlyDiscardedOverflowBehaviorAssignment
- : DiagGroup<"implicitly-discarded-overflow-behavior-assignment">;
-def ImplicitlyDiscardedOverflowBehavior
- : DiagGroup<"implicitly-discarded-overflow-behavior",
- [ImplicitlyDiscardedOverflowBehaviorPedantic,
- ImplicitlyDiscardedOverflowBehaviorAssignment]>;
def ImplicitConstIntFloatConversion : DiagGroup<"implicit-const-int-float-conversion">;
def ImplicitIntFloatConversion : DiagGroup<"implicit-int-float-conversion",
[ImplicitConstIntFloatConversion]>;
@@ -138,6 +131,14 @@ def ImplicitFloatConversion : DiagGroup<"implicit-float-conversion",
[ImplicitIntFloatConversion,
ObjCSignedCharBoolImplicitFloatConversion]>;
def ImplicitFixedPointConversion : DiagGroup<"implicit-fixed-point-conversion">;
+def ImplicitOverflowBehaviorConversion
+ : DiagGroup<"implicit-overflow-behavior-conversion">;
+def ImplicitOverflowBehaviorConversionPedantic
+ : DiagGroup<"implicit-overflow-behavior-conversion-pedantic">;
+def OverflowBehaviorConversion
+ : DiagGroup<"overflow-behavior-conversion",
+ [ImplicitOverflowBehaviorConversion,
+ ImplicitOverflowBehaviorConversionPedantic]>;
def FloatOverflowConversion : DiagGroup<"float-overflow-conversion">;
def FloatZeroConversion : DiagGroup<"float-zero-conversion">;
@@ -1152,23 +1153,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,
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 94fdcbe7ae378..a7b841e7c6ceb 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -4043,16 +4043,14 @@ def warn_overflow_behavior_attribute_disabled
: Warning<"%0 attribute is ignored because it is not enabled; pass "
"-foverflow-behavior-types">,
InGroup<OverflowBehaviorAttributeIgnored>;
-def warn_implicitly_discarded_overflow_behavior
- : Warning<"implicit conversion from %0 to %1 discards overflow behavior">,
- InGroup<ImplicitlyDiscardedOverflowBehavior>;
-def warn_implicitly_discarded_overflow_behavior_pedantic
- : Warning<"implicit conversion from %0 to %1 discards overflow behavior">,
- InGroup<ImplicitlyDiscardedOverflowBehaviorPedantic>;
-def warn_implicitly_discarded_overflow_behavior_assignment
+def warn_impcast_overflow_behavior_assignment
: Warning<"implicit conversion from %0 to %1 during assignment discards "
"overflow behavior">,
- InGroup<ImplicitlyDiscardedOverflowBehaviorAssignment>,
+ InGroup<ImplicitOverflowBehaviorConversion>,
+ DefaultIgnore;
+def warn_impcast_overflow_behavior_pedantic
+ : Warning<"implicit conversion from %0 to %1 discards overflow behavior">,
+ InGroup<ImplicitOverflowBehaviorConversionPedantic>,
DefaultIgnore;
// Availability attribute
def warn_availability_unknown_platform : Warning<
@@ -4335,6 +4333,10 @@ def warn_impcast_vector_scalar : Warning<
def warn_impcast_complex_scalar : Warning<
"implicit conversion discards imaginary component: %0 to %1">,
InGroup<Conversion>, DefaultIgnore;
+def warn_impcast_overflow_behavior
+ : Warning<"implicit conversion from %0 to %1 discards overflow behavior">,
+ InGroup<ImplicitOverflowBehaviorConversion>,
+ DefaultIgnore;
def err_impcast_complex_scalar : Error<
"implicit conversion from %0 to %1 is not permitted in C++">;
def warn_impcast_float_precision : Warning<
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 11346433b67e8..5bcc633cd4866 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -12190,17 +12190,15 @@ void Sema::CheckImplicitConversion(Expr *E, QualType T, SourceLocation CC,
if (Target->isIntegerType() && !Target->isOverflowBehaviorType()) {
// Implicit casts from unsigned wrap types to unsigned types are less
// problematic but still warrant some diagnostic.
- if (DiscardedDuringAssignment)
- return DiagnoseImpCast(
- *this, E, T, CC,
- diag::warn_implicitly_discarded_overflow_behavior_assignment);
if (OBT->isUnsignedIntegerType() && OBT->isWrapKind() &&
Target->isUnsignedIntegerType())
- return DiagnoseImpCast(
- *this, E, T, CC,
- diag::warn_implicitly_discarded_overflow_behavior_pedantic);
+ return DiagnoseImpCast(*this, E, T, CC,
+ diag::warn_impcast_overflow_behavior_pedantic);
+ if (DiscardedDuringAssignment)
+ return DiagnoseImpCast(*this, E, T, CC,
+ diag::warn_impcast_overflow_behavior_assignment);
return DiagnoseImpCast(*this, E, T, CC,
- diag::warn_implicitly_discarded_overflow_behavior);
+ diag::warn_impcast_overflow_behavior);
}
}
diff --git a/clang/test/Sema/attr-overflow-behavior.c b/clang/test/Sema/attr-overflow-behavior.c
index 53f7bb51b6c7a..0a97d3ee9d7c1 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 -Wimplicitly-discarded-overflow-behavior -verify -fsyntax-only
+// RUN: %clang_cc1 %s -Winteger-overflow -Wno-unused-value -foverflow-behavior-types -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'}}
@@ -34,7 +34,7 @@ void imp_disc_test(unsigned __attribute__((overflow_behavior(wrap))) a) {
imp_disc(a); // expected-warning {{implicit conversion from '__wrap unsigned int' to 'int' discards overflow behavior}}
}
-// -Wimplicitly-discarded-overflow-behavior-assignment
+// -Wconversion for assignments that discard overflow behavior
void assignment_disc_test(unsigned __attribute__((overflow_behavior(wrap))) a) {
int b = a; // expected-warning {{implicit conversion from '__wrap unsigned int' to 'int' during assignment discards overflow behavior}}
int c = (int)a; // OK
>From 9c4ade0605ae0470a92c9c8b8f944d8a6b8a3e78 Mon Sep 17 00:00:00 2001
From: Justin Stitt <justinstitt at google.com>
Date: Thu, 24 Jul 2025 15:05:57 -0700
Subject: [PATCH 05/21] update docs with more promotion/narrowing info
Signed-off-by: Justin Stitt <justinstitt at google.com>
---
clang/docs/OverflowBehaviorTypes.rst | 100 +++++++++++++---------
clang/docs/UndefinedBehaviorSanitizer.rst | 14 +++
2 files changed, 74 insertions(+), 40 deletions(-)
diff --git a/clang/docs/OverflowBehaviorTypes.rst b/clang/docs/OverflowBehaviorTypes.rst
index 3a0873f070b3f..41c0077737e8e 100644
--- a/clang/docs/OverflowBehaviorTypes.rst
+++ b/clang/docs/OverflowBehaviorTypes.rst
@@ -43,6 +43,10 @@ Where ``behavior`` can be one of the following:
This attribute can be applied to ``typedef`` declarations and to integer types directly.
+Arithmetic operations containing one or more overflow behavior types may have
+result types that do not match standard integer promotion rules. The
+characteristics of result types across all scenarios are described under `Promotion Rules`_.
+
Examples
========
@@ -75,46 +79,6 @@ integral types.
Note that C++ overload set formation rules treat promotions to and from
overflow behavior types the same as normal integral promotions and conversions.
-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
- - No trap
- - Wraps
- - No report
- - Overrides SSCL
- * - ``overflow_behavior(no_wrap)``
- - 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(no_wrap)``
-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 instrumentation.
-
Promotion Rules
===============
@@ -123,6 +87,9 @@ specified overflow behavior throughout an arithmetic expression. They differ
from standard C/C++ integer promotions but in a predictable way, similar to
how ``_Complex`` and ``_BitInt`` have their own promotion rules.
+The resulting type characteristics for overflow behavior types (OBTs) across a
+variety of scenarios is detailed below.
+
* **OBT and Standard Integer Type**: In an operation involving an overflow
behavior type (OBT) and a standard integer type, the result will have the
type of the OBT, including its overflow behavior, sign, and bit-width. The
@@ -156,6 +123,59 @@ how ``_Complex`` and ``_BitInt`` have their own promotion rules.
Regardless, the resulting type matches the bit-width, sign and behavior of
the ``no_wrap`` type.
+.. list-table:: Promotion Rules Summary
+ :widths: 30 70
+ :header-rows: 1
+
+ * - Operation Type
+ - Result Type
+ * - OBT + Standard Integer
+ - OBT type (preserves overflow behavior, sign, and bit-width)
+ * - Same Kind OBTs (both ``wrap`` or both ``no_wrap``)
+ - Larger bit-width; unsigned favored if same width
+ * - Different Kind OBTs (``wrap`` + ``no_wrap``)
+ - ``no_wrap`` type (matches ``no_wrap`` operand's characteristics)
+
+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
+ - Overrides SSCL
+ * - ``overflow_behavior(no_wrap)``
+ - 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(no_wrap)``
+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 instrumentation.
+
Diagnostics
===========
diff --git a/clang/docs/UndefinedBehaviorSanitizer.rst b/clang/docs/UndefinedBehaviorSanitizer.rst
index f98eda1e9399c..49dded2d5db88 100644
--- a/clang/docs/UndefinedBehaviorSanitizer.rst
+++ b/clang/docs/UndefinedBehaviorSanitizer.rst
@@ -390,6 +390,20 @@ 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
+``no_wrap`` 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(no_wrap)))``
>From b87713533421d14511d19f193e0fb15349a5fc30 Mon Sep 17 00:00:00 2001
From: Justin Stitt <justinstitt at google.com>
Date: Thu, 24 Jul 2025 15:12:50 -0700
Subject: [PATCH 06/21] update mangling
Signed-off-by: Justin Stitt <justinstitt at google.com>
---
clang/lib/AST/ItaniumMangle.cpp | 8 +++++++-
clang/test/CodeGen/overflow-behavior-types-promotions.cpp | 4 ++--
clang/test/CodeGen/overflow-behavior-types.cpp | 2 +-
3 files changed, 10 insertions(+), 4 deletions(-)
diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp
index a7696dd6d4554..e5d057040d88c 100644
--- a/clang/lib/AST/ItaniumMangle.cpp
+++ b/clang/lib/AST/ItaniumMangle.cpp
@@ -4609,7 +4609,13 @@ void CXXNameMangler::mangleType(const PipeType *T) {
}
void CXXNameMangler::mangleType(const OverflowBehaviorType *T) {
- Out << "U9Obt_";
+ // Vender-extended type mangling for OverflowBehaviorType
+ // <type> ::= U <behavior> <underlying_type>
+ if (T->isWrapKind()) {
+ Out << "U11ObtWrap_";
+ } else {
+ Out << "U13ObtNoWrap_";
+ }
mangleType(T->getUnderlyingType());
}
diff --git a/clang/test/CodeGen/overflow-behavior-types-promotions.cpp b/clang/test/CodeGen/overflow-behavior-types-promotions.cpp
index fd8622e88b44f..795439d2578c3 100644
--- a/clang/test/CodeGen/overflow-behavior-types-promotions.cpp
+++ b/clang/test/CodeGen/overflow-behavior-types-promotions.cpp
@@ -10,7 +10,7 @@ typedef int __nowrap nowrap_int;
typedef unsigned int __wrap u_wrap_int;
typedef unsigned int __nowrap u_nowrap_int;
-// CHECK-LABEL: define {{.*}} @_Z30conditional_operator_promotionbU9Obt_cU9Obt_ii
+// CHECK-LABEL: define {{.*}} @_Z30conditional_operator_promotionbU11ObtWrap_cU13ObtNoWrap_ii
void conditional_operator_promotion(bool cond, wrap_char w, nowrap_int nw, int i) {
// OBT wins over regular integer.
// CHECK: cond.end:
@@ -31,7 +31,7 @@ void conditional_operator_promotion(bool cond, wrap_char w, nowrap_int nw, int i
(void)(r2 + 2147483647);
}
-// CHECK-LABEL: define {{.*}} @_Z20promotion_rules_testU9Obt_iU9Obt_iU9Obt_jU9Obt_j
+// CHECK-LABEL: define {{.*}} @_Z20promotion_rules_testU11ObtWrap_iU13ObtNoWrap_iU11ObtWrap_jU13ObtNoWrap_j
void promotion_rules_test(wrap_int sw, nowrap_int snw, u_wrap_int uw, u_nowrap_int unw) {
// Unsigned is favored over signed for same-behavior OBTs.
// CHECK: add i32
diff --git a/clang/test/CodeGen/overflow-behavior-types.cpp b/clang/test/CodeGen/overflow-behavior-types.cpp
index e552a20a91d17..a4ae0d2378d31 100644
--- a/clang/test/CodeGen/overflow-behavior-types.cpp
+++ b/clang/test/CodeGen/overflow-behavior-types.cpp
@@ -49,7 +49,7 @@ void test_members(char some) {
(void)(foo.getA() + 1);
}
-// DEFAULT-LABEL: define {{.*}} @_Z9test_autoU9Obt_c
+// DEFAULT-LABEL: define {{.*}} @_Z9test_autoU11ObtWrap_c
void test_auto(char __wrap a) {
auto b = a;
>From eef6e7cfeb5211bc4501fe50664d32e927404fb1 Mon Sep 17 00:00:00 2001
From: Justin Stitt <justinstitt at google.com>
Date: Tue, 29 Jul 2025 13:39:31 -0700
Subject: [PATCH 07/21] better define narrowing semantics for OBTs
also fix a bug concerning -Wconstant-conversion and -Wc++11-narrowing
when mixed with OBT casts.
Signed-off-by: Justin Stitt <justinstitt at google.com>
---
clang/docs/OverflowBehaviorTypes.rst | 62 ++++++++-
clang/include/clang/Sema/Sema.h | 5 +
clang/lib/Sema/SemaChecking.cpp | 119 ++++++++++++++----
clang/lib/Sema/SemaInit.cpp | 10 ++
.../Sema/attr-overflow-behavior-constexpr.cpp | 1 -
clang/test/Sema/attr-overflow-behavior.c | 18 ++-
clang/test/Sema/attr-overflow-behavior.cpp | 23 +++-
7 files changed, 210 insertions(+), 28 deletions(-)
diff --git a/clang/docs/OverflowBehaviorTypes.rst b/clang/docs/OverflowBehaviorTypes.rst
index 41c0077737e8e..f5e7a0d75478d 100644
--- a/clang/docs/OverflowBehaviorTypes.rst
+++ b/clang/docs/OverflowBehaviorTypes.rst
@@ -73,8 +73,68 @@ Here is an example of how to use the ``overflow_behavior`` attribute with a type
semantics and won't be removed via eager compiler optimizations (like some
undefined behavior might).
+Conversion Semantics
+====================
+
Overflow behavior types are implicitly convertible to and from built-in
-integral types.
+integral types with specific semantics for warnings and constant evaluation.
+
+Constant Conversion Warnings
+----------------------------
+
+When a constant expression is converted to a narrower type that would cause
+value truncation, the ``-Wconstant-conversion`` warning behavior depends on
+the **destination type**:
+
+* **Destination is wrapping type** (``__wrap``): No warning is issued because
+ truncation with wrapping behavior is expected and well-defined.
+
+* **Destination is non-wrapping or standard type**: Warning is issued if the
+ constant value would be truncated.
+
+.. code-block:: c++
+
+ // Examples of constant conversion behavior
+ short x1 = (int __wrap)100000; // Warning: truncation to standard type
+ short __wrap x2 = (int)100000; // No warning: wrapping destination
+ short __no_wrap x3 = (int)100000; // Warning: truncation with overflow check
+ short x4 = (int __no_wrap)100000; // Warning: truncation to standard type
+
+This rule ensures that explicit use of wrapping types suppresses warnings
+only when the destination is intended to wrap, while preserving warnings
+for potentially unintended truncation to standard or non-wrapping types.
+
+C++ Narrowing Conversions
+-------------------------
+
+In C++, overflow behavior types also affect narrowing conversion diagnostics
+in brace initialization. When a constant expression would cause a narrowing
+conversion, the behavior depends on the **destination type**:
+
+* **Destination is wrapping type** (``__wrap``): No C++ narrowing conversion
+ error is issued, allowing the initialization to succeed even in ``constexpr``
+ contexts.
+
+* **Destination is non-wrapping or standard type**: C++ narrowing conversion
+ error is issued as normal.
+
+.. code-block:: c++
+
+ // C++ narrowing conversion behavior
+ constexpr short __wrap x1 = {(int)100000}; // OK: wrapping destination
+ constexpr short x2 = {(int)100000}; // Error: standard destination
+ constexpr short __no_wrap x3 = {(int)100000}; // Error: non-wrapping destination
+
+This allows ``__wrap`` types to be used seamlessly in ``constexpr`` contexts
+where truncation is intentional and expected.
+
+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.
+
+C++ Template and Overload Resolution
+-------------------------------------
Note that C++ overload set formation rules treat promotions to and from
overflow behavior types the same as normal integral promotions and conversions.
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 8dd88dcad5940..f729d7881f2de 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -2870,6 +2870,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 =
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 5bcc633cd4866..0c2143f0c5bdd 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -12180,27 +12180,8 @@ void Sema::CheckImplicitConversion(Expr *E, QualType T, SourceLocation CC,
}
}
- // Diagnose potentially problematic implicit casts from an overflow behavior
- // type to an integer type.
- if (const auto *OBT = Source->getAs<OverflowBehaviorType>()) {
- bool DiscardedDuringAssignment = false;
- if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E))
- DiscardedDuringAssignment = DRE->isOverflowBehaviorDiscarded();
-
- if (Target->isIntegerType() && !Target->isOverflowBehaviorType()) {
- // Implicit casts from unsigned wrap types to unsigned types are less
- // problematic but still warrant some diagnostic.
- if (OBT->isUnsignedIntegerType() && OBT->isWrapKind() &&
- Target->isUnsignedIntegerType())
- return DiagnoseImpCast(*this, E, T, CC,
- diag::warn_impcast_overflow_behavior_pedantic);
- if (DiscardedDuringAssignment)
- return DiagnoseImpCast(*this, E, T, CC,
- diag::warn_impcast_overflow_behavior_assignment);
- return DiagnoseImpCast(*this, E, T, CC,
- diag::warn_impcast_overflow_behavior);
- }
- }
+ if (CheckOverflowBehaviorTypeConversion(E, T, CC))
+ return;
// 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
@@ -12534,6 +12515,14 @@ 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 because wrapping behavior is defined for truncation
+ if (const auto *TargetOBT = Target->getAs<OverflowBehaviorType>()) {
+ if (TargetOBT->isWrapKind()) {
+ return;
+ }
+ }
+
// If the source is a constant, use a default-on diagnostic.
// TODO: this should happen for bitfield stores, too.
Expr::EvalResult Result;
@@ -13206,6 +13195,94 @@ 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>()) {
+
+ bool DiscardedDuringAssignment = false;
+
+ if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E))
+ DiscardedDuringAssignment = DRE->isOverflowBehaviorDiscarded();
+
+ if (Target->isIntegerType() && !Target->isOverflowBehaviorType()) {
+ // Before issuing the general OBT warning, check if this is a constant
+ // conversion that should get the more specific constant conversion
+ // warning
+ QualType UnderlyingType = OBT->getUnderlyingType();
+ IntRange SourceRange = IntRange::forTargetOfCanonicalType(
+ Context, UnderlyingType.getTypePtr());
+ IntRange TargetRange =
+ IntRange::forTargetOfCanonicalType(Context, Target.getTypePtr());
+
+ if (SourceRange.Width > TargetRange.Width) {
+ // Try to evaluate as constant - look through potential OBT cast
+ Expr::EvalResult Result;
+ bool HasConstant = false;
+
+ const Expr *ExprToEval = E;
+ if (const auto *ICE = dyn_cast<ImplicitCastExpr>(E)) {
+ if (const auto *CE = dyn_cast<CStyleCastExpr>(ICE->getSubExpr())) {
+ if (CE->getType()->getAs<OverflowBehaviorType>()) {
+ ExprToEval = CE->getSubExpr();
+ }
+ }
+ }
+
+ if (ExprToEval->EvaluateAsInt(Result, Context,
+ Expr::SE_AllowSideEffects,
+ isConstantEvaluatedContext())) {
+ HasConstant = true;
+ }
+
+ if (HasConstant) {
+ llvm::APSInt Value(32);
+ Value = Result.Val.getInt();
+
+ if (!SourceMgr.isInSystemMacro(CC)) {
+ std::string PrettySourceValue = toString(Value, 10);
+ std::string PrettyTargetValue =
+ PrettyPrintInRange(Value, TargetRange);
+
+ DiagRuntimeBehavior(
+ E->getExprLoc(), E,
+ PDiag(diag::warn_impcast_integer_precision_constant)
+ << PrettySourceValue << PrettyTargetValue << E->getType()
+ << T << E->getSourceRange() << clang::SourceRange(CC));
+ return true;
+ }
+ }
+ }
+
+ // Implicit casts from unsigned wrap types to unsigned types are less
+ // problematic but still warrant some diagnostic.
+ if (OBT->isUnsignedIntegerType() && OBT->isWrapKind() &&
+ Target->isUnsignedIntegerType()) {
+ DiagnoseImpCast(*this, E, T, CC,
+ diag::warn_impcast_overflow_behavior_pedantic);
+ return true;
+ }
+ if (DiscardedDuringAssignment) {
+ DiagnoseImpCast(*this, E, T, CC,
+ diag::warn_impcast_overflow_behavior_assignment);
+ return true;
+ }
+ DiagnoseImpCast(*this, E, T, CC, diag::warn_impcast_overflow_behavior);
+ return true;
+ }
+ }
+
+ 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/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index 60f9d449fc037..5d2e5808603f0 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -9789,6 +9789,16 @@ static void DiagnoseNarrowingInInitList(Sema &S,
case NK_Constant_Narrowing: {
// A constant value was narrowed.
+
+ // Overflow behavior destination types with a 'wrap' kind can elide
+ // narrowing diagnostics.
+ QualType DestType = EntityType.getNonReferenceType();
+ if (const auto *DestOBT = DestType->getAs<OverflowBehaviorType>()) {
+ if (DestOBT->isWrapKind()) {
+ return;
+ }
+ }
+
MakeDiag(EntityType.getNonReferenceType() != EntityType,
diag::ext_init_list_constant_narrowing,
diag::ext_init_list_constant_narrowing_const_reference,
diff --git a/clang/test/Sema/attr-overflow-behavior-constexpr.cpp b/clang/test/Sema/attr-overflow-behavior-constexpr.cpp
index a1ce02a581bb6..171c620712c99 100644
--- a/clang/test/Sema/attr-overflow-behavior-constexpr.cpp
+++ b/clang/test/Sema/attr-overflow-behavior-constexpr.cpp
@@ -21,7 +21,6 @@ void constexpr_test() {
constexpr nowrap_int min = -2147483648;
constexpr nowrap_int one_nw = 1;
- // This should fail to compile because of overflow.
constexpr nowrap_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)'}}
}
diff --git a/clang/test/Sema/attr-overflow-behavior.c b/clang/test/Sema/attr-overflow-behavior.c
index 0a97d3ee9d7c1..6558912220627 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 -verify -fsyntax-only
+// RUN: %clang_cc1 %s -Winteger-overflow -Wno-unused-value -foverflow-behavior-types -Woverflow-behavior-conversion -Wconstant-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'}}
@@ -15,6 +15,7 @@ void foo() {
(ok_wrap)2147483647 + 100; // no warn
}
+#define __wrap __attribute__((overflow_behavior(wrap)))
#define __no_wrap __attribute__((overflow_behavior(no_wrap)))
void ptr(int a) {
@@ -35,7 +36,20 @@ void imp_disc_test(unsigned __attribute__((overflow_behavior(wrap))) a) {
}
// -Wconversion for assignments that discard overflow behavior
-void assignment_disc_test(unsigned __attribute__((overflow_behavior(wrap))) a) {
+void assignment_disc(unsigned __attribute__((overflow_behavior(wrap))) a) {
int b = a; // expected-warning {{implicit conversion from '__wrap unsigned int' to 'int' during assignment discards overflow behavior}}
int c = (int)a; // OK
}
+
+void constant_conversion() {
+ short x1 = (int __wrap)100000; // expected-warning {{implicit conversion from '__wrap int' to 'short' changes value from 100000 to -31072}}
+ short __wrap x2 = (int)100000; // No warning expected
+ short __no_wrap x3 = (int)100000; // expected-warning {{implicit conversion from 'int' to '__no_wrap short' changes value from 100000 to -31072}}
+ short x4 = (int __no_wrap)100000; // expected-warning {{implicit conversion from '__no_wrap int' to 'short' changes value from 100000 to -31072}}
+
+ unsigned short __wrap ux1 = (unsigned int)100000; // No warning - wrapping expected
+ unsigned short ux2 = (unsigned int __wrap)100000; // expected-warning {{implicit conversion from '__wrap unsigned int' to 'unsigned short' changes value from 100000 to 34464}}
+ unsigned short __no_wrap ux3 = (unsigned int)100000; // expected-warning {{implicit conversion from 'unsigned int' to '__no_wrap unsigned short' changes value from 100000 to 34464}}
+ unsigned short __no_wrap ux4 = (unsigned int __no_wrap)100000; // expected-warning {{implicit conversion from '__no_wrap unsigned int' to '__no_wrap unsigned short' changes value from 100000 to 34464}}
+ unsigned short __no_wrap ux5 = (unsigned int __wrap)100000; // expected-warning {{implicit conversion from '__wrap unsigned int' to '__no_wrap unsigned short' changes value from 100000 to 34464}}
+}
diff --git a/clang/test/Sema/attr-overflow-behavior.cpp b/clang/test/Sema/attr-overflow-behavior.cpp
index 1eedd736a6b91..0fbbe58e2bcc1 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 -verify -fsyntax-only
+// RUN: %clang_cc1 %s -Winteger-overflow -Wno-unused-value -foverflow-behavior-types -Wconstant-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'}}
@@ -10,6 +10,9 @@ typedef long __attribute__((overflow_behavior(no_wrap))) ok_nowrap; // OK
typedef unsigned long __attribute__((overflow_behavior("wrap"))) str_ok_wrap; // OK
typedef char __attribute__((overflow_behavior("no_wrap"))) str_ok_nowrap; // OK
+#define __wrap __attribute__((overflow_behavior(wrap)))
+#define __no_wrap __attribute__((overflow_behavior(no_wrap)))
+
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}}
@@ -66,7 +69,6 @@ void test_overload2(wrap_int a) {
add_one(b); // expected-error {{call to 'add_one' is ambiguous}}
}
-#define __no_wrap __attribute__((overflow_behavior(no_wrap)))
void func(__no_wrap int i);
void func(int i); // Overload, not invalid redeclaration
@@ -103,7 +105,7 @@ void f(void) __attribute__((overflow_behavior(wrap))); // expected-warning {{'ov
typedef float __attribute__((overflow_behavior(wrap))) wrap_float; // expected-warning {{'overflow_behavior' attribute cannot be applied to non-integer type 'float'; attribute ignored}}
-void pointer_compatibility_test(int* i_ptr) {
+void pointer_compatibility(int* i_ptr) {
__no_wrap int* nowrap_ptr;
// static_cast should fail.
@@ -113,3 +115,18 @@ void pointer_compatibility_test(int* i_ptr) {
nowrap_ptr = reinterpret_cast<__no_wrap int*>(i_ptr);
(void)nowrap_ptr;
}
+
+void cpp_constexpr_bracket_initialization() {
+ constexpr short cx1 = {(int __wrap)100000}; // expected-error {{constant expression evaluates to 100000 which cannot be narrowed to type 'short'}}
+ // expected-warning at -1 {{implicit conversion from '__wrap int' to 'const short' changes value from 100000 to -31072}}
+ // expected-note at -2 {{insert an explicit cast to silence this issue}}
+
+ constexpr short __wrap cx2 = {(int)100000}; // OK - wrapping destination
+
+ constexpr short __no_wrap cx3 = {(int)100000}; // expected-error {{constant expression evaluates to 100000 which cannot be narrowed to type '__no_wrap short'}}
+ // expected-warning at -1 {{implicit conversion from 'int' to '__no_wrap short const' changes value from 100000 to -31072}}
+
+ constexpr short cx4 = {(int __no_wrap)100000}; // expected-error {{constant expression evaluates to 100000 which cannot be narrowed to type 'short'}}
+ // expected-warning at -1 {{implicit conversion from '__no_wrap int' to 'const short' changes value from 100000 to -31072}}
+ // expected-note at -2 {{insert an explicit cast to silence this issue}}
+}
>From f936ebacdac95af60b3aed35209819de91bf3877 Mon Sep 17 00:00:00 2001
From: Justin Stitt <justinstitt at google.com>
Date: Tue, 29 Jul 2025 15:57:49 -0700
Subject: [PATCH 08/21] reorder docs, add ubsan notes, expand on narrowing
specifically, expand upon UBSan integrations in docs and add more notes
about narrowing semantics in C and C++.
Signed-off-by: Justin Stitt <justinstitt at google.com>
---
clang/docs/OverflowBehaviorTypes.rst | 164 ++++++++++++------
.../test/CodeGen/overflow-behavior-types.cpp | 19 --
2 files changed, 112 insertions(+), 71 deletions(-)
diff --git a/clang/docs/OverflowBehaviorTypes.rst b/clang/docs/OverflowBehaviorTypes.rst
index f5e7a0d75478d..fa1753626e038 100644
--- a/clang/docs/OverflowBehaviorTypes.rst
+++ b/clang/docs/OverflowBehaviorTypes.rst
@@ -73,6 +73,63 @@ Here is an example of how to use the ``overflow_behavior`` attribute with a type
semantics and won't be removed via eager compiler optimizations (like some
undefined behavior might).
+Promotion Rules
+===============
+
+The promotion rules for overflow behavior types are designed to preserve the
+specified overflow behavior throughout an arithmetic expression. They differ
+from standard C/C++ integer promotions but in a predictable way, similar to
+how ``_Complex`` and ``_BitInt`` have their own promotion rules.
+
+The resulting type characteristics for overflow behavior types (OBTs) across a
+variety of scenarios is detailed below.
+
+* **OBT and Standard Integer Type**: In an operation involving an overflow
+ behavior type (OBT) and a standard integer type, the result will have the
+ type of the OBT, including its overflow behavior, sign, and bit-width. The
+ standard integer type is implicitly converted to match the OBT.
+
+ .. code-block:: c++
+
+ typedef char __attribute__((overflow_behavior(no_wrap))) no_wrap_char;
+ // The result of this expression is no_wrap_char.
+ no_wrap_char c;
+ unsigned long ul;
+ auto result = c + ul;
+
+* **Two OBTs of the Same Kind**: When an operation involves two OBTs of the
+ same kind (e.g., both ``wrap``), the result will have the larger of the two
+ bit-widths. If the bit-widths are the same, an unsigned type is favored over
+ a signed one.
+
+ .. code-block:: c++
+
+ typedef unsigned char __attribute__((overflow_behavior(wrap))) u8_wrap;
+ typedef unsigned short __attribute__((overflow_behavior(wrap))) u16_wrap;
+ // The result of this expression is u16_wrap.
+ u8_wrap a;
+ u16_wrap b;
+ auto result = a + b;
+
+* **Two OBTs of Different Kinds**: In an operation between a ``wrap`` and a
+ ``no_wrap`` type, a ``no_wrap`` is produced. It is recommended to avoid such
+ operations, as Clang may emit a warning for such cases in the future.
+ Regardless, the resulting type matches the bit-width, sign and behavior of
+ the ``no_wrap`` type.
+
+.. list-table:: Promotion Rules Summary
+ :widths: 30 70
+ :header-rows: 1
+
+ * - Operation Type
+ - Result Type
+ * - OBT + Standard Integer
+ - OBT type (preserves overflow behavior, sign, and bit-width)
+ * - Same Kind OBTs (both ``wrap`` or both ``no_wrap``)
+ - Larger bit-width; unsigned favored if same width
+ * - Different Kind OBTs (``wrap`` + ``no_wrap``)
+ - ``no_wrap`` type (matches ``no_wrap`` operand's characteristics)
+
Conversion Semantics
====================
@@ -86,7 +143,8 @@ When a constant expression is converted to a narrower type that would cause
value truncation, the ``-Wconstant-conversion`` warning behavior depends on
the **destination type**:
-* **Destination is wrapping type** (``__wrap``): No warning is issued because
+* **Destination is wrapping type**
+ (``__attribute__((overflow_behavior(wrap)))``): No warning is issued because
truncation with wrapping behavior is expected and well-defined.
* **Destination is non-wrapping or standard type**: Warning is issued if the
@@ -94,9 +152,12 @@ the **destination type**:
.. code-block:: c++
+ #define __wrap __attribute__((overflow_behavior(wrap)))
+ #define __no_wrap __attribute__((overflow_behavior(no_wrap)))
+
// Examples of constant conversion behavior
short x1 = (int __wrap)100000; // Warning: truncation to standard type
- short __wrap x2 = (int)100000; // No warning: wrapping destination
+ short __wrap x2 = (int)100000; // No warning: wrapping destination (also no UBSan truncation warning)
short __no_wrap x3 = (int)100000; // Warning: truncation with overflow check
short x4 = (int __no_wrap)100000; // Warning: truncation to standard type
@@ -111,7 +172,8 @@ In C++, overflow behavior types also affect narrowing conversion diagnostics
in brace initialization. When a constant expression would cause a narrowing
conversion, the behavior depends on the **destination type**:
-* **Destination is wrapping type** (``__wrap``): No C++ narrowing conversion
+* **Destination is wrapping type**
+ (``__attribute__((overflow_behavior(wrap)))``): No C++ narrowing conversion
error is issued, allowing the initialization to succeed even in ``constexpr``
contexts.
@@ -120,81 +182,79 @@ conversion, the behavior depends on the **destination type**:
.. code-block:: c++
+ #define __wrap __attribute__((overflow_behavior(wrap)))
+ #define __no_wrap __attribute__((overflow_behavior(no_wrap)))
+
// C++ narrowing conversion behavior
constexpr short __wrap x1 = {(int)100000}; // OK: wrapping destination
constexpr short x2 = {(int)100000}; // Error: standard destination
constexpr short __no_wrap x3 = {(int)100000}; // Error: non-wrapping destination
-This allows ``__wrap`` types to be used seamlessly in ``constexpr`` contexts
+This allows ``wrap`` types to be used seamlessly in ``constexpr`` contexts
where truncation is intentional and expected.
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
+discarded, which is the wrapping behavior that ``wrap`` types are
designed to handle predictably.
+UBSan Truncation Checks
+------------------------
+
+The destination type also determines UBSan's
+``implicit-signed-integer-truncation`` and
+``implicit-unsigned-integer-truncation`` behavior. Conversions to
+``__attribute__((overflow_behavior(wrap)))`` destinations suppress these UBSan
+checks since truncation is expected and well-defined for wrapping types.
+
C++ Template and Overload Resolution
-------------------------------------
-Note that C++ overload set formation rules treat promotions to and from
-overflow behavior types the same as normal integral promotions and conversions.
+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.
-Promotion Rules
-===============
+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.
-The promotion rules for overflow behavior types are designed to preserve the
-specified overflow behavior throughout an arithmetic expression. They differ
-from standard C/C++ integer promotions but in a predictable way, similar to
-how ``_Complex`` and ``_BitInt`` have their own promotion rules.
+.. code-block:: c++
-The resulting type characteristics for overflow behavior types (OBTs) across a
-variety of scenarios is detailed below.
+ void foo(__attribute__((overflow_behavior(no_wrap))) int a);
+ void foo(short a);
-* **OBT and Standard Integer Type**: In an operation involving an overflow
- behavior type (OBT) and a standard integer type, the result will have the
- type of the OBT, including its overflow behavior, sign, and bit-width. The
- standard integer type is implicitly converted to match the OBT.
+ void bar(int a) {
+ foo(a); // call to 'foo' is ambiguous
+ }
- .. code-block:: c++
+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.
- typedef char __attribute__((overflow_behavior(no_wrap))) no_wrap_char;
- // The result of this expression is no_wrap_char.
- no_wrap_char c;
- unsigned long ul;
- auto result = c + ul;
+.. code-block:: c++
-* **Two OBTs of the Same Kind**: When an operation involves two OBTs of the
- same kind (e.g., both ``wrap``), the result will have the larger of the two
- bit-widths. If the bit-widths are the same, an unsigned type is favored over
- a signed one.
+ void foo(__attribute__((overflow_behavior(no_wrap))) int a);
+ void foo(char *a);
- .. code-block:: c++
+ void bar(int a) {
+ foo(a); // picks foo(__no_wrap int)
+ }
- typedef unsigned char __attribute__((overflow_behavior(wrap))) u8_wrap;
- typedef unsigned short __attribute__((overflow_behavior(wrap))) u16_wrap;
- // The result of this expression is u16_wrap.
- u8_wrap a;
- u16_wrap b;
- auto result = a + b;
-* **Two OBTs of Different Kinds**: In an operation between a ``wrap`` and a
- ``no_wrap`` type, a ``no_wrap`` is produced. It is recommended to avoid such
- operations, as Clang may emit a warning for such cases in the future.
- Regardless, the resulting type matches the bit-width, sign and behavior of
- the ``no_wrap`` type.
+Overflow behavior types with differing kinds may also create ambiguity in
+certain contexts.
-.. list-table:: Promotion Rules Summary
- :widths: 30 70
- :header-rows: 1
+.. code-block:: c++
- * - Operation Type
- - Result Type
- * - OBT + Standard Integer
- - OBT type (preserves overflow behavior, sign, and bit-width)
- * - Same Kind OBTs (both ``wrap`` or both ``no_wrap``)
- - Larger bit-width; unsigned favored if same width
- * - Different Kind OBTs (``wrap`` + ``no_wrap``)
- - ``no_wrap`` type (matches ``no_wrap`` operand's characteristics)
+ void foo(__attribute__((overflow_behavior(no_wrap))) int a);
+ void foo(__attribute__((overflow_behavior(wrap))) int 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.
Interaction with Command-Line Flags and Sanitizer Special Case Lists
====================================================================
diff --git a/clang/test/CodeGen/overflow-behavior-types.cpp b/clang/test/CodeGen/overflow-behavior-types.cpp
index a4ae0d2378d31..527fa8c5b79e9 100644
--- a/clang/test/CodeGen/overflow-behavior-types.cpp
+++ b/clang/test/CodeGen/overflow-behavior-types.cpp
@@ -2,9 +2,6 @@
// 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
-
-// Test the __attribute__((overflow_behavior())) for C++
-
#define __wrap __attribute__((overflow_behavior(wrap)))
#define __no_wrap __attribute__((overflow_behavior(no_wrap)))
@@ -18,22 +15,6 @@ class Foo {
decltype(a) getA() const { return a; }
};
-/* define dso_local void @_Z12test_membersc(i8 noundef signext %some) #0 {
-entry:
- %some.addr = alloca i8, align 1
- %foo = alloca %class.Foo, align 8
- store i8 %some, ptr %some.addr, align 1
- %0 = load i8, ptr %some.addr, align 1
- call void @_ZN3FooC2Ec(ptr noundef nonnull align 8 dereferenceable(9) %foo, i8 noundef signext %0)
- %a = getelementptr inbounds nuw %class.Foo, ptr %foo, i32 0, i32 1
- %1 = load i8, ptr %a, align 8
- %inc = add i8 %1, 1
- store i8 %inc, ptr %a, align 8
- %call = call noundef i8 @_ZNK3Foo4getAEv(ptr noundef nonnull align 8 dereferenceable(9) %foo)
- %add = add i8 %call, 1
- ret void
-}
-*/
// DEFAULT-LABEL: define {{.*}} @_Z12test_membersc
void test_members(char some) {
>From c5cbdaf82db8b105bc1cdf82cfb65ec985d3edbe Mon Sep 17 00:00:00 2001
From: Justin Stitt <justinstitt at google.com>
Date: Fri, 15 Aug 2025 11:48:35 -0700
Subject: [PATCH 09/21] fix TreeTransform bug, add more template specialization
tests
Signed-off-by: Justin Stitt <justinstitt at google.com>
---
clang/include/clang/AST/TypeLoc.h | 12 +++-
clang/lib/Sema/TreeTransform.h | 18 ++++-
.../overflow-behavior-types-extensions.c | 6 ++
.../Sema/attr-overflow-behavior-templates.cpp | 72 +++++++++++++++++++
4 files changed, 104 insertions(+), 4 deletions(-)
create mode 100644 clang/test/Sema/attr-overflow-behavior-templates.cpp
diff --git a/clang/include/clang/AST/TypeLoc.h b/clang/include/clang/AST/TypeLoc.h
index ee9da8f24ed38..5900c7672823d 100644
--- a/clang/include/clang/AST/TypeLoc.h
+++ b/clang/include/clang/AST/TypeLoc.h
@@ -1072,7 +1072,9 @@ class BTFTagAttributedTypeLoc
QualType getInnerType() const { return getTypePtr()->getWrappedType(); }
};
-struct OverflowBehaviorLocInfo {}; // Nothing.
+struct OverflowBehaviorLocInfo {
+ SourceLocation AttrLoc;
+};
class OverflowBehaviorTypeLoc
: public ConcreteTypeLoc<UnqualTypeLoc, OverflowBehaviorTypeLoc,
@@ -1087,7 +1089,13 @@ class OverflowBehaviorTypeLoc
SourceRange getLocalSourceRange() const;
- void initializeLocal(ASTContext &Context, SourceLocation loc) {}
+ 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(); }
};
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 6ea3d72ecc681..2f5945082e6df 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -7541,8 +7541,22 @@ QualType TreeTransform<Derived>::TransformBTFTagAttributedType(
template <typename Derived>
QualType TreeTransform<Derived>::TransformOverflowBehaviorType(
TypeLocBuilder &TLB, OverflowBehaviorTypeLoc TL) {
- // The OverflowBehaviorType is available for C only.
- llvm_unreachable("Unexpected TreeTransform for OverflowBehaviorType");
+ 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>
diff --git a/clang/test/CodeGen/overflow-behavior-types-extensions.c b/clang/test/CodeGen/overflow-behavior-types-extensions.c
index 56ef12329d905..6e05a2db41161 100644
--- a/clang/test/CodeGen/overflow-behavior-types-extensions.c
+++ b/clang/test/CodeGen/overflow-behavior-types-extensions.c
@@ -18,6 +18,12 @@ int generic_selection_test_obtmatch(w_int x) {
return _Generic(x, w_int: 1, nw_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) __nowrap a, _BitInt(8) __nowrap b, _BitInt(99) __wrap c) {
// CHECK: call { i4, i1 } @llvm.sadd.with.overflow.i4(i4
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..de1e5cad06b2a
--- /dev/null
+++ b/clang/test/Sema/attr-overflow-behavior-templates.cpp
@@ -0,0 +1,72 @@
+// RUN: %clang_cc1 %s -foverflow-behavior-types -verify -fsyntax-only
+// expected-no-diagnostics
+
+#define __wrap __attribute__((overflow_behavior(wrap)))
+#define __no_wrap __attribute__((overflow_behavior(no_wrap)))
+
+template<typename T>
+constexpr int template_overload_test(T) {
+ return 10;
+}
+
+constexpr int template_overload_test(int) {
+ return 20;
+}
+
+constexpr int template_overload_test(__wrap int) {
+ return 30;
+}
+
+constexpr int template_overload_test(__no_wrap 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((__wrap int)42) == 30, "__wrap int should pick __wrap int overload");
+ static_assert(template_overload_test((__no_wrap int)42) == 40, "__no_wrap int should pick __no_wrap 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<__wrap int>::value == 0, "__wrap int should match generic when there isn't a __wrap specialization");
+ static_assert(MultiSpecTester<__no_wrap int>::value == 0, "__no_wrap int should match generic when there isn't a __no_wrap 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");
+ static_assert(only_int_template<int>((__wrap int)42) == 142, "__wrap int implicit conversion should work");
+ static_assert(only_int_template<int>((__no_wrap int)42) == 142, "__no_wrap int implicit conversion should work");
+}
+
+void simple_overload_test(int);
+void simple_overload_test(__wrap int);
+
+template<typename T>
+void simple_overload_test(T) {}
+
+void test_function_vs_template_overload() {
+ int regular = 42;
+ __wrap int wrapped = 42;
+ __no_wrap int no_wrap = 42;
+
+ simple_overload_test(regular);
+ simple_overload_test(wrapped);
+ simple_overload_test(no_wrap);
+}
>From 579a796e8ff70752c415050b9045bc8811136275 Mon Sep 17 00:00:00 2001
From: Justin Stitt <justinstitt at google.com>
Date: Fri, 15 Aug 2025 12:00:46 -0700
Subject: [PATCH 10/21] update docs, generics, C++ templates specializations
Add more info regarding C _Generic expressions, C++ template specializations
and implicit conversions stemming from assignments.
This is based on offline review from Peter Zijlstra.
Signed-off-by: Justin Stitt <justinstitt at google.com>
---
clang/docs/OverflowBehaviorTypes.rst | 101 +++++++++++++++++++++++++--
clang/lib/AST/ASTContext.cpp | 6 --
2 files changed, 95 insertions(+), 12 deletions(-)
diff --git a/clang/docs/OverflowBehaviorTypes.rst b/clang/docs/OverflowBehaviorTypes.rst
index fa1753626e038..d5fafb3bffbe5 100644
--- a/clang/docs/OverflowBehaviorTypes.rst
+++ b/clang/docs/OverflowBehaviorTypes.rst
@@ -144,11 +144,12 @@ value truncation, the ``-Wconstant-conversion`` warning behavior depends on
the **destination type**:
* **Destination is wrapping type**
- (``__attribute__((overflow_behavior(wrap)))``): No warning is issued because
- truncation with wrapping behavior is expected and well-defined.
+ (``__attribute__((overflow_behavior(wrap)))``): No warning is issued
+ regardless of compiler flags because truncation with wrapping behavior is
+ expected and well-defined.
-* **Destination is non-wrapping or standard type**: Warning is issued if the
- constant value would be truncated.
+* **Destination is non-wrapping or standard type**: Warnings are issued based
+ on compiler flags.
.. code-block:: c++
@@ -165,6 +166,38 @@ This rule ensures that explicit use of wrapping types suppresses warnings
only when the destination is intended to wrap, while preserving warnings
for potentially unintended truncation to standard or non-wrapping types.
+See below for more details specifically regarding implicit conversions due to
+assignment.
+
+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 __attribute__((overflow_behavior(wrap))) a = x; // x converted to int __attribute__((overflow_behavior(wrap)))
+
+When assigning one overflow behavior type to another, the left-hand side's type
+is always used - just like with traditional integer types.
+
+.. code-block:: c++
+
+ long __attribute__((overflow_behavior(wrap))) x = __LONG_MAX__;
+ int __attribute__((overflow_behavior(no_wrap))) a = x; // x converted to int __attribute__((overflow_behavior(no_wrap)))
+
+For the purposes of truncation warnings from UBSAN or ``-Wconversion``, the
+left-hand side's overflow behavior determines the instrumentation and
+reporting. For example, the code above would cause a ``-Wshorten-64-to-32``
+warning. Swapping the overflow behavior kinds in the above example would not
+result in a warning diagnostic as the left-hand side would be ``wraps`` which
+silences any truncation warnings.
+
C++ Narrowing Conversions
-------------------------
@@ -207,8 +240,8 @@ The destination type also determines UBSan's
``__attribute__((overflow_behavior(wrap)))`` destinations suppress these UBSan
checks since truncation is expected and well-defined for wrapping types.
-C++ Template and Overload Resolution
--------------------------------------
+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
@@ -256,6 +289,62 @@ certain contexts.
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++
+
+ typedef int __attribute__((overflow_behavior(wrap))) wrap_int;
+
+ int foo(wrap_int x) {
+ return _Generic(x, int: 1, char: 2, default: 3); // returns 3
+ }
+
+ int bar(wrap_int x) {
+ return _Generic(x, wrap_int: 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 __attribute__((overflow_behavior(wrap)))> {
+ static constexpr int value = 2; // __wrap int specialization
+ };
+
+ template<>
+ struct TypeProcessor<int __attribute__((overflow_behavior(no_wrap)))> {
+ static constexpr int value = 3; // __no_wrap 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
====================================================================
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index eb80ceed3d1a3..6079dc542dcc6 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -3797,12 +3797,6 @@ ASTContext::adjustType(QualType Orig,
adjustType(OB->getUnderlyingType(), Adjust));
}
- case Type::Elaborated: {
- const auto *ET = cast<ElaboratedType>(Orig);
- return getElaboratedType(ET->getKeyword(), ET->getQualifier(),
- adjustType(ET->getNamedType(), Adjust));
- }
-
case Type::Paren:
return getParenType(
adjustType(cast<ParenType>(Orig)->getInnerType(), Adjust));
>From f47b4df0920836d56cede5a451074a20a936d91e Mon Sep 17 00:00:00 2001
From: Justin Stitt <justinstitt at google.com>
Date: Mon, 18 Aug 2025 15:18:50 -0700
Subject: [PATCH 11/21] improve promotion rules documentation
Signed-off-by: Justin Stitt <justinstitt at google.com>
---
clang/docs/OverflowBehaviorTypes.rst | 37 ++++++++++++++++++++++------
1 file changed, 29 insertions(+), 8 deletions(-)
diff --git a/clang/docs/OverflowBehaviorTypes.rst b/clang/docs/OverflowBehaviorTypes.rst
index d5fafb3bffbe5..20b6462b8d312 100644
--- a/clang/docs/OverflowBehaviorTypes.rst
+++ b/clang/docs/OverflowBehaviorTypes.rst
@@ -92,10 +92,9 @@ variety of scenarios is detailed below.
.. code-block:: c++
typedef char __attribute__((overflow_behavior(no_wrap))) no_wrap_char;
- // The result of this expression is no_wrap_char.
no_wrap_char c;
unsigned long ul;
- auto result = c + ul;
+ auto result = c + ul; // result is no_wrap_char
* **Two OBTs of the Same Kind**: When an operation involves two OBTs of the
same kind (e.g., both ``wrap``), the result will have the larger of the two
@@ -106,16 +105,18 @@ variety of scenarios is detailed below.
typedef unsigned char __attribute__((overflow_behavior(wrap))) u8_wrap;
typedef unsigned short __attribute__((overflow_behavior(wrap))) u16_wrap;
- // The result of this expression is u16_wrap.
u8_wrap a;
u16_wrap b;
- auto result = a + b;
+ auto result = a + b; // result is u16_wrap
* **Two OBTs of Different Kinds**: In an operation between a ``wrap`` and a
- ``no_wrap`` type, a ``no_wrap`` is produced. It is recommended to avoid such
- operations, as Clang may emit a warning for such cases in the future.
- Regardless, the resulting type matches the bit-width, sign and behavior of
- the ``no_wrap`` type.
+ ``no_wrap`` type, a ``no_wrap`` type is always produced. Clang will bias the
+ ``no_wrap`` type as converting from ``wrap`` to ``no_wrap`` is not as
+ troubling as converting from ``no_wrap`` to ``wrap``, in most cases.
+ Therefore, the resulting type matches the bit-width, sign and behavior of the
+ ``no_wrap`` type. It is recommended to avoid such operations, as Clang may
+ emit a warning for these cases in the future.
+
.. list-table:: Promotion Rules Summary
:widths: 30 70
@@ -130,6 +131,26 @@ variety of scenarios is detailed below.
* - Different Kind OBTs (``wrap`` + ``no_wrap``)
- ``no_wrap`` type (matches ``no_wrap`` operand's characteristics)
+Following traditional C promotion rules for integer types often results in
+modified bit boundaries for types. Since overflow behavior types aim to make
+guarantees about the overflow behavior of a type, it is important to maintain
+consistent bit-widths across arithmetic expressions containing overflow
+behavior types. Therefore, we have the above promotion rules as they preserve
+bit-widths and overflow resolution behaviors during promotions.
+
+Practical example of the promotion rules and how they differ from traditional C
+promotion rules:
+
+.. code-block:: c++
+
+ unsigned short __attribute__((overflow_behavior(no_wrap))) a = 0; // u16 __no_wrap
+
+ // Normally, arithmetic that is less-than-int is promoted to at least int.
+ // Following traditional C promotion rules: u16 + s32 results in s32
+ // However, since `a` is an OBT, our special promotion rules apply: u16 __no_wrap + s32 results in u16 __no_wrap
+
+ a + 1; // result is short __attribute__((overflow_behavior(no_wrap)))
+
Conversion Semantics
====================
>From 9f5bd277568323fc35db701de741d8aa69be52dc Mon Sep 17 00:00:00 2001
From: Justin Stitt <justinstitt at google.com>
Date: Wed, 20 Aug 2025 09:50:36 -0700
Subject: [PATCH 12/21] use canonical type for target range
fixes a crash occurring from nested typedefs:
| typedef long s64;
| typedef s64 __attribute__((overflow_behavior(no_wrap))) nw_s64 ;
| nw_s64 a;
| void foo() { if(a) {} /*crash!*/}
Signed-off-by: Justin Stitt <justinstitt at google.com>
---
clang/lib/Sema/SemaChecking.cpp | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 0c2143f0c5bdd..516eb8031671d 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -13213,9 +13213,9 @@ bool Sema::CheckOverflowBehaviorTypeConversion(Expr *E, QualType T,
// warning
QualType UnderlyingType = OBT->getUnderlyingType();
IntRange SourceRange = IntRange::forTargetOfCanonicalType(
- Context, UnderlyingType.getTypePtr());
- IntRange TargetRange =
- IntRange::forTargetOfCanonicalType(Context, Target.getTypePtr());
+ Context, Context.getCanonicalType(UnderlyingType).getTypePtr());
+ IntRange TargetRange = IntRange::forTargetOfCanonicalType(
+ Context, Context.getCanonicalType(Target).getTypePtr());
if (SourceRange.Width > TargetRange.Width) {
// Try to evaluate as constant - look through potential OBT cast
>From d30e03e5c6c2fb14ce35ff37a7bdf861265891c8 Mon Sep 17 00:00:00 2001
From: Justin Stitt <justinstitt at google.com>
Date: Wed, 20 Aug 2025 10:07:00 -0700
Subject: [PATCH 13/21] add test for nested typedefs during implicit conversion
check
Signed-off-by: Justin Stitt <justinstitt at google.com>
---
clang/test/Sema/attr-overflow-behavior.c | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/clang/test/Sema/attr-overflow-behavior.c b/clang/test/Sema/attr-overflow-behavior.c
index 6558912220627..7eb1957d6f957 100644
--- a/clang/test/Sema/attr-overflow-behavior.c
+++ b/clang/test/Sema/attr-overflow-behavior.c
@@ -53,3 +53,12 @@ void constant_conversion() {
unsigned short __no_wrap ux4 = (unsigned int __no_wrap)100000; // expected-warning {{implicit conversion from '__no_wrap unsigned int' to '__no_wrap unsigned short' changes value from 100000 to 34464}}
unsigned short __no_wrap ux5 = (unsigned int __wrap)100000; // expected-warning {{implicit conversion from '__wrap unsigned int' to '__no_wrap unsigned short' changes value from 100000 to 34464}}
}
+
+typedef long s64_typedef1;
+typedef s64_typedef1 __attribute__((overflow_behavior(no_wrap))) 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'}}
+}
>From 7d64d02b5aee554b17c56b06517afb1ba3213268 Mon Sep 17 00:00:00 2001
From: Justin Stitt <justinstitt at google.com>
Date: Thu, 21 Aug 2025 15:19:36 -0700
Subject: [PATCH 14/21] remove superfluous constant conversion check
let CheckImplicitConversion handle the constant conversion check, only
diagnose discarded overflow behaviors in
CheckOverflowBehaviorConversion.
Signed-off-by: Justin Stitt <justinstitt at google.com>
---
clang/lib/Sema/SemaChecking.cpp | 63 ++----------------------
clang/test/Sema/attr-overflow-behavior.c | 19 +++++--
2 files changed, 20 insertions(+), 62 deletions(-)
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 516eb8031671d..2fbc5bc771d4d 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -12180,8 +12180,7 @@ void Sema::CheckImplicitConversion(Expr *E, QualType T, SourceLocation CC,
}
}
- if (CheckOverflowBehaviorTypeConversion(E, T, CC))
- return;
+ 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
@@ -13208,69 +13207,17 @@ bool Sema::CheckOverflowBehaviorTypeConversion(Expr *E, QualType T,
DiscardedDuringAssignment = DRE->isOverflowBehaviorDiscarded();
if (Target->isIntegerType() && !Target->isOverflowBehaviorType()) {
- // Before issuing the general OBT warning, check if this is a constant
- // conversion that should get the more specific constant conversion
- // warning
- QualType UnderlyingType = OBT->getUnderlyingType();
- IntRange SourceRange = IntRange::forTargetOfCanonicalType(
- Context, Context.getCanonicalType(UnderlyingType).getTypePtr());
- IntRange TargetRange = IntRange::forTargetOfCanonicalType(
- Context, Context.getCanonicalType(Target).getTypePtr());
-
- if (SourceRange.Width > TargetRange.Width) {
- // Try to evaluate as constant - look through potential OBT cast
- Expr::EvalResult Result;
- bool HasConstant = false;
-
- const Expr *ExprToEval = E;
- if (const auto *ICE = dyn_cast<ImplicitCastExpr>(E)) {
- if (const auto *CE = dyn_cast<CStyleCastExpr>(ICE->getSubExpr())) {
- if (CE->getType()->getAs<OverflowBehaviorType>()) {
- ExprToEval = CE->getSubExpr();
- }
- }
- }
-
- if (ExprToEval->EvaluateAsInt(Result, Context,
- Expr::SE_AllowSideEffects,
- isConstantEvaluatedContext())) {
- HasConstant = true;
- }
-
- if (HasConstant) {
- llvm::APSInt Value(32);
- Value = Result.Val.getInt();
-
- if (!SourceMgr.isInSystemMacro(CC)) {
- std::string PrettySourceValue = toString(Value, 10);
- std::string PrettyTargetValue =
- PrettyPrintInRange(Value, TargetRange);
-
- DiagRuntimeBehavior(
- E->getExprLoc(), E,
- PDiag(diag::warn_impcast_integer_precision_constant)
- << PrettySourceValue << PrettyTargetValue << E->getType()
- << T << E->getSourceRange() << clang::SourceRange(CC));
- return true;
- }
- }
- }
-
- // Implicit casts from unsigned wrap types to unsigned types are less
- // problematic but still warrant some diagnostic.
+ // Overflow behavior type is being stripped - issue warning
if (OBT->isUnsignedIntegerType() && OBT->isWrapKind() &&
Target->isUnsignedIntegerType()) {
DiagnoseImpCast(*this, E, T, CC,
diag::warn_impcast_overflow_behavior_pedantic);
- return true;
- }
- if (DiscardedDuringAssignment) {
+ } else if (DiscardedDuringAssignment) {
DiagnoseImpCast(*this, E, T, CC,
diag::warn_impcast_overflow_behavior_assignment);
- return true;
+ } else {
+ DiagnoseImpCast(*this, E, T, CC, diag::warn_impcast_overflow_behavior);
}
- DiagnoseImpCast(*this, E, T, CC, diag::warn_impcast_overflow_behavior);
- return true;
}
}
diff --git a/clang/test/Sema/attr-overflow-behavior.c b/clang/test/Sema/attr-overflow-behavior.c
index 7eb1957d6f957..ef82c012e5361 100644
--- a/clang/test/Sema/attr-overflow-behavior.c
+++ b/clang/test/Sema/attr-overflow-behavior.c
@@ -42,13 +42,20 @@ void assignment_disc(unsigned __attribute__((overflow_behavior(wrap))) a) {
}
void constant_conversion() {
- short x1 = (int __wrap)100000; // expected-warning {{implicit conversion from '__wrap int' to 'short' changes value from 100000 to -31072}}
+ // expected-warning at +2 {{implicit conversion from '__wrap int' to 'short' changes value from 100000 to -31072}}
+ // expected-warning at +1 {{implicit conversion from '__wrap int' to 'short' discards overflow behavior}}
+ short x1 = (int __wrap)100000;
short __wrap x2 = (int)100000; // No warning expected
- short __no_wrap x3 = (int)100000; // expected-warning {{implicit conversion from 'int' to '__no_wrap short' changes value from 100000 to -31072}}
- short x4 = (int __no_wrap)100000; // expected-warning {{implicit conversion from '__no_wrap int' to 'short' changes value from 100000 to -31072}}
+ // expected-warning at +1 {{implicit conversion from 'int' to '__no_wrap short' changes value from 100000 to -31072}}
+ short __no_wrap x3 = (int)100000;
+ // expected-warning at +2 {{implicit conversion from '__no_wrap int' to 'short' changes value from 100000 to -31072}}
+ // expected-warning at +1 {{implicit conversion from '__no_wrap int' to 'short' discards overflow behavior}}
+ short x4 = (int __no_wrap)100000;
unsigned short __wrap ux1 = (unsigned int)100000; // No warning - wrapping expected
- unsigned short ux2 = (unsigned int __wrap)100000; // expected-warning {{implicit conversion from '__wrap unsigned int' to 'unsigned short' changes value from 100000 to 34464}}
+ // expected-warning at +2 {{implicit conversion from '__wrap unsigned int' to 'unsigned short' changes value from 100000 to 34464}}
+ // expected-warning at +1 {{implicit conversion from '__wrap unsigned int' to 'unsigned short' discards overflow behavior}}
+ unsigned short ux2 = (unsigned int __wrap)100000;
unsigned short __no_wrap ux3 = (unsigned int)100000; // expected-warning {{implicit conversion from 'unsigned int' to '__no_wrap unsigned short' changes value from 100000 to 34464}}
unsigned short __no_wrap ux4 = (unsigned int __no_wrap)100000; // expected-warning {{implicit conversion from '__no_wrap unsigned int' to '__no_wrap unsigned short' changes value from 100000 to 34464}}
unsigned short __no_wrap ux5 = (unsigned int __wrap)100000; // expected-warning {{implicit conversion from '__wrap unsigned int' to '__no_wrap unsigned short' changes value from 100000 to 34464}}
@@ -62,3 +69,7 @@ void test_nested_typedef_control_flow() {
// 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 __no_wrap a) {
+ return a; // expected-warning {{implicit conversion from '__no_wrap unsigned long' to 'int' discards overflow behavior}}
+}
>From 0fb3ee13ed8c86f0c80462c8856beb2fd4962ebd Mon Sep 17 00:00:00 2001
From: Justin Stitt <justinstitt at google.com>
Date: Thu, 21 Aug 2025 17:50:52 -0700
Subject: [PATCH 15/21] add incompatible pointer OBT specialization
Signed-off-by: Justin Stitt <justinstitt at google.com>
---
clang/include/clang/Basic/DiagnosticGroups.td | 9 ++-
.../clang/Basic/DiagnosticSemaKinds.td | 15 ++++
clang/include/clang/Sema/Sema.h | 5 ++
clang/lib/Sema/SemaExpr.cpp | 21 ++++-
clang/test/Sema/attr-overflow-behavior.c | 79 ++++++++++++++++++-
5 files changed, 123 insertions(+), 6 deletions(-)
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index a3f6bf29dadd8..7f6f79e985aec 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -570,12 +570,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">;
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index a7b841e7c6ceb..8605943780347 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -9104,6 +9104,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 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/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index f729d7881f2de..fb46a9bf89878 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -733,6 +733,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
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index b32a4c6a70445..48ac6f80ae8ba 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -9142,8 +9142,21 @@ static AssignConvertType checkPointerTypesForAssignment(Sema &S,
QualType ltrans = QualType(lhptee, 0), rtrans = QualType(rhptee, 0);
if (ltrans->isOverflowBehaviorType() || rtrans->isOverflowBehaviorType()) {
- if (!S.Context.hasSameType(ltrans, rtrans))
+ 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;
+
return AssignConvertType::IncompatiblePointer;
+ }
}
if (!S.Context.typesAreCompatible(ltrans, rtrans)) {
@@ -17162,6 +17175,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*
diff --git a/clang/test/Sema/attr-overflow-behavior.c b/clang/test/Sema/attr-overflow-behavior.c
index ef82c012e5361..365b935bb002b 100644
--- a/clang/test/Sema/attr-overflow-behavior.c
+++ b/clang/test/Sema/attr-overflow-behavior.c
@@ -19,11 +19,11 @@ void foo() {
#define __no_wrap __attribute__((overflow_behavior(no_wrap)))
void ptr(int a) {
- int __no_wrap *p = &a; // expected-warning-re {{incompatible pointer types initializing '__no_wrap int *' {{.*}}of type 'int *'}}
+ int __no_wrap *p = &a; // expected-warning {{initializing '__no_wrap int *' with an expression of type 'int *' discards overflow behavior}}
}
void ptr2(__no_wrap int a) {
- int *p = &a; // expected-warning-re {{incompatible pointer types initializing 'int *' {{.*}}of type '__no_wrap int *'}}
+ int *p = &a; // expected-warning {{initializing 'int *' with an expression of type '__no_wrap int *' discards overflow behavior}}
}
@@ -73,3 +73,78 @@ void test_nested_typedef_control_flow() {
int test_discard_on_return(unsigned long __no_wrap a) {
return a; // expected-warning {{implicit conversion from '__no_wrap unsigned long' to 'int' discards overflow behavior}}
}
+
+// Test OBT pointer compatibility
+void test_obt_pointer_compatibility() {
+ unsigned long x = 42;
+ unsigned long __no_wrap y = 42;
+ unsigned long __wrap z = 42;
+
+ unsigned long *px = &x;
+ unsigned long __no_wrap *py = &y;
+ unsigned long __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 '__no_wrap unsigned long *' discards overflow behavior}}
+ px = py;
+ // expected-warning at +1 {{assigning to 'unsigned long *' from '__wrap unsigned long *' discards overflow behavior}}
+ px = pz;
+ // expected-warning at +1 {{assigning to '__no_wrap unsigned long *' from 'unsigned long *' discards overflow behavior}}
+ py = px;
+ // expected-warning at +1 {{assigning to '__wrap unsigned long *' from 'unsigned long *' discards overflow behavior}}
+ pz = px;
+ // expected-warning at +1 {{assigning to '__no_wrap unsigned long *' from '__wrap unsigned long *' discards overflow behavior}}
+ py = pz;
+ // expected-warning at +1 {{assigning to '__wrap unsigned long *' from '__no_wrap unsigned long *' discards overflow behavior}}
+ 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_no_wrap_ptr(unsigned long __no_wrap *p) {}
+ // expected-note at +1 {{passing argument to parameter 'p' here}}
+void func_takes_wrap_ptr(unsigned long __wrap *p) {}
+
+void test_function_parameters() {
+ unsigned long x = 42;
+ unsigned long __no_wrap y = 42;
+ unsigned long __wrap z = 42;
+
+ unsigned long *px = &x;
+ unsigned long __no_wrap *py = &y;
+ unsigned long __wrap *pz = &z;
+
+ // Same types - should not warn
+ func_takes_regular_ptr(px); // OK
+ func_takes_no_wrap_ptr(py); // OK
+ func_takes_wrap_ptr(pz); // OK
+
+ // Different OBT annotations - should warn but allow
+ // expected-warning at +1 {{passing '__no_wrap unsigned long *' to parameter of type 'unsigned long *' discards overflow behavior}}
+ func_takes_regular_ptr(py);
+ // expected-warning at +1 {{passing '__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 '__no_wrap unsigned long *' discards overflow behavior}}
+ func_takes_no_wrap_ptr(px);
+ // expected-warning at +1 {{passing 'unsigned long *' to parameter of type '__wrap unsigned long *' discards overflow behavior}}
+ func_takes_wrap_ptr(px);
+}
+
+// Test different underlying types - should still error
+void test_different_underlying_types() {
+ int x = 42;
+ unsigned long __no_wrap y = 42;
+
+ int *px = &x;
+ unsigned long __no_wrap *py = &y;
+
+ px = py; // expected-warning {{incompatible pointer types assigning to 'int *' from '__no_wrap unsigned long *'}}
+}
>From a213aa84ba6facfdcd8eeac30e3440b4f0f49eac Mon Sep 17 00:00:00 2001
From: Justin Stitt <justinstitt at google.com>
Date: Fri, 22 Aug 2025 09:51:09 -0700
Subject: [PATCH 16/21] add new diagnostic for conflicting behavior kinds
Signed-off-by: Justin Stitt <justinstitt at google.com>
---
.../clang/Basic/DiagnosticSemaKinds.td | 4 +++
clang/lib/Sema/SemaType.cpp | 19 +++++++++++++-
clang/test/Sema/attr-overflow-behavior.c | 25 +++++++++++++++----
3 files changed, 42 insertions(+), 6 deletions(-)
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 8605943780347..a041dab96c3b6 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -4043,6 +4043,10 @@ def warn_overflow_behavior_attribute_disabled
: Warning<"%0 attribute is ignored because it is not enabled; pass "
"-foverflow-behavior-types">,
InGroup<OverflowBehaviorAttributeIgnored>;
+def warn_conflicting_overflow_behavior_attributes
+ : Warning<"conflicting 'overflow_behavior' attributes on the same type; "
+ "'no_wrap' takes precedence over 'wrap'">,
+ InGroup<OverflowBehaviorAttributeIgnored>;
def warn_impcast_overflow_behavior_assignment
: Warning<"implicit conversion from %0 to %1 during assignment discards "
"overflow behavior">,
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index ff28507cf40cd..872eca4a5a22e 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -6715,7 +6715,24 @@ static void HandleOverflowBehaviorAttr(QualType &Type, const ParsedAttr &Attr,
return;
}
- Type = State.getOverflowBehaviorType(Kind, Type);
+ // Check for conflicting overflow behavior attributes
+ if (const auto *ExistingOBT = Type->getAs<OverflowBehaviorType>()) {
+ OverflowBehaviorType::OverflowBehaviorKind ExistingKind =
+ ExistingOBT->getBehaviorKind();
+ if (ExistingKind != Kind) {
+ // Conflicting attributes - issue warning and let no_wrap take precedence
+ S.Diag(Attr.getLoc(),
+ diag::warn_conflicting_overflow_behavior_attributes);
+ if (Kind == OverflowBehaviorType::OverflowBehaviorKind::NoWrap) {
+ // Current is no_wrap, replace existing
+ Type = State.getOverflowBehaviorType(Kind,
+ ExistingOBT->getUnderlyingType());
+ }
+ return;
+ }
+ } else {
+ Type = State.getOverflowBehaviorType(Kind, Type);
+ }
}
/// handleObjCOwnershipTypeAttr - Process an objc_ownership
diff --git a/clang/test/Sema/attr-overflow-behavior.c b/clang/test/Sema/attr-overflow-behavior.c
index 365b935bb002b..07d678cf71fee 100644
--- a/clang/test/Sema/attr-overflow-behavior.c
+++ b/clang/test/Sema/attr-overflow-behavior.c
@@ -79,16 +79,16 @@ void test_obt_pointer_compatibility() {
unsigned long x = 42;
unsigned long __no_wrap y = 42;
unsigned long __wrap z = 42;
-
+
unsigned long *px = &x;
unsigned long __no_wrap *py = &y;
unsigned long __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 '__no_wrap unsigned long *' discards overflow behavior}}
px = py;
@@ -138,8 +138,7 @@ void test_function_parameters() {
func_takes_wrap_ptr(px);
}
-// Test different underlying types - should still error
-void test_different_underlying_types() {
+void test_different_underlying_types_for_pointers() {
int x = 42;
unsigned long __no_wrap y = 42;
@@ -148,3 +147,19 @@ void test_different_underlying_types() {
px = py; // expected-warning {{incompatible pointer types assigning to 'int *' from '__no_wrap unsigned long *'}}
}
+
+// expected-warning at +1 {{conflicting 'overflow_behavior' attributes on the same type; 'no_wrap' takes precedence over 'wrap'}}
+typedef int __attribute__((overflow_behavior(wrap))) __attribute__((overflow_behavior(no_wrap))) conflicting_no_wrap_wins;
+// expected-warning at +1 {{conflicting 'overflow_behavior' attributes on the same type; 'no_wrap' takes precedence over 'wrap'}}
+typedef int __attribute__((overflow_behavior(no_wrap))) __attribute__((overflow_behavior(wrap))) conflicting_wrap_ignored;
+
+void test_conflicting_behavior_kinds() {
+ conflicting_no_wrap_wins x = 42;
+ conflicting_wrap_ignored y = 42;
+
+ int __no_wrap *px = &x;
+ int __no_wrap *py = &y;
+}
+
+typedef int __attribute__((overflow_behavior(wrap))) __attribute__((overflow_behavior(wrap))) duplicate_wrap; // no warn
+typedef int __attribute__((overflow_behavior(no_wrap))) __attribute__((overflow_behavior(no_wrap))) duplicate_no_wrap; // no warn
>From 04af8bf6e9620c71673a435d3b2eebccd7db3682 Mon Sep 17 00:00:00 2001
From: Justin Stitt <justinstitt at google.com>
Date: Mon, 25 Aug 2025 13:31:23 -0700
Subject: [PATCH 17/21] add ms mangling, better typedef merging, remove
commented code
address feedback from gh at mizvekov.
Signed-off-by: Justin Stitt <justinstitt at google.com>
---
clang/include/clang/AST/Type.h | 19 ++---------
clang/include/clang/AST/TypeLoc.h | 2 --
clang/lib/AST/ASTContext.cpp | 34 ++++++++++---------
clang/lib/AST/ASTDiagnostic.cpp | 2 --
clang/lib/AST/FormatString.cpp | 3 +-
clang/lib/AST/MicrosoftMangle.cpp | 15 +++++++-
clang/lib/AST/Type.cpp | 4 ---
clang/lib/Sema/SemaExpr.cpp | 10 +++---
.../CodeGen/mangle-ms-overflow-behavior.cpp | 12 +++++++
clang/test/Sema/attr-overflow-behavior.c | 14 +++++++-
10 files changed, 66 insertions(+), 49 deletions(-)
create mode 100644 clang/test/CodeGen/mangle-ms-overflow-behavior.cpp
diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h
index 257ec30358e78..0824a9cb9351c 100644
--- a/clang/include/clang/AST/Type.h
+++ b/clang/include/clang/AST/Type.h
@@ -3139,10 +3139,6 @@ template <> const BoundsAttributedType *Type::getAs() const;
/// sugar until it reaches an CountAttributedType or a non-sugared type.
template <> const CountAttributedType *Type::getAs() const;
-/// This will check for a OverflowBehaviorType by removing any existing
-/// sugar until it reaches an OverflowBehaviorType or a non-sugared type.
-template <> const OverflowBehaviorType *Type::getAs() const;
-
// We can do canonical leaf types faster, because we don't have to
// worry about preserving child type decoration.
#define TYPE(Class, Base)
@@ -6694,11 +6690,6 @@ class OverflowBehaviorType : public Type, public llvm::FoldingSetNode {
return BehaviorKind == OverflowBehaviorKind::NoWrap;
}
- OverflowBehaviorKind setBehaviorKind(OverflowBehaviorKind Kind) {
- BehaviorKind = Kind;
- return BehaviorKind;
- }
-
bool isSugared() const { return false; }
QualType desugar() const { return getUnderlyingType(); }
@@ -8976,10 +8967,8 @@ inline bool Type::isIntegerType() const {
!IsEnumDeclScoped(ET->getOriginalDecl());
}
- if (isOverflowBehaviorType())
- return cast<OverflowBehaviorType>(CanonicalType)
- ->getUnderlyingType()
- ->isIntegerType();
+ if (const auto *OT = dyn_cast<OverflowBehaviorType>(CanonicalType))
+ return OT->getUnderlyingType()->isIntegerType();
return isBitIntType();
}
@@ -9043,7 +9032,7 @@ inline bool Type::isScalarType() const {
isa<MemberPointerType>(CanonicalType) ||
isa<ComplexType>(CanonicalType) ||
isa<ObjCObjectPointerType>(CanonicalType) ||
- isa<OverflowBehaviorType>(CanonicalType) || isBitIntType();
+ isOverflowBehaviorType() || isBitIntType();
}
inline bool Type::isIntegralOrEnumerationType() const {
@@ -9191,8 +9180,6 @@ template <typename T> const T *Type::getAsAdjusted() const {
Ty = A->getModifiedType().getTypePtr();
else if (const auto *A = dyn_cast<BTFTagAttributedType>(Ty))
Ty = A->getWrappedType().getTypePtr();
- // else if (const auto *A = dyn_cast<OverflowBehaviorType>(Ty))
- // Ty = A->getWrappedType().getTypePtr();
else if (const auto *A = dyn_cast<HLSLAttributedResourceType>(Ty))
Ty = A->getWrappedType().getTypePtr();
else if (const auto *P = dyn_cast<ParenType>(Ty))
diff --git a/clang/include/clang/AST/TypeLoc.h b/clang/include/clang/AST/TypeLoc.h
index 5900c7672823d..1654b7554f42b 100644
--- a/clang/include/clang/AST/TypeLoc.h
+++ b/clang/include/clang/AST/TypeLoc.h
@@ -2872,8 +2872,6 @@ inline T TypeLoc::getAsAdjusted() const {
Cur = ATL.getModifiedLoc();
else if (auto ATL = Cur.getAs<BTFTagAttributedTypeLoc>())
Cur = ATL.getWrappedLoc();
- // else if (auto ATL = Cur.getAs<OverflowBehaviorTypeLoc>())
- // Cur = ATL.getWrappedLoc();
else if (auto ATL = Cur.getAs<HLSLAttributedResourceTypeLoc>())
Cur = ATL.getWrappedLoc();
else if (auto ATL = Cur.getAs<AdjustedTypeLoc>())
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 6079dc542dcc6..7f62b97a17a6d 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -5857,10 +5857,9 @@ QualType ASTContext::getOverflowBehaviorType(
QualType Canonical;
if (!Underlying.isCanonical()) {
Canonical = getOverflowBehaviorType(Kind, getCanonicalType(Underlying));
- OverflowBehaviorType *NewOBT =
+ [[maybe_unused]] OverflowBehaviorType *NewOBT =
OverflowBehaviorTypes.FindNodeOrInsertPos(ID, InsertPos);
assert(!NewOBT && "Shouldn't be in the map!");
- (void)NewOBT;
}
OverflowBehaviorType *Ty = new (*this, alignof(OverflowBehaviorType))
@@ -11820,9 +11819,8 @@ std::optional<QualType> ASTContext::tryMergeOverflowBehaviorTypes(
if (LHSOBT) {
if (RHSOBT) {
- // Both are OverflowBehaviorTypes.
if (LHSOBT->getBehaviorKind() != RHSOBT->getBehaviorKind())
- return QualType(); // Incompatible if behaviors differ.
+ return QualType();
QualType MergedUnderlying = mergeTypes(
LHSOBT->getUnderlyingType(), RHSOBT->getUnderlyingType(),
@@ -11831,15 +11829,16 @@ std::optional<QualType> ASTContext::tryMergeOverflowBehaviorTypes(
if (MergedUnderlying.isNull())
return QualType();
- // If the merged underlying type is the same as one of the original
- // underlying types, we can return the original OBT to preserve typedefs.
- if (getCanonicalType(MergedUnderlying) ==
- getCanonicalType(LHSOBT->getUnderlyingType()))
- return LHS;
- if (getCanonicalType(MergedUnderlying) ==
- getCanonicalType(RHSOBT->getUnderlyingType()))
- return RHS;
+ 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);
}
@@ -14456,10 +14455,13 @@ static QualType getCommonNonSugarTypeNode(const ASTContext &Ctx, const Type *X,
getCommonQualifier(Ctx, NX, NY, /*IsSame=*/true), NX->getIdentifier());
}
case Type::OverflowBehavior: {
- // FIXME: Should we consider both types?
- const auto *NX = cast<OverflowBehaviorType>(X);
- return Ctx.getOverflowBehaviorType(NX->getBehaviorKind(),
- NX->getUnderlyingType());
+ const auto *NX = cast<OverflowBehaviorType>(X),
+ *NY = cast<OverflowBehaviorType>(Y);
+ assert(NX->getBehaviorKind() == NY->getBehaviorKind());
+ return Ctx.getOverflowBehaviorType(
+ NX->getBehaviorKind(),
+ getCommonNonSugarTypeNode(Ctx, NX->getUnderlyingType().getTypePtr(), QX,
+ NY->getUnderlyingType().getTypePtr(), QY));
}
case Type::DependentTemplateSpecialization: {
const auto *TX = cast<DependentTemplateSpecializationType>(X),
diff --git a/clang/lib/AST/ASTDiagnostic.cpp b/clang/lib/AST/ASTDiagnostic.cpp
index 40db5d2dc6fb6..a44b82aa88fe5 100644
--- a/clang/lib/AST/ASTDiagnostic.cpp
+++ b/clang/lib/AST/ASTDiagnostic.cpp
@@ -76,8 +76,6 @@ QualType clang::desugarForDiagnostic(ASTContext &Context, QualType QT,
}
// ... or an overflow behavior type.
if (const OverflowBehaviorType *OBT = dyn_cast<OverflowBehaviorType>(Ty)) {
- if (!OBT->isSugared())
- break;
QT = OBT->desugar();
continue;
}
diff --git a/clang/lib/AST/FormatString.cpp b/clang/lib/AST/FormatString.cpp
index 89072f044032d..4be42a8b2ff27 100644
--- a/clang/lib/AST/FormatString.cpp
+++ b/clang/lib/AST/FormatString.cpp
@@ -405,8 +405,7 @@ ArgType::matchesType(ASTContext &C, QualType argTy) const {
argTy = PT->getPointeeType();
}
- if (const OverflowBehaviorType *OBT =
- dyn_cast<OverflowBehaviorType>(argTy.getCanonicalType()))
+ if (const auto *OBT = argTy->getAs<OverflowBehaviorType>())
argTy = OBT->getUnderlyingType();
switch (K) {
diff --git a/clang/lib/AST/MicrosoftMangle.cpp b/clang/lib/AST/MicrosoftMangle.cpp
index 9391879fbe1bc..6bd18444280d1 100644
--- a/clang/lib/AST/MicrosoftMangle.cpp
+++ b/clang/lib/AST/MicrosoftMangle.cpp
@@ -3782,7 +3782,20 @@ void MicrosoftCXXNameMangler::mangleType(const HLSLInlineSpirvType *T,
void MicrosoftCXXNameMangler::mangleType(const OverflowBehaviorType *T,
Qualifiers, SourceRange Range) {
- llvm_unreachable("OverflowBehaviorType uses Itanium name mangling");
+ 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("ObtNoWrap_");
+ }
+ Extra.mangleType(UnderlyingType, Range, QMM_Escape);
+
+ mangleArtificialTagType(TagTypeKind::Struct, TemplateMangling, {"__clang"});
}
// <this-adjustment> ::= <no-adjustment> | <static-adjustment> |
diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index 2977e3db6dbd3..001860848c2f2 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -649,10 +649,6 @@ template <> const CountAttributedType *Type::getAs() const {
return getAsSugar<CountAttributedType>(this);
}
-template <> const OverflowBehaviorType *Type::getAs() const {
- return getAsSugar<OverflowBehaviorType>(this);
-}
-
/// getUnqualifiedDesugaredType - Pull any qualifiers and syntactic
/// sugar off the given type. This should produce an object of the
/// same dynamic type as the canonical type.
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 48ac6f80ae8ba..57ccc6c1d0b86 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -1691,6 +1691,11 @@ QualType Sema::UsualArithmeticConversions(ExprResult &LHS, ExprResult &RHS,
if (const AtomicType *AtomicLHS = LHSType->getAs<AtomicType>())
LHSType = AtomicLHS->getValueType();
+ if (LHSType->isOverflowBehaviorType() || RHSType->isOverflowBehaviorType()) {
+ return handleOverflowBehaviorTypeConversion(
+ *this, LHS, RHS, ACK == ArithConvKind::CompAssign);
+ }
+
// If both types are identical, no conversion is needed.
if (Context.hasSameType(LHSType, RHSType)) {
return Context.getCommonSugaredType(LHSType, RHSType);
@@ -1740,11 +1745,6 @@ 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, ACK == ArithConvKind::CompAssign);
- }
-
// Finally, we have two differing integer types.
return handleIntegerConversion<doIntegralCast, doIntegralCast>(
*this, LHS, RHS, LHSType, RHSType, ACK == ArithConvKind::CompAssign);
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..da49d0c540a82
--- /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 __no_wrap __attribute__((overflow_behavior(no_wrap)))
+
+typedef int __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_no_wrap_int@@YAXU?$ObtNoWrap_ at H@__clang@@@Z"
+void test_no_wrap_int(int __no_wrap y) {}
diff --git a/clang/test/Sema/attr-overflow-behavior.c b/clang/test/Sema/attr-overflow-behavior.c
index 07d678cf71fee..ebed740bb6be0 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
+// RUN: %clang_cc1 %s -Winteger-overflow -Wno-unused-value -foverflow-behavior-types -Woverflow-behavior-conversion -Wconstant-conversion -verify -fsyntax-only -std=c11
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'}}
@@ -163,3 +163,15 @@ void test_conflicting_behavior_kinds() {
typedef int __attribute__((overflow_behavior(wrap))) __attribute__((overflow_behavior(wrap))) duplicate_wrap; // no warn
typedef int __attribute__((overflow_behavior(no_wrap))) __attribute__((overflow_behavior(no_wrap))) duplicate_no_wrap; // 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 __wrap a = 1;
+ clockid_t __wrap b = 2;
+ pid_t __wrap c = 4;
+ _Static_assert(_Generic((a + b), int __wrap: 1, default: 0), "a + b should be __wrap int");
+ _Static_assert(_Generic((a + c), pid_t __wrap: 1, default: 0), "a + c should be __wrap pid_t");
+}
>From af6395691ad98c3763d89e4794f353e77f0e3d9b Mon Sep 17 00:00:00 2001
From: Justin Stitt <justinstitt at google.com>
Date: Tue, 26 Aug 2025 10:23:41 -0700
Subject: [PATCH 18/21] treat all qualifier placements as equal for
canonicalization
Signed-off-by: Justin Stitt <justinstitt at google.com>
---
clang/lib/AST/ASTContext.cpp | 6 ++++--
clang/test/Sema/attr-overflow-behavior.cpp | 14 ++++++++++++++
2 files changed, 18 insertions(+), 2 deletions(-)
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 7f62b97a17a6d..fcf453f38fb22 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -5855,8 +5855,10 @@ QualType ASTContext::getOverflowBehaviorType(
}
QualType Canonical;
- if (!Underlying.isCanonical()) {
- Canonical = getOverflowBehaviorType(Kind, getCanonicalType(Underlying));
+ if (!Underlying.isCanonical() || Underlying.hasLocalQualifiers()) {
+ SplitQualType canonSplit = getCanonicalType(Underlying).split();
+ Canonical = getOverflowBehaviorType(Kind, QualType(canonSplit.Ty, 0));
+ Canonical = getQualifiedType(Canonical, canonSplit.Quals);
[[maybe_unused]] OverflowBehaviorType *NewOBT =
OverflowBehaviorTypes.FindNodeOrInsertPos(ID, InsertPos);
assert(!NewOBT && "Shouldn't be in the map!");
diff --git a/clang/test/Sema/attr-overflow-behavior.cpp b/clang/test/Sema/attr-overflow-behavior.cpp
index 0fbbe58e2bcc1..3e368283921a3 100644
--- a/clang/test/Sema/attr-overflow-behavior.cpp
+++ b/clang/test/Sema/attr-overflow-behavior.cpp
@@ -130,3 +130,17 @@ void cpp_constexpr_bracket_initialization() {
// expected-warning at -1 {{implicit conversion from '__no_wrap int' to '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 __wrap const_int_wrap;
+ typedef int __wrap const int_wrap_const;
+ typedef int __no_wrap const int_no_wrap_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_no_wrap_const));
+}
>From 1ea51b8d1fced1cc9c9ca2d398c137b13f266bda Mon Sep 17 00:00:00 2001
From: Justin Stitt <justinstitt at google.com>
Date: Tue, 26 Aug 2025 14:41:06 -0700
Subject: [PATCH 19/21] add getCommonTypeWithQualifierLifting and better handle
scoped enums
also add tests and remove diagnostic desugaring for OBTs.
Signed-off-by: Justin Stitt <justinstitt at google.com>
---
clang/lib/AST/ASTContext.cpp | 30 +++++++++++------
clang/lib/AST/ASTDiagnostic.cpp | 5 ---
clang/lib/Sema/SemaOverload.cpp | 10 +++++-
clang/test/SemaCXX/sugar-common-types.cpp | 41 ++++++++++++++++++++++-
4 files changed, 68 insertions(+), 18 deletions(-)
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index fcf453f38fb22..9000797864a13 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -14043,23 +14043,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) {
@@ -14462,8 +14469,8 @@ static QualType getCommonNonSugarTypeNode(const ASTContext &Ctx, const Type *X,
assert(NX->getBehaviorKind() == NY->getBehaviorKind());
return Ctx.getOverflowBehaviorType(
NX->getBehaviorKind(),
- getCommonNonSugarTypeNode(Ctx, NX->getUnderlyingType().getTypePtr(), QX,
- NY->getUnderlyingType().getTypePtr(), QY));
+ getCommonTypeWithQualifierLifting(Ctx, NX->getUnderlyingType(),
+ NY->getUnderlyingType(), QX, QY));
}
case Type::DependentTemplateSpecialization: {
const auto *TX = cast<DependentTemplateSpecializationType>(X),
@@ -14814,7 +14821,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/ASTDiagnostic.cpp b/clang/lib/AST/ASTDiagnostic.cpp
index a44b82aa88fe5..d7fd411ab464c 100644
--- a/clang/lib/AST/ASTDiagnostic.cpp
+++ b/clang/lib/AST/ASTDiagnostic.cpp
@@ -74,11 +74,6 @@ QualType clang::desugarForDiagnostic(ASTContext &Context, QualType QT,
QT = AT->desugar();
continue;
}
- // ... or an overflow behavior type.
- if (const OverflowBehaviorType *OBT = dyn_cast<OverflowBehaviorType>(Ty)) {
- QT = OBT->desugar();
- continue;
- }
// Desugar FunctionType if return type or any parameter type should be
// desugared. Preserve nullability attribute on desugared types.
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index e9a8dd5d433a5..88f75dec50dbb 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -2873,8 +2873,16 @@ bool Sema::IsOverflowBehaviorTypeConversion(QualType FromType,
if (!getLangOpts().OverflowBehaviorTypes)
return false;
- if (FromType->isOverflowBehaviorType() && !ToType->isOverflowBehaviorType())
+ 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->getOriginalDecl()->getDefinitionOrSelf();
+ if (ToED->isScoped())
+ return false;
+ }
return true;
+ }
if (!FromType->isOverflowBehaviorType() && ToType->isOverflowBehaviorType())
return true;
diff --git a/clang/test/SemaCXX/sugar-common-types.cpp b/clang/test/SemaCXX/sugar-common-types.cpp
index dd5fc4a654795..7fc7a29ccc9e2 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 '__wrap B1')}}
+ N tb = b;
+ // expected-error at -1 {{cannot initialize a variable of type 'N' with an lvalue of type 'WrapB1_2' (aka '__wrap B1')}}
+ N tc = 0 ? a : b;
+ // expected-error at -1 {{cannot initialize a variable of type 'N' with an lvalue of type '__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 '__wrap X1')}}
+ N tb = b;
+ // expected-error at -1 {{cannot initialize a variable of type 'N' with an lvalue of type 'WrapY1' (aka '__wrap Y1')}}
+ N tc = 0 ? a : b;
+ // expected-error at -1 {{cannot initialize a variable of type 'N' with an lvalue of type '__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 '__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 '__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 '__wrap B1 const volatile'}}
+ } // namespace balanced_qualifiers
+} // namespace overflow_behavior_types
+
namespace member_pointers {
template <class T> struct W {
X1 a;
>From 8c875bb1750c1f5fb1ac37ead58fb046501dd9f2 Mon Sep 17 00:00:00 2001
From: Justin Stitt <justinstitt at google.com>
Date: Wed, 27 Aug 2025 14:55:58 -0700
Subject: [PATCH 20/21] simplify
-Wimplicit-overflow-behavior-conversion-assignment diagnostics
cleanups:
* move flag to ExprBits from DeclRefExprBits.
* add new diagnostic group -Wimplicit-overflow-behavior-conversion-assignment.
* expand docs
based on feedback from gh at efriedma-quic:
* add comments explaining isOverflowBehaviorDiscarded.
* remove leftover code
Signed-off-by: Justin Stitt <justinstitt at google.com>
---
clang/docs/OverflowBehaviorTypes.rst | 26 ++++++++++++++++++-
clang/include/clang/AST/Expr.h | 20 ++++++++------
clang/include/clang/AST/Stmt.h | 8 +++---
clang/include/clang/Basic/DiagnosticGroups.td | 13 ++++++----
.../clang/Basic/DiagnosticSemaKinds.td | 2 +-
clang/lib/CodeGen/CGExprScalar.cpp | 13 +---------
clang/lib/Sema/SemaChecking.cpp | 11 +++-----
clang/lib/Sema/SemaDecl.cpp | 4 +--
clang/test/Sema/attr-overflow-behavior.c | 4 +--
9 files changed, 57 insertions(+), 44 deletions(-)
diff --git a/clang/docs/OverflowBehaviorTypes.rst b/clang/docs/OverflowBehaviorTypes.rst
index 20b6462b8d312..f101c4004a50e 100644
--- a/clang/docs/OverflowBehaviorTypes.rst
+++ b/clang/docs/OverflowBehaviorTypes.rst
@@ -443,7 +443,8 @@ integer type.
}
This warning group includes
-``-Wimplicit-overflow-behavior-conversion`` and
+``-Wimplicit-overflow-behavior-conversion``, which includes
+``-Wimplicit-overflow-behavior-conversion-assignment`` and
``-Wimplicit-overflow-behavior-conversion-pedantic``.
.. note::
@@ -505,6 +506,29 @@ integer type.
}
+-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. This is a
+subset of the more general ``-Wimplicit-overflow-behavior-conversion`` warning,
+allowing developers to control assignment-specific warnings separately.
+
+.. code-block:: c++
+
+ typedef int __attribute__((overflow_behavior(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 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).
+
-Wimplicit-overflow-behavior-conversion-pedantic
------------------------------------------------
diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h
index d4c5e730e6ea7..0f2b3c8345675 100644
--- a/clang/include/clang/AST/Expr.h
+++ b/clang/include/clang/AST/Expr.h
@@ -124,6 +124,7 @@ class Expr : public ValueStmt {
ExprBits.Dependent = 0;
ExprBits.ValueKind = VK;
ExprBits.ObjectKind = OK;
+ ExprBits.IsOverflowBehaviorDiscarded = false;
assert(ExprBits.ObjectKind == OK && "truncated kind");
setType(T);
}
@@ -255,6 +256,17 @@ class Expr : public ValueStmt {
/// in C++11 onwards. This applies to certain forms of volatile glvalues.
bool isReadIfDiscardedInCPlusPlus11() const;
+ /// Does this Expr refer to an expression whose type is an
+ /// OverflowBehaviorType but is assigned to a variable with a type that isn't
+ /// an OverflowBehaviorType?
+ bool isOverflowBehaviorDiscarded() const {
+ return ExprBits.IsOverflowBehaviorDiscarded;
+ }
+
+ void setOverflowBehaviorDiscarded(bool Set) {
+ ExprBits.IsOverflowBehaviorDiscarded = Set;
+ }
+
/// isUnusedResultAWarning - Return true if this immediate expression should
/// be warned about if the result is unused. If so, fill in expr, location,
/// and ranges with expr to warn on and source locations/ranges appropriate
@@ -1480,14 +1492,6 @@ class DeclRefExpr final
return DeclRefExprBits.IsImmediateEscalating;
}
- bool isOverflowBehaviorDiscarded() const {
- return DeclRefExprBits.IsOverflwBehaviorDiscarded;
- }
-
- void setOverflowBehaviorDiscarded(bool Set) {
- DeclRefExprBits.IsOverflwBehaviorDiscarded = Set;
- }
-
void setIsImmediateEscalating(bool Set) {
DeclRefExprBits.IsImmediateEscalating = Set;
}
diff --git a/clang/include/clang/AST/Stmt.h b/clang/include/clang/AST/Stmt.h
index 42469c43cd845..08876c5d7d7bc 100644
--- a/clang/include/clang/AST/Stmt.h
+++ b/clang/include/clang/AST/Stmt.h
@@ -362,8 +362,11 @@ class alignas(void *) Stmt {
unsigned ObjectKind : 3;
LLVM_PREFERRED_TYPE(ExprDependence)
unsigned Dependent : llvm::BitWidth<ExprDependence>;
+
+ LLVM_PREFERRED_TYPE(bool)
+ unsigned IsOverflowBehaviorDiscarded : 1;
};
- enum { NumExprBits = NumStmtBits + 5 + llvm::BitWidth<ExprDependence> };
+ enum { NumExprBits = NumStmtBits + 6 + llvm::BitWidth<ExprDependence> };
class ConstantExprBitfields {
friend class ASTStmtReader;
@@ -450,9 +453,6 @@ class alignas(void *) Stmt {
LLVM_PREFERRED_TYPE(bool)
unsigned IsImmediateEscalating : 1;
- LLVM_PREFERRED_TYPE(bool)
- unsigned IsOverflwBehaviorDiscarded : 1;
-
/// The location of the declaration name itself.
SourceLocation Loc;
};
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index 7f6f79e985aec..a480fdba9c0f8 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -131,14 +131,17 @@ def ImplicitFloatConversion : DiagGroup<"implicit-float-conversion",
[ImplicitIntFloatConversion,
ObjCSignedCharBoolImplicitFloatConversion]>;
def ImplicitFixedPointConversion : DiagGroup<"implicit-fixed-point-conversion">;
-def ImplicitOverflowBehaviorConversion
- : DiagGroup<"implicit-overflow-behavior-conversion">;
+def ImplicitOverflowBehaviorConversionAssignment
+ : DiagGroup<"implicit-overflow-behavior-conversion-assignment">;
def ImplicitOverflowBehaviorConversionPedantic
: DiagGroup<"implicit-overflow-behavior-conversion-pedantic">;
-def OverflowBehaviorConversion
- : DiagGroup<"overflow-behavior-conversion",
- [ImplicitOverflowBehaviorConversion,
+def ImplicitOverflowBehaviorConversion
+ : DiagGroup<"implicit-overflow-behavior-conversion",
+ [ImplicitOverflowBehaviorConversionAssignment,
ImplicitOverflowBehaviorConversionPedantic]>;
+def OverflowBehaviorConversion
+ : DiagGroup<
+ "overflow-behavior-conversion", [ImplicitOverflowBehaviorConversion]>;
def FloatOverflowConversion : DiagGroup<"float-overflow-conversion">;
def FloatZeroConversion : DiagGroup<"float-zero-conversion">;
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index a041dab96c3b6..6c4d6da2ddb5d 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -4050,7 +4050,7 @@ def warn_conflicting_overflow_behavior_attributes
def warn_impcast_overflow_behavior_assignment
: Warning<"implicit conversion from %0 to %1 during assignment discards "
"overflow behavior">,
- InGroup<ImplicitOverflowBehaviorConversion>,
+ InGroup<ImplicitOverflowBehaviorConversionAssignment>,
DefaultIgnore;
def warn_impcast_overflow_behavior_pedantic
: Warning<"implicit conversion from %0 to %1 discards overflow behavior">,
diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp
index ae51517c62bd2..5185b5926bd3f 100644
--- a/clang/lib/CodeGen/CGExprScalar.cpp
+++ b/clang/lib/CodeGen/CGExprScalar.cpp
@@ -3230,18 +3230,7 @@ ScalarExprEmitter::EmitScalarPrePostIncDec(const UnaryOperator *E, LValue LV,
} else if (type->isSignedIntegerOrEnumerationType() ||
type->isUnsignedIntegerType()) {
value = EmitIncDecConsiderOverflowBehavior(E, value, isInc);
- }
- // else if (E->canOverflow() && type->isSignedIntegerOrEnumerationType()) {
- // 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 {
+ } else {
llvm::Value *amt = llvm::ConstantInt::get(value->getType(), amount, true);
value = Builder.CreateAdd(value, amt, isInc ? "inc" : "dec");
}
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 2fbc5bc771d4d..57bfac7acaae9 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -12515,7 +12515,8 @@ void Sema::CheckImplicitConversion(Expr *E, QualType T, SourceLocation CC,
if (LikelySourceRange->Width > TargetRange.Width) {
// Check if target is a wrapping OBT - if so, don't warn about constant
- // conversion because wrapping behavior is defined for truncation
+ // 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;
@@ -13200,19 +13201,13 @@ bool Sema::CheckOverflowBehaviorTypeConversion(Expr *E, QualType T,
QualType Target = T;
if (const auto *OBT = Source->getAs<OverflowBehaviorType>()) {
-
- bool DiscardedDuringAssignment = false;
-
- if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E))
- DiscardedDuringAssignment = DRE->isOverflowBehaviorDiscarded();
-
if (Target->isIntegerType() && !Target->isOverflowBehaviorType()) {
// Overflow behavior type is being stripped - issue warning
if (OBT->isUnsignedIntegerType() && OBT->isWrapKind() &&
Target->isUnsignedIntegerType()) {
DiagnoseImpCast(*this, E, T, CC,
diag::warn_impcast_overflow_behavior_pedantic);
- } else if (DiscardedDuringAssignment) {
+ } else if (E->isOverflowBehaviorDiscarded()) {
DiagnoseImpCast(*this, E, T, CC,
diag::warn_impcast_overflow_behavior_assignment);
} else {
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 6513fc6260af6..6be0b38d25c97 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -13722,9 +13722,7 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init, bool DirectInit) {
if (!VDecl->getType()->isOverflowBehaviorType() &&
!Init->getType().isNull() && Init->getType()->isOverflowBehaviorType()) {
- if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Init)) {
- DRE->setOverflowBehaviorDiscarded(true);
- }
+ Init->setOverflowBehaviorDiscarded(true);
}
// C++11 [decl.spec.auto]p6. Deduce the type which 'auto' stands in for.
diff --git a/clang/test/Sema/attr-overflow-behavior.c b/clang/test/Sema/attr-overflow-behavior.c
index ebed740bb6be0..2f12d22373dc6 100644
--- a/clang/test/Sema/attr-overflow-behavior.c
+++ b/clang/test/Sema/attr-overflow-behavior.c
@@ -43,13 +43,13 @@ void assignment_disc(unsigned __attribute__((overflow_behavior(wrap))) a) {
void constant_conversion() {
// expected-warning at +2 {{implicit conversion from '__wrap int' to 'short' changes value from 100000 to -31072}}
- // expected-warning at +1 {{implicit conversion from '__wrap int' to 'short' discards overflow behavior}}
+ // expected-warning at +1 {{implicit conversion from '__wrap int' to 'short' during assignment discards overflow behavior}}
short x1 = (int __wrap)100000;
short __wrap x2 = (int)100000; // No warning expected
// expected-warning at +1 {{implicit conversion from 'int' to '__no_wrap short' changes value from 100000 to -31072}}
short __no_wrap x3 = (int)100000;
// expected-warning at +2 {{implicit conversion from '__no_wrap int' to 'short' changes value from 100000 to -31072}}
- // expected-warning at +1 {{implicit conversion from '__no_wrap int' to 'short' discards overflow behavior}}
+ // expected-warning at +1 {{implicit conversion from '__no_wrap int' to 'short' during assignment discards overflow behavior}}
short x4 = (int __no_wrap)100000;
unsigned short __wrap ux1 = (unsigned int)100000; // No warning - wrapping expected
>From 4f3062dca4f7627b04bceb456b5ad4d0a8d68eee Mon Sep 17 00:00:00 2001
From: Justin Stitt <justinstitt at google.com>
Date: Wed, 27 Aug 2025 15:44:20 -0700
Subject: [PATCH 21/21] add docs and tests for OBT format string interactions
Signed-off-by: Justin Stitt <justinstitt at google.com>
---
clang/docs/OverflowBehaviorTypes.rst | 33 +++++++
.../attr-overflow-behavior-format-strings.c | 93 +++++++++++++++++++
2 files changed, 126 insertions(+)
create mode 100644 clang/test/Sema/attr-overflow-behavior-format-strings.c
diff --git a/clang/docs/OverflowBehaviorTypes.rst b/clang/docs/OverflowBehaviorTypes.rst
index f101c4004a50e..469f581dd38c9 100644
--- a/clang/docs/OverflowBehaviorTypes.rst
+++ b/clang/docs/OverflowBehaviorTypes.rst
@@ -550,6 +550,39 @@ explicit ``overflow_behavior`` attribute.
// [-Wimplicit-overflow-behavior-conversion-pedantic]
}
+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.
+
+.. code-block:: c++
+
+ #include <cstdio>
+
+ typedef int __attribute__((overflow_behavior(wrap))) wrap_int;
+ typedef unsigned int __attribute__((overflow_behavior(no_wrap))) 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 __attribute__((overflow_behavior(wrap)))`` is fully compatible with
+``%d``, ``%i``, ``%x``, etc., just like a regular ``int`` would be.
+
-Woverflow-behavior-attribute-ignored
-------------------------------------
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..40ef80e3fad47
--- /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 __no_wrap __attribute__((overflow_behavior(no_wrap)))
+
+typedef int __wrap wrap_int;
+typedef int __no_wrap nowrap_int;
+typedef unsigned int __wrap wrap_uint;
+typedef unsigned int __no_wrap nowrap_uint;
+typedef short __wrap wrap_short;
+typedef long __no_wrap nowrap_long;
+
+void test_printf_compatibility() {
+ wrap_int wi = 42;
+ nowrap_int ni = 42;
+ wrap_uint wu = 42U;
+ nowrap_uint nu = 42U;
+ wrap_short ws = 42;
+ nowrap_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;
+ nowrap_int ni;
+ wrap_uint wu;
+ nowrap_uint nu;
+ wrap_short ws;
+ nowrap_long nl;
+
+ // These should all work without warnings - pointers to OBTs should be treated as pointers to underlying types
+ scanf("%d", &wi);
+ scanf("%d", &ni);
+ scanf("%u", &wu);
+ scanf("%u", &nu);
+ scanf("%hd", &ws);
+ scanf("%ld", &nl);
+ scanf("%x", &wu);
+ scanf("%o", &nu);
+
+ scanf("%s", &wi); // expected-warning{{format specifies type 'char *' but the argument has type 'wrap_int *'}}
+ scanf("%f", &wi); // expected-warning{{format specifies type 'float *' but the argument has type 'wrap_int *'}}
+ scanf("%ld", &wi); // expected-warning{{format specifies type 'long *' but the argument has type 'wrap_int *'}}
+}
+
+void test_mixed_formats() {
+ wrap_int wi = 42;
+ int regular_int = 42;
+
+ printf("%d + %d = %d", wi, regular_int, wi + regular_int);
+ scanf("%d %d", &wi, ®ular_int);
+}
+
+typedef unsigned char __wrap wrap_byte;
+typedef long long __no_wrap 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); // expected-warning{{format specifies type 'int' but the argument has type 'unsigned char'}}
+ printf("%d", sll); // expected-warning{{format specifies type 'int' but the argument has type 'long long'}}
+}
More information about the cfe-commits
mailing list