diff --git a/clang/docs/OverflowBehaviorTypes.rst b/clang/docs/OverflowBehaviorTypes.rst new file mode 100644 index 0000000000000..5fe5112bf451c --- /dev/null +++ b/clang/docs/OverflowBehaviorTypes.rst @@ -0,0 +1,584 @@ +===================== +OverflowBehaviorTypes +===================== + +.. contents:: + :local: + +Introduction +============ + +Clang provides overflow behavior types that allow developers to have fine-grained control +over the overflow behavior of integer types. Overflow behavior can be specified using +either attribute syntax or keyword syntax to control how arithmetic operations on a given integer +type should behave upon overflow. This is particularly useful for projects that +need to balance performance and safety, allowing developers to enable or +disable overflow checks for specific types. + +Overflow behavior types can be enabled using the compiler option +``-foverflow-behavior-types``. + +There are two syntax options for specifying overflow behavior: + +**Attribute syntax:** + +.. code-block:: c++ + + __attribute__((overflow_behavior(behavior))) + +Where ``behavior`` can be one of the following: + +* ``wrap``: Specifies that arithmetic operations on the integer type should + wrap on overflow. This is equivalent to the behavior of ``-fwrapv``, but it + applies only to the attributed type and may be used with both signed and + unsigned types. When this is enabled, UBSan's integer overflow and integer + truncation checks (``signed-integer-overflow``, + ``unsigned-integer-overflow``, ``implicit-signed-integer-truncation``, and + ``implicit-unsigned-integer-truncation``) are suppressed for the attributed + type. Similar to ``-fwrapv``, this behavior defines wrapping behavior which + also disables eager compiler optimizations concerning undefined behaviors. + +* ``trap``: Specifies that arithmetic operations on the integer type should + be checked for overflow. When using the ``signed-integer-overflow`` sanitizer + or when using ``-ftrapv`` alongside a signed type, this is the default + behavior. Using this, one may enforce overflow checks for a type even when + ``-fwrapv`` is enabled globally. + +**Keyword syntax:** + +.. code-block:: c++ + + __ob_wrap // equivalent to __attribute__((overflow_behavior(wrap))) + __ob_trap // equivalent to __attribute__((overflow_behavior(trap))) + +Either of these spellings can be applied to ``typedef`` declarations and to +integer types directly. + +Arithmetic operations containing one or more overflow behavior types follow +standard C integer promotion and conversion rules while preserving overflow +behavior information. + +Examples +======== + +Here are examples using both syntax options: + +**Using attribute syntax with a typedef:** + +.. code-block:: c++ + + typedef unsigned int __attribute__((overflow_behavior(trap))) non_wrapping_uint; + + non_wrapping_uint add_one(non_wrapping_uint a) { + return a + 1; // Overflow is checked for this operation. + } + +**Using keyword syntax with a typedef:** + +.. code-block:: c++ + + typedef unsigned int __ob_trap non_wrapping_uint; + + non_wrapping_uint add_one(non_wrapping_uint a) { + return a + 1; // Overflow is checked for this operation. + } + +**Using attribute syntax with a type directly:** + +.. code-block:: c++ + + int mul_alot(int n) { + int __attribute__((overflow_behavior(wrap))) a = n; + return a * 1337; // Potential overflow is not checked and is well-defined + } + +**Using keyword syntax with a type directly:** + +.. code-block:: c++ + + int mul_alot(int n) { + int __ob_wrap a = n; + return a * 1337; // Potential overflow is not checked and is well-defined + } + +"Well-defined" overflow is consistent with two's complement wrap-around +semantics and won't be removed via eager compiler optimizations (like some +undefined behavior might). + +Promotion Rules +=============== + +Overflow behavior types (OBTs) follow the traditional C integer promotion and +conversion rules while propagating overflow behavior qualifiers through implicit +casts. This approach ensures compatibility with existing C semantics while +maintaining overflow behavior information throughout arithmetic expressions. + +The resulting type characteristics for overflow behavior types (OBTs) across a +variety of scenarios is detailed below. + +* **OBT and Standard Integer Type**: The result follows standard C conversion + rules, with the OBT qualifier applied to the standard result type. + + .. code-block:: c++ + + typedef char __ob_trap trap_char; + trap_char c; + unsigned long ul; + auto result = c + ul; // result is __ob_trap unsigned long + +* **Two OBTs with Same Behavior**: When both operands have the same overflow + behavior, the result follows standard C arithmetic conversions with that + behavior applied. + + .. code-block:: c++ + + typedef unsigned char __ob_wrap u8_wrap; + typedef unsigned short __ob_wrap u16_wrap; + u8_wrap a; + u16_wrap b; + auto result = a + b; // result is __ob_wrap int (C promotes both to int) + +* **Two OBTs with Different Behaviors**: ``trap`` behavior dominates ``wrap`` + behavior. The result follows standard C arithmetic conversions with ``trap`` + behavior applied. + + .. code-block:: c++ + + typedef unsigned short __ob_wrap u16_wrap; + typedef int __ob_trap int_trap; + u16_wrap a; + int_trap b; + auto result = a + b; // result is __ob_trap int (C promotes u16->int, dominance gives trap) + + +.. list-table:: Promotion Rules Summary + :widths: 30 70 + :header-rows: 1 + + * - Operation Type + - Result Type + * - OBT + Standard Integer + - Standard C conversion result with OBT qualifier applied + * - Same Kind OBTs (both ``wrap`` or both ``trap``) + - Standard C conversion result with common overflow behavior + * - Different Kind OBTs (``wrap`` + ``trap``) + - Standard C conversion result with ``trap`` behavior (dominance) + +**Overflow Behavior Dominance Rules:** + +1. If either operand has ``trap`` behavior → result has ``trap`` behavior +2. If either operand has ``wrap`` behavior (and neither has ``trap`) → result has ``wrap`` behavior +3. Otherwise → no overflow behavior annotation + +This model preserves traditional C semantics while ensuring overflow behavior +information is correctly propagated through arithmetic expressions. + +Conversion Semantics +==================== + +Overflow behavior types are implicitly convertible to and from built-in +integral types with specific semantics for warnings and constant evaluation. + +Truncation Semantics +-------------------- + +Truncation and overflow are related and are both often desirable in some +contexts and undesirable in others. To provide control over these behaviors, +overflow behavior types may also be used to control truncation instrumentation +at the type level. + +Note that implicit integer truncation is not an undefined behavior in C. Due to +this, overflow behavior types make no special guarantees about whether implicit +integer truncation is defined or not -- the behavior is simply always defined. +Use of overflow behavior types in these contexts only control instrumentation +related to truncation. + +When an overflow behavior type is involved as the source or destination type of +truncation, instrumentation checks behave as follows: + +* **One or more types is 'trap'**: Truncation checks are inserted and may issue + a trap or sanitizer warning based on compiler settings. + +* **One or more types is 'wrap'**: No truncation checks are added regardless of + compiler flags because truncation with wrapping behavior well-defined. If any + of the types is ``trap`` then the truncation rules match the behavior + mentioned above. + +* **Both types are standard integer types**: Behaviors surrounding standard + integer types is unchanged. + +.. code-block:: c++ + + void foo(char a, int __ob_trap b) { + a = b; // truncation checks are inserted, may trap + } + + void bar(char a, int __ob_wrap b) { + a = b; // sanitizer truncation checks disallowed + } + +Note that truncation itself is a form of overflow behavior - when a value +is too large to fit in the destination type, the high-order bits are +discarded, which is the wrapping behavior that ``wrap`` types are +designed to handle predictably. + +Implicit Conversions Due to Assignment +-------------------------------------- + +Like with the basic integral types in C and C++, types on the right-hand side +of an assignment may be implicitly converted to match the left-hand side. + +All built-in integral types can be implicitly converted to an overflow behavior +version. + +.. code-block:: c++ + + char x = 1; + int __ob_wrap a = x; // x converted to __ob_wrap int + +Assigning one overflow behavior type to another is legal only when the two +type's have matching overflow behavior kinds (i.e., `wrap` and `wrap` or `trap` +and `trap`). Assigning overflow behavior types with differing behavior kinds +will result in an error. + +.. code-block:: c++ + + long __ob_wrap x = __LONG_MAX__; + int __ob_trap a = x; // x converted to int __ob_trap + +C++ Overload Resolution +----------------------- + +For the purposes of C++ overload set formation, promotions or conversions to +and from overflow behavior types are of the same rank as normal integer +promotions and conversions. These rules have implications during overload +candidate selection which may lead to unexpected but correct ambiguity errors. + +The example below shows potential ambiguity as matching just the underlying +type of an OBT parameter is not enough to precisely pick an overload candidate. + +.. code-block:: c++ + + void foo(int __ob_trap a); + void foo(short a); + + void bar(int a) { + foo(a); // call to 'foo' is ambiguous + } + +Most integral types can be implicitly converted to match OBT parameter types +and this can be done unambiguously in certain cases. Especially, when all other +candidates are not implicitly convertible. + +.. code-block:: c++ + + void foo(int __ob_trap a); + void foo(char *a); + + void bar(int a) { + foo(a); // picks foo(__ob_trap int) + } + + +Overflow behavior types with differing kinds may also create ambiguity in +certain contexts. + +.. code-block:: c++ + + void foo(int __ob_trap a); + void foo(int __ob_wrap a); + + void bar(int a) { + foo(a); // call to 'foo' is ambiguous + } + +Overflow behavior types may also be used as template parameters and used within +C ``_Generic`` expressions. + +C _Generic Expressions +---------------------- + +Overflow behavior types may be used within C ``_Generic`` expressions. + +Overflow behavior types do not match against their underlying types within C +``_Generic`` expressions. This means that an OBT will not be considered +equivalent to its base type for generic selection purposes. OBTs will match +against exact types considering bitwidth, signedness and overflow +behavior kind. + +.. code-block:: c++ + + int foo(int __ob_wrap x) { + return _Generic(x, int: 1, char: 2, default: 3); // returns 3 + } + + int bar(int __ob_wrap x) { + return _Generic(x, int __ob_wrap: 1, int: 2, default: 3); // returns 1 + } + + +C++ Template Specializations +----------------------------- + +Like with ``_Generic``, each OBT is treated as a distinct type for template +specialization purposes, enabling precise type-based template selection. + +.. code-block:: c++ + + template + struct TypeProcessor { + static constexpr int value = 0; // default case + }; + + template<> + struct TypeProcessor { + static constexpr int value = 1; // int specialization + }; + + template<> + struct TypeProcessor { + static constexpr int value = 2; // __ob_wrap int specialization + }; + + template<> + struct TypeProcessor { + static constexpr int value = 3; // __ob_trap int specialization + }; + +When no exact template specialization exists for an OBT, it falls back to the +default template rather than matching the underlying type specialization, +maintaining type safety and avoiding unexpected behavior. + +Interaction with Command-Line Flags and Sanitizer Special Case Lists +==================================================================== + +The ``overflow_behavior`` attribute interacts with sanitizers, ``-ftrapv``, +``-fwrapv``, and Sanitizer Special Case Lists (SSCL) by wholly overriding these +global flags. The following table summarizes the interactions: + +.. list-table:: Overflow Behavior Precedence + :widths: 15 15 15 15 20 15 + :header-rows: 1 + + * - Behavior + - Default(No Flags) + - -ftrapv + - -fwrapv + - Sanitizers + - SSCL + * - ``overflow_behavior(wrap)`` + - Wraps + - Wraps + - Wraps + - No report + - Overrides SSCL + * - ``overflow_behavior(trap)`` + - Traps + - Traps + - Traps + - Reports + - Overrides SSCL + +It is important to note the distinction between signed and unsigned types. For +unsigned integers, which wrap on overflow by default, ``overflow_behavior(trap)`` +is particularly useful for enabling overflow checks. For signed integers, whose +overflow behavior is undefined by default, ``overflow_behavior(wrap)`` provides +a guaranteed wrapping behavior. + +The ``overflow_behavior`` attribute can be used to override the behavior of +entries from a :doc:`SanitizerSpecialCaseList`. This is useful for allowlisting +specific types into overflow or truncation instrumentation. + +Diagnostics +=========== + +Clang provides diagnostics to help developers manage overflow behavior types. + +-Woverflow-behavior-conversion +------------------------------ + +This warning group is issued when an overflow behavior type is implicitly +converted to a standard integer type, which may lead to the loss of the +specified overflow behavior. + +.. code-block:: c++ + + typedef int __ob_wrap wrapping_int; + + void some_function(int); + + void another_function(wrapping_int w) { + some_function(w); // warning: implicit conversion from 'wrapping_int' to + // 'int' discards overflow behavior + } + +To fix this, you can explicitly cast the overflow behavior type to a standard +integer type. + +.. code-block:: c++ + + typedef int __ob_wrap wrapping_int; + + void some_function(int); + + void another_function(wrapping_int w) { + some_function(static_cast(w)); // OK + } + +This warning group includes +``-Wimplicit-overflow-behavior-conversion``, which includes +``-Wimplicit-overflow-behavior-conversion-assignment`` and +``-Wimplicit-overflow-behavior-conversion-pedantic``. + +.. note:: + ``-Woverflow-behavior-conversion`` is implied by ``-Wconversion``. + +-Wimplicit-overflow-behavior-conversion +--------------------------------------- + +This warning is issued when an overflow behavior type is implicitly converted +to a standard integer type as part of most conversions, which may lead to the +loss of the specified overflow behavior. This is the main warning in the +``-Woverflow-behavior-conversion`` group. + +.. code-block:: c++ + + typedef int __ob_wrap wrapping_int; + + void some_function() { + wrapping_int w = 1; + int i = w; // warning: implicit conversion from 'wrapping_int' to 'int' + // during assignment discards overflow behavior + // [-Wimplicit-overflow-behavior-conversion] + } + +Here's another example showing function parameter conversion with a ``trap`` type: + +.. code-block:: c++ + + typedef int __ob_trap safe_int; + + void bar(int x); // Function expects standard int + + void foo() { + safe_int s = 42; + bar(s); // warning: implicit conversion from 'safe_int' to 'int' + // discards overflow behavior + // [-Wimplicit-overflow-behavior-conversion] + } + +To fix this, you can explicitly cast the overflow behavior type to a standard +integer type. + +.. code-block:: c++ + + typedef int __ob_wrap wrapping_int; + typedef int __ob_trap safe_int; + + void some_function() { + wrapping_int w = 1; + int i = static_cast(w); // OK + int j = (int)w; // C-style OK + } + + void bar(int x); + + void foo() { + safe_int s = 42; + bar(static_cast(s)); // OK + } + + +-Wimplicit-overflow-behavior-conversion-assignment +-------------------------------------------------- + +This warning is issued specifically when an overflow behavior type is implicitly +converted to a standard integer type during assignment operations. 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 __ob_wrap wrapping_int; + + void some_function() { + wrapping_int w = 1; + int i = w; // warning: implicit conversion from 'wrapping_int' to 'int' + // during assignment discards overflow behavior + // [-Wimplicit-overflow-behavior-conversion-assignment] + } + +This 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 +------------------------------------------------ + +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 __ob_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] + } + +Format String Functions +======================= + +When overflow behavior types are used with format string functions (printf-family +functions like ``printf``, ``fprintf``, ``sprintf``, etc., and scanf-family +functions like ``scanf``, ``fscanf``, ``sscanf``, etc.), they are treated based +on their underlying integer types for format specifier compatibility checking. +More generally, overflow behavior types are ABI-compatible with their underlying +types when passed to any varargs function. + +.. code-block:: c++ + + #include + + typedef int __ob_wrap wrap_int; + typedef unsigned int __ob_trap nowrap_uint; + + void example() { + wrap_int wi = 42; + nowrap_uint su = 100; + + scanf("%d\n", &wi); // OK: &wi treated as int* for %d + printf("%d\n", wi); // OK: wi treated as int for %d + printf("%u\n", su); // OK: su treated as unsigned int for %u + printf("%s\n", wi); // Error: int incompatible with %s (same as regular int) + } + +This behavior ensures that overflow behavior types work seamlessly with existing +format string functions without requiring special format specifiers, while +still maintaining their overflow behavior semantics in arithmetic operations. + +The format string checker uses the underlying type to determine compatibility, +so ``int __ob_wrap`` is fully compatible with ``%d``, ``%i``, ``%x``, etc., +just like a regular ``int`` would be. + +Using With Non-Integer Types +---------------------------- + +An error is issued when attempting to create an overflow behavior type from +a non-integer type. + +.. code-block:: c++ + + typedef float __attribute__((overflow_behavior(wrap))) wrapping_float; + // error: 'overflow_behavior' attribute cannot be applied to non-integer type 'float' + + typedef struct S { int i; } __attribute__((overflow_behavior(wrap))) S_t; + // error: 'overflow_behavior' attribute cannot be applied to non-integer type 'struct S' + diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 390e0fa7a9a2b..85cf7c9124a78 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -259,7 +259,7 @@ New Compiler Flags ------------------ - New option ``-fno-sanitize-debug-trap-reasons`` added to disable emitting trap reasons into the debug info when compiling with trapping UBSan (e.g. ``-fsanitize-trap=undefined``). - New option ``-fsanitize-debug-trap-reasons=`` added to control emitting trap reasons into the debug info when compiling with trapping UBSan (e.g. ``-fsanitize-trap=undefined``). - +- New option ``-foverflow-behavior-types`` added to enable parsing of the ``overflow_behavior`` type attribute and type specifiers. Lanai Support ^^^^^^^^^^^^^^ @@ -284,6 +284,11 @@ Attribute Changes in Clang - New format attributes ``gnu_printf``, ``gnu_scanf``, ``gnu_strftime`` and ``gnu_strfmon`` are added as aliases for ``printf``, ``scanf``, ``strftime`` and ``strfmon``. (#GH16219) +- Introduced a new type attribute ``__attribute__((overflow_behavior))`` which + currently accepts either ``wrap`` or ``trap`` as an argument, enabling + type-level control over overflow behavior. There is also an accompanying type + specifier for each behavior kind via `__ob_wrap` and `__ob_trap`. + Improvements to Clang's diagnostics ----------------------------------- - Added a separate diagnostic group ``-Wfunction-effect-redeclarations``, for the more pedantic diff --git a/clang/docs/SanitizerSpecialCaseList.rst b/clang/docs/SanitizerSpecialCaseList.rst index 307c001664fba..a2d942154a830 100644 --- a/clang/docs/SanitizerSpecialCaseList.rst +++ b/clang/docs/SanitizerSpecialCaseList.rst @@ -134,6 +134,40 @@ precedence. Here are a few examples. fun:*bar fun:bad_bar=sanitize +Interaction with Overflow Behavior Types +---------------------------------------- + +The ``overflow_behavior`` attribute provides a more granular, source-level +control that takes precedence over the Sanitizer Special Case List. If a type +is given an ``overflow_behavior`` attribute, it will override any matching +``type:`` entry in a special case list. + +This allows developers to enforce a specific overflow behavior for a critical +type, even if a broader rule in the special case list would otherwise disable +instrumentation for it. + +.. code-block:: bash + + $ cat ignorelist.txt + # Disable signed overflow checks for all types by default. + [signed-integer-overflow] + type:* + + $ cat foo.c + // Force 'critical_type' to always have overflow checks, + // overriding the ignorelist. + typedef int __attribute__((overflow_behavior(trap))) critical_type; + + void foo(int x) { + critical_type a = x; + a++; // Overflow is checked here due to the 'trap' attribute. + + int b = x; + b++; // Overflow is NOT checked here due to the ignorelist. + } + +For more details on overflow behavior types, see :doc:`OverflowBehaviorTypes`. + Format ====== diff --git a/clang/docs/UndefinedBehaviorSanitizer.rst b/clang/docs/UndefinedBehaviorSanitizer.rst index 0a2d833783e57..565bb2697b156 100644 --- a/clang/docs/UndefinedBehaviorSanitizer.rst +++ b/clang/docs/UndefinedBehaviorSanitizer.rst @@ -380,6 +380,42 @@ This attribute may not be supported by other compilers, so consider using it together with ``#if defined(__clang__)``. +Disabling Overflow Instrumentation with ``__attribute__((overflow_behavior(wrap)))`` +------------------------------------------------------------------------------------ + +For more fine-grained control over how integer overflow is handled, you can use +the ``__attribute__((overflow_behavior(wrap)))`` attribute. This attribute can +be applied to ``typedef`` declarations and integer types to specify that +arithmetic operations on that type should wrap on overflow. This can be used to +disable overflow sanitization for specific types, while leaving it enabled for +all other types. + +The ``overflow_behavior`` attribute not only affects UBSan instrumentation +but also changes the fundamental overflow behavior of arithmetic operations +on the annotated type. Operations on types marked with ``wrap`` will have +well-defined wrapping semantics, while operations on types marked with +``trap`` will be checked for overflow (regardless of global flags like +``-fwrapv``). + +The attribute also affects implicit type promotion rules: when an overflow +behavior type participates in arithmetic operations with standard integer +types, the result maintains the overflow behavior type's characteristics, +including its bit-width. This means annotated types can preserve narrower +widths that would normally be promoted, allowing operations to stay within +the constraints of the smallest annotated type in the expression. + +For more information, see :doc:`OverflowBehaviorTypes`. + +Enforcing Overflow Instrumentation with ``__attribute__((overflow_behavior(trap)))`` +--------------------------------------------------------------------------------------- + +Conversely, you can use ``__attribute__((overflow_behavior(trap)))`` to +enforce overflow checks for a specific type, even when ``-fwrapv`` is enabled +globally. This is useful for ensuring that critical calculations are always +checked for overflow, regardless of the global compiler settings. + +For more information, see :doc:`OverflowBehaviorTypes`. + Suppressing Errors in Recompiled Code (Ignorelist) -------------------------------------------------- diff --git a/clang/docs/index.rst b/clang/docs/index.rst index be654af57f890..d384f477b5e6d 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 78220d4d8ff5b..07e1880cf5434 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -14,6 +14,7 @@ #ifndef LLVM_CLANG_AST_ASTCONTEXT_H #define LLVM_CLANG_AST_ASTCONTEXT_H +#include "Type.h" #include "clang/AST/ASTFwd.h" #include "clang/AST/CanonicalType.h" #include "clang/AST/CommentCommandTraits.h" @@ -291,6 +292,7 @@ class ASTContext : public RefCountedBase { mutable llvm::ContextualFoldingSet DependentBitIntTypes; mutable llvm::FoldingSet BTFTagAttributedTypes; + mutable llvm::FoldingSet OverflowBehaviorTypes; llvm::FoldingSet HLSLAttributedResourceTypes; llvm::FoldingSet HLSLInlineSpirvTypes; @@ -938,6 +940,8 @@ class ASTContext : public RefCountedBase { bool isTypeIgnoredBySanitizer(const SanitizerMask &Mask, const QualType &Ty) const; + bool isUnaryOverflowPatternExcluded(const UnaryOperator *UO); + const XRayFunctionFilter &getXRayFilter() const { return *XRayFilter; } @@ -1038,6 +1042,15 @@ class ASTContext : public RefCountedBase { 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 + 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. @@ -1913,6 +1926,13 @@ class ASTContext : public RefCountedBase { 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); @@ -2610,6 +2630,11 @@ class ASTContext : public RefCountedBase { /// types. bool areCompatibleVectorTypes(QualType FirstVec, QualType SecondVec); + /// Return true if two OverflowBehaviorTypes are compatible for assignment. + /// This checks both the underlying type compatibility and the overflow + /// behavior kind (trap vs wrap). + bool areCompatibleOverflowBehaviorTypes(QualType LHS, QualType RHS); + /// Return true if the given types are an RISC-V vector builtin type and a /// VectorType that is a fixed-length representation of the RISC-V vector /// builtin type for a specific vector-length. diff --git a/clang/include/clang/AST/ASTNodeTraverser.h b/clang/include/clang/AST/ASTNodeTraverser.h index 092160405aff4..b3309190329ec 100644 --- a/clang/include/clang/AST/ASTNodeTraverser.h +++ b/clang/include/clang/AST/ASTNodeTraverser.h @@ -447,6 +447,9 @@ class ASTNodeTraverser void VisitBTFTagAttributedType(const BTFTagAttributedType *T) { Visit(T->getWrappedType()); } + void VisitOverflowBehaviorType(const OverflowBehaviorType *T) { + Visit(T->getUnderlyingType()); + } void VisitHLSLAttributedResourceType(const HLSLAttributedResourceType *T) { QualType Contained = T->getContainedType(); if (!Contained.isNull()) diff --git a/clang/include/clang/AST/PropertiesBase.td b/clang/include/clang/AST/PropertiesBase.td index 5b10127526e4e..4581e55c28027 100644 --- a/clang/include/clang/AST/PropertiesBase.td +++ b/clang/include/clang/AST/PropertiesBase.td @@ -81,6 +81,8 @@ def AutoTypeKeyword : EnumPropertyType; def Bool : PropertyType<"bool">; def BuiltinTypeKind : EnumPropertyType<"BuiltinType::Kind">; def BTFTypeTagAttr : PropertyType<"const BTFTypeTagAttr *">; +def OverflowBehaviorKind + : EnumPropertyType<"OverflowBehaviorType::OverflowBehaviorKind">; def CallingConv : EnumPropertyType; def DeclarationName : PropertyType; def DeclarationNameKind : EnumPropertyType<"DeclarationName::NameKind">; diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h index 7a2881f6124f3..b3d185b161aba 100644 --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -1157,6 +1157,9 @@ DEF_TRAVERSE_TYPE(CountAttributedType, { DEF_TRAVERSE_TYPE(BTFTagAttributedType, { TRY_TO(TraverseType(T->getWrappedType())); }) +DEF_TRAVERSE_TYPE(OverflowBehaviorType, + { TRY_TO(TraverseType(T->getUnderlyingType())); }) + DEF_TRAVERSE_TYPE(HLSLAttributedResourceType, { TRY_TO(TraverseType(T->getWrappedType())); }) @@ -1512,6 +1515,9 @@ DEF_TRAVERSE_TYPELOC(CountAttributedType, DEF_TRAVERSE_TYPELOC(BTFTagAttributedType, { TRY_TO(TraverseTypeLoc(TL.getWrappedLoc())); }) +DEF_TRAVERSE_TYPELOC(OverflowBehaviorType, + { TRY_TO(TraverseTypeLoc(TL.getWrappedLoc())); }) + DEF_TRAVERSE_TYPELOC(HLSLAttributedResourceType, { TRY_TO(TraverseTypeLoc(TL.getWrappedLoc())); }) diff --git a/clang/include/clang/AST/TypeBase.h b/clang/include/clang/AST/TypeBase.h index 6786b2f6cbc78..5f3559bdfa341 100644 --- a/clang/include/clang/AST/TypeBase.h +++ b/clang/include/clang/AST/TypeBase.h @@ -1149,6 +1149,12 @@ class QualType { /// Returns true if it is a WebAssembly Funcref Type. bool isWebAssemblyFuncrefType() const; + /// Returns true if it is a OverflowBehaviorType of Wrap kind. + bool isWrapType() const; + + /// Returns true if it is a OverflowBehaviorType of Trap kind. + bool isTrapType() const; + // Don't promise in the API that anything besides 'const' can be // easily added. @@ -2643,6 +2649,7 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase { bool isSubscriptableVectorType() const; bool isMatrixType() const; // Matrix type. bool isConstantMatrixType() const; // Constant matrix type. + bool isOverflowBehaviorType() const; // __attribute__((no_sanitize)) bool isDependentAddressSpaceType() const; // value-dependent address space qualifier bool isObjCObjectPointerType() const; // pointer to ObjC object bool isObjCRetainableType() const; // ObjC object or block pointer @@ -6690,6 +6697,44 @@ class BTFTagAttributedType : public Type, public llvm::FoldingSetNode { } }; +class OverflowBehaviorType : public Type, public llvm::FoldingSetNode { +public: + enum OverflowBehaviorKind { Wrap, Trap }; + +private: + friend class ASTContext; // ASTContext creates these + + QualType UnderlyingType; + OverflowBehaviorKind BehaviorKind; + + OverflowBehaviorType(QualType Canon, QualType Underlying, + OverflowBehaviorKind Kind); + +public: + QualType getUnderlyingType() const { return UnderlyingType; } + OverflowBehaviorKind getBehaviorKind() const { return BehaviorKind; } + + bool isWrapKind() const { return BehaviorKind == OverflowBehaviorKind::Wrap; } + bool isTrapKind() const { return BehaviorKind == OverflowBehaviorKind::Trap; } + + bool isSugared() const { return false; } + QualType desugar() const { return getUnderlyingType(); } + + void Profile(llvm::FoldingSetNodeID &ID) { + Profile(ID, UnderlyingType, BehaviorKind); + } + + static void Profile(llvm::FoldingSetNodeID &ID, QualType Underlying, + OverflowBehaviorKind Kind) { + ID.AddPointer(Underlying.getAsOpaquePtr()); + ID.AddInteger((int)Kind); + } + + static bool classof(const Type *T) { + return T->getTypeClass() == OverflowBehavior; + } +}; + class HLSLAttributedResourceType : public Type, public llvm::FoldingSetNode { public: struct Attributes { @@ -8693,6 +8738,10 @@ inline bool Type::isConstantMatrixType() const { return isa(CanonicalType); } +inline bool Type::isOverflowBehaviorType() const { + return isa(CanonicalType); +} + inline bool Type::isDependentAddressSpaceType() const { return isa(CanonicalType); } @@ -8937,6 +8986,10 @@ inline bool Type::isIntegerType() const { return IsEnumDeclComplete(ET->getOriginalDecl()) && !IsEnumDeclScoped(ET->getOriginalDecl()); } + + if (const auto *OT = dyn_cast(CanonicalType)) + return OT->getUnderlyingType()->isIntegerType(); + return isBitIntType(); } @@ -8999,7 +9052,7 @@ inline bool Type::isScalarType() const { isa(CanonicalType) || isa(CanonicalType) || isa(CanonicalType) || - isBitIntType(); + isOverflowBehaviorType() || isBitIntType(); } inline bool Type::isIntegralOrEnumerationType() const { @@ -9011,6 +9064,9 @@ inline bool Type::isIntegralOrEnumerationType() const { if (const auto *ET = dyn_cast(CanonicalType)) return IsEnumDeclComplete(ET->getOriginalDecl()); + if (const auto *OBT = dyn_cast(CanonicalType)) + return OBT->getUnderlyingType()->isIntegralOrEnumerationType(); + return isBitIntType(); } diff --git a/clang/include/clang/AST/TypeLoc.h b/clang/include/clang/AST/TypeLoc.h index 38e8fba569396..17b001602170c 100644 --- a/clang/include/clang/AST/TypeLoc.h +++ b/clang/include/clang/AST/TypeLoc.h @@ -1082,6 +1082,34 @@ class BTFTagAttributedTypeLoc QualType getInnerType() const { return getTypePtr()->getWrappedType(); } }; +struct OverflowBehaviorLocInfo { + SourceLocation AttrLoc; +}; + +class OverflowBehaviorTypeLoc + : public ConcreteTypeLoc { +public: + TypeLoc getWrappedLoc() const { return getInnerTypeLoc(); } + + /// The no_sanitize type attribute. + OverflowBehaviorType::OverflowBehaviorKind getBehaviorKind() const { + return getTypePtr()->getBehaviorKind(); + } + + SourceRange getLocalSourceRange() const; + + void initializeLocal(ASTContext &Context, SourceLocation loc) { + setAttrLoc(loc); + } + + SourceLocation getAttrLoc() const { return getLocalData()->AttrLoc; } + + void setAttrLoc(SourceLocation loc) { getLocalData()->AttrLoc = loc; } + + QualType getInnerType() const { return getTypePtr()->getUnderlyingType(); } +}; + struct HLSLAttributedResourceLocInfo { SourceRange Range; TypeSourceInfo *ContainedTyInfo; diff --git a/clang/include/clang/AST/TypeProperties.td b/clang/include/clang/AST/TypeProperties.td index 9dc85fb88e267..576707a627149 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(node->getAttrs().ResourceClass) }]; diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 3c697ed8dd882..8cc1a6c10b4cf 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -5280,3 +5280,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 0c994e0b5ca4d..d4e0ab97b6adf 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -117,8 +117,9 @@ def ObjCSignedCharBoolImplicitIntConversion : DiagGroup<"objc-signed-char-bool-implicit-int-conversion">; def Shorten64To32 : DiagGroup<"shorten-64-to-32">; def ImplicitIntConversionOnNegation : DiagGroup<"implicit-int-conversion-on-negation">; -def ImplicitIntConversion : DiagGroup<"implicit-int-conversion", - [Shorten64To32, +def ImplicitIntConversion + : DiagGroup< + "implicit-int-conversion", [Shorten64To32, ObjCSignedCharBoolImplicitIntConversion, ImplicitIntConversionOnNegation]>; def ImplicitConstIntFloatConversion : DiagGroup<"implicit-const-int-float-conversion">; @@ -130,6 +131,17 @@ def ImplicitFloatConversion : DiagGroup<"implicit-float-conversion", [ImplicitIntFloatConversion, ObjCSignedCharBoolImplicitFloatConversion]>; def ImplicitFixedPointConversion : DiagGroup<"implicit-fixed-point-conversion">; +def ImplicitOverflowBehaviorConversionAssignment + : DiagGroup<"implicit-overflow-behavior-conversion-assignment">; +def ImplicitOverflowBehaviorConversionPedantic + : DiagGroup<"implicit-overflow-behavior-conversion-pedantic">; +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">; @@ -561,12 +573,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">; @@ -1146,23 +1161,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, @@ -1666,6 +1674,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 b157cbb0b8069..b187c6b2e4ef3 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -4058,6 +4058,38 @@ def note_cannot_use_trivial_abi_reason : Note< "it has a __weak field|it has a field of a non-trivial class type|" "it has an address-discriminated '__ptrauth' field}1">; +// OverflowBehavior attribute +def err_overflow_behavior_unknown_ident + : Error<"'%0' is not a valid argument to attribute %1, only 'wrap' and " + "'trap' are supported">; +def err_overflow_behavior_non_integer_type + : Error<"%0 %select{attribute|specifier}2 cannot be applied to " + "non-integer type '%1'">; +def warn_overflow_behavior_attribute_disabled + : Warning<"%0 attribute is ignored because it is not enabled; pass " + "-foverflow-behavior-types">, + InGroup; +def err_conflicting_overflow_behaviors + : Error<"conflicting %select{'overflow_behavior' attributes|overflow " + "behavior specification; specifier specifies '%1' but attribute " + "specifies " + "'%2'}0 on the same type">; +def err_incompatible_obt_kinds_assignment + : Error<"cannot assign between incompatible overflow behavior types " + "('%0' and '%1')">; +def warn_redundant_overflow_behaviors_mixed + : Warning<"redundant overflow behavior specification; both specifier and " + "attribute specify '%0'">, + InGroup; +def warn_impcast_overflow_behavior_assignment + : Warning<"implicit conversion from %0 to %1 during assignment discards " + "overflow behavior">, + InGroup, + DefaultIgnore; +def warn_impcast_overflow_behavior_pedantic + : Warning<"implicit conversion from %0 to %1 discards overflow behavior">, + InGroup, + DefaultIgnore; // Availability attribute def warn_availability_unknown_platform : Warning< "unknown platform %0 in availability macro">, InGroup; @@ -4339,6 +4371,10 @@ def warn_impcast_vector_scalar : Warning< def warn_impcast_complex_scalar : Warning< "implicit conversion discards imaginary component: %0 to %1">, InGroup, DefaultIgnore; +def warn_impcast_overflow_behavior + : Warning<"implicit conversion from %0 to %1 discards overflow behavior">, + InGroup, + DefaultIgnore; def err_impcast_complex_scalar : Error< "implicit conversion from %0 to %1 is not permitted in C++">; def warn_impcast_float_precision : Warning< @@ -9120,6 +9156,21 @@ def ext_typecheck_convert_discards_qualifiers : ExtWarn< "|%diff{casting $ to type $|casting between types}0,1}2" " discards qualifiers">, InGroup; +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; def err_typecheck_convert_discards_qualifiers : Error< "%select{%diff{assigning to $ from $|assigning to different types}0,1" "|%diff{passing $ to parameter of type $|" diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def index 9e850089ad87f..16d7a98d1628b 100644 --- a/clang/include/clang/Basic/LangOptions.def +++ b/clang/include/clang/Basic/LangOptions.def @@ -425,6 +425,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 41595ec2a060d..1443aec7a8dd1 100644 --- a/clang/include/clang/Basic/LangOptions.h +++ b/clang/include/clang/Basic/LangOptions.h @@ -112,6 +112,25 @@ class LangOptionsBase { SOB_Trapping }; + // Used by __attribute__((overflow_behavior())) to describe overflow behavior + // on a per-type basis. + enum OverflowBehaviorKind { + // Default C standard behavior (type dependent). + OB_Unset, + + // __attribute__((overflow_behavior("wrap"))) + OB_Wrap, + + // __attribute__((overflow_behavior("trap"))) + OB_Trap, + + // Signed types defined as wrapping via -fwrapv can still be instrumented + // by sanitizers (PR82432). This field is needed to disambiguate canonical + // wrapping type behaviors from -fwrapv behaviors. + // -fwrapv + OB_SignedAndDefined + }; + // FIXME: Unify with TUKind. enum CompilingModuleKind { /// Not compiling a module interface at all. diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def index 564d6010181cc..cc3acd62cfcf1 100644 --- a/clang/include/clang/Basic/TokenKinds.def +++ b/clang/include/clang/Basic/TokenKinds.def @@ -350,6 +350,10 @@ KEYWORD(__objc_yes , KEYALL) KEYWORD(__objc_no , KEYALL) KEYWORD(__ptrauth , KEYALL) +// Overflow behavior types +KEYWORD(__ob_wrap , KEYALL) +KEYWORD(__ob_trap , KEYALL) + // C2y UNARY_EXPR_OR_TYPE_TRAIT(_Countof, CountOf, KEYNOCXX) diff --git a/clang/include/clang/Basic/TypeNodes.td b/clang/include/clang/Basic/TypeNodes.td index db43a8529f02b..a9965a4a89aa1 100644 --- a/clang/include/clang/Basic/TypeNodes.td +++ b/clang/include/clang/Basic/TypeNodes.td @@ -110,3 +110,4 @@ def AtomicType : TypeNode; def BitIntType : TypeNode; def DependentBitIntType : TypeNode, AlwaysDependent; def PredefinedSugarType : TypeNode, NeverCanonical; +def OverflowBehaviorType : TypeNode; diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index 60c4ad408ee43..cbad1255c5c17 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -2329,6 +2329,12 @@ defm fixed_point : BoolFOption<"fixed-point", PosFlag, NegFlag, BothFlags<[], [ClangOption], " fixed point types">>; +defm overflow_behavior_types + : BoolFOption<"overflow-behavior-types", LangOpts<"OverflowBehaviorTypes">, + DefaultFalse, + PosFlag, + NegFlag, + BothFlags<[], [ClangOption], " overflow behavior types">>; def cxx_static_destructors_EQ : Joined<["-"], "fc++-static-destructors=">, Group, HelpText<"Controls which variables C++ static destructors are registered for">, Values<"all,thread-local,none">, diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h index c1a99a1fddc80..248849281e7b9 100644 --- a/clang/include/clang/Sema/DeclSpec.h +++ b/clang/include/clang/Sema/DeclSpec.h @@ -48,6 +48,7 @@ namespace clang { class ObjCDeclSpec; class Sema; class Declarator; + class OverflowBehaviorType; struct TemplateIdAnnotation; /// Represents a C++ nested-name-specifier or a global scope specifier. @@ -322,6 +323,12 @@ class DeclSpec { enum FriendSpecified : bool { No, Yes }; + enum class OverflowBehaviorState { + Unspecified, // No overflow behavior specified + Wrap, // __ob_wrap or __attribute__((overflow_behavior(wrap))) + Trap // __ob_trap or __attribute__((overflow_behavior(trap))) + }; + private: // storage-class-specifier LLVM_PREFERRED_TYPE(SCS) @@ -359,6 +366,10 @@ class DeclSpec { LLVM_PREFERRED_TYPE(TQ) unsigned TypeQualifiers : 5; // Bitwise OR of TQ. + // overflow behavior qualifiers + LLVM_PREFERRED_TYPE(OverflowBehaviorState) + unsigned OB_state : 2; + // function-specifier LLVM_PREFERRED_TYPE(bool) unsigned FS_inline_specified : 1; @@ -409,6 +420,7 @@ class DeclSpec { SourceRange TypeofParensRange; SourceLocation TQ_constLoc, TQ_restrictLoc, TQ_volatileLoc, TQ_atomicLoc, TQ_unalignedLoc; + SourceLocation OB_Loc; SourceLocation FS_inlineLoc, FS_virtualLoc, FS_explicitLoc, FS_noreturnLoc; SourceLocation FS_explicitCloseParenLoc; SourceLocation FS_forceinlineLoc; @@ -460,11 +472,12 @@ class DeclSpec { TypeSpecType(TST_unspecified), TypeAltiVecVector(false), TypeAltiVecPixel(false), TypeAltiVecBool(false), TypeSpecOwned(false), TypeSpecPipe(false), TypeSpecSat(false), ConstrainedAuto(false), - TypeQualifiers(TQ_unspecified), FS_inline_specified(false), - FS_forceinline_specified(false), FS_virtual_specified(false), - FS_noreturn_specified(false), FriendSpecifiedFirst(false), - ConstexprSpecifier( - static_cast(ConstexprSpecKind::Unspecified)), + TypeQualifiers(TQ_unspecified), + OB_state(static_cast(OverflowBehaviorState::Unspecified)), + FS_inline_specified(false), FS_forceinline_specified(false), + FS_virtual_specified(false), FS_noreturn_specified(false), + FriendSpecifiedFirst(false), ConstexprSpecifier(static_cast( + ConstexprSpecKind::Unspecified)), Attrs(attrFactory), writtenBS(), ObjCQualifiers(nullptr) {} // storage-class-specifier @@ -579,6 +592,7 @@ class DeclSpec { static const char *getSpecifierName(DeclSpec::SCS S); static const char *getSpecifierName(DeclSpec::TSCS S); static const char *getSpecifierName(ConstexprSpecKind C); + static const char *getSpecifierName(OverflowBehaviorState S); // type-qualifiers @@ -592,6 +606,25 @@ class DeclSpec { SourceLocation getPipeLoc() const { return TQ_pipeLoc; } SourceLocation getEllipsisLoc() const { return EllipsisLoc; } + // overflow behavior qualifiers + OverflowBehaviorState getOverflowBehaviorState() const { + return static_cast(OB_state); + } + bool isWrapSpecified() const { + return getOverflowBehaviorState() == OverflowBehaviorState::Wrap; + } + bool isTrapSpecified() const { + return getOverflowBehaviorState() == OverflowBehaviorState::Trap; + } + bool isOverflowBehaviorSpecified() const { + return getOverflowBehaviorState() != OverflowBehaviorState::Unspecified; + } + SourceLocation getOverflowBehaviorLoc() const { return OB_Loc; } + + bool SetOverflowBehavior(OverflowBehaviorType::OverflowBehaviorKind Kind, + SourceLocation Loc, const char *&PrevSpec, + unsigned &DiagID); + /// Clear out all of the type qualifiers. void ClearTypeQualifiers() { TypeQualifiers = 0; @@ -601,6 +634,8 @@ class DeclSpec { TQ_atomicLoc = SourceLocation(); TQ_unalignedLoc = SourceLocation(); TQ_pipeLoc = SourceLocation(); + OB_state = static_cast(OverflowBehaviorState::Unspecified); + OB_Loc = SourceLocation(); } // function-specifier diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 265462a86e405..dbd1317bfd265 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -735,6 +735,11 @@ enum class AssignConvertType { /// like address spaces. IncompatiblePointerDiscardsQualifiers, + /// IncompatiblePointerDiscardsOverflowBehavior - The assignment + /// discards overflow behavior annotations between otherwise compatible + /// pointer types. + IncompatiblePointerDiscardsOverflowBehavior, + /// IncompatibleNestedPointerAddressSpaceMismatch - The assignment /// changes address spaces in nested pointer types which is not allowed. /// For instance, converting __private int ** to __generic int ** is @@ -768,6 +773,10 @@ enum class AssignConvertType { /// object with __weak qualifier. IncompatibleObjCWeakRef, + /// IncompatibleOBTKinds - Assigning between incompatible OverflowBehaviorType + /// kinds, e.g., from __ob_trap to __ob_wrap or vice versa. + IncompatibleOBTKinds, + /// Incompatible - We reject this conversion outright, it is invalid to /// represent it in the AST. Incompatible @@ -1345,6 +1354,10 @@ class Sema final : public SemaBase { /// whether the next info diagnostic should be immediate. bool IsLastErrorImmediate = true; + /// Track if we're currently analyzing overflow behavior types in assignment + /// context. + bool InOverflowBehaviorAssignmentContext = false; + class DelayedDiagnostics; class DelayedDiagnosticsState { @@ -2880,6 +2893,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 = @@ -10103,6 +10121,18 @@ class Sema final : public SemaBase { /// floating-point or integral promotion. bool IsComplexPromotion(QualType FromType, QualType ToType); + /// IsOverflowBehaviorTypePromotion - Determines whether the conversion from + /// FromType to ToType involves an OverflowBehaviorType FromType being + /// promoted to an OverflowBehaviorType ToType which has a larger bitwidth. + /// If so, returns true and sets FromType to ToType. + bool IsOverflowBehaviorTypePromotion(QualType FromType, QualType ToType); + + /// IsOverflowBehaviorTypeConversion - Determines whether the conversion from + /// FromType to ToType necessarily involves both an OverflowBehaviorType and + /// a non-OverflowBehaviorType. If so, returns true and sets FromType to + /// ToType. + bool IsOverflowBehaviorTypeConversion(QualType FromType, QualType ToType); + /// IsPointerConversion - Determines whether the conversion of the /// expression From, which has the (possibly adjusted) type FromType, /// can be converted to the type ToType via a pointer conversion (C++ diff --git a/clang/include/clang/Serialization/TypeBitCodes.def b/clang/include/clang/Serialization/TypeBitCodes.def index d6c484563409c..9f1da65e0f940 100644 --- a/clang/include/clang/Serialization/TypeBitCodes.def +++ b/clang/include/clang/Serialization/TypeBitCodes.def @@ -69,5 +69,6 @@ TYPE_BIT_CODE(HLSLAttributedResource, HLSLRESOURCE_ATTRIBUTED, 59) TYPE_BIT_CODE(HLSLInlineSpirv, HLSL_INLINE_SPIRV, 60) TYPE_BIT_CODE(PredefinedSugar, PREDEFINED_SUGAR, 61) TYPE_BIT_CODE(SubstBuiltinTemplatePack, SUBST_BUILTIN_TEMPLATE_PACK, 62) +TYPE_BIT_CODE(OverflowBehavior, OVERFLOWBEHAVIOR, 63) #undef TYPE_BIT_CODE diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index a8b41ba18fa01..17e7a968d14d4 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -782,6 +782,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() != 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, @@ -1925,6 +1961,11 @@ bool ASTContext::isPromotableIntegerType(QualType T) const { return true; } + // OverflowBehaviorTypes are promotable if their underlying type is promotable + if (const auto *OBT = T->getAs()) { + return isPromotableIntegerType(OBT->getUnderlyingType()); + } + return false; } @@ -2469,6 +2510,10 @@ TypeInfo ASTContext::getTypeInfoImpl(const Type *T) const { return getTypeInfo( cast(T)->getWrappedType().getTypePtr()); + case Type::OverflowBehavior: + return getTypeInfo( + cast(T)->getUnderlyingType().getTypePtr()); + case Type::HLSLAttributedResource: return getTypeInfo( cast(T)->getWrappedType().getTypePtr()); @@ -3502,6 +3547,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: @@ -3647,6 +3695,12 @@ ASTContext::adjustType(QualType Orig, adjustType(BTFT->getWrappedType(), Adjust)); } + case Type::OverflowBehavior: { + const auto *OB = dyn_cast(Orig); + return getOverflowBehaviorType(OB->getBehaviorKind(), + adjustType(OB->getUnderlyingType(), Adjust)); + } + case Type::Paren: return getParenType( adjustType(cast(Orig)->getInnerType(), Adjust)); @@ -4214,6 +4268,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 @@ -5679,6 +5734,51 @@ QualType ASTContext::getBTFTagAttributedType(const BTFTypeTagAttr *BTFAttr, return QualType(Ty, 0); } +QualType ASTContext::getOverflowBehaviorType(const OverflowBehaviorAttr *Attr, + QualType Underlying) const { + IdentifierInfo *II = Attr->getBehaviorKind(); + StringRef IdentName = II->getName(); + OverflowBehaviorType::OverflowBehaviorKind Kind; + if (IdentName == "wrap") { + Kind = OverflowBehaviorType::OverflowBehaviorKind::Wrap; + } else if (IdentName == "trap") { + Kind = OverflowBehaviorType::OverflowBehaviorKind::Trap; + } else { + return Underlying; + } + + return getOverflowBehaviorType(Kind, Underlying); +} + +QualType ASTContext::getOverflowBehaviorType( + OverflowBehaviorType::OverflowBehaviorKind Kind, + QualType Underlying) const { + llvm::FoldingSetNodeID ID; + OverflowBehaviorType::Profile(ID, Underlying, Kind); + void *InsertPos = nullptr; + + if (OverflowBehaviorType *OBT = + OverflowBehaviorTypes.FindNodeOrInsertPos(ID, InsertPos)) { + return QualType(OBT, 0); + } + + QualType Canonical; + if (!Underlying.isCanonical() || Underlying.hasLocalQualifiers()) { + SplitQualType canonSplit = getCanonicalType(Underlying).split(); + Canonical = getOverflowBehaviorType(Kind, QualType(canonSplit.Ty, 0)); + Canonical = getQualifiedType(Canonical, canonSplit.Quals); + assert(!OverflowBehaviorTypes.FindNodeOrInsertPos(ID, InsertPos) && + "Shouldn't be in the map"); + } + + OverflowBehaviorType *Ty = new (*this, alignof(OverflowBehaviorType)) + OverflowBehaviorType(Canonical, Underlying, Kind); + + Types.push_back(Ty); + OverflowBehaviorTypes.InsertNode(Ty, InsertPos); + return QualType(Ty, 0); +} + QualType ASTContext::getHLSLAttributedResourceType( QualType Wrapped, QualType Contained, const HLSLAttributedResourceType::Attributes &Attrs) { @@ -8098,6 +8198,9 @@ unsigned ASTContext::getIntegerRank(const Type *T) const { if (const auto *EIT = dyn_cast(T)) return 0 + (EIT->getNumBits() << 3); + if (const auto *OBT = dyn_cast(T)) + return getIntegerRank(OBT->getUnderlyingType().getTypePtr()); + switch (cast(T)->getKind()) { default: llvm_unreachable("getIntegerRank(): not a built-in integer"); case BuiltinType::Bool: @@ -8214,6 +8317,14 @@ QualType ASTContext::getPromotedIntegerType(QualType Promotable) const { if (const auto *ED = Promotable->getAsEnumDecl()) return ED->getPromotionType(); + // OverflowBehaviorTypes promote their underlying type and preserve OBT + // qualifier. + if (const auto *OBT = Promotable->getAs()) { + QualType PromotedUnderlying = + getPromotedIntegerType(OBT->getUnderlyingType()); + return getOverflowBehaviorType(OBT->getBehaviorKind(), PromotedUnderlying); + } + if (const auto *BT = Promotable->getAs()) { // C++ [conv.prom]: A prvalue of type char16_t, char32_t, or wchar_t // (3.9.1) can be converted to a prvalue of the first of the following @@ -9577,6 +9688,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: @@ -10529,6 +10641,26 @@ bool ASTContext::areCompatibleVectorTypes(QualType FirstVec, return false; } +bool ASTContext::areCompatibleOverflowBehaviorTypes(QualType LHS, + QualType RHS) { + const auto *LHSOBT = LHS->getAs(); + const auto *RHSOBT = RHS->getAs(); + + // If neither type is an OBT, they're compatible (not our concern) + if (!LHSOBT && !RHSOBT) + return true; + + // If only one is an OBT, allow the assignment. The normal type checking will + // handle underlying type compatibility + if (!LHSOBT || !RHSOBT) + return true; + + if (LHSOBT->getBehaviorKind() != RHSOBT->getBehaviorKind()) + return false; + + return true; +} + /// getRVVTypeSize - Return RVV vector register size. static uint64_t getRVVTypeSize(ASTContext &Context, const BuiltinType *Ty) { assert(Ty->isRVVVLSBuiltinType() && "Invalid RVV Type"); @@ -11591,6 +11723,48 @@ QualType ASTContext::mergeTagDefinitions(QualType LHS, QualType RHS) { return Ctx.IsEquivalent(LHS, RHS) ? LHS : QualType{}; } +std::optional ASTContext::tryMergeOverflowBehaviorTypes( + QualType LHS, QualType RHS, bool OfBlockPointer, bool Unqualified, + bool BlockReturnType, bool IsConditionalOperator) { + const auto *LHSOBT = LHS->getAs(); + const auto *RHSOBT = RHS->getAs(); + + if (!LHSOBT && !RHSOBT) + return std::nullopt; + + if (LHSOBT) { + if (RHSOBT) { + if (LHSOBT->getBehaviorKind() != RHSOBT->getBehaviorKind()) + return QualType(); + + QualType MergedUnderlying = mergeTypes( + LHSOBT->getUnderlyingType(), RHSOBT->getUnderlyingType(), + OfBlockPointer, Unqualified, BlockReturnType, IsConditionalOperator); + + if (MergedUnderlying.isNull()) + return QualType(); + + if (getCanonicalType(LHSOBT) == getCanonicalType(RHSOBT)) { + if (LHSOBT->getUnderlyingType() == RHSOBT->getUnderlyingType()) + return getCommonSugaredType(LHS, RHS); + return getOverflowBehaviorType( + LHSOBT->getBehaviorKind(), + getCanonicalType(LHSOBT->getUnderlyingType())); + } + + // For different underlying types that successfully merge, wrap the + // merged underlying type with the common overflow behavior + return getOverflowBehaviorType(LHSOBT->getBehaviorKind(), + MergedUnderlying); + } + return mergeTypes(LHSOBT->getUnderlyingType(), RHS, OfBlockPointer, + Unqualified, BlockReturnType, IsConditionalOperator); + } + + return mergeTypes(LHS, RHSOBT->getUnderlyingType(), OfBlockPointer, + Unqualified, BlockReturnType, IsConditionalOperator); +} + QualType ASTContext::mergeTypes(QualType LHS, QualType RHS, bool OfBlockPointer, bool Unqualified, bool BlockReturnType, bool IsConditionalOperator) { @@ -11611,6 +11785,11 @@ QualType ASTContext::mergeTypes(QualType LHS, QualType RHS, bool OfBlockPointer, if (LHSRefTy || RHSRefTy) return {}; + if (std::optional MergedOBT = + tryMergeOverflowBehaviorTypes(LHS, RHS, OfBlockPointer, Unqualified, + BlockReturnType, IsConditionalOperator)) + return *MergedOBT; + if (Unqualified) { LHS = LHS.getUnqualifiedType(); RHS = RHS.getUnqualifiedType(); @@ -11733,6 +11912,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: @@ -13779,23 +13959,30 @@ static QualType getCommonElementType(const ASTContext &Ctx, const T *X, return Ctx.getCommonSugaredType(X->getElementType(), Y->getElementType()); } -template -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 +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 static QualType getCommonPointeeType(const ASTContext &Ctx, const T *X, const T *Y) { @@ -14197,6 +14384,15 @@ static QualType getCommonNonSugarTypeNode(const ASTContext &Ctx, const Type *X, getCommonTypeKeyword(NX, NY, /*IsSame=*/true), getCommonQualifier(Ctx, NX, NY, /*IsSame=*/true), NX->getIdentifier()); } + case Type::OverflowBehavior: { + const auto *NX = cast(X), + *NY = cast(Y); + assert(NX->getBehaviorKind() == NY->getBehaviorKind()); + return Ctx.getOverflowBehaviorType( + NX->getBehaviorKind(), + getCommonTypeWithQualifierLifting(Ctx, NX->getUnderlyingType(), + NY->getUnderlyingType(), QX, QY)); + } case Type::UnaryTransform: { const auto *TX = cast(X), *TY = cast(Y); @@ -14270,6 +14466,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) @@ -14530,7 +14727,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(SX.Ty); + bool KeepCommonQualifiers = + Unqualified || isa(SX.Ty); if (SX.Ty != SY.Ty) { // The canonical nodes differ. Build a common canonical node out of the two, diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index f43fa8c90ad3b..4b70c02a9d38a 100644 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -1985,6 +1985,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 155734679b2da..de2263e32e7b6 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(T1)->getUnderlyingType(), + cast(T2)->getUnderlyingType())) + return false; + break; + case Type::HLSLAttributedResource: if (!IsStructurallyEquivalent( Context, cast(T1)->getWrappedType(), diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 618e1636e9e53..9d18fb225426d 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -2886,7 +2886,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) @@ -13368,6 +13368,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; @@ -16166,7 +16167,8 @@ bool IntExprEvaluator::VisitUnaryOperator(const UnaryOperator *E) { return false; if (!Result.isInt()) return Error(E); const APSInt &Value = Result.getInt(); - if (Value.isSigned() && Value.isMinSignedValue() && E->canOverflow()) { + if (Value.isSigned() && Value.isMinSignedValue() && E->canOverflow() && + !E->getType().isWrapType()) { if (Info.checkingForUndefinedBehavior()) Info.Ctx.getDiagnostics().Report(E->getExprLoc(), diag::warn_integer_constant_overflow) diff --git a/clang/lib/AST/FormatString.cpp b/clang/lib/AST/FormatString.cpp index d4cb89b43ae87..5f48e759b833b 100644 --- a/clang/lib/AST/FormatString.cpp +++ b/clang/lib/AST/FormatString.cpp @@ -405,6 +405,9 @@ ArgType::matchesType(ASTContext &C, QualType argTy) const { argTy = PT->getPointeeType(); } + if (const auto *OBT = argTy->getAs()) + 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 844db79f18a4a..7dadd9438286e 100644 --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -2411,6 +2411,7 @@ bool CXXNameMangler::mangleUnresolvedTypeOrSimpleId(QualType Ty, case Type::Paren: case Type::Attributed: case Type::BTFTagAttributed: + case Type::OverflowBehavior: case Type::HLSLAttributedResource: case Type::HLSLInlineSpirv: case Type::Auto: @@ -4582,6 +4583,17 @@ void CXXNameMangler::mangleType(const PipeType *T) { Out << "8ocl_pipe"; } +void CXXNameMangler::mangleType(const OverflowBehaviorType *T) { + // Vender-extended type mangling for OverflowBehaviorType + // ::= U + if (T->isWrapKind()) { + Out << "U8ObtWrap_"; + } else { + Out << "U8ObtTrap_"; + } + mangleType(T->getUnderlyingType()); +} + void CXXNameMangler::mangleType(const BitIntType *T) { // 5.1.5.2 Builtin types // ::= DB _ diff --git a/clang/lib/AST/MicrosoftMangle.cpp b/clang/lib/AST/MicrosoftMangle.cpp index 8cbc72b1db735..c504fa78446fb 100644 --- a/clang/lib/AST/MicrosoftMangle.cpp +++ b/clang/lib/AST/MicrosoftMangle.cpp @@ -3785,6 +3785,24 @@ void MicrosoftCXXNameMangler::mangleType(const HLSLInlineSpirvType *T, llvm_unreachable("HLSL uses Itanium name mangling"); } +void MicrosoftCXXNameMangler::mangleType(const OverflowBehaviorType *T, + Qualifiers, SourceRange Range) { + QualType UnderlyingType = T->getUnderlyingType(); + + llvm::SmallString<64> TemplateMangling; + llvm::raw_svector_ostream Stream(TemplateMangling); + MicrosoftCXXNameMangler Extra(Context, Stream); + Stream << "?$"; + if (T->isWrapKind()) { + Extra.mangleSourceName("ObtWrap_"); + } else { + Extra.mangleSourceName("ObtTrap_"); + } + Extra.mangleType(UnderlyingType, Range, QMM_Escape); + + mangleArtificialTagType(TagTypeKind::Struct, TemplateMangling, {"__clang"}); +} + // ::= | | // // ::= A # private near diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index 9794314a98f81..7566d0e793bf2 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -1153,6 +1153,18 @@ struct SimpleTransformVisitor : public TypeVisitor { T->getNumColumns()); } + QualType VisitOverflowBehaviorType(const OverflowBehaviorType *T) { + QualType UnderlyingType = recurse(T->getUnderlyingType()); + if (UnderlyingType.isNull()) + return {}; + + if (UnderlyingType.getAsOpaquePtr() == + T->getUnderlyingType().getAsOpaquePtr()) + return QualType(T, 0); + + return Ctx.getOverflowBehaviorType(T->getBehaviorKind(), UnderlyingType); + } + QualType VisitFunctionNoProtoType(const FunctionNoProtoType *T) { QualType returnType = recurse(T->getReturnType()); if (returnType.isNull()) @@ -2042,6 +2054,10 @@ class GetContainedDeducedTypeVisitor return Visit(T->getUnderlyingType()); } + Type *VisitOverflowBehaviorType(const OverflowBehaviorType *T) { + return Visit(T->getUnderlyingType()); + } + Type *VisitAdjustedType(const AdjustedType *T) { return Visit(T->getOriginalType()); } @@ -2105,10 +2121,15 @@ bool Type::isIntegralType(const ASTContext &Ctx) const { return BT->isInteger(); // Complete enum types are integral in C. - if (!Ctx.getLangOpts().CPlusPlus) + if (!Ctx.getLangOpts().CPlusPlus) { if (const auto *ET = dyn_cast(CanonicalType)) return IsEnumDeclComplete(ET->getOriginalDecl()); + if (const OverflowBehaviorType *OBT = + dyn_cast(CanonicalType)) + return OBT->getUnderlyingType()->isIntegralOrEnumerationType(); + } + return isBitIntType(); } @@ -2116,6 +2137,9 @@ bool Type::isIntegralOrUnscopedEnumerationType() const { if (const auto *BT = dyn_cast(CanonicalType)) return BT->isInteger(); + if (const auto *OBT = dyn_cast(CanonicalType)) + return OBT->getUnderlyingType()->isIntegerType(); + if (isBitIntType()) return true; @@ -2219,6 +2243,9 @@ bool Type::isSignedIntegerType() const { if (const auto *IT = dyn_cast(CanonicalType)) return IT->isSigned(); + if (const auto *OBT = dyn_cast(CanonicalType)) + return OBT->getUnderlyingType()->isSignedIntegerType(); + return false; } @@ -2237,6 +2264,9 @@ bool Type::isSignedIntegerOrEnumerationType() const { if (const auto *IT = dyn_cast(CanonicalType)) return IT->isSigned(); + if (const auto *OBT = dyn_cast(CanonicalType)) + return OBT->getUnderlyingType()->isSignedIntegerOrEnumerationType(); + return false; } @@ -2267,6 +2297,9 @@ bool Type::isUnsignedIntegerType() const { if (const auto *IT = dyn_cast(CanonicalType)) return IT->isUnsigned(); + if (const auto *OBT = dyn_cast(CanonicalType)) + return OBT->getUnderlyingType()->isUnsignedIntegerType(); + return false; } @@ -2285,6 +2318,9 @@ bool Type::isUnsignedIntegerOrEnumerationType() const { if (const auto *IT = dyn_cast(CanonicalType)) return IT->isUnsigned(); + if (const auto *OBT = dyn_cast(CanonicalType)) + return OBT->getUnderlyingType()->isUnsignedIntegerOrEnumerationType(); + return false; } @@ -2348,6 +2384,11 @@ bool Type::isArithmeticType() const { const auto *ED = ET->getOriginalDecl(); return !ED->isScoped() && ED->getDefinitionOrSelf()->isComplete(); } + + if (isOverflowBehaviorType() && + getAs()->getUnderlyingType()->isArithmeticType()) + return true; + return isa(CanonicalType) || isBitIntType(); } @@ -2394,6 +2435,8 @@ Type::ScalarTypeKind Type::getScalarTypeKind() const { return STK_IntegralComplex; } else if (isBitIntType()) { return STK_Integral; + } else if (isa(T)) { + return STK_Integral; } llvm_unreachable("unknown scalar type"); @@ -2740,6 +2783,7 @@ bool QualType::isCXX98PODType(const ASTContext &Context) const { case Type::Vector: case Type::ExtVector: case Type::BitInt: + case Type::OverflowBehavior: return true; case Type::Enum: @@ -2950,6 +2994,22 @@ bool QualType::isWebAssemblyFuncrefType() const { getAddressSpace() == LangAS::wasm_funcref; } +bool QualType::isWrapType() const { + if (const auto *OBT = getCanonicalType()->getAs()) + return OBT->getBehaviorKind() == + OverflowBehaviorType::OverflowBehaviorKind::Wrap; + + return false; +} + +bool QualType::isTrapType() const { + if (const auto *OBT = getCanonicalType()->getAs()) + return OBT->getBehaviorKind() == + OverflowBehaviorType::OverflowBehaviorKind::Trap; + + return false; +} + QualType::PrimitiveDefaultInitializeKind QualType::isNonTrivialToPrimitiveDefaultInitialize() const { if (const auto *RD = @@ -3048,6 +3108,9 @@ bool Type::isLiteralType(const ASTContext &Ctx) const { if (const auto *AT = BaseTy->getAs()) return AT->getValueType()->isLiteralType(Ctx); + if (const auto *OBT = BaseTy->getAs()) + return OBT->getUnderlyingType()->isLiteralType(Ctx); + // If this type hasn't been deduced yet, then conservatively assume that // it'll work out to be a literal type. if (isa(BaseTy->getCanonicalTypeInternal())) @@ -3978,6 +4041,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) {} @@ -4890,6 +4959,8 @@ static CachedProperties computeCachedProperties(const Type *T) { return Cache::get(cast(T)->getWrappedType()); case Type::HLSLInlineSpirv: return CachedProperties(Linkage::External, false); + case Type::OverflowBehavior: + return Cache::get(cast(T)->getUnderlyingType()); } llvm_unreachable("unhandled type class"); @@ -4985,6 +5056,9 @@ LinkageInfo LinkageComputer::computeTypeLinkageInfo(const Type *T) { return computeTypeLinkageInfo(cast(T)->getValueType()); case Type::Pipe: return computeTypeLinkageInfo(cast(T)->getElementType()); + case Type::OverflowBehavior: + return computeTypeLinkageInfo( + cast(T)->getUnderlyingType()); case Type::HLSLAttributedResource: return computeTypeLinkageInfo(cast(T) ->getContainedType() @@ -5179,6 +5253,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 55476e2175a1f..9c81cbced2c73 100644 --- a/clang/lib/AST/TypeLoc.cpp +++ b/clang/lib/AST/TypeLoc.cpp @@ -632,6 +632,10 @@ SourceRange BTFTagAttributedTypeLoc::getLocalSourceRange() const { return getAttr() ? getAttr()->getRange() : SourceRange(); } +SourceRange OverflowBehaviorTypeLoc::getLocalSourceRange() const { + return SourceRange(); +} + void TypeOfTypeLoc::initializeLocal(ASTContext &Context, SourceLocation Loc) { TypeofLikeTypeLoc @@ -890,6 +894,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 66a1b684ec68b..136a6ccde7945 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; @@ -2115,6 +2116,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: @@ -2195,6 +2197,24 @@ void TypePrinter::printBTFTagAttributedAfter(const BTFTagAttributedType *T, printAfter(T->getWrappedType(), OS); } +void TypePrinter::printOverflowBehaviorBefore(const OverflowBehaviorType *T, + raw_ostream &OS) { + switch (T->getBehaviorKind()) { + case clang::OverflowBehaviorType::OverflowBehaviorKind::Wrap: + OS << "__ob_wrap "; + break; + case clang::OverflowBehaviorType::OverflowBehaviorKind::Trap: + OS << "__ob_trap "; + break; + } + printBefore(T->getUnderlyingType(), OS); +} + +void TypePrinter::printOverflowBehaviorAfter(const OverflowBehaviorType *T, + raw_ostream &OS) { + printAfter(T->getUnderlyingType(), OS); +} + void TypePrinter::printHLSLAttributedResourceBefore( const HLSLAttributedResourceType *T, raw_ostream &OS) { printBefore(T->getWrappedType(), OS); diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp index fee6bc0cbb64b..ca3eb5ada4b20 100644 --- a/clang/lib/CodeGen/CGDebugInfo.cpp +++ b/clang/lib/CodeGen/CGDebugInfo.cpp @@ -1116,6 +1116,11 @@ 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; @@ -4086,6 +4091,8 @@ llvm::DIType *CGDebugInfo::CreateTypeNode(QualType Ty, llvm::DIFile *Unit) { case Type::BitInt: return CreateType(cast(Ty)); + case Type::OverflowBehavior: + return CreateType(cast(Ty), Unit); case Type::Pipe: return CreateType(cast(Ty), Unit); diff --git a/clang/lib/CodeGen/CGDebugInfo.h b/clang/lib/CodeGen/CGDebugInfo.h index 78c3eb9c5792e..56f3e539638cb 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 06d9d812bc60b..88e82ef7229e7 100644 --- a/clang/lib/CodeGen/CGExprScalar.cpp +++ b/clang/lib/CodeGen/CGExprScalar.cpp @@ -191,8 +191,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(); + /// FIXME: Having two enums named `OverflowBehaviorKind` is not ideal, these + /// should be unified into one coherent enum that supports both unsigned and + /// signed overflow behavior semantics. + if (OBT) { + switch (OBT->getBehaviorKind()) { + case OverflowBehaviorType::OverflowBehaviorKind::Wrap: + return LangOptions::OverflowBehaviorKind::OB_Wrap; + case OverflowBehaviorType::OverflowBehaviorKind::Trap: + return LangOptions::OverflowBehaviorKind::OB_Trap; + } + llvm_unreachable("Unknown OverflowBehaviorKind"); + } + + if (Ty->isUnsignedIntegerType()) { + return LangOptions::OverflowBehaviorKind::OB_Unset; + } + + switch (CGF.getLangOpts().getSignedOverflowBehavior()) { + case LangOptions::SignedOverflowBehaviorTy::SOB_Defined: + return LangOptions::OverflowBehaviorKind::OB_SignedAndDefined; + case LangOptions::SignedOverflowBehaviorTy::SOB_Undefined: + return LangOptions::OverflowBehaviorKind::OB_Unset; + case LangOptions::SignedOverflowBehaviorTy::SOB_Trapping: + return LangOptions::OverflowBehaviorKind::OB_Trap; + } +} + /// Check if we can skip the overflow check for \p Op. -static bool CanElideOverflowCheck(const ASTContext &Ctx, const BinOpInfo &Op) { +static bool CanElideOverflowCheck(ASTContext &Ctx, const BinOpInfo &Op) { assert((isa(Op.E) || isa(Op.E)) && "Expected a unary or binary operator"); @@ -201,6 +235,19 @@ static bool CanElideOverflowCheck(const ASTContext &Ctx, const BinOpInfo &Op) { if (!Op.mayHaveIntegerOverflow()) return true; + const UnaryOperator *UO = dyn_cast(Op.E); + if (UO && Ctx.isUnaryOverflowPatternExcluded(UO)) + return true; + + const auto *BO = dyn_cast(Op.E); + if (BO && BO->hasExcludedOverflowPattern()) + return true; + + if (Op.Ty.isWrapType()) + return true; + if (Op.Ty.isTrapType()) + return false; + if (Op.Ty->isSignedIntegerType() && Ctx.isTypeIgnoredBySanitizer(SanitizerKind::SignedIntegerOverflow, Op.Ty)) { @@ -213,24 +260,12 @@ static bool CanElideOverflowCheck(const ASTContext &Ctx, const BinOpInfo &Op) { return true; } - const UnaryOperator *UO = dyn_cast(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(Op.E); - if (BO->hasExcludedOverflowPattern()) - return true; - auto OptionalLHSTy = getUnwidenedIntegerType(Ctx, BO->getLHS()); if (!OptionalLHSTy) return false; @@ -361,7 +396,8 @@ class ScalarExprEmitter /// Emit a check that an [implicit] truncation of an integer does not /// discard any bits. It is not UB, so we use the value after truncation. void EmitIntegerTruncationCheck(Value *Src, QualType SrcType, Value *Dst, - QualType DstType, SourceLocation Loc); + QualType DstType, SourceLocation Loc, + bool OBTrapInvolved = false); /// Emit a check that an [implicit] conversion of an integer does not change /// the sign of the value. It is not UB, so we use the value after conversion. @@ -781,19 +817,28 @@ class ScalarExprEmitter // Binary Operators. Value *EmitMul(const BinOpInfo &Ops) { - if (Ops.Ty->isSignedIntegerOrEnumerationType()) { - switch (CGF.getLangOpts().getSignedOverflowBehavior()) { - case LangOptions::SOB_Defined: - if (!CGF.SanOpts.has(SanitizerKind::SignedIntegerOverflow)) + if (Ops.Ty->isSignedIntegerOrEnumerationType() || + Ops.Ty->isUnsignedIntegerType()) { + const bool isSigned = Ops.Ty->isSignedIntegerOrEnumerationType(); + const bool hasSan = + isSigned ? CGF.SanOpts.has(SanitizerKind::SignedIntegerOverflow) + : CGF.SanOpts.has(SanitizerKind::UnsignedIntegerOverflow); + switch (getOverflowBehaviorConsideringType(CGF, Ops.Ty)) { + case LangOptions::OB_Wrap: + return Builder.CreateMul(Ops.LHS, Ops.RHS, "mul"); + case LangOptions::OB_SignedAndDefined: + if (!hasSan) return Builder.CreateMul(Ops.LHS, Ops.RHS, "mul"); [[fallthrough]]; - case LangOptions::SOB_Undefined: - if (!CGF.SanOpts.has(SanitizerKind::SignedIntegerOverflow)) - return Builder.CreateNSWMul(Ops.LHS, Ops.RHS, "mul"); + case LangOptions::OB_Unset: + if (!hasSan) + return isSigned ? Builder.CreateNSWMul(Ops.LHS, Ops.RHS, "mul") + : Builder.CreateMul(Ops.LHS, Ops.RHS, "mul"); [[fallthrough]]; - case LangOptions::SOB_Trapping: + case LangOptions::OB_Trap: if (CanElideOverflowCheck(CGF.getContext(), Ops)) - return Builder.CreateNSWMul(Ops.LHS, Ops.RHS, "mul"); + return isSigned ? Builder.CreateNSWMul(Ops.LHS, Ops.RHS, "mul") + : Builder.CreateMul(Ops.LHS, Ops.RHS, "mul"); return EmitOverflowCheckedBinOp(Ops); } } @@ -815,11 +860,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); @@ -1116,8 +1156,10 @@ static bool PromotionIsPotentiallyEligibleForImplicitIntegerConversionCheck( void ScalarExprEmitter::EmitIntegerTruncationCheck(Value *Src, QualType SrcType, Value *Dst, QualType DstType, - SourceLocation Loc) { - if (!CGF.SanOpts.hasOneOf(SanitizerKind::ImplicitIntegerTruncation)) + SourceLocation Loc, + bool OBTrapInvolved) { + if (!CGF.SanOpts.hasOneOf(SanitizerKind::ImplicitIntegerTruncation) && + !OBTrapInvolved) return; // We only care about int->int conversions here. @@ -1163,14 +1205,27 @@ void ScalarExprEmitter::EmitIntegerTruncationCheck(Value *Src, QualType SrcType, } // Do we care about this type of truncation? - if (!CGF.SanOpts.has(Check.second.second)) + if (!CGF.SanOpts.has(Check.second.second)) { + // Just emit a trap check if an __ob_trap was involved but appropriate + // sanitizer isn't enabled. + if (OBTrapInvolved) + CGF.EmitTrapCheck(Check.second.first, CheckHandler); return; + } SanitizerDebugLocation SanScope(&CGF, {Check.second.second}, CheckHandler); // Does some SSCL ignore this type? - if (CGF.getContext().isTypeIgnoredBySanitizer( - SanitizerMask::bitPosToMask(Check.second.second), DstType)) + const bool ignoredBySanitizer = CGF.getContext().isTypeIgnoredBySanitizer( + SanitizerMask::bitPosToMask(Check.second.second), DstType); + + // Consider OverflowBehaviorTypes which override SSCL type entries for + // truncation sanitizers. + if (const auto *OBT = DstType->getAs()) { + if (OBT->isWrapKind()) + return; + } + if (ignoredBySanitizer) return; llvm::Constant *StaticArgs[] = { @@ -1742,9 +1797,18 @@ Value *ScalarExprEmitter::EmitScalarConversion(Value *Src, QualType SrcType, } } - if (Opts.EmitImplicitIntegerTruncationChecks) + // Determine whether an overflow behavior of 'trap' has been specified for + // either the destination or the source types. If so, we can elide sanitizer + // capability checks as this overflow behavior kind is also capable of + // emitting traps without runtime sanitizer support. + const auto *DstOBT = DstType->getAs(); + const auto *SrcOBT = SrcType->getAs(); + bool OBTrapInvolved = + (DstOBT && DstOBT->isTrapKind()) || (SrcOBT && SrcOBT->isTrapKind()); + + if (Opts.EmitImplicitIntegerTruncationChecks || OBTrapInvolved) EmitIntegerTruncationCheck(Src, NoncanonicalSrcType, Res, - NoncanonicalDstType, Loc); + NoncanonicalDstType, Loc, OBTrapInvolved); if (Opts.EmitImplicitIntegerSignChangeChecks) EmitIntegerSignChangeCheck(Src, NoncanonicalSrcType, Res, @@ -3004,43 +3068,37 @@ llvm::Value *ScalarExprEmitter::EmitIncDecConsiderOverflowBehavior( llvm::Value *Amount = llvm::ConstantInt::get(InVal->getType(), IsInc ? 1 : -1, true); StringRef Name = IsInc ? "inc" : "dec"; - switch (CGF.getLangOpts().getSignedOverflowBehavior()) { - case LangOptions::SOB_Defined: - if (!CGF.SanOpts.has(SanitizerKind::SignedIntegerOverflow)) + QualType Ty = E->getType(); + const bool isSigned = Ty->isSignedIntegerOrEnumerationType(); + const bool hasSan = + isSigned ? CGF.SanOpts.has(SanitizerKind::SignedIntegerOverflow) + : CGF.SanOpts.has(SanitizerKind::UnsignedIntegerOverflow); + + switch (getOverflowBehaviorConsideringType(CGF, Ty)) { + case LangOptions::OB_Wrap: + return Builder.CreateAdd(InVal, Amount, Name); + case LangOptions::OB_SignedAndDefined: + if (!hasSan) return Builder.CreateAdd(InVal, Amount, Name); [[fallthrough]]; - case LangOptions::SOB_Undefined: - if (!CGF.SanOpts.has(SanitizerKind::SignedIntegerOverflow)) - return Builder.CreateNSWAdd(InVal, Amount, Name); + case LangOptions::OB_Unset: + if (!E->canOverflow()) + return Builder.CreateAdd(InVal, Amount, Name); + if (!hasSan) + return isSigned ? Builder.CreateNSWAdd(InVal, Amount, Name) + : Builder.CreateAdd(InVal, Amount, Name); [[fallthrough]]; - case LangOptions::SOB_Trapping: + case LangOptions::OB_Trap: + if (!Ty->getAs() && !E->canOverflow()) + return Builder.CreateAdd(InVal, Amount, Name); BinOpInfo Info = createBinOpInfoFromIncDec( E, InVal, IsInc, E->getFPFeaturesInEffect(CGF.getLangOpts())); - if (!E->canOverflow() || CanElideOverflowCheck(CGF.getContext(), Info)) - return Builder.CreateNSWAdd(InVal, Amount, Name); + if (CanElideOverflowCheck(CGF.getContext(), Info)) + return isSigned ? Builder.CreateNSWAdd(InVal, Amount, Name) + : Builder.CreateAdd(InVal, Amount, Name); return EmitOverflowCheckedBinOp(Info); } - llvm_unreachable("Unknown SignedOverflowBehaviorTy"); -} - -/// For the purposes of overflow pattern exclusion, does this match the -/// "while(i--)" pattern? -static bool matchesPostDecrInWhile(const UnaryOperator *UO, bool isInc, - bool isPre, ASTContext &Ctx) { - if (isInc || isPre) - return false; - - // -fsanitize-undefined-ignore-overflow-pattern=unsigned-post-decr-while - if (!Ctx.getLangOpts().isOverflowPatternExcluded( - LangOptions::OverflowPatternExclusionKind::PostDecrInWhile)) - return false; - - // all Parents (usually just one) must be a WhileStmt - for (const auto &Parent : Ctx.getParentMapContext().getParents(*UO)) - if (!Parent.get()) - return false; - - return true; + llvm_unreachable("Unknown OverflowBehaviorKind"); } namespace { @@ -3159,9 +3217,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."); @@ -3218,15 +3273,9 @@ ScalarExprEmitter::EmitScalarPrePostIncDec(const UnaryOperator *E, LValue LV, // Note that signed integer inc/dec with width less than int can't // overflow because of promotion rules; we're just eliding a few steps // here. - } else if (E->canOverflow() && type->isSignedIntegerOrEnumerationType()) { + } else if (type->isSignedIntegerOrEnumerationType() || + type->isUnsignedIntegerType()) { value = EmitIncDecConsiderOverflowBehavior(E, value, isInc); - } else if (E->canOverflow() && type->isUnsignedIntegerType() && - CGF.SanOpts.has(SanitizerKind::UnsignedIntegerOverflow) && - !excludeOverflowPattern && - !CGF.getContext().isTypeIgnoredBySanitizer( - SanitizerKind::UnsignedIntegerOverflow, E->getType())) { - value = EmitOverflowCheckedBinOp(createBinOpInfoFromIncDec( - E, value, isInc, E->getFPFeaturesInEffect(CGF.getLangOpts()))); } else { llvm::Value *amt = llvm::ConstantInt::get(value->getType(), amount, true); value = Builder.CreateAdd(value, amt, isInc ? "inc" : "dec"); @@ -4009,7 +4058,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(Zero->getType()); llvm::Value *IntMin = @@ -4155,14 +4204,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; @@ -4486,19 +4546,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_SignedAndDefined: + if (!hasSan) return Builder.CreateAdd(op.LHS, op.RHS, "add"); [[fallthrough]]; - case LangOptions::SOB_Undefined: - if (!CGF.SanOpts.has(SanitizerKind::SignedIntegerOverflow)) - return Builder.CreateNSWAdd(op.LHS, op.RHS, "add"); + case LangOptions::OB_Unset: + if (!hasSan) + return isSigned ? Builder.CreateNSWAdd(op.LHS, op.RHS, "add") + : Builder.CreateAdd(op.LHS, op.RHS, "add"); [[fallthrough]]; - case LangOptions::SOB_Trapping: + case LangOptions::OB_Trap: if (CanElideOverflowCheck(CGF.getContext(), op)) - return Builder.CreateNSWAdd(op.LHS, op.RHS, "add"); + return isSigned ? Builder.CreateNSWAdd(op.LHS, op.RHS, "add") + : Builder.CreateAdd(op.LHS, op.RHS, "add"); return EmitOverflowCheckedBinOp(op); } } @@ -4517,11 +4588,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"); @@ -4642,19 +4708,28 @@ Value *ScalarExprEmitter::EmitFixedPointBinOp(const BinOpInfo &op) { Value *ScalarExprEmitter::EmitSub(const BinOpInfo &op) { // The LHS is always a pointer if either side is. if (!op.LHS->getType()->isPointerTy()) { - if (op.Ty->isSignedIntegerOrEnumerationType()) { - switch (CGF.getLangOpts().getSignedOverflowBehavior()) { - case LangOptions::SOB_Defined: - if (!CGF.SanOpts.has(SanitizerKind::SignedIntegerOverflow)) + if (op.Ty->isSignedIntegerOrEnumerationType() || + op.Ty->isUnsignedIntegerType()) { + const bool isSigned = op.Ty->isSignedIntegerOrEnumerationType(); + const bool hasSan = + isSigned ? CGF.SanOpts.has(SanitizerKind::SignedIntegerOverflow) + : CGF.SanOpts.has(SanitizerKind::UnsignedIntegerOverflow); + switch (getOverflowBehaviorConsideringType(CGF, op.Ty)) { + case LangOptions::OB_Wrap: + return Builder.CreateSub(op.LHS, op.RHS, "sub"); + case LangOptions::OB_SignedAndDefined: + if (!hasSan) return Builder.CreateSub(op.LHS, op.RHS, "sub"); [[fallthrough]]; - case LangOptions::SOB_Undefined: - if (!CGF.SanOpts.has(SanitizerKind::SignedIntegerOverflow)) - return Builder.CreateNSWSub(op.LHS, op.RHS, "sub"); + case LangOptions::OB_Unset: + if (!hasSan) + return isSigned ? Builder.CreateNSWSub(op.LHS, op.RHS, "sub") + : Builder.CreateSub(op.LHS, op.RHS, "sub"); [[fallthrough]]; - case LangOptions::SOB_Trapping: + case LangOptions::OB_Trap: if (CanElideOverflowCheck(CGF.getContext(), op)) - return Builder.CreateNSWSub(op.LHS, op.RHS, "sub"); + return isSigned ? Builder.CreateNSWSub(op.LHS, op.RHS, "sub") + : Builder.CreateSub(op.LHS, op.RHS, "sub"); return EmitOverflowCheckedBinOp(op); } } @@ -4673,11 +4748,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 b2fe9171372d8..2c976115ee2fb 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. @@ -2577,6 +2578,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 3ffe999d01178..e9a8a1537da06 100644 --- a/clang/lib/CodeGen/CodeGenTypes.cpp +++ b/clang/lib/CodeGen/CodeGenTypes.cpp @@ -770,6 +770,10 @@ llvm::Type *CodeGenTypes::ConvertType(QualType T) { case Type::HLSLInlineSpirv: ResultType = CGM.getHLSLRuntime().convertHLSLSpecificType(Ty); break; + case Type::OverflowBehavior: + ResultType = + ConvertType(dyn_cast(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 7dc2eaf1e9f75..4cbfef9558cc7 100644 --- a/clang/lib/CodeGen/ItaniumCXXABI.cpp +++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp @@ -3954,6 +3954,7 @@ void ItaniumRTTIBuilder::BuildVTablePointer(const Type *Ty, case Type::Builtin: case Type::BitInt: + case Type::OverflowBehavior: // GCC treats vector and complex types as fundamental types. case Type::Vector: case Type::ExtVector: @@ -4310,6 +4311,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 107b9ffd439a3..93b6ecdffeffd 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -6298,6 +6298,9 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, Args.addOptInFlag(CmdArgs, options::OPT_ffixed_point, options::OPT_fno_fixed_point); + Args.addOptInFlag(CmdArgs, options::OPT_foverflow_behavior_types, + options::OPT_fno_overflow_behavior_types); + if (Arg *A = Args.getLastArg(options::OPT_fcxx_abi_EQ)) A->render(Args, CmdArgs); diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index d6cd7eb8c2c3d..89c5e43dd8133 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -4487,6 +4487,16 @@ void Parser::ParseDeclarationSpecifiers( isInvalid = DS.SetTypeQual(DeclSpec::TQ_restrict, Loc, PrevSpec, DiagID, getLangOpts()); break; + case tok::kw___ob_wrap: + isInvalid = DS.SetOverflowBehavior( + OverflowBehaviorType::OverflowBehaviorKind::Wrap, Loc, PrevSpec, + DiagID); + break; + case tok::kw___ob_trap: + isInvalid = DS.SetOverflowBehavior( + OverflowBehaviorType::OverflowBehaviorKind::Trap, Loc, PrevSpec, + DiagID); + break; // C++ typename-specifier: case tok::kw_typename: @@ -5628,6 +5638,8 @@ bool Parser::isTypeSpecifierQualifier() { case tok::kw_const: case tok::kw_volatile: case tok::kw_restrict: + case tok::kw___ob_wrap: + case tok::kw___ob_trap: case tok::kw__Sat: // Debugger support. @@ -5841,6 +5853,8 @@ bool Parser::isDeclarationSpecifier( case tok::kw_const: case tok::kw_volatile: case tok::kw_restrict: + case tok::kw___ob_wrap: + case tok::kw___ob_trap: case tok::kw__Sat: // function-specifier @@ -6154,6 +6168,16 @@ void Parser::ParseTypeQualifierListOpt( isInvalid = DS.SetTypeQual(DeclSpec::TQ_restrict, Loc, PrevSpec, DiagID, getLangOpts()); break; + case tok::kw___ob_wrap: + isInvalid = DS.SetOverflowBehavior( + OverflowBehaviorType::OverflowBehaviorKind::Wrap, Loc, PrevSpec, + DiagID); + break; + case tok::kw___ob_trap: + isInvalid = DS.SetOverflowBehavior( + OverflowBehaviorType::OverflowBehaviorKind::Trap, Loc, PrevSpec, + DiagID); + break; case tok::kw__Atomic: if (!AtomicOrPtrauthAllowed) goto DoneWithTypeQuals; diff --git a/clang/lib/Parse/ParseTentative.cpp b/clang/lib/Parse/ParseTentative.cpp index 82f2294ff5bb7..511cd834cf5f1 100644 --- a/clang/lib/Parse/ParseTentative.cpp +++ b/clang/lib/Parse/ParseTentative.cpp @@ -1231,6 +1231,11 @@ Parser::isCXXDeclarationSpecifier(ImplicitTypenameContext AllowImplicitTypename, case tok::kw___auto_type: return TPResult::True; + // OverflowBehaviorTypes + case tok::kw___ob_wrap: + case tok::kw___ob_trap: + return TPResult::True; + // Microsoft case tok::kw___declspec: case tok::kw___cdecl: diff --git a/clang/lib/Sema/DeclSpec.cpp b/clang/lib/Sema/DeclSpec.cpp index 8756ce5f0d850..59ef7c082c4c3 100644 --- a/clang/lib/Sema/DeclSpec.cpp +++ b/clang/lib/Sema/DeclSpec.cpp @@ -616,6 +616,18 @@ const char *DeclSpec::getSpecifierName(TQ T) { llvm_unreachable("Unknown typespec!"); } +const char *DeclSpec::getSpecifierName(OverflowBehaviorState S) { + switch (S) { + case OverflowBehaviorState::Unspecified: + return "unspecified"; + case OverflowBehaviorState::Wrap: + return "__ob_wrap"; + case OverflowBehaviorState::Trap: + return "__ob_trap"; + } + llvm_unreachable("Unknown overflow behavior state!"); +} + bool DeclSpec::SetStorageClassSpec(Sema &S, SCS SC, SourceLocation Loc, const char *&PrevSpec, unsigned &DiagID, @@ -1003,6 +1015,25 @@ bool DeclSpec::SetTypeQual(TQ T, SourceLocation Loc) { llvm_unreachable("Unknown type qualifier!"); } +bool DeclSpec::SetOverflowBehavior( + OverflowBehaviorType::OverflowBehaviorKind Kind, SourceLocation Loc, + const char *&PrevSpec, unsigned &DiagID) { + OverflowBehaviorState NewState = + (Kind == OverflowBehaviorType::OverflowBehaviorKind::Wrap) + ? OverflowBehaviorState::Wrap + : OverflowBehaviorState::Trap; + + OverflowBehaviorState CurrentState = getOverflowBehaviorState(); + + if (CurrentState != OverflowBehaviorState::Unspecified) { + return BadSpecifier(NewState, CurrentState, PrevSpec, DiagID); + } + + OB_state = static_cast(NewState); + OB_Loc = Loc; + return false; +} + bool DeclSpec::setFunctionSpecInline(SourceLocation Loc, const char *&PrevSpec, unsigned &DiagID) { // 'inline inline' is ok. However, since this is likely not what the user diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index 3cc61b167ba98..b53d20e9e4da8 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -8504,6 +8504,10 @@ CheckPrintfHandler::checkFormatExpr(const analyze_printf::PrintfSpecifier &FS, ExprTy = TET->getUnderlyingExpr()->getType(); } + if (const OverflowBehaviorType *OBT = + dyn_cast(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. @@ -10756,6 +10760,8 @@ struct IntRange { T = CT->getElementType().getTypePtr(); if (const auto *AT = dyn_cast(T)) T = AT->getValueType().getTypePtr(); + if (const OverflowBehaviorType *OBT = dyn_cast(T)) + T = OBT->getUnderlyingType().getTypePtr(); if (!C.getLangOpts().CPlusPlus) { // For enum types in C code, use the underlying datatype. @@ -10805,6 +10811,8 @@ struct IntRange { T = AT->getValueType().getTypePtr(); if (const auto *ED = T->getAsEnumDecl()) T = C.getCanonicalType(ED->getIntegerType()).getTypePtr(); + if (const OverflowBehaviorType *OBT = dyn_cast(T)) + T = OBT->getUnderlyingType().getTypePtr(); if (const auto *EIT = dyn_cast(T)) return IntRange(EIT->getNumBits(), EIT->isUnsigned()); @@ -11938,6 +11946,11 @@ static void AnalyzeAssignment(Sema &S, BinaryOperator *E) { } } + // Set context flag for overflow behavior type assignment analysis, use RAII + // pattern to handle nested assignments. + llvm::SaveAndRestore OBTAssignmentContext( + S.InOverflowBehaviorAssignmentContext, true); + AnalyzeImplicitConversions(S, E->getRHS(), E->getOperatorLoc()); // Diagnose implicitly sequentially-consistent atomic assignment. @@ -12425,6 +12438,8 @@ void Sema::CheckImplicitConversion(Expr *E, QualType T, SourceLocation CC, } } + CheckOverflowBehaviorTypeConversion(E, T, CC); + // If the we're converting a constant to an ObjC BOOL on a platform where BOOL // is a typedef for signed char (macOS), then that constant value has to be 1 // or 0. @@ -12757,6 +12772,15 @@ void Sema::CheckImplicitConversion(Expr *E, QualType T, SourceLocation CC, IntRange TargetRange = IntRange::forTargetOfCanonicalType(Context, Target); if (LikelySourceRange->Width > TargetRange.Width) { + // Check if target is a wrapping OBT - if so, don't warn about constant + // conversion as this type may be used intentionally with implicit + // truncation, especially during assignments. + if (const auto *TargetOBT = Target->getAs()) { + 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; @@ -13441,6 +13465,36 @@ 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()) { + 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 { + unsigned DiagId = InOverflowBehaviorAssignmentContext + ? diag::warn_impcast_overflow_behavior_assignment + : diag::warn_impcast_overflow_behavior; + DiagnoseImpCast(*this, E, T, CC, DiagId); + } + } + } + + if (const auto *TargetOBT = Target->getAs()) { + if (TargetOBT->isWrapKind()) { + return true; + } + } + + return false; +} + void Sema::CheckImplicitConversions(Expr *E, SourceLocation CC) { // Don't diagnose in unevaluated contexts. if (isUnevaluatedContext()) diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 6eaf7b9435491..222df5289bb80 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -14012,6 +14012,10 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init, bool DirectInit) { // struct T { S a, b; } t = { Temp(), Temp() } // // we should destroy the first Temp before constructing the second. + + // Set context flag for OverflowBehaviorType initialization analysis + llvm::SaveAndRestore OBTAssignmentContext(InOverflowBehaviorAssignmentContext, + true); ExprResult Result = ActOnFinishFullExpr(Init, VDecl->getLocation(), /*DiscardedValue*/ false, VDecl->isConstexpr()); diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 4d3c7d611f370..bb95339a3d7a2 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -1410,6 +1410,49 @@ static QualType handleComplexIntConversion(Sema &S, ExprResult &LHS, return ComplexType; } +static QualType handleOverflowBehaviorTypeConversion(Sema &S, ExprResult &LHS, + ExprResult &RHS, + QualType LHSType, + QualType RHSType, + bool IsCompAssign) { + + const auto *LhsOBT = LHSType->getAs(); + const auto *RhsOBT = RHSType->getAs(); + + assert(LHSType->isIntegerType() && RHSType->isIntegerType() && + "Non-integer type conversion not supported for OverflowBehaviorTypes"); + + bool LHSHasTrap = + LhsOBT && LhsOBT->getBehaviorKind() == + OverflowBehaviorType::OverflowBehaviorKind::Trap; + bool RHSHasTrap = + RhsOBT && RhsOBT->getBehaviorKind() == + OverflowBehaviorType::OverflowBehaviorKind::Trap; + bool LHSHasWrap = + LhsOBT && LhsOBT->getBehaviorKind() == + OverflowBehaviorType::OverflowBehaviorKind::Wrap; + bool RHSHasWrap = + RhsOBT && RhsOBT->getBehaviorKind() == + OverflowBehaviorType::OverflowBehaviorKind::Wrap; + + QualType LHSUnderlyingType = LhsOBT ? LhsOBT->getUnderlyingType() : LHSType; + QualType RHSUnderlyingType = RhsOBT ? RhsOBT->getUnderlyingType() : RHSType; + + QualType StandardResultType = + handleIntegerConversion( + S, LHS, RHS, LHSUnderlyingType, RHSUnderlyingType, IsCompAssign); + + // Apply OBT qualifier precedence rules to the result type + if (LHSHasTrap || RHSHasTrap) + return S.Context.getOverflowBehaviorType( + OverflowBehaviorType::OverflowBehaviorKind::Trap, StandardResultType); + if (LHSHasWrap || RHSHasWrap) + return S.Context.getOverflowBehaviorType( + OverflowBehaviorType::OverflowBehaviorKind::Wrap, StandardResultType); + + return StandardResultType; +} + /// 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. @@ -1714,6 +1757,10 @@ QualType Sema::UsualArithmeticConversions(ExprResult &LHS, ExprResult &RHS, if (LHSType->isFixedPointType() || RHSType->isFixedPointType()) return handleFixedPointConversion(*this, LHSType, RHSType); + if (LHSType->isOverflowBehaviorType() || RHSType->isOverflowBehaviorType()) + return handleOverflowBehaviorTypeConversion( + *this, LHS, RHS, LHSType, RHSType, ACK == ArithConvKind::CompAssign); + // Finally, we have two differing integer types. return handleIntegerConversion( *this, LHS, RHS, LHSType, RHSType, ACK == ArithConvKind::CompAssign); @@ -1756,6 +1803,29 @@ ExprResult Sema::ActOnGenericSelectionExpr( return ER; } +// Helper function to determine type compatibility for C _Generic expressions. +// Multiple compatible types within the same _Generic expression is ambiguous +// and not valid. +static bool areTypesCompatibleForGeneric(ASTContext &Ctx, QualType T, + QualType U) { + // Try to handle special types like OverflowBehaviorTypes + const auto *TOBT = T->getAs(); + const auto *UOBT = U.getCanonicalType()->getAs(); + + if (TOBT || UOBT) { + if (TOBT && UOBT) { + if (TOBT->getBehaviorKind() == UOBT->getBehaviorKind()) + return Ctx.typesAreCompatible(TOBT->getUnderlyingType(), + UOBT->getUnderlyingType()); + return false; + } + return false; + } + + // We're dealing with types that don't require special handling. + return Ctx.typesAreCompatible(T, U); +} + ExprResult Sema::CreateGenericSelectionExpr( SourceLocation KeyLoc, SourceLocation DefaultLoc, SourceLocation RParenLoc, bool PredicateIsExpr, void *ControllingExprOrType, @@ -1880,8 +1950,8 @@ ExprResult Sema::CreateGenericSelectionExpr( // selection shall specify compatible types." for (unsigned j = i+1; j < NumAssocs; ++j) if (Types[j] && !Types[j]->getType()->isDependentType() && - Context.typesAreCompatible(Types[i]->getType(), - Types[j]->getType())) { + areTypesCompatibleForGeneric(Context, Types[i]->getType(), + Types[j]->getType())) { Diag(Types[j]->getTypeLoc().getBeginLoc(), diag::err_assoc_compatible_types) << Types[j]->getTypeLoc().getSourceRange() @@ -1919,16 +1989,19 @@ ExprResult Sema::CreateGenericSelectionExpr( for (unsigned i = 0; i < NumAssocs; ++i) { if (!Types[i]) DefaultIndex = i; - else if (ControllingExpr && - Context.typesAreCompatible( - ControllingExpr->getType().getCanonicalType(), - Types[i]->getType())) - CompatIndices.push_back(i); - else if (ControllingType && - Context.typesAreCompatible( - ControllingType->getType().getCanonicalType(), - Types[i]->getType())) - CompatIndices.push_back(i); + else { + bool Compatible; + QualType ControllingQT = + ControllingExpr ? ControllingExpr->getType().getCanonicalType() + : ControllingType->getType().getCanonicalType(); + QualType AssocQT = Types[i]->getType(); + + Compatible = + areTypesCompatibleForGeneric(Context, ControllingQT, AssocQT); + + if (Compatible) + CompatIndices.push_back(i); + } } auto GetControllingRangeAndType = [](Expr *ControllingExpr, @@ -4541,6 +4614,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: @@ -9129,6 +9203,26 @@ static AssignConvertType checkPointerTypesForAssignment(Sema &S, // C99 6.5.16.1p1 (constraint 3): both operands are pointers to qualified or // unqualified versions of compatible types, ... QualType ltrans = QualType(lhptee, 0), rtrans = QualType(rhptee, 0); + + if (ltrans->isOverflowBehaviorType() || rtrans->isOverflowBehaviorType()) { + if (!S.Context.hasSameType(ltrans, rtrans)) { + QualType LUnderlying = + ltrans->isOverflowBehaviorType() + ? ltrans->castAs()->getUnderlyingType() + : ltrans; + QualType RUnderlying = + rtrans->isOverflowBehaviorType() + ? rtrans->castAs()->getUnderlyingType() + : rtrans; + + if (S.Context.hasSameType(LUnderlying, RUnderlying)) + return AssignConvertType::IncompatiblePointerDiscardsOverflowBehavior; + + ltrans = LUnderlying; + rtrans = RUnderlying; + } + } + if (!S.Context.typesAreCompatible(ltrans, rtrans)) { // Check if the pointee types are compatible ignoring the sign. // We explicitly check for char so that we catch "char" vs @@ -9357,6 +9451,12 @@ AssignConvertType Sema::CheckAssignmentConstraints(QualType LHSType, } } + if ((LHSType->isOverflowBehaviorType() || + RHSType->isOverflowBehaviorType()) && + !Context.areCompatibleOverflowBehaviorTypes(LHSType, RHSType)) { + return AssignConvertType::IncompatibleOBTKinds; + } + // If we have an atomic type, try a non-atomic assignment, then just add an // atomic qualification step. if (const AtomicType *AtomicTy = dyn_cast(LHSType)) { @@ -14350,6 +14450,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. @@ -17222,6 +17324,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* @@ -17306,6 +17414,22 @@ bool Sema::DiagnoseAssignmentResult(AssignConvertType ConvTy, DiagKind = diag::err_arc_weak_unavailable_assign; isInvalid = true; break; + case AssignConvertType::IncompatibleOBTKinds: { + auto getOBTKindName = [](QualType Ty) -> StringRef { + if (const auto *OBT = Ty->getAs()) { + return OBT->getBehaviorKind() == + OverflowBehaviorType::OverflowBehaviorKind::Trap + ? "__ob_trap" + : "__ob_wrap"; + } + return "non-OBT"; + }; + + Diag(Loc, diag::err_incompatible_obt_kinds_assignment) + << getOBTKindName(DstType) << getOBTKindName(SrcType); + isInvalid = true; + return true; + } case AssignConvertType::Incompatible: if (maybeDiagnoseAssignmentToFunction(*this, DstType, SrcExpr)) { if (Complained) diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp index 25728de1779ad..d084db0fa462f 100644 --- a/clang/lib/Sema/SemaLookup.cpp +++ b/clang/lib/Sema/SemaLookup.cpp @@ -3303,6 +3303,8 @@ addAssociatedClassesAndNamespaces(AssociatedLookup &Result, QualType Ty) { // Inline SPIR-V types are treated as fundamental types. case Type::HLSLInlineSpirv: break; + case Type::OverflowBehavior: + T = cast(T)->getUnderlyingType().getTypePtr(); } if (Queue.empty()) diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index 5657dfe0b9553..ccefc937cd60f 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, @@ -2238,6 +2243,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 @@ -2428,9 +2439,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). @@ -2496,6 +2514,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) { @@ -2846,6 +2867,41 @@ bool Sema::IsComplexPromotion(QualType FromType, QualType ToType) { ToComplex->getElementType()); } +bool Sema::IsOverflowBehaviorTypePromotion(QualType FromType, QualType ToType) { + if (!getLangOpts().OverflowBehaviorTypes) + return false; + + if (!FromType->isOverflowBehaviorType() || !ToType->isOverflowBehaviorType()) + return false; + + return Context.getTypeSize(FromType) < Context.getTypeSize(ToType); +} + +bool Sema::IsOverflowBehaviorTypeConversion(QualType FromType, + QualType ToType) { + if (!getLangOpts().OverflowBehaviorTypes) + return false; + + if (FromType->isOverflowBehaviorType() && !ToType->isOverflowBehaviorType()) { + // Don't allow implicit conversion from OverflowBehaviorType to scoped enum + if (const EnumType *ToEnumType = ToType->getAs()) { + const EnumDecl *ToED = + ToEnumType->getOriginalDecl()->getDefinitionOrSelf(); + if (ToED->isScoped()) + return false; + } + return true; + } + + if (!FromType->isOverflowBehaviorType() && ToType->isOverflowBehaviorType()) + return true; + + if (FromType->isOverflowBehaviorType() && ToType->isOverflowBehaviorType()) + return Context.getTypeSize(FromType) > Context.getTypeSize(ToType); + + return false; +} + /// BuildSimilarlyQualifiedPointerType - In a pointer conversion from /// the pointer type FromPtr to a pointer to type ToPointee, with the /// same type qualifiers as FromPtr has on its pointee type. ToType, @@ -3878,6 +3934,35 @@ static bool tryAtomicConversion(Sema &S, Expr *From, QualType ToType, return true; } +static bool tryOverflowBehaviorTypeConversion(Sema &S, Expr *From, + QualType ToType, + bool InOverloadResolution, + StandardConversionSequence &SCS, + bool CStyle) { + const OverflowBehaviorType *ToOBT = ToType->getAs(); + if (!ToOBT) + return false; + + // Check for incompatible OBT kinds (e.g., trap vs wrap) + QualType FromType = From->getType(); + if (!S.Context.areCompatibleOverflowBehaviorTypes(FromType, ToType)) + return false; + + StandardConversionSequence InnerSCS; + if (!IsStandardConversion(S, From, ToOBT->getUnderlyingType(), + InOverloadResolution, InnerSCS, CStyle, + /*AllowObjCWritebackConversion=*/false)) + return false; + + SCS.Second = InnerSCS.Second; + SCS.setToType(1, InnerSCS.getToType(1)); + SCS.Third = InnerSCS.Third; + SCS.QualificationIncludesObjCLifetime = + InnerSCS.QualificationIncludesObjCLifetime; + SCS.setToType(2, InnerSCS.getToType(2)); + return true; +} + static bool isFirstArgumentCompatibleWithType(ASTContext &Context, CXXConstructorDecl *Constructor, QualType Type) { @@ -4672,6 +4757,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 @@ -4784,6 +4873,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 419f3e1ad30ed..b4e84a09c0c05 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -6443,6 +6443,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 3baa9775a49e4..ac877fbda64f1 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -2550,6 +2550,7 @@ static TemplateDeductionResult DeduceTemplateArgumentsByTypeMatch( case Type::ArrayParameter: case Type::HLSLAttributedResource: case Type::HLSLInlineSpirv: + case Type::OverflowBehavior: // No template argument deduction for these types return TemplateDeductionResult::Success; @@ -7101,6 +7102,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 bee613aa5f1c5..071080addc52a 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -300,6 +300,14 @@ namespace { return sema.Context.getBTFTagAttributedType(BTFAttr, WrappedType); } + /// Get a OverflowBehaviorType type for the overflow_behavior type + /// attribute. + QualType + getOverflowBehaviorType(OverflowBehaviorType::OverflowBehaviorKind Kind, + QualType UnderlyingType) { + return sema.Context.getOverflowBehaviorType(Kind, UnderlyingType); + } + /// Completely replace the \c auto in \p TypeWithAuto by /// \p Replacement. Also replace \p TypeWithAuto in \c TypeAttrPair if /// necessary. @@ -1554,6 +1562,23 @@ static QualType ConvertDeclSpecToType(TypeProcessingState &state) { Result = Qualified; } + // Check for __ob_wrap and __ob_trap + if (DS.isOverflowBehaviorSpecified()) { + if (!Result->isIntegerType()) { + SourceLocation Loc = DS.getOverflowBehaviorLoc(); + StringRef SpecifierName = + DeclSpec::getSpecifierName(DS.getOverflowBehaviorState()); + S.Diag(Loc, diag::err_overflow_behavior_non_integer_type) + << SpecifierName << Result.getAsString() << 1; + } else { + OverflowBehaviorType::OverflowBehaviorKind Kind = + DS.isWrapSpecified() + ? OverflowBehaviorType::OverflowBehaviorKind::Wrap + : OverflowBehaviorType::OverflowBehaviorKind::Trap; + Result = state.getOverflowBehaviorType(Kind, Result); + } + } + if (S.getLangOpts().HLSL) Result = S.HLSL().ProcessResourceTypeAttributes(Result); @@ -5891,6 +5916,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); @@ -6176,6 +6204,9 @@ namespace { void VisitBTFTagAttributedTypeLoc(BTFTagAttributedTypeLoc TL) { // nothing } + void VisitOverflowBehaviorTypeLoc(OverflowBehaviorTypeLoc TL) { + // nothing + } void VisitAdjustedTypeLoc(AdjustedTypeLoc TL) { // nothing } @@ -6626,6 +6657,109 @@ static void HandleAddressSpaceTypeAttribute(QualType &Type, } } +static void HandleOverflowBehaviorAttr(QualType &Type, const ParsedAttr &Attr, + TypeProcessingState &State) { + Sema &S = State.getSema(); + + // Check for -foverflow-behavior-types + if (!S.getLangOpts().OverflowBehaviorTypes) { + S.Diag(Attr.getLoc(), diag::warn_overflow_behavior_attribute_disabled) + << Attr << 1; + Attr.setInvalid(); + return; + } + + // Check the number of attribute arguments. + if (Attr.getNumArgs() != 1) { + S.Diag(Attr.getLoc(), diag::err_attribute_wrong_number_arguments) + << Attr << 1; + Attr.setInvalid(); + return; + } + + // Check that the underlying type is an integer type + if (!Type->isIntegerType()) { + S.Diag(Attr.getLoc(), diag::err_overflow_behavior_non_integer_type) + << Attr << Type.getAsString() << 0; // 0 for attribute + Attr.setInvalid(); + return; + } + + StringRef KindName = ""; + IdentifierInfo *Ident = nullptr; + + if (Attr.isArgIdent(0)) { + Ident = Attr.getArgAsIdent(0)->getIdentifierInfo(); + KindName = Ident->getName(); + } + + // Support identifier or string argument types. Failure to provide one of + // these two types results in a diagnostic that hints towards using string + // arguments (either "wrap" or "trap") as this is the most common use + // pattern. + if (!Ident) { + auto *Str = dyn_cast(Attr.getArgAsExpr(0)); + if (Str) + KindName = Str->getString(); + else { + S.Diag(Attr.getLoc(), diag::err_attribute_argument_type) + << Attr << AANT_ArgumentString; + Attr.setInvalid(); + return; + } + } + + OverflowBehaviorType::OverflowBehaviorKind Kind; + if (KindName == "wrap") { + Kind = OverflowBehaviorType::OverflowBehaviorKind::Wrap; + } else if (KindName == "trap") { + Kind = OverflowBehaviorType::OverflowBehaviorKind::Trap; + } else { + S.Diag(Attr.getLoc(), diag::err_overflow_behavior_unknown_ident) + << KindName << Attr; + Attr.setInvalid(); + return; + } + + // Check for mixed specifier/attribute usage + const DeclSpec &DS = State.getDeclarator().getDeclSpec(); + if (DS.isWrapSpecified() || DS.isTrapSpecified()) { + // We have both specifier and attribute on the same type. If + // OverflowBehaviorKinds are the same we can just warn. + OverflowBehaviorType::OverflowBehaviorKind SpecifierKind = + DS.isWrapSpecified() ? OverflowBehaviorType::OverflowBehaviorKind::Wrap + : OverflowBehaviorType::OverflowBehaviorKind::Trap; + + if (SpecifierKind != Kind) { + StringRef SpecifierName = DS.isWrapSpecified() ? "wrap" : "trap"; + S.Diag(Attr.getLoc(), diag::err_conflicting_overflow_behaviors) + << 1 << SpecifierName << KindName; + Attr.setInvalid(); + return; + } + S.Diag(Attr.getLoc(), diag::warn_redundant_overflow_behaviors_mixed) + << KindName; + Attr.setInvalid(); + return; + } + + // Check for conflicting overflow behavior attributes + if (const auto *ExistingOBT = Type->getAs()) { + OverflowBehaviorType::OverflowBehaviorKind ExistingKind = + ExistingOBT->getBehaviorKind(); + if (ExistingKind != Kind) { + S.Diag(Attr.getLoc(), diag::err_conflicting_overflow_behaviors) << 0; + if (Kind == OverflowBehaviorType::OverflowBehaviorKind::Trap) { + Type = State.getOverflowBehaviorType(Kind, + ExistingOBT->getUnderlyingType()); + } + return; + } + } else { + Type = State.getOverflowBehaviorType(Kind, Type); + } +} + /// handleObjCOwnershipTypeAttr - Process an objc_ownership /// attribute on the specified type. /// @@ -8968,6 +9102,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 51b55b82f4208..c5eff4b6c59e1 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -7641,6 +7641,27 @@ QualType TreeTransform::TransformBTFTagAttributedType( llvm_unreachable("Unexpected TreeTransform for BTFTagAttributedType"); } +template +QualType TreeTransform::TransformOverflowBehaviorType( + TypeLocBuilder &TLB, OverflowBehaviorTypeLoc TL) { + const OverflowBehaviorType *OldTy = TL.getTypePtr(); + QualType InnerTy = getDerived().TransformType(TLB, TL.getWrappedLoc()); + if (InnerTy.isNull()) + return QualType(); + + QualType Result = TL.getType(); + if (getDerived().AlwaysRebuild() || InnerTy != OldTy->getUnderlyingType()) { + Result = SemaRef.Context.getOverflowBehaviorType(OldTy->getBehaviorKind(), + InnerTy); + if (Result.isNull()) + return QualType(); + } + + OverflowBehaviorTypeLoc NewTL = TLB.push(Result); + NewTL.initializeLocal(SemaRef.Context, TL.getAttrLoc()); + return Result; +} + template QualType TreeTransform::TransformHLSLAttributedResourceType( TypeLocBuilder &TLB, HLSLAttributedResourceTypeLoc TL) { diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp index 6acf79acea111..e162acedb433a 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -7477,6 +7477,10 @@ void TypeLocReader::VisitBTFTagAttributedTypeLoc(BTFTagAttributedTypeLoc TL) { // Nothing to do. } +void TypeLocReader::VisitOverflowBehaviorTypeLoc(OverflowBehaviorTypeLoc TL) { + TL.setAttrLoc(readSourceLocation()); +} + void TypeLocReader::VisitHLSLAttributedResourceTypeLoc( HLSLAttributedResourceTypeLoc TL) { // Nothing to do. diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp index 09b1e58ef220c..d99924061dd53 100644 --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -604,6 +604,10 @@ void TypeLocWriter::VisitBTFTagAttributedTypeLoc(BTFTagAttributedTypeLoc TL) { // Nothing to do. } +void TypeLocWriter::VisitOverflowBehaviorTypeLoc(OverflowBehaviorTypeLoc TL) { + addSourceLocation(TL.getAttrLoc()); +} + void TypeLocWriter::VisitHLSLAttributedResourceTypeLoc( HLSLAttributedResourceTypeLoc TL) { // Nothing to do. diff --git a/clang/test/AST/ast-print-overflow-behavior.cpp b/clang/test/AST/ast-print-overflow-behavior.cpp new file mode 100644 index 0000000000000..f66479951021f --- /dev/null +++ b/clang/test/AST/ast-print-overflow-behavior.cpp @@ -0,0 +1,17 @@ +// RUN: %clang_cc1 -foverflow-behavior-types -std=c++11 -ast-print %s -o - | FileCheck %s --check-prefix=PRINT + +// RUN: %clang_cc1 -foverflow-behavior-types -std=c++11 -fsyntax-only %s +// RUN: %clang_cc1 -foverflow-behavior-types -std=c++11 -ast-print %s -o %t.1.cpp +// RUN: %clang_cc1 -foverflow-behavior-types -std=c++11 -ast-print %t.1.cpp -o %t.2.cpp +// RUN: diff %t.1.cpp %t.2.cpp + +extern int __attribute__((overflow_behavior(trap))) a; +extern int __attribute__((overflow_behavior(wrap))) b; + +extern int __ob_trap c; +extern int __ob_wrap d; + +// PRINT: extern __ob_trap int a; +// PRINT: extern __ob_wrap int b; +// PRINT: extern __ob_trap int c; +// PRINT: extern __ob_wrap int d; diff --git a/clang/test/AST/overflow-behavior-keywords-ast.cpp b/clang/test/AST/overflow-behavior-keywords-ast.cpp new file mode 100644 index 0000000000000..4269db7bdbfbb --- /dev/null +++ b/clang/test/AST/overflow-behavior-keywords-ast.cpp @@ -0,0 +1,16 @@ +// RUN: %clang_cc1 -foverflow-behavior-types -ast-dump %s | FileCheck %s + +// Test that keyword and attribute syntax produce the same OverflowBehaviorType + +// Attribute syntax +int __attribute__((overflow_behavior(wrap))) attr_wrap; +int __attribute__((overflow_behavior(trap))) attr_trap; + +// Keyword syntax +int __ob_wrap keyword_wrap; +int __ob_trap keyword_trap; + +// CHECK: VarDecl {{.*}} attr_wrap '__ob_wrap int' +// CHECK: VarDecl {{.*}} attr_trap '__ob_trap int' +// CHECK: VarDecl {{.*}} keyword_wrap '__ob_wrap int' +// CHECK: VarDecl {{.*}} keyword_trap '__ob_trap int' diff --git a/clang/test/CodeGen/mangle-ms-overflow-behavior.cpp b/clang/test/CodeGen/mangle-ms-overflow-behavior.cpp new file mode 100644 index 0000000000000..06af5b36f3eb1 --- /dev/null +++ b/clang/test/CodeGen/mangle-ms-overflow-behavior.cpp @@ -0,0 +1,12 @@ +// RUN: %clang_cc1 -emit-llvm %s -o - -fms-extensions -triple=x86_64-pc-win32 -foverflow-behavior-types | FileCheck %s + +#define __wrap __attribute__((overflow_behavior(wrap))) +#define __trap __attribute__((overflow_behavior(trap))) + +typedef int __ob_wrap int_wrap; + +// CHECK: define dso_local void @"?test_wrap_int@@YAXU?$ObtWrap_@H@__clang@@@Z" +void test_wrap_int(int_wrap x) {} + +// CHECK: define dso_local void @"?test_trap_int@@YAXU?$ObtTrap_@H@__clang@@@Z" +void test_trap_int(int __ob_trap y) {} diff --git a/clang/test/CodeGen/overflow-behavior-types-extensions.c b/clang/test/CodeGen/overflow-behavior-types-extensions.c new file mode 100644 index 0000000000000..50e86c64571a1 --- /dev/null +++ b/clang/test/CodeGen/overflow-behavior-types-extensions.c @@ -0,0 +1,104 @@ +// RUN: %clang_cc1 -triple x86_64-linux-gnu -foverflow-behavior-types -std=c2x %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK + +#define __wrap __attribute__((overflow_behavior(wrap))) +#define __no_trap __attribute__((overflow_behavior(trap))) + +typedef int __ob_wrap w_int; +typedef int __ob_trap no_trap_int; + + +// CHECK-LABEL: define {{.*}} @generic_selection_test_nomatch +int generic_selection_test_nomatch(int x) { + // CHECK: ret i32 3 + return _Generic(x, w_int: 1, no_trap_int: 2, default: 3); +} + +// CHECK-LABEL: define {{.*}} @generic_selection_test_obtmatch +int generic_selection_test_obtmatch(w_int x) { + // CHECK: ret i32 1 + return _Generic(x, w_int: 1, no_trap_int: 2, default: 3); +} + +// CHECK-LABEL: define {{.*}} @generic_selection_test_obt_nomatch +int generic_selection_test_obt_nomatch(w_int x) { + // CHECK: ret i32 3 + return _Generic(x, int: 1, char: 2, default: 3); +} + +// CHECK-LABEL: define {{.*}} @signed_bitint_test +void signed_bitint_test(_BitInt(4) __ob_trap a, _BitInt(8) __ob_trap b, _BitInt(99) __ob_wrap c) { + // CHECK: call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 + (a + 1); + + // CHECK: call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 + (b + 1); + + // CHECK: add i99 {{.*}}, 1 + (c + 1); +} + +// CHECK-LABEL: define {{.*}} @unsigned_bitint_test +void unsigned_bitint_test(unsigned _BitInt(4) __ob_trap a, unsigned _BitInt(8) __ob_trap b, unsigned _BitInt(99) __ob_wrap c) { + // CHECK: call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 + (a + 1); + + // CHECK: call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 + (b + 1); + + // CHECK: add i99 {{.*}}, 1 + (c + 1); +} + +// CHECK-LABEL: define {{.*}} @generic_obt_vs_underlying +int generic_obt_vs_underlying(int plain, int __ob_wrap wrapped) { + // Regular int should match int case, store 1 + // CHECK: store i32 1, ptr %plain_result + int plain_result = _Generic(plain, + int: 1, + int __ob_wrap: 2, + default: 3); + + // Wrapped int should match __ob_wrap case, store 2 + // CHECK: store i32 2, ptr %wrapped_result + int wrapped_result = _Generic(wrapped, + int: 1, + int __ob_wrap: 2, + default: 3); + + // CHECK: add nsw i32 + // CHECK: ret i32 + return plain_result + wrapped_result; // Should return 1 + 2 = 3 +} + +// CHECK-LABEL: define {{.*}} @generic_comprehensive +int generic_comprehensive(int __ob_wrap w, int __ob_trap t, int plain) { + // CHECK: store i32 111, ptr %w_val + int w_val = _Generic(w, + int: 100, + int __ob_wrap: 111, + int __ob_trap: 222, + char: 333, + default: 999); + + // CHECK: store i32 222, ptr %t_val + int t_val = _Generic(t, + int: 100, + int __ob_wrap: 111, + int __ob_trap: 222, + char: 333, + default: 999); + + // CHECK: store i32 100, ptr %p_val + int p_val = _Generic(plain, + int: 100, + int __ob_wrap: 111, + int __ob_trap: 222, + char: 333, + default: 999); + + // CHECK: add nsw i32 + // CHECK: add nsw i32 + // CHECK: ret i32 + return w_val + t_val + p_val; // Should return 111 + 222 + 100 = 433 +} + diff --git a/clang/test/CodeGen/overflow-behavior-types-operators.cpp b/clang/test/CodeGen/overflow-behavior-types-operators.cpp new file mode 100644 index 0000000000000..22794cac4f3ad --- /dev/null +++ b/clang/test/CodeGen/overflow-behavior-types-operators.cpp @@ -0,0 +1,70 @@ +// RUN: %clang_cc1 -triple x86_64-linux-gnu %s -foverflow-behavior-types \ +// RUN: -fsanitize=signed-integer-overflow -emit-llvm -o - -std=c++14 | FileCheck %s + +#define __wrap __attribute__((overflow_behavior(wrap))) +#define __no_trap __attribute__((overflow_behavior(trap))) + +typedef int __ob_wrap wrap_int; +typedef int __ob_trap no_trap_int; +typedef unsigned int __ob_wrap u_wrap_int; +typedef unsigned int __ob_trap u_no_trap_int; + +//===----------------------------------------------------------------------===// +// Compound Assignment Operators +//===----------------------------------------------------------------------===// + +// CHECK-LABEL: define {{.*}} @_Z28compound_assignment_operatorv +void compound_assignment_operator() { + wrap_int a = 1; + // CHECK: add i32 + a += 1; + + no_trap_int b = 1; + // CHECK: llvm.sadd.with.overflow.i32 + b += 1; + + u_wrap_int c = 1; + // CHECK: sub i32 + c -= 1; + + u_no_trap_int d = 1; + // CHECK: llvm.usub.with.overflow.i32 + d -= 1; + + wrap_int e = 2; + // CHECK: mul i32 + e *= 2; + + no_trap_int f = 2; + // CHECK: llvm.smul.with.overflow.i32 + f *= 2; +} + +//===----------------------------------------------------------------------===// +// Bitwise and Shift Operators +//===----------------------------------------------------------------------===// + +// CHECK-LABEL: define {{.*}} @_Z27bitwise_and_shift_operatorsv +void bitwise_and_shift_operators() { + wrap_int a = 1; + // CHECK: shl i32 + // No overflow check for shifts + a <<= 1; + + no_trap_int b = 1; + // CHECK: ashr i32 + // No overflow check for shifts + b >>= 1; + + wrap_int c = 1; + // CHECK: and i32 + c &= 1; + + no_trap_int d = 1; + // CHECK: xor i32 + d ^= 1; + + u_wrap_int e = 1; + // CHECK: or i32 + e |= 1; +} diff --git a/clang/test/CodeGen/overflow-behavior-types-promotions.cpp b/clang/test/CodeGen/overflow-behavior-types-promotions.cpp new file mode 100644 index 0000000000000..bebc2c3b7001f --- /dev/null +++ b/clang/test/CodeGen/overflow-behavior-types-promotions.cpp @@ -0,0 +1,49 @@ +// RUN: %clang_cc1 -triple x86_64-linux-gnu %s -foverflow-behavior-types \ +// RUN: -fsanitize=signed-integer-overflow,unsigned-integer-overflow -emit-llvm -o - -std=c++14 | FileCheck %s + +#define __wrap __attribute__((overflow_behavior(wrap))) +#define __no_trap __attribute__((overflow_behavior(trap))) + +typedef int __ob_wrap wrap_int; +typedef char __ob_wrap wrap_char; +typedef int __ob_trap no_trap_int; +typedef unsigned int __ob_wrap u_wrap_int; +typedef unsigned int __ob_trap u_no_trap_int; + +// CHECK-LABEL: define {{.*}} @_Z30conditional_operator_promotionbU8ObtWrap_cU8ObtTrap_ii +void conditional_operator_promotion(bool cond, wrap_char w, no_trap_int nw, int i) { + // CHECK: cond.end: + // CHECK-NEXT: %cond1 = phi i32 + // CHECK-NEXT: store i32 %cond1, ptr %r1 + // CHECK-NEXT: %{{.*}} = load i32, ptr %r1 + // CHECK-NEXT: add i32 + auto r1 = cond ? w : i; + (void)(r1 + 2147483647); + + // no_trap wins over wrap. + // CHECK: cond.end6: + // CHECK-NEXT: %cond7 = phi i32 + // CHECK-NEXT: store i32 %cond7, ptr %r2 + // CHECK-NEXT: %{{.*}} = load i32, ptr %r2 + // CHECK-NEXT: call { i32, i1 } @llvm.sadd.with.overflow.i32 + auto r2 = cond ? w : nw; + (void)(r2 + 2147483647); +} + +// CHECK-LABEL: define {{.*}} @_Z20promotion_rules_testU8ObtWrap_iU8ObtTrap_iU8ObtWrap_jU8ObtTrap_j +void promotion_rules_test(wrap_int sw, no_trap_int snw, u_wrap_int uw, u_no_trap_int unw) { + // Unsigned is favored over signed for same-behavior OBTs. + // CHECK: add i32 + auto r1 = sw + uw; + (void)r1; + + // trap is favored over wrap. Result is unsigned no_trap. + // CHECK: call { i32, i1 } @llvm.uadd.with.overflow.i32 + auto r2 = sw + unw; + (void)r2; + + // trap is favored over wrap. Result is unsigned trap (unsigned int + int → unsigned int in C). + // CHECK: call { i32, i1 } @llvm.uadd.with.overflow.i32 + auto r3 = uw + snw; + (void)r3; +} diff --git a/clang/test/CodeGen/overflow-behavior-types-scl.c b/clang/test/CodeGen/overflow-behavior-types-scl.c new file mode 100644 index 0000000000000..1bc1139bb72b0 --- /dev/null +++ b/clang/test/CodeGen/overflow-behavior-types-scl.c @@ -0,0 +1,43 @@ +// RUN: rm -rf %t +// RUN: split-file %s %t + +// RUN: %clang_cc1 -triple x86_64-linux-gnu %t/test.c -fsanitize-ignorelist=%t/sio.scl -foverflow-behavior-types -fsanitize=signed-integer-overflow -emit-llvm -o - | FileCheck %s --check-prefix=SIO +// RUN: %clang_cc1 -triple x86_64-linux-gnu %t/test.c -fsanitize-ignorelist=%t/uio.scl -foverflow-behavior-types -fsanitize=unsigned-integer-overflow -emit-llvm -o - | FileCheck %s --check-prefix=UIO + +//--- sio.scl +[signed-integer-overflow] +# ignore signed-integer-overflow instrumentation across all types +type:* + +//--- uio.scl +[unsigned-integer-overflow] +# ignore unsigned-integer-overflow instrumentation across all types +type:* + +//--- test.c +#define __wrap __attribute__((overflow_behavior("wrap"))) +#define __no_trap __attribute__((overflow_behavior("trap"))) + +// SIO-LABEL: define {{.*}} @foo +// UIO-LABEL: define {{.*}} @foo +void foo(void) { + // SIO-LABEL: load volatile i32, ptr @a, align 4 + volatile extern int a; + volatile extern char b; + volatile extern char __ob_trap c; // nowrap has precedence over scl entries + + // SIO: add nsw i32 + (a + 1); + // SIO: add nsw i32 + (b + 1); + // SIO: @llvm.sadd.with.overflow.i32 + (c + 1); + + // UIO-LABEL: load volatile i32, ptr @d, align 4 + volatile extern unsigned int d; + volatile extern unsigned short __ob_trap e; + // UIO: add i32 + (d + 1); + // UIO: @llvm.sadd.with.overflow.i32 + (e + 1); +} diff --git a/clang/test/CodeGen/overflow-behavior-types.c b/clang/test/CodeGen/overflow-behavior-types.c new file mode 100644 index 0000000000000..5e951c89ecde6 --- /dev/null +++ b/clang/test/CodeGen/overflow-behavior-types.c @@ -0,0 +1,142 @@ +// RUN: %clang_cc1 -triple x86_64-linux-gnu -foverflow-behavior-types %s \ +// RUN: -fsanitize=signed-integer-overflow,unsigned-integer-overflow,implicit-signed-integer-truncation,implicit-unsigned-integer-truncation \ +// RUN: -emit-llvm -o - | FileCheck %s --check-prefix=DEFAULT + +// RUN: %clang_cc1 -triple x86_64-linux-gnu -foverflow-behavior-types %s -ftrapv \ +// RUN: -fsanitize=signed-integer-overflow,unsigned-integer-overflow,implicit-signed-integer-truncation,implicit-unsigned-integer-truncation \ +// RUN: -emit-llvm -o - | FileCheck %s --check-prefix=DEFAULT + +// RUN: %clang_cc1 -triple x86_64-linux-gnu -foverflow-behavior-types %s \ +// RUN: -ftrapv -ftrapv-handler OVERFLOW_HANDLER \ +// RUN: -emit-llvm -o - | FileCheck %s --check-prefix=TRAPV-HANDLER + +// RUN: %clang_cc1 -triple x86_64-linux-gnu -foverflow-behavior-types %s -fwrapv \ +// RUN: -fsanitize=signed-integer-overflow,unsigned-integer-overflow,implicit-signed-integer-truncation,implicit-unsigned-integer-truncation \ +// RUN: -emit-llvm -o - | FileCheck %s --check-prefix=DEFAULT + +// RUN: %clang_cc1 -triple x86_64-linux-gnu -foverflow-behavior-types %s \ +// RUN: -fsanitize-undefined-ignore-overflow-pattern=all \ +// RUN: -fsanitize=signed-integer-overflow,unsigned-integer-overflow,implicit-signed-integer-truncation,implicit-unsigned-integer-truncation \ +// RUN: -emit-llvm -o - | FileCheck %s --check-prefix=EXCL + +// RUN: %clang_cc1 -triple x86_64-linux-gnu -foverflow-behavior-types %s \ +// RUN: -emit-llvm -o - | FileCheck %s --check-prefix=NOSAN + +#define __wrap __attribute__((overflow_behavior("wrap"))) +#define __no_trap __attribute__((overflow_behavior("trap"))) + +// DEFAULT-LABEL: define {{.*}} @test1 +// TRAPV-HANDLER-LABEL: define {{.*}} @test1 +// NOSAN-LABEL: define {{.*}} @test1 +void test1(int __ob_wrap a, int __ob_trap b) { + // DEFAULT: add i32 + // TRAPV-HANDLER: add i32 + // NOSAN: add i32 + (a + 1); + + // DEFAULT: llvm.sadd.with.overflow.i32 + // TRAPV-HANDLER: %[[T0:.*]] = load i32, ptr %b + // TRAPV-HANDLER: call {{.*}} @OVERFLOW_HANDLER(i64 %[[T0]] + // NOSAN: %[[T0:.*]] = load i32, ptr %b + // NOSAN-NEXT: %[[T1:.*]] = call {{.*}} @llvm.sadd.with.overflow.i32(i32 %[[T0]] + // NOSAN: %[[OF:.*]] = extractvalue {{.*}} %[[T1]], 1 + // NOSAN-NEXT: %[[XOR:.*]] = xor i1 %[[OF]] + // NOSAN-NEXT: br i1 %[[XOR]]{{.*}}cont, label %[[TRAP:.*]], !prof + // NOSAN: [[TRAP]]: + // NOSAN-NEXT: call void @llvm.ubsantrap + (b + 1); + + // DEFAULT: sub i32 0 + (-a); + + // DEFAULT: llvm.ssub.with.overflow.i32 + (-b); + + // DEFAULT: add i32 + a++; + // DEFAULT: llvm.sadd.with.overflow.i32 + b++; + + // DEFAULT: add i32 + ++a; + // DEFAULT: llvm.sadd.with.overflow.i32 + ++b; + + volatile extern int divisor; + // DEFAULT: %[[T0:.*]] = load i32, ptr %a + // DEFAULT-NEXT: %[[T1:.*]] = load volatile i32, ptr @divisor + // DEFAULT-NOT: br {{.*}} %handler.divrem_overflow + // DEFAULT: sdiv i32 %[[T0]], %[[T1]] + a/divisor; + + // DEFAULT: %[[T0:.*]] = load i32, ptr %b + // DEFAULT-NEXT: %[[T1:.*]] = load volatile i32, ptr @divisor + // DEFAULT: br {{.*}} %handler.divrem_overflow + b/divisor; +} + +// DEFAULT-LABEL: define {{.*}} @test2 +void test2(unsigned char __ob_wrap a, unsigned char __ob_trap b) { + // DEFAULT: add i32 + (a + 1); + // DEFAULT: llvm.sadd.with.overflow.i32 + (b + 1); + + // DEFAULT: %[[T0:.*]] = load volatile i64, ptr @big + // DEFAULT: %[[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 __ob_wrap a; + volatile extern short __ob_wrap b; + // DEFAULT: add i32 + (a + b); + + // no_trap has precedence over wrap, regardless of bit widths + volatile extern unsigned long long __ob_wrap c; + volatile extern char __ob_trap d; + + // DEFAULT: %[[T0:.*]] = load volatile i64, ptr @c + // DEFAULT: %[[T1:.*]] = load volatile i8, ptr @d + // DEFAULT: @llvm.uadd.with.overflow.i64 + (c + d); + + volatile extern int __ob_trap e; + volatile extern unsigned int __ob_wrap f; + + // DEFAULT: @llvm.usub.with.overflow.i32 + (e - f); +} + +typedef int __attribute__((overflow_behavior(wrap))) wrap_int; +typedef int __attribute__((overflow_behavior(trap))) no_trap_int; +// DEFAULT-LABEL: define {{.*}} @typedefs +void typedefs(no_trap_int a, wrap_int b) { + // DEFAULT: llvm.sadd.with.overflow.i32 + (a + 100); + + // DEFAULT: add i32 + (b + 100); +} + +// EXCL-LABEL: define {{.*}} @ignored_patterns +void ignored_patterns(unsigned long __attribute__((overflow_behavior(trap))) a) { + // EXCL: %[[T0:.*]] = load i64, ptr %a.addr + // EXCL-NEXT: add i64 %[[T0]], -1 + while (a--) { /*...*/ } + + // EXCL: %[[T1:.*]] = load i64, ptr %a.addr + // EXCL: %[[T2:.*]] = load volatile i64, ptr %b + // EXCL-NEXT: add i64 %[[T1]], %[[T2]] + volatile unsigned long __attribute__((overflow_behavior(trap))) b; + if (a + b < a) { /*...*/ } +} diff --git a/clang/test/CodeGen/overflow-behavior-types.cpp b/clang/test/CodeGen/overflow-behavior-types.cpp new file mode 100644 index 0000000000000..25ca14bbd3b40 --- /dev/null +++ b/clang/test/CodeGen/overflow-behavior-types.cpp @@ -0,0 +1,51 @@ +// RUN: %clang_cc1 -triple x86_64-linux-gnu %s -foverflow-behavior-types \ +// RUN: -fsanitize=signed-integer-overflow,unsigned-integer-overflow,implicit-signed-integer-truncation,implicit-unsigned-integer-truncation \ +// RUN: -emit-llvm -o - | FileCheck %s --check-prefix=DEFAULT + +#define __wrap __attribute__((overflow_behavior(wrap))) +#define __trap __attribute__((overflow_behavior(trap))) + +class Foo { +public: + unsigned long other; + char __ob_wrap a; + + Foo() = delete; + Foo(char _a) : a(_a) {} + + decltype(a) getA() const { return a; } +}; + +// DEFAULT-LABEL: define {{.*}} @_Z12test_membersc +void test_members(char some) { + Foo foo{some}; + + // DEFAULT: %[[A:.*]] = getelementptr inbounds nuw %class.Foo, ptr %foo, i32 0, i32 1 + // DEFAULT-NEXT: %[[T1:.*]] = load i8, ptr %[[A]] + // DEFAULT-NEXT: %[[CONV:.*]] = sext i8 %[[T1]] to i32 + // DEFAULT-NEXT: %inc{{\d*}} = add i32 %[[CONV]], 1 + (++foo.a); + + // DEFAULT: %[[CALL:.*]] = call noundef signext i8 @_ZNK3Foo4getAEv + // DEFAULT-NEXT: sext i8 %[[CALL]] to i32 + // DEFAULT-NEXT: add i32 {{.*}}, 1 + (void)(foo.getA() + 1); +} + +// DEFAULT-LABEL: define {{.*}} @_Z9test_autoU8ObtWrap_c +void test_auto(char __ob_wrap a) { + auto b = a; + + // DEFAULT: %[[T1:.*]] = load i8, ptr %b + // DEFAULT: sub i32 {{.*}}, 1 + (b - 1); // no instrumentation +} + + +int overloadme(__ob_trap int a) { return 0; } +int overloadme(int a) { return 1; } // make sure we pick this one +// DEFAULT-LABEL: define {{.*}}test_overload_set_exact_match +int test_overload_set_exact_match(int a) { + // DEFAULT: call {{.*}} @_Z10overloadmei + return overloadme(a); +} diff --git a/clang/test/Misc/pragma-attribute-supported-attributes-list.test b/clang/test/Misc/pragma-attribute-supported-attributes-list.test index 73d4cb1769ed5..436d7121480fc 100644 --- a/clang/test/Misc/pragma-attribute-supported-attributes-list.test +++ b/clang/test/Misc/pragma-attribute-supported-attributes-list.test @@ -168,6 +168,7 @@ // CHECK-NEXT: OpenCLIntelReqdSubGroupSize (SubjectMatchRule_function) // CHECK-NEXT: OpenCLNoSVM (SubjectMatchRule_variable) // CHECK-NEXT: OptimizeNone (SubjectMatchRule_function, SubjectMatchRule_objc_method) +// CHECK-NEXT: OverflowBehavior (SubjectMatchRule_variable, SubjectMatchRule_type_alias, SubjectMatchRule_field) // CHECK-NEXT: Overloadable (SubjectMatchRule_function) // CHECK-NEXT: Owner (SubjectMatchRule_record_not_is_union) // CHECK-NEXT: ParamTypestate (SubjectMatchRule_variable_is_parameter) diff --git a/clang/test/Sema/attr-overflow-behavior-constexpr.cpp b/clang/test/Sema/attr-overflow-behavior-constexpr.cpp new file mode 100644 index 0000000000000..0c9ed48a6fef4 --- /dev/null +++ b/clang/test/Sema/attr-overflow-behavior-constexpr.cpp @@ -0,0 +1,43 @@ +// RUN: %clang_cc1 -triple x86_64-linux-gnu %s -foverflow-behavior-types -verify -fsyntax-only -std=c++14 + +#define __wrap __attribute__((overflow_behavior(wrap))) +#define __no_trap __attribute__((overflow_behavior(trap))) + +typedef int __ob_wrap wrap_int; +typedef int __ob_trap no_trap_int; + +constexpr wrap_int add(wrap_int a, wrap_int b) { + return a + b; +} + +constexpr no_trap_int sub(no_trap_int a, no_trap_int b) { + return a - b; // expected-note {{-2147483649 is outside the range of representable values}} +} + +void constexpr_test() { + constexpr wrap_int max = 2147483647; + constexpr wrap_int one = 1; + static_assert(add(max, one) == -2147483648, "constexpr wrapping failed"); + + constexpr no_trap_int min = -2147483648; + constexpr no_trap_int one_nw = 1; + constexpr no_trap_int res = sub(min, one_nw); // expected-error {{constexpr variable 'res' must be initialized by a constant expression}} expected-note {{in call to 'sub(-2147483648, 1)'}} +} + +template +void check_deduction_wrap(T) { + static_assert(__is_same(T, wrap_int), "T should be deduced as wrap_int"); +} + +template +void check_deduction_no_trap(T) { + static_assert(__is_same(T, no_trap_int), "T should be deduced as no_trap_int"); +} + +void template_deduction_test() { + wrap_int w = 0; + check_deduction_wrap(w); + + no_trap_int nw = 0; + check_deduction_no_trap(nw); +} diff --git a/clang/test/Sema/attr-overflow-behavior-format-strings.c b/clang/test/Sema/attr-overflow-behavior-format-strings.c new file mode 100644 index 0000000000000..83f8c35824b6c --- /dev/null +++ b/clang/test/Sema/attr-overflow-behavior-format-strings.c @@ -0,0 +1,93 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -Wformat -foverflow-behavior-types -isystem %S/Inputs %s + +// Test format string checking with overflow behavior types +// This ensures that OverflowBehaviorTypes work seamlessly with printf/scanf +// without spurious format warnings. + +int printf(const char *restrict, ...); +int sprintf(char *restrict, const char *restrict, ...); +int snprintf(char *restrict, __SIZE_TYPE__, const char *restrict, ...); + +int scanf(const char *restrict, ...); +int sscanf(const char *restrict, const char *restrict, ...); + +#define __wrap __attribute__((overflow_behavior(wrap))) +#define __trap __attribute__((overflow_behavior(trap))) + +typedef int __ob_wrap wrap_int; +typedef int __ob_trap no_trap_int; +typedef unsigned int __ob_wrap wrap_uint; +typedef unsigned int __ob_trap no_trap_uint; +typedef short __ob_wrap wrap_short; +typedef long __ob_trap no_trap_long; + +void test_printf_compatibility() { + wrap_int wi = 42; + no_trap_int ni = 42; + wrap_uint wu = 42U; + no_trap_uint nu = 42U; + wrap_short ws = 42; + no_trap_long nl = 42L; + + // These should all work without warnings - OBTs should be treated as their underlying types + printf("%d", wi); + printf("%d", ni); + printf("%u", wu); + printf("%u", nu); + printf("%hd", ws); + printf("%ld", nl); + printf("%x", wu); + printf("%o", nu); + + printf("%s", wi); // expected-warning{{format specifies type 'char *' but the argument has type 'int'}} + printf("%f", wi); // expected-warning{{format specifies type 'double' but the argument has type 'int'}} + printf("%ld", wi); // expected-warning{{format specifies type 'long' but the argument has type 'int'}} +} + +void test_scanf_compatibility() { + wrap_int wi; + no_trap_int ni; + wrap_uint wu; + no_trap_uint nu; + wrap_short ws; + no_trap_long nl; + + // These should all work without warnings - pointers to OBTs should be treated as pointers to underlying types + scanf("%d", &wi); + scanf("%d", &ni); + scanf("%u", &wu); + scanf("%u", &nu); + scanf("%hd", &ws); + scanf("%ld", &nl); + scanf("%x", &wu); + scanf("%o", &nu); + + scanf("%s", &wi); // expected-warning{{format specifies type 'char *' but the argument has type 'wrap_int *'}} + scanf("%f", &wi); // expected-warning{{format specifies type 'float *' but the argument has type 'wrap_int *'}} + scanf("%ld", &wi); // expected-warning{{format specifies type 'long *' but the argument has type 'wrap_int *'}} +} + +void test_mixed_formats() { + wrap_int wi = 42; + int regular_int = 42; + + printf("%d + %d = %d", wi, regular_int, wi + regular_int); + scanf("%d %d", &wi, ®ular_int); +} + +typedef unsigned char __ob_wrap wrap_byte; +typedef long long __ob_trap safe_longlong; + +void test_typedef_formats() { + wrap_byte wb = 255; + safe_longlong sll = 123456789LL; + + printf("%hhu", wb); // OK: wrap_byte -> unsigned char for %hhu + printf("%lld", sll); // OK: safe_longlong -> long long for %lld + + scanf("%hhu", &wb); // OK: &wb treated as unsigned char* for %hhu + scanf("%lld", &sll); // OK: &sll treated as long long* for %lld + + printf("%d", wb); // OK: wb implicitly cast to int + printf("%d", sll); // expected-warning{{format specifies type 'int' but the argument has type 'long long'}} +} diff --git a/clang/test/Sema/attr-overflow-behavior-off.c b/clang/test/Sema/attr-overflow-behavior-off.c new file mode 100644 index 0000000000000..2aac2a887e060 --- /dev/null +++ b/clang/test/Sema/attr-overflow-behavior-off.c @@ -0,0 +1,3 @@ +// RUN: %clang_cc1 %s -Winteger-overflow -Wno-unused-value -verify -fsyntax-only + +typedef int __attribute__((overflow_behavior(wrap))) wrap_int; // expected-warning {{'overflow_behavior' attribute is ignored because it is not enabled;}} diff --git a/clang/test/Sema/attr-overflow-behavior-templates.cpp b/clang/test/Sema/attr-overflow-behavior-templates.cpp new file mode 100644 index 0000000000000..aa6067cc875b1 --- /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 __trap __attribute__((overflow_behavior(trap))) + +template +constexpr int template_overload_test(T) { + return 10; +} + +constexpr int template_overload_test(int) { + return 20; +} + +constexpr int template_overload_test(__ob_wrap int) { + return 30; +} + +constexpr int template_overload_test(__ob_trap int) { + return 40; +} + +void test_template_overload_resolution() { + static_assert(template_overload_test(42) == 20, "int should pick int overload"); + static_assert(template_overload_test((__ob_wrap int)42) == 30, "__ob_wrap int should pick __ob_wrap int overload"); + static_assert(template_overload_test((__ob_trap int)42) == 40, "__ob_trap int should pick __ob_trap int overload"); +} + +template +struct MultiSpecTester { + static constexpr int value = 0; +}; + +template<> +struct MultiSpecTester { + static constexpr int value = 1; +}; + +void test_choosing_generic_when_only_underlying_present() { + static_assert(MultiSpecTester::value == 1, "int should match int specialization"); + // OBTs don't choose template specialization based on underlying type, they should go with the generic + static_assert(MultiSpecTester<__ob_wrap int>::value == 0, "__ob_wrap int should match generic when there isn't a __ob_wrap specialization"); + static_assert(MultiSpecTester<__ob_trap int>::value == 0, "__ob_trap int should match generic when there isn't a __ob_trap specialization"); +} + +template +constexpr int only_int_template(int value) { + return value + 100; +} + +void test_template_conversion_fallback() { + static_assert(only_int_template(42) == 142, "int direct match should work"); + static_assert(only_int_template((__ob_wrap int)42) == 142, "__ob_wrap int implicit conversion should work"); + static_assert(only_int_template((__ob_trap int)42) == 142, "__ob_trap int implicit conversion should work"); +} + +void simple_overload_test(int); +void simple_overload_test(__ob_wrap int); + +template +void simple_overload_test(T) {} + +void test_function_vs_template_overload() { + int regular = 42; + __ob_wrap int wrapped = 42; + __ob_trap int trap = 42; + + simple_overload_test(regular); + simple_overload_test(wrapped); + simple_overload_test(trap); +} diff --git a/clang/test/Sema/attr-overflow-behavior.c b/clang/test/Sema/attr-overflow-behavior.c new file mode 100644 index 0000000000000..12e53e25c90b7 --- /dev/null +++ b/clang/test/Sema/attr-overflow-behavior.c @@ -0,0 +1,225 @@ +// RUN: %clang_cc1 %s -Winteger-overflow -Wno-unused-value -foverflow-behavior-types -Woverflow-behavior-conversion -Wconstant-conversion -verify -fsyntax-only -std=c11 -Wno-pointer-sign + +typedef int __attribute__((overflow_behavior)) bad_arg_count; // expected-error {{'overflow_behavior' attribute takes one argument}} +typedef int __attribute__((overflow_behavior(not_real))) bad_arg_spec; // expected-error {{'not_real' is not a valid argument to attribute 'overflow_behavior'}} +typedef int __attribute__((overflow_behavior("not_real"))) bad_arg_spec_str; // expected-error {{'not_real' is not a valid argument to attribute 'overflow_behavior'}} +typedef char* __attribute__((overflow_behavior("wrap"))) bad_type; // expected-error {{'overflow_behavior' attribute cannot be applied to non-integer type 'char *'}} + +typedef int __attribute__((overflow_behavior(wrap))) ok_wrap; // OK +typedef long __attribute__((overflow_behavior(trap))) ok_nowrap; // OK +typedef unsigned long __attribute__((overflow_behavior("wrap"))) str_ok_wrap; // OK +typedef char __attribute__((overflow_behavior("trap"))) str_ok_nowrap; // OK + +void foo() { + (2147483647 + 100); // expected-warning {{overflow in expression; result is }} + (ok_wrap)2147483647 + 100; // no warn +} + +#define __wrap __attribute__((overflow_behavior(wrap))) +#define __trap __attribute__((overflow_behavior(trap))) + +void ptr(int a) { + int __ob_trap *p = &a; // expected-warning {{initializing '__ob_trap int *' with an expression of type 'int *' discards overflow behavior}} +} + +void ptr2(__ob_trap int a) { + int *p = &a; // expected-warning {{initializing 'int *' with an expression of type '__ob_trap int *' discards overflow behavior}} +} + + +// verify semantics of -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 '__ob_wrap unsigned int' to 'unsigned int' discards overflow behavior}} + imp_disc(a); // expected-warning {{implicit conversion from '__ob_wrap unsigned int' to 'int' discards overflow behavior}} +} + +// -Wconversion for assignments that discard overflow behavior +void assignment_disc(unsigned __attribute__((overflow_behavior(wrap))) a) { + int b = a; // expected-warning {{implicit conversion from '__ob_wrap unsigned int' to 'int' during assignment discards overflow behavior}} + int c = (int)a; // OK +} + +void constant_conversion() { + // expected-warning@+2 {{implicit conversion from '__ob_wrap int' to 'short' changes value from 100000 to -31072}} + // expected-warning@+1 {{implicit conversion from '__ob_wrap int' to 'short' during assignment discards overflow behavior}} + short x1 = (int __ob_wrap)100000; + short __ob_wrap x2 = (int)100000; // No warning expected + // expected-warning@+1 {{implicit conversion from 'int' to '__ob_trap short' changes value from 100000 to -31072}} + short __ob_trap x3 = (int)100000; + // expected-warning@+2 {{implicit conversion from '__ob_trap int' to 'short' changes value from 100000 to -31072}} + // expected-warning@+1 {{implicit conversion from '__ob_trap int' to 'short' during assignment discards overflow behavior}} + short x4 = (int __ob_trap)100000; + + unsigned short __ob_wrap ux1 = (unsigned int)100000; // No warning - wrapping expected + // expected-warning@+2 {{implicit conversion from '__ob_wrap unsigned int' to 'unsigned short' changes value from 100000 to 34464}} + // expected-warning@+1 {{implicit conversion from '__ob_wrap unsigned int' to 'unsigned short' discards overflow behavior}} + unsigned short ux2 = (unsigned int __ob_wrap)100000; + unsigned short __ob_trap ux3 = (unsigned int)100000; // expected-warning {{implicit conversion from 'unsigned int' to '__ob_trap unsigned short' changes value from 100000 to 34464}} + unsigned short __ob_trap ux4 = (unsigned int __ob_trap)100000; // expected-warning {{implicit conversion from '__ob_trap unsigned int' to '__ob_trap unsigned short' changes value from 100000 to 34464}} + unsigned short __ob_trap ux5 = (unsigned int __ob_wrap)100000; // expected-error {{cannot assign between incompatible overflow behavior types ('__ob_trap' and '__ob_wrap')}} +} + +typedef long s64_typedef1; +typedef s64_typedef1 __attribute__((overflow_behavior(trap))) nw_s64_typedef2; +nw_s64_typedef2 global_var; +void test_nested_typedef_control_flow() { + // We had a crash during Sema with nested typedefs and control flow, make + // sure we don't crash and just warn. + if (global_var) {} // expected-warning {{implicit conversion from 'nw_s64_typedef2'}} +} + +int test_discard_on_return(unsigned long __ob_trap a) { + return a; // expected-warning {{implicit conversion from '__ob_trap unsigned long' to 'int' discards overflow behavior}} +} + +// Test OBT pointer compatibility +void test_obt_pointer_compatibility() { + unsigned long x = 42; + unsigned long __ob_trap y = 42; + unsigned long __ob_wrap z = 42; + + unsigned long *px = &x; + unsigned long __ob_trap *py = &y; + unsigned long __ob_wrap *pz = &z; + + // Same types - should not warn + px = &x; // OK + py = &y; // OK + pz = &z; // OK + + // Different OBT annotations - should warn but allow + // expected-warning@+1 {{assigning to 'unsigned long *' from '__ob_trap unsigned long *' discards overflow behavior}} + px = py; + // expected-warning@+1 {{assigning to 'unsigned long *' from '__ob_wrap unsigned long *' discards overflow behavior}} + px = pz; + // expected-warning@+1 {{assigning to '__ob_trap unsigned long *' from 'unsigned long *' discards overflow behavior}} + py = px; + // expected-warning@+1 {{assigning to '__ob_wrap unsigned long *' from 'unsigned long *' discards overflow behavior}} + pz = px; + // expected-warning@+1 {{assigning to '__ob_trap unsigned long *' from '__ob_wrap unsigned long *' discards overflow behavior}} + py = pz; + // expected-warning@+1 {{assigning to '__ob_wrap unsigned long *' from '__ob_trap unsigned long *' discards overflow behavior}} + pz = py; +} + +// Test function parameter passing +// expected-note@+2 {{passing argument to parameter 'p' here}} +// expected-note@+1 {{passing argument to parameter 'p' here}} +void func_takes_regular_ptr(unsigned long *p) {} +// expected-note@+1 {{passing argument to parameter 'p' here}} +void func_takes_trap_ptr(unsigned long __ob_trap *p) {} + // expected-note@+1 {{passing argument to parameter 'p' here}} +void func_takes_wrap_ptr(unsigned long __ob_wrap *p) {} + +void test_function_parameters() { + unsigned long x = 42; + unsigned long __ob_trap y = 42; + unsigned long __ob_wrap z = 42; + + unsigned long *px = &x; + unsigned long __ob_trap *py = &y; + unsigned long __ob_wrap *pz = &z; + + // Same types - should not warn + func_takes_regular_ptr(px); // OK + func_takes_trap_ptr(py); // OK + func_takes_wrap_ptr(pz); // OK + + // Different OBT annotations - should warn but allow + // expected-warning@+1 {{passing '__ob_trap unsigned long *' to parameter of type 'unsigned long *' discards overflow behavior}} + func_takes_regular_ptr(py); + // expected-warning@+1 {{passing '__ob_wrap unsigned long *' to parameter of type 'unsigned long *' discards overflow behavior}} + func_takes_regular_ptr(pz); + // expected-warning@+1 {{passing 'unsigned long *' to parameter of type '__ob_trap unsigned long *' discards overflow behavior}} + func_takes_trap_ptr(px); + // expected-warning@+1 {{passing 'unsigned long *' to parameter of type '__ob_wrap unsigned long *' discards overflow behavior}} + func_takes_wrap_ptr(px); +} + +void test_different_underlying_types_for_pointers() { + int x = 42; + unsigned long __ob_trap y = 42; + + int *px = &x; + unsigned long __ob_trap *py = &y; + + px = py; // expected-error {{incompatible pointer types assigning to 'int *' from '__ob_trap unsigned long *'}} +} + +typedef unsigned long __ob_trap nw_ul; +typedef signed long sl; + +void qux(nw_ul *ptr) {} + +void test_signed_unsigned_pointer_compatibility() { + sl a; + qux(&a); +} + +// expected-error@+1 {{conflicting 'overflow_behavior' attributes on the same type}} +typedef int __attribute__((overflow_behavior(wrap))) __attribute__((overflow_behavior(trap))) conflicting_trap_wins; +// expected-error@+1 {{conflicting 'overflow_behavior' attributes on the same type}} +typedef int __attribute__((overflow_behavior(trap))) __attribute__((overflow_behavior(wrap))) conflicting_wrap_ignored; + +void test_conflicting_behavior_kinds() { + conflicting_trap_wins x = 42; + conflicting_wrap_ignored y = 42; + + int __ob_trap *px = &x; + int __ob_trap *py = &y; +} + +typedef int __attribute__((overflow_behavior(wrap))) __attribute__((overflow_behavior(wrap))) duplicate_wrap; // no warn +typedef int __attribute__((overflow_behavior(trap))) __attribute__((overflow_behavior(trap))) duplicate_trap; // no warn + +// Test type merging behavior for OBTs on top of typedefs +typedef int pid_t; +typedef int clockid_t; + +void test_obt_type_merging() { + pid_t __ob_wrap a = 1; + clockid_t __ob_wrap b = 2; + pid_t __ob_wrap c = 4; + _Static_assert(_Generic((a + b), int __ob_wrap: 1, default: 0), "a + b should be __ob_wrap int"); + _Static_assert(_Generic((a + c), pid_t __ob_wrap: 1, default: 0), "a + c should be __ob_wrap pid_t"); +} + +typedef unsigned long __ob_trap test_size_t; +typedef int __ob_wrap test_wrap_int; + +void test_pointer_arithmetic_crash_fix() { + int a = 42; + test_size_t offset = 10; + test_wrap_int w_offset = 5; + + int *ptr1 = &a + offset; + int *ptr2 = &a + w_offset; + int *ptr3 = offset + &a; + + test_size_t bad1 = &a + offset; // expected-error {{incompatible pointer to integer conversion}} + test_wrap_int bad2 = &a + w_offset; // expected-error {{incompatible pointer to integer conversion}} + test_size_t b = &a + b; // expected-error {{incompatible pointer to integer conversion}} + int arr[10]; + test_size_t diff = &arr[5] - &arr[0]; // OK: pointer difference assigned to OBT +} + +void test_mixed_specifier_attribute() { + int __ob_wrap __attribute__((overflow_behavior(wrap))) a; // expected-warning {{redundant overflow behavior specification; both specifier and attribute specify 'wrap'}} + int __ob_trap __attribute__((overflow_behavior(trap))) b; // expected-warning {{redundant overflow behavior specification; both specifier and attribute specify 'trap'}} + + int __ob_wrap __attribute__((overflow_behavior(trap))) c; // expected-error {{conflicting overflow behavior specification; specifier specifies 'wrap' but attribute specifies 'trap'}} + int __ob_trap __attribute__((overflow_behavior(wrap))) d; // expected-error {{conflicting overflow behavior specification; specifier specifies 'trap' but attribute specifies 'wrap'}} + + int __ob_wrap e; // OK + int __attribute__((overflow_behavior(trap))) f; // OK +} + +void test_incompatible_obt_assignment() { + int __ob_trap trap_var; + int __ob_wrap wrap_var; + + trap_var = wrap_var; // expected-error {{cannot assign between incompatible overflow behavior types ('__ob_trap' and '__ob_wrap')}} + wrap_var = trap_var; // expected-error {{cannot assign between incompatible overflow behavior types ('__ob_wrap' and '__ob_trap')}} +} diff --git a/clang/test/Sema/attr-overflow-behavior.cpp b/clang/test/Sema/attr-overflow-behavior.cpp new file mode 100644 index 0000000000000..df948c8f0bca0 --- /dev/null +++ b/clang/test/Sema/attr-overflow-behavior.cpp @@ -0,0 +1,164 @@ +// 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'}} +typedef int __attribute__((overflow_behavior("not_real"))) bad_arg_spec_str; // expected-error {{'not_real' is not a valid argument to attribute 'overflow_behavior'}} +typedef char* __attribute__((overflow_behavior("wrap"))) bad_type; // expected-error {{'overflow_behavior' attribute cannot be applied to non-integer type 'char *'}} + +typedef int __attribute__((overflow_behavior(wrap))) ok_wrap; // OK +typedef long __attribute__((overflow_behavior(trap))) ok_nowrap; // OK +typedef unsigned long __attribute__((overflow_behavior("wrap"))) str_ok_wrap; // OK +typedef char __attribute__((overflow_behavior("trap"))) str_ok_nowrap; // OK + +#define __wrap __attribute__((overflow_behavior(wrap))) +#define __trap __attribute__((overflow_behavior(trap))) + +struct struct_not_allowed { + int i; +} __attribute__((overflow_behavior(wrap))); // expected-warning {{'overflow_behavior' attribute only applies to variables, typedefs, and non-static data members}} + +void foo() { + (2147483647 + 100); // expected-warning {{overflow in expression; result is }} + (ok_wrap)2147483647 + 100; // no warn +} + +// C++ stuff expects no warns +typedef int __attribute__((overflow_behavior(wrap))) wrap_int; + +template +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@+1 {{candidate function}} +int add_one(long a) { // expected-note {{candidate function}} + return (a + 1); +} + +// expected-note@+1 {{candidate function}} +int add_one(char a) { // expected-note {{candidate function}} + return (a + 1); +} + +// expected-note@+1 {{candidate function}} +int add_one(int a) { // expected-note {{candidate function}} + return (a + 1); +} + +void test_overload2(wrap_int a) { + // to be clear, this is the same ambiguity expected when using a non-OBT int type. + add_one(a); // expected-error {{call to 'add_one' is ambiguous}} + long __attribute__((overflow_behavior(trap))) b; // don't consider underlying type an exact match. + add_one(b); // expected-error {{call to 'add_one' is ambiguous}} +} + +void func(__ob_trap int i); +void func(int i); // Overload, not invalid redeclaration + +template +void func2(__ob_trap Ty i) {} // expected-error {{__ob_trap specifier cannot be applied to non-integer type 'Ty'}} + +template +struct S {}; + +template <> +struct S<__ob_trap int> {}; + +template <> +struct S {}; + +void ptr(int a) { + int __ob_trap *p = &a; // expected-error-re {{cannot initialize a variable of type '__ob_trap int *' {{.*}}with an rvalue of type 'int *'}} +} + +void ptr2(__ob_trap int a) { + int *p = &a; // expected-error-re {{cannot initialize a variable of type 'int *' {{.*}}with an rvalue of type '__ob_trap int *'}} +} + +void overloadme(__ob_trap int a); // expected-note {{candidate function}} +void overloadme(short a); // expected-note {{candidate function}} + +void test_overload_ambiguity() { + int a; + overloadme(a); // expected-error {{call to 'overloadme' is ambiguous}} +} + +void f(void) __attribute__((overflow_behavior(wrap))); // expected-error {{'overflow_behavior' attribute cannot be applied to non-integer type 'void (void)'}} + +typedef float __attribute__((overflow_behavior(wrap))) wrap_float; // expected-error {{'overflow_behavior' attribute cannot be applied to non-integer type 'float'}} + +void pointer_compatibility(int* i_ptr) { + __ob_trap int* nowrap_ptr; + + // static_cast should fail. + nowrap_ptr = static_cast<__ob_trap int*>(i_ptr); // expected-error {{static_cast from 'int *' to '__ob_trap int *' is not allowed}} + + // reinterpret_cast should succeed. + nowrap_ptr = reinterpret_cast<__ob_trap int*>(i_ptr); + (void)nowrap_ptr; +} + +void cpp_constexpr_bracket_initialization() { + constexpr short cx1 = {(int __ob_wrap)100000}; // expected-error {{constant expression evaluates to 100000 which cannot be narrowed to type 'short'}} + // expected-warning@-1 {{implicit conversion from '__ob_wrap int' to 'const short' changes value from 100000 to -31072}} + // expected-note@-2 {{insert an explicit cast to silence this issue}} + + constexpr short __ob_wrap cx2 = {100000}; // expected-error {{constant expression evaluates to 100000 which cannot be narrowed to type '__ob_wrap short'}} + + constexpr short __ob_trap cx3 = {(int)100000}; // expected-error {{constant expression evaluates to 100000 which cannot be narrowed to type '__ob_trap short'}} + // expected-warning@-1 {{implicit conversion from 'int' to '__ob_trap short const' changes value from 100000 to -31072}} + + constexpr short cx4 = {(int __ob_trap)100000}; // expected-error {{constant expression evaluates to 100000 which cannot be narrowed to type 'short'}} + // expected-warning@-1 {{implicit conversion from '__ob_trap int' to 'const short' changes value from 100000 to -31072}} + // expected-note@-2 {{insert an explicit cast to silence this issue}} +} + +// ensure that all qualifier placements result in the same canonical type +void test_qualifier_placements() { + using ConstInt = const int; + using WrapConstInt1 = ConstInt __attribute__((overflow_behavior(wrap))); + using WrapConstInt2 = const int __attribute__((overflow_behavior(wrap))); + typedef const int __ob_wrap const_int_wrap; + typedef int __ob_wrap const int_wrap_const; + typedef int __ob_trap const int_trap_const; + + static_assert(__is_same(WrapConstInt1, WrapConstInt2)); + static_assert(__is_same(const_int_wrap, int_wrap_const)); + static_assert(!__is_same(const_int_wrap, int_trap_const)); +} + +void test_mixed_specifier_attribute() { + int __ob_wrap __attribute__((overflow_behavior(wrap))) a; // expected-warning {{redundant overflow behavior specification; both specifier and attribute specify 'wrap'}} + int __ob_trap __attribute__((overflow_behavior(trap))) b; // expected-warning {{redundant overflow behavior specification; both specifier and attribute specify 'trap'}} + + int __ob_wrap __attribute__((overflow_behavior(trap))) c; // expected-error {{conflicting overflow behavior specification; specifier specifies 'wrap' but attribute specifies 'trap'}} + int __ob_trap __attribute__((overflow_behavior(wrap))) d; // expected-error {{conflicting overflow behavior specification; specifier specifies 'trap' but attribute specifies 'wrap'}} + + int __ob_wrap e; // OK + int __attribute__((overflow_behavior(trap))) f; // OK +} + +void test_incompatible_obt_initialization() { + int __ob_trap a = 10; + int __ob_wrap b = 20; + + int __ob_wrap c = a; // expected-error {{cannot initialize a variable of type '__ob_wrap int' with an lvalue of type '__ob_trap int'}} + int __ob_trap d = b; // expected-error {{cannot initialize a variable of type '__ob_trap int' with an lvalue of type '__ob_wrap int'}} +} diff --git a/clang/test/Sema/overflow-behavior-keywords.c b/clang/test/Sema/overflow-behavior-keywords.c new file mode 100644 index 0000000000000..a9e0e2f79d622 --- /dev/null +++ b/clang/test/Sema/overflow-behavior-keywords.c @@ -0,0 +1,66 @@ +// RUN: %clang_cc1 -fsyntax-only -foverflow-behavior-types -verify %s + +int __ob_wrap a; +int __ob_trap b; + +const int __ob_wrap c; +volatile int __ob_trap d; +const volatile int __ob_wrap e; + +int __attribute__((overflow_behavior(wrap))) attr_style_var; +int __ob_wrap keyword_style_var; + +int __ob_wrap __ob_trap conflicting_var; // expected-error{{cannot combine with previous '__ob_wrap' declaration specifier}} + +// Test duplicate qualifiers +int __ob_wrap __ob_wrap duplicate_wrap; // expected-warning{{duplicate '__ob_wrap' declaration specifier}} +int __ob_trap __ob_trap duplicate_trap; // expected-warning{{duplicate '__ob_trap' declaration specifier}} + +int __attribute__((overflow_behavior(wrap))) __attribute__((overflow_behavior(trap))) attr_conflict; // expected-error{{conflicting 'overflow_behavior' attributes on the same type}} + +// Test duplicate attributes - no warning, less problematic than duplicate decl specifiers +int __attribute__((overflow_behavior(wrap))) __attribute__((overflow_behavior(wrap))) duplicate_attr; + +const volatile int __ob_wrap __ob_trap __ob_wrap complex_conflict; // expected-error{{cannot combine with previous '__ob_wrap' declaration specifier}} expected-warning{{duplicate '__ob_wrap' declaration specifier}} + +extern int __ob_wrap __ob_trap extern_conflict; // expected-error{{cannot combine with previous '__ob_wrap' declaration specifier}} +static int __ob_wrap __ob_trap static_conflict; // expected-error{{cannot combine with previous '__ob_wrap' declaration specifier}} + +void test_storage_class(void) { + register int __ob_wrap __ob_trap register_conflict; // expected-error{{cannot combine with previous '__ob_wrap' declaration specifier}} +} + +int __ob_wrap __ob_trap *ptr_conflict; // expected-error{{cannot combine with previous '__ob_wrap' declaration specifier}} +int __ob_wrap __ob_trap arr_conflict[5]; // expected-error{{cannot combine with previous '__ob_wrap' declaration specifier}} + +int __ob_wrap __ob_trap (*func_ptr_conflict)(void); // expected-error{{cannot combine with previous '__ob_wrap' declaration specifier}} + +void param_test(int __ob_wrap __ob_trap param); // expected-error{{cannot combine with previous '__ob_wrap' declaration specifier}} + +struct conflict_struct { + int __ob_wrap __ob_trap member; // expected-error{{cannot combine with previous '__ob_wrap' declaration specifier}} + int __ob_wrap __ob_wrap dup_member; // expected-warning{{duplicate '__ob_wrap' declaration specifier}} +}; + +typedef int __ob_wrap __ob_trap conflict_typedef; // expected-error{{cannot combine with previous '__ob_wrap' declaration specifier}} +typedef int __ob_wrap __ob_wrap dup_typedef; // expected-warning{{duplicate '__ob_wrap' declaration specifier}} + +int __ob_wrap *ptr_to_wrap; +int __ob_trap arr[10]; +int __ob_wrap (*func_ptr)(int); + +void test_function(int __ob_wrap param1, int __ob_trap param2); + +struct test_struct { + int __ob_wrap member1; + int __ob_trap member2; +}; + +typedef int __ob_wrap wrap_int_t; +typedef int __ob_trap trap_int_t; + +typedef float __ob_wrap float_wrap; // expected-error{{__ob_wrap specifier cannot be applied to non-integer type 'float'}} +typedef double __ob_trap double_trap; // expected-error{{__ob_trap specifier cannot be applied to non-integer type 'double'}} + +struct S { int i; }; +typedef struct S __ob_wrap struct_wrap; // expected-error{{__ob_wrap specifier cannot be applied to non-integer type 'struct S'}} diff --git a/clang/test/SemaCXX/sugar-common-types.cpp b/clang/test/SemaCXX/sugar-common-types.cpp index 4db0d2ac2f2ae..b0271569dfffa 100644 --- a/clang/test/SemaCXX/sugar-common-types.cpp +++ b/clang/test/SemaCXX/sugar-common-types.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++20 -x objective-c++ -fobjc-arc -fenable-matrix -triple i686-pc-win32 +// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++20 -x objective-c++ -fobjc-arc -fenable-matrix -foverflow-behavior-types -triple i686-pc-win32 enum class N {}; @@ -188,6 +188,45 @@ namespace arrays { } // namespace balanced_qualifiers } // namespace arrays +namespace overflow_behavior_types { + namespace same_canonical { + using WrapB1 = B1 __attribute__((overflow_behavior(wrap))); + using WrapB1_2 = B1 __attribute__((overflow_behavior(wrap))); + WrapB1 a = 0; + WrapB1_2 b = 0; + N ta = a; + // expected-error@-1 {{cannot initialize a variable of type 'N' with an lvalue of type 'WrapB1' (aka '__ob_wrap B1')}} + N tb = b; + // expected-error@-1 {{cannot initialize a variable of type 'N' with an lvalue of type 'WrapB1_2' (aka '__ob_wrap B1')}} + N tc = 0 ? a : b; + // expected-error@-1 {{cannot initialize a variable of type 'N' with an lvalue of type '__ob_wrap B1'}} + } // namespace same_canonical + namespace same_underlying { + using WrapX1 = X1 __attribute__((overflow_behavior(wrap))); + using WrapY1 = Y1 __attribute__((overflow_behavior(wrap))); + WrapX1 a = 0; + WrapY1 b = 0; + N ta = a; + // expected-error@-1 {{cannot initialize a variable of type 'N' with an lvalue of type 'WrapX1' (aka '__ob_wrap X1')}} + N tb = b; + // expected-error@-1 {{cannot initialize a variable of type 'N' with an lvalue of type 'WrapY1' (aka '__ob_wrap Y1')}} + N tc = 0 ? a : b; + // expected-error@-1 {{cannot initialize a variable of type 'N' with an lvalue of type '__ob_wrap B1'}} + } // namespace same_underlying + namespace balanced_qualifiers { + using ConstWrapX1 = const volatile X1 __attribute__((overflow_behavior(wrap))); + using WrapY1 = volatile Y1 __attribute__((overflow_behavior(wrap))); + volatile ConstWrapX1 a = 0; + const volatile WrapY1 b = 0; + N ta = a; + // expected-error@-1 {{cannot initialize a variable of type 'N' with an lvalue of type 'volatile ConstWrapX1' (aka '__ob_wrap X1 const volatile')}} + N tb = b; + // expected-error@-1 {{cannot initialize a variable of type 'N' with an lvalue of type 'const volatile WrapY1' (aka '__ob_wrap Y1 const volatile')}} + N tc = 0 ? a : b; + // expected-error@-1 {{cannot initialize a variable of type 'N' with an lvalue of type '__ob_wrap B1 const volatile'}} + } // namespace balanced_qualifiers +} // namespace overflow_behavior_types + namespace member_pointers { template struct W { X1 a; diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp index c39f337665a40..a0319bb0a725c 100644 --- a/clang/tools/libclang/CIndex.cpp +++ b/clang/tools/libclang/CIndex.cpp @@ -1747,6 +1747,10 @@ bool CursorVisitor::VisitBTFTagAttributedTypeLoc(BTFTagAttributedTypeLoc TL) { return Visit(TL.getWrappedLoc()); } +bool CursorVisitor::VisitOverflowBehaviorTypeLoc(OverflowBehaviorTypeLoc TL) { + return Visit(TL.getWrappedLoc()); +} + bool CursorVisitor::VisitHLSLAttributedResourceTypeLoc( HLSLAttributedResourceTypeLoc TL) { return Visit(TL.getWrappedLoc()); diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp index 21c265ede0bc5..d3797659e66b5 100644 --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp @@ -4191,6 +4191,7 @@ TypeSystemClang::GetTypeClass(lldb::opaque_compiler_type_t type) { // Ext-Int is just an integer type. case clang::Type::BitInt: case clang::Type::DependentBitInt: + case clang::Type::OverflowBehavior: return lldb::eTypeClassBuiltin; case clang::Type::ObjCObjectPointer: return lldb::eTypeClassObjCObjectPointer; @@ -4892,6 +4893,7 @@ lldb::Encoding TypeSystemClang::GetEncoding(lldb::opaque_compiler_type_t type, case clang::Type::BitInt: case clang::Type::DependentBitInt: + case clang::Type::OverflowBehavior: return qual_type->isUnsignedIntegerType() ? lldb::eEncodingUint : lldb::eEncodingSint; @@ -5193,6 +5195,7 @@ lldb::Format TypeSystemClang::GetFormat(lldb::opaque_compiler_type_t type) { case clang::Type::BitInt: case clang::Type::DependentBitInt: + case clang::Type::OverflowBehavior: return qual_type->isUnsignedIntegerType() ? lldb::eFormatUnsigned : lldb::eFormatDecimal;