diff --git a/clang/docs/OverflowBehaviorTypes.rst b/clang/docs/OverflowBehaviorTypes.rst new file mode 100644 index 0000000000000..18e337e181e8a --- /dev/null +++ b/clang/docs/OverflowBehaviorTypes.rst @@ -0,0 +1,269 @@ +===================== +OverflowBehaviorTypes +===================== + +.. contents:: + :local: + +Introduction +============ + +Clang provides a type attribute that allows developers to have fine-grained control +over the overflow behavior of integer types. The ``overflow_behavior`` +attribute can be used to specify how arithmetic operations on a given integer +type should behave upon overflow. This is particularly useful for projects that +need to balance performance and safety, allowing developers to enable or +disable overflow checks for specific types. + +The attribute can be enabled using the compiler option +``-foverflow-behavior-types``. + +The attribute syntax is as follows: + +.. code-block:: c++ + + __attribute__((overflow_behavior(behavior))) + +Where ``behavior`` can be one of the following: + +* ``wrap``: Specifies that arithmetic operations on the integer type should + wrap on overflow. This is equivalent to the behavior of ``-fwrapv``, but it + applies only to the attributed type and may be used with both signed and + unsigned types. When this is enabled, UBSan's integer overflow and integer + truncation checks (``signed-integer-overflow``, + ``unsigned-integer-overflow``, ``implicit-signed-integer-truncation``, and + ``implicit-unsigned-integer-truncation``) are suppressed for the attributed + type. + +* ``no_wrap``: Specifies that arithmetic operations on the integer type should + be checked for overflow. When using the ``signed-integer-overflow`` sanitizer + or when using ``-ftrapv`` alongside a signed type, this is the default + behavior. Using this, one may enforce overflow checks for a type even when + ``-fwrapv`` is enabled globally. + +This attribute can be applied to ``typedef`` declarations and to integer types directly. + +Examples +======== + +Here is an example of how to use the ``overflow_behavior`` attribute with a ``typedef``: + +.. code-block:: c++ + + typedef unsigned int __attribute__((overflow_behavior(no_wrap))) non_wrapping_uint; + + non_wrapping_uint add_one(non_wrapping_uint a) { + return a + 1; // Overflow is checked for this operation. + } + +Here is an example of how to use the ``overflow_behavior`` attribute with a type directly: + +.. code-block:: c++ + + int mul_alot(int n) { + int __attribute__((overflow_behavior(wrap))) a = n; + return a * 1337; // Potential overflow is not checked and is well-defined + } + +"Well-defined" overflow is consistent with two's complement wrap-around +semantics and won't be removed via eager compiler optimizations (like some +undefined behavior might). + +Overflow behavior types are implicitly convertible to and from built-in +integral types. + +Note that C++ overload set formation rules treat promotions to and from +overflow behavior types the same as normal integral promotions and conversions. + +Interaction with Command-Line Flags and Sanitizer Special Case Lists +==================================================================== + +The ``overflow_behavior`` attribute interacts with sanitizers, ``-ftrapv``, +``-fwrapv``, and Sanitizer Special Case Lists (SSCL) by wholly overriding these +global flags. The following table summarizes the interactions: + +.. list-table:: Overflow Behavior Precedence + :widths: 15 15 15 15 20 15 + :header-rows: 1 + + * - Behavior + - Default(No Flags) + - -ftrapv + - -fwrapv + - Sanitizers + - SSCL + * - ``overflow_behavior(wrap)`` + - Wraps + - No trap + - Wraps + - No report + - Overrides SSCL + * - ``overflow_behavior(no_wrap)`` + - Traps + - Traps + - Traps + - Reports + - Overrides SSCL + +It is important to note the distinction between signed and unsigned types. For +unsigned integers, which wrap on overflow by default, ``overflow_behavior(no_wrap)`` +is particularly useful for enabling overflow checks. For signed integers, whose +overflow behavior is undefined by default, ``overflow_behavior(wrap)`` provides +a guaranteed wrapping behavior. + +The ``overflow_behavior`` attribute can be used to override the behavior of +entries from a :doc:`SanitizerSpecialCaseList`. This is useful for allowlisting +specific types into overflow instrumentation. + +Promotion Rules +=============== + +The promotion rules for overflow behavior types are designed to preserve the +specified overflow behavior throughout an arithmetic expression. They differ +from standard C/C++ integer promotions but in a predictable way, similar to +how ``_Complex`` and ``_BitInt`` have their own promotion rules. + +* **OBT and Standard Integer Type**: In an operation involving an overflow + behavior type (OBT) and a standard integer type, the result will have the + type of the OBT, including its overflow behavior, sign, and bit-width. The + standard integer type is implicitly converted to match the OBT. + + .. code-block:: c++ + + typedef char __attribute__((overflow_behavior(no_wrap))) no_wrap_char; + // The result of this expression is no_wrap_char. + no_wrap_char c; + unsigned long ul; + auto result = c + ul; + +* **Two OBTs of the Same Kind**: When an operation involves two OBTs of the + same kind (e.g., both ``wrap``), the result will have the larger of the two + bit-widths. If the bit-widths are the same, an unsigned type is favored over + a signed one. + + .. code-block:: c++ + + typedef unsigned char __attribute__((overflow_behavior(wrap))) u8_wrap; + typedef unsigned short __attribute__((overflow_behavior(wrap))) u16_wrap; + // The result of this expression is u16_wrap. + u8_wrap a; + u16_wrap b; + auto result = a + b; + +* **Two OBTs of Different Kinds**: In an operation between a ``wrap`` and a + ``no_wrap`` type, a ``no_wrap`` is produced. It is recommended to avoid such + operations, as Clang may emit a warning for such cases in the future. + Regardless, the resulting type matches the bit-width, sign and behavior of + the ``no_wrap`` type. + +Diagnostics +=========== + +Clang provides diagnostics to help developers manage overflow behavior types. + +-Wimplicitly-discarded-overflow-behavior +---------------------------------------- + +This warning is issued when an overflow behavior type is implicitly converted +to a standard integer type, which may lead to the loss of the specified +overflow behavior. + +.. code-block:: c++ + + typedef int __attribute__((overflow_behavior(wrap))) wrapping_int; + + void some_function(int); + + void another_function(wrapping_int w) { + some_function(w); // warning: implicit conversion from 'wrapping_int' to + // 'int' discards overflow behavior + } + +To fix this, you can explicitly cast the overflow behavior type to a standard +integer type. + +.. code-block:: c++ + + typedef int __attribute__((overflow_behavior(wrap))) wrapping_int; + + void some_function(int); + + void another_function(wrapping_int w) { + some_function(static_cast(w)); // OK + } + +This warning acts as a group that includes +``-Wimplicitly-discarded-overflow-behavior-pedantic`` and +``-Wimplicitly-discarded-overflow-behavior-assignment``. + +-Wimplicitly-discarded-overflow-behavior-pedantic +------------------------------------------------- + +A less severe version of the warning, ``-Wimplicitly-discarded-overflow-behavior-pedantic``, +is issued for implicit conversions from an unsigned wrapping type to a standard +unsigned integer type. This is considered less problematic because both types +have well-defined wrapping behavior, but the conversion still discards the +explicit ``overflow_behavior`` attribute. + +.. code-block:: c++ + + typedef unsigned int __attribute__((overflow_behavior(wrap))) wrapping_uint; + + void some_function(unsigned int); + + void another_function(wrapping_uint w) { + some_function(w); // warning: implicit conversion from 'wrapping_uint' to + // 'unsigned int' discards overflow behavior + // [-Wimplicitly-discarded-overflow-behavior-pedantic] + } + +-Wimplicitly-discarded-overflow-behavior-assignment +--------------------------------------------------- + +This warning is issued when an overflow behavior type is implicitly converted +to a standard integer type as part of an assignment, which may lead to the +loss of the specified overflow behavior. This is a more specific version of +the ``-Wimplicitly-discarded-overflow-behavior`` warning, and it is off by +default. + +.. code-block:: c++ + + typedef int __attribute__((overflow_behavior(wrap))) wrapping_int; + + void some_function() { + wrapping_int w = 1; + int i = w; // warning: implicit conversion from 'wrapping_int' to 'int' + // discards overflow behavior + // [-Wimplicitly-discarded-overflow-behavior-assignment] + } + +To fix this, you can explicitly cast the overflow behavior type to a standard +integer type. + +.. code-block:: c++ + + typedef int __attribute__((overflow_behavior(wrap))) wrapping_int; + + void some_function() { + wrapping_int w = 1; + int i = static_cast(w); // OK + int j = (int)w; // C-style OK + } + + +-Woverflow-behavior-attribute-ignored +------------------------------------- + +This warning is issued when the ``overflow_behavior`` attribute is applied to +a type that is not an integer type. + +.. code-block:: c++ + + typedef float __attribute__((overflow_behavior(wrap))) wrapping_float; + // warning: 'overflow_behavior' attribute only applies to integer types; + // attribute is ignored [-Woverflow-behavior-attribute-ignored] + + typedef struct S { int i; } __attribute__((overflow_behavior(wrap))) S_t; + // warning: 'overflow_behavior' attribute only applies to integer types; + // attribute is ignored [-Woverflow-behavior-attribute-ignored] + diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 970825c98fec1..0c9139cb252d2 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -378,6 +378,8 @@ New Compiler Flags - New options ``-g[no-]key-instructions`` added, disabled by default. Reduces jumpiness of debug stepping for optimized code in some debuggers (not LLDB at this time). Not recommended for use without optimizations. DWARF only. Note both the positive and negative flags imply ``-g``. +- New option ``-foverflow-behavior-types`` added to enable parsing of the ``overflow_behavior`` type attribute. + Deprecated Compiler Flags ------------------------- @@ -478,6 +480,10 @@ related warnings within the method body. - Clang will print the "reason" string argument passed on to ``[[clang::warn_unused_result("reason")]]`` as part of the warning diagnostic. +- Introduced a new type attribute ``__attribute__((overflow_behavior))`` which + currently accepts either ``wrap`` or ``no_wrap`` as an argument, enabling + type-level control over overflow behavior. + Improvements to Clang's diagnostics ----------------------------------- diff --git a/clang/docs/SanitizerSpecialCaseList.rst b/clang/docs/SanitizerSpecialCaseList.rst index 2c50778d0f491..f4c9274b89b68 100644 --- a/clang/docs/SanitizerSpecialCaseList.rst +++ b/clang/docs/SanitizerSpecialCaseList.rst @@ -133,6 +133,40 @@ precedence. Here are a few examples. fun:*bar fun:bad_bar=sanitize +Interaction with Overflow Behavior Types +---------------------------------------- + +The ``overflow_behavior`` attribute provides a more granular, source-level +control that takes precedence over the Sanitizer Special Case List. If a type +is given an ``overflow_behavior`` attribute, it will override any matching +``type:`` entry in a special case list. + +This allows developers to enforce a specific overflow behavior for a critical +type, even if a broader rule in the special case list would otherwise disable +instrumentation for it. + +.. code-block:: bash + + $ cat ignorelist.txt + # Disable signed overflow checks for all types by default. + [signed-integer-overflow] + type:* + + $ cat foo.c + // Force 'critical_type' to always have overflow checks, + // overriding the ignorelist. + typedef int __attribute__((overflow_behavior(no_wrap))) critical_type; + + void foo(int x) { + critical_type a = x; + a++; // Overflow is checked here due to the 'no_wrap' attribute. + + int b = x; + b++; // Overflow is NOT checked here due to the ignorelist. + } + +For more details on overflow behavior types, see :doc:`OverflowBehaviorTypes`. + Format ====== diff --git a/clang/docs/UndefinedBehaviorSanitizer.rst b/clang/docs/UndefinedBehaviorSanitizer.rst index 0a2d833783e57..f98eda1e9399c 100644 --- a/clang/docs/UndefinedBehaviorSanitizer.rst +++ b/clang/docs/UndefinedBehaviorSanitizer.rst @@ -380,6 +380,28 @@ This attribute may not be supported by other compilers, so consider using it together with ``#if defined(__clang__)``. +Disabling Overflow Instrumentation with ``__attribute__((overflow_behavior(wrap)))`` +------------------------------------------------------------------------------------ + +For more fine-grained control over how integer overflow is handled, you can use +the ``__attribute__((overflow_behavior(wrap)))`` attribute. This attribute can +be applied to ``typedef`` declarations and integer types to specify that +arithmetic operations on that type should wrap on overflow. This can be used to +disable overflow sanitization for specific types, while leaving it enabled for +all other types. + +For more information, see :doc:`OverflowBehaviorTypes`. + +Enforcing Overflow Instrumentation with ``__attribute__((overflow_behavior(no_wrap)))`` +--------------------------------------------------------------------------------------- + +Conversely, you can use ``__attribute__((overflow_behavior(no_wrap)))`` to +enforce overflow checks for a specific type, even when ``-fwrapv`` is enabled +globally. This is useful for ensuring that critical calculations are always +checked for overflow, regardless of the global compiler settings. + +For more information, see :doc:`OverflowBehaviorTypes`. + Suppressing Errors in Recompiled Code (Ignorelist) -------------------------------------------------- diff --git a/clang/docs/index.rst b/clang/docs/index.rst index 6c792af66a62c..3205709aa395a 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 2b9cd035623cc..dbfc8df6fba5e 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" @@ -259,6 +260,7 @@ class ASTContext : public RefCountedBase { mutable llvm::ContextualFoldingSet DependentBitIntTypes; mutable llvm::FoldingSet BTFTagAttributedTypes; + mutable llvm::FoldingSet OverflowBehaviorTypes; llvm::FoldingSet HLSLAttributedResourceTypes; llvm::FoldingSet HLSLInlineSpirvTypes; @@ -899,6 +901,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; } @@ -999,6 +1003,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. @@ -1843,6 +1856,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); diff --git a/clang/include/clang/AST/ASTNodeTraverser.h b/clang/include/clang/AST/ASTNodeTraverser.h index 8ebabb2bde10d..e684dcd5c27e8 100644 --- a/clang/include/clang/AST/ASTNodeTraverser.h +++ b/clang/include/clang/AST/ASTNodeTraverser.h @@ -445,6 +445,9 @@ class ASTNodeTraverser void VisitBTFTagAttributedType(const BTFTagAttributedType *T) { Visit(T->getWrappedType()); } + void VisitOverflowBehaviorType(const OverflowBehaviorType *T) { + Visit(T->getUnderlyingType()); + } void VisitHLSLAttributedResourceType(const HLSLAttributedResourceType *T) { QualType Contained = T->getContainedType(); if (!Contained.isNull()) diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h index 523c0326d47ef..4fbea7272d004 100644 --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -1477,6 +1477,14 @@ class DeclRefExpr final return DeclRefExprBits.IsImmediateEscalating; } + bool isOverflowBehaviorDiscarded() const { + return DeclRefExprBits.IsOverflwBehaviorDiscarded; + } + + void setOverflowBehaviorDiscarded(bool Set) { + DeclRefExprBits.IsOverflwBehaviorDiscarded = Set; + } + void setIsImmediateEscalating(bool Set) { DeclRefExprBits.IsImmediateEscalating = Set; } diff --git a/clang/include/clang/AST/PropertiesBase.td b/clang/include/clang/AST/PropertiesBase.td index 1215056ffde1b..26dd53760c3a2 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 5cb2f57edffe4..4a681a58b1cc9 100644 --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -1151,6 +1151,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())); }) @@ -1462,6 +1465,9 @@ DEF_TRAVERSE_TYPELOC(CountAttributedType, DEF_TRAVERSE_TYPELOC(BTFTagAttributedType, { TRY_TO(TraverseTypeLoc(TL.getWrappedLoc())); }) +DEF_TRAVERSE_TYPELOC(OverflowBehaviorType, + { TRY_TO(TraverseTypeLoc(TL.getWrappedLoc())); }) + DEF_TRAVERSE_TYPELOC(HLSLAttributedResourceType, { TRY_TO(TraverseTypeLoc(TL.getWrappedLoc())); }) diff --git a/clang/include/clang/AST/Stmt.h b/clang/include/clang/AST/Stmt.h index a5b0d5053003f..42469c43cd845 100644 --- a/clang/include/clang/AST/Stmt.h +++ b/clang/include/clang/AST/Stmt.h @@ -450,6 +450,9 @@ class alignas(void *) Stmt { LLVM_PREFERRED_TYPE(bool) unsigned IsImmediateEscalating : 1; + LLVM_PREFERRED_TYPE(bool) + unsigned IsOverflwBehaviorDiscarded : 1; + /// The location of the declaration name itself. SourceLocation Loc; }; diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h index 21b97102db95a..4b00f7d45ab2e 100644 --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -70,6 +70,7 @@ class TagDecl; class TemplateParameterList; class Type; class Attr; +class NoSanitizeAttr; enum { TypeAlignmentInBits = 4, @@ -1148,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 NoWrap kind. + bool isNoWrapType() const; + // Don't promise in the API that anything besides 'const' can be // easily added. @@ -2607,6 +2614,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 @@ -3075,6 +3083,10 @@ template <> const BoundsAttributedType *Type::getAs() const; /// sugar until it reaches an CountAttributedType or a non-sugared type. template <> const CountAttributedType *Type::getAs() const; +/// This will check for a OverflowBehaviorType by removing any existing +/// sugar until it reaches an OverflowBehaviorType or a non-sugared type. +template <> const OverflowBehaviorType *Type::getAs() const; + // We can do canonical leaf types faster, because we don't have to // worry about preserving child type decoration. #define TYPE(Class, Base) @@ -6309,6 +6321,51 @@ class BTFTagAttributedType : public Type, public llvm::FoldingSetNode { } }; +class OverflowBehaviorType : public Type, public llvm::FoldingSetNode { +public: + enum OverflowBehaviorKind { Wrap, NoWrap }; + +private: + friend class ASTContext; // ASTContext creates these + + QualType UnderlyingType; + OverflowBehaviorKind BehaviorKind; + + OverflowBehaviorType(QualType Canon, QualType Underlying, + OverflowBehaviorKind Kind); + +public: + QualType getUnderlyingType() const { return UnderlyingType; } + OverflowBehaviorKind getBehaviorKind() const { return BehaviorKind; } + + bool isWrapKind() const { return BehaviorKind == OverflowBehaviorKind::Wrap; } + bool isNoWrapKind() const { + return BehaviorKind == OverflowBehaviorKind::NoWrap; + } + + OverflowBehaviorKind setBehaviorKind(OverflowBehaviorKind Kind) { + BehaviorKind = Kind; + return BehaviorKind; + } + + bool isSugared() const { return false; } + QualType desugar() const { return getUnderlyingType(); } + + void Profile(llvm::FoldingSetNodeID &ID) { + Profile(ID, UnderlyingType, BehaviorKind); + } + + static void Profile(llvm::FoldingSetNodeID &ID, QualType Underlying, + OverflowBehaviorKind Kind) { + ID.AddPointer(Underlying.getAsOpaquePtr()); + ID.AddInteger((int)Kind); + } + + static bool classof(const Type *T) { + return T->getTypeClass() == OverflowBehavior; + } +}; + class HLSLAttributedResourceType : public Type, public llvm::FoldingSetNode { public: struct Attributes { @@ -8519,6 +8576,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); } @@ -8763,6 +8824,12 @@ inline bool Type::isIntegerType() const { return IsEnumDeclComplete(ET->getDecl()) && !IsEnumDeclScoped(ET->getDecl()); } + + if (isOverflowBehaviorType()) + return cast(CanonicalType) + ->getUnderlyingType() + ->isIntegerType(); + return isBitIntType(); } @@ -8825,7 +8892,7 @@ inline bool Type::isScalarType() const { isa(CanonicalType) || isa(CanonicalType) || isa(CanonicalType) || - isBitIntType(); + isa(CanonicalType) || isBitIntType(); } inline bool Type::isIntegralOrEnumerationType() const { @@ -8837,6 +8904,10 @@ inline bool Type::isIntegralOrEnumerationType() const { if (const auto *ET = dyn_cast(CanonicalType)) return IsEnumDeclComplete(ET->getDecl()); + if (const OverflowBehaviorType *OBT = + dyn_cast(CanonicalType)) + return OBT->getUnderlyingType()->isIntegralOrEnumerationType(); + return isBitIntType(); } @@ -8969,6 +9040,8 @@ template const T *Type::getAsAdjusted() const { Ty = A->getModifiedType().getTypePtr(); else if (const auto *A = dyn_cast(Ty)) Ty = A->getWrappedType().getTypePtr(); + // else if (const auto *A = dyn_cast(Ty)) + // Ty = A->getWrappedType().getTypePtr(); else if (const auto *A = dyn_cast(Ty)) Ty = A->getWrappedType().getTypePtr(); else if (const auto *E = dyn_cast(Ty)) diff --git a/clang/include/clang/AST/TypeLoc.h b/clang/include/clang/AST/TypeLoc.h index cf06e27758996..9bd7f7f380526 100644 --- a/clang/include/clang/AST/TypeLoc.h +++ b/clang/include/clang/AST/TypeLoc.h @@ -942,6 +942,26 @@ class BTFTagAttributedTypeLoc QualType getInnerType() const { return getTypePtr()->getWrappedType(); } }; +struct OverflowBehaviorLocInfo {}; // Nothing. + +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) {} + + QualType getInnerType() const { return getTypePtr()->getUnderlyingType(); } +}; + struct HLSLAttributedResourceLocInfo { SourceRange Range; TypeSourceInfo *ContainedTyInfo; @@ -2747,6 +2767,8 @@ inline T TypeLoc::getAsAdjusted() const { Cur = ATL.getModifiedLoc(); else if (auto ATL = Cur.getAs()) Cur = ATL.getWrappedLoc(); + // else if (auto ATL = Cur.getAs()) + // Cur = ATL.getWrappedLoc(); else if (auto ATL = Cur.getAs()) Cur = ATL.getWrappedLoc(); else if (auto ETL = Cur.getAs()) diff --git a/clang/include/clang/AST/TypeProperties.td b/clang/include/clang/AST/TypeProperties.td index a6157649060b1..c6e1e0450f336 100644 --- a/clang/include/clang/AST/TypeProperties.td +++ b/clang/include/clang/AST/TypeProperties.td @@ -683,6 +683,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 224cb6a32af28..eb96168121470 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -5223,3 +5223,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 c28a919e35d08..062a14ac1ec9f 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -121,6 +121,14 @@ def ImplicitIntConversion : DiagGroup<"implicit-int-conversion", [Shorten64To32, ObjCSignedCharBoolImplicitIntConversion, ImplicitIntConversionOnNegation]>; +def ImplicitlyDiscardedOverflowBehaviorPedantic + : DiagGroup<"implicitly-discarded-overflow-behavior-pedantic">; +def ImplicitlyDiscardedOverflowBehaviorAssignment + : DiagGroup<"implicitly-discarded-overflow-behavior-assignment">; +def ImplicitlyDiscardedOverflowBehavior + : DiagGroup<"implicitly-discarded-overflow-behavior", + [ImplicitlyDiscardedOverflowBehaviorPedantic, + ImplicitlyDiscardedOverflowBehaviorAssignment]>; def ImplicitConstIntFloatConversion : DiagGroup<"implicit-const-int-float-conversion">; def ImplicitIntFloatConversion : DiagGroup<"implicit-int-float-conversion", [ImplicitConstIntFloatConversion]>; @@ -1656,6 +1664,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 0f6d15ede4383..491bad1098cbc 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -4030,6 +4030,30 @@ def note_cannot_use_trivial_abi_reason : Note< "it has a __weak field|it has a field of a non-trivial class type|" "it has an address-discriminated '__ptrauth' field}1">; +// OverflowBehavior attribute +def err_overflow_behavior_unknown_ident + : Error<"'%0' is not a valid argument to attribute %1, only 'wrap' and " + "'no_wrap' " + "are supported">; +def warn_overflow_behavior_non_integer_type + : Warning<"%0 attribute cannot be applied to non-integer type '%1'; " + "attribute ignored">, + InGroup; +def warn_overflow_behavior_attribute_disabled + : Warning<"%0 attribute is ignored because it is not enabled; pass " + "-foverflow-behavior-types">, + InGroup; +def warn_implicitly_discarded_overflow_behavior + : Warning<"implicit conversion from %0 to %1 discards overflow behavior">, + InGroup; +def warn_implicitly_discarded_overflow_behavior_pedantic + : Warning<"implicit conversion from %0 to %1 discards overflow behavior">, + InGroup; +def warn_implicitly_discarded_overflow_behavior_assignment + : Warning<"implicit conversion from %0 to %1 during assignment discards " + "overflow behavior">, + InGroup, + DefaultIgnore; // Availability attribute def warn_availability_unknown_platform : Warning< "unknown platform %0 in availability macro">, InGroup; diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def index e43238ba683f2..bdf08e819d520 100644 --- a/clang/include/clang/Basic/LangOptions.def +++ b/clang/include/clang/Basic/LangOptions.def @@ -422,6 +422,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 4c642c9e10c91..9a6729f862b42 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("no_wrap"))) + OB_NoWrap, + + // Signed types defined as wrapping via -fwrapv can still be instrumented + // by sanitizers (PR82432). This field is needed to disambiguate canonical + // wrapping type behaviors from -fwrapv behaviors. + // -fwrapv + OB_FWrapv + }; + // FIXME: Unify with TUKind. enum CompilingModuleKind { /// Not compiling a module interface at all. diff --git a/clang/include/clang/Basic/TypeNodes.td b/clang/include/clang/Basic/TypeNodes.td index 567b8a5ca5a4d..de3ec94eea9cc 100644 --- a/clang/include/clang/Basic/TypeNodes.td +++ b/clang/include/clang/Basic/TypeNodes.td @@ -117,3 +117,4 @@ def PipeType : TypeNode; def AtomicType : TypeNode; def BitIntType : TypeNode; def DependentBitIntType : TypeNode, AlwaysDependent; +def OverflowBehaviorType : TypeNode; diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index d0b54a446309b..372aed1eb93cc 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -2300,6 +2300,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/Sema.h b/clang/include/clang/Sema/Sema.h index b331acbe606b7..f8381c975cca0 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -10066,6 +10066,18 @@ class Sema final : public SemaBase { /// floating-point or integral promotion. bool IsComplexPromotion(QualType FromType, QualType ToType); + /// IsOverflowBehaviorTypePromotion - Determines whether the conversion from + /// FromType to ToType involves an OverflowBehaviorType FromType being + /// promoted to an OverflowBehaviorType ToType which has a larger bitwidth. + /// If so, returns true and sets FromType to ToType. + bool IsOverflowBehaviorTypePromotion(QualType FromType, QualType ToType); + + /// IsOverflowBehaviorTypeConversion - Determines whether the conversion from + /// FromType to ToType necessarily involves both an OverflowBehaviorType and + /// a non-OverflowBehaviorType. If so, returns true and sets FromType to + /// ToType. + bool IsOverflowBehaviorTypeConversion(QualType FromType, QualType ToType); + /// IsPointerConversion - Determines whether the conversion of the /// expression From, which has the (possibly adjusted) type FromType, /// can be converted to the type ToType via a pointer conversion (C++ diff --git a/clang/include/clang/Serialization/ASTRecordReader.h b/clang/include/clang/Serialization/ASTRecordReader.h index 1472497ff5e7e..ae5f79f9afd07 100644 --- a/clang/include/clang/Serialization/ASTRecordReader.h +++ b/clang/include/clang/Serialization/ASTRecordReader.h @@ -350,6 +350,10 @@ class ASTRecordReader return cast(readAttr()); } + NoSanitizeAttr *readNoSanitizeAttr() { + return cast(readAttr()); + } + /// Reads a token out of a record, advancing Idx. Token readToken() { return Reader->ReadToken(*F, Record, Idx); diff --git a/clang/include/clang/Serialization/ASTRecordWriter.h b/clang/include/clang/Serialization/ASTRecordWriter.h index ee005ec287708..f43b3dfea2641 100644 --- a/clang/include/clang/Serialization/ASTRecordWriter.h +++ b/clang/include/clang/Serialization/ASTRecordWriter.h @@ -141,6 +141,8 @@ class ASTRecordWriter /// Write an BTFTypeTagAttr object. void writeBTFTypeTagAttr(const BTFTypeTagAttr *A) { AddAttr(A); } + void writeNoSanitizeAttr(const NoSanitizeAttr *A) { AddAttr(A); } + /// Add a definition for the given function to the queue of statements /// to emit. void AddFunctionDefinition(const FunctionDecl *FD); diff --git a/clang/include/clang/Serialization/TypeBitCodes.def b/clang/include/clang/Serialization/TypeBitCodes.def index b8cde2e370960..edeecc7cadc4f 100644 --- a/clang/include/clang/Serialization/TypeBitCodes.def +++ b/clang/include/clang/Serialization/TypeBitCodes.def @@ -69,5 +69,6 @@ TYPE_BIT_CODE(CountAttributed, COUNT_ATTRIBUTED, 57) TYPE_BIT_CODE(ArrayParameter, ARRAY_PARAMETER, 58) TYPE_BIT_CODE(HLSLAttributedResource, HLSLRESOURCE_ATTRIBUTED, 59) TYPE_BIT_CODE(HLSLInlineSpirv, HLSL_INLINE_SPIRV, 60) +TYPE_BIT_CODE(OverflowBehavior, OVERFLOWBEHAVIOR, 61) #undef TYPE_BIT_CODE diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 679812adcdf12..86f68b3cba472 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -870,6 +870,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, @@ -2555,6 +2591,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()); @@ -3590,6 +3630,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: @@ -3735,6 +3778,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::Elaborated: { const auto *ET = cast(Orig); return getElaboratedType(ET->getKeyword(), ET->getQualifier(), @@ -4310,6 +4359,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 @@ -5545,6 +5595,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 == "no_wrap") { + Kind = OverflowBehaviorType::OverflowBehaviorKind::NoWrap; + } else { + return Underlying; + } + + return getOverflowBehaviorType(Kind, Underlying); +} + +QualType ASTContext::getOverflowBehaviorType( + OverflowBehaviorType::OverflowBehaviorKind Kind, + QualType Underlying) const { + llvm::FoldingSetNodeID ID; + OverflowBehaviorType::Profile(ID, Underlying, Kind); + void *InsertPos = nullptr; + + if (OverflowBehaviorType *OBT = + OverflowBehaviorTypes.FindNodeOrInsertPos(ID, InsertPos)) { + return QualType(OBT, 0); + } + + QualType Canonical; + if (!Underlying.isCanonical()) { + Canonical = getOverflowBehaviorType(Kind, getCanonicalType(Underlying)); + OverflowBehaviorType *NewOBT = + OverflowBehaviorTypes.FindNodeOrInsertPos(ID, InsertPos); + assert(!NewOBT && "Shouldn't be in the map!"); + (void)NewOBT; + } + + OverflowBehaviorType *Ty = new (*this, alignof(OverflowBehaviorType)) + OverflowBehaviorType(Canonical, Underlying, Kind); + + Types.push_back(Ty); + OverflowBehaviorTypes.InsertNode(Ty, InsertPos); + return QualType(Ty, 0); +} + QualType ASTContext::getHLSLAttributedResourceType( QualType Wrapped, QualType Contained, const HLSLAttributedResourceType::Attributes &Attrs) { @@ -8091,6 +8186,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: @@ -9568,6 +9666,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: @@ -11566,6 +11665,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) { + // Both are OverflowBehaviorTypes. + if (LHSOBT->getBehaviorKind() != RHSOBT->getBehaviorKind()) + return QualType(); // Incompatible if behaviors differ. + + QualType MergedUnderlying = mergeTypes( + LHSOBT->getUnderlyingType(), RHSOBT->getUnderlyingType(), + OfBlockPointer, Unqualified, BlockReturnType, IsConditionalOperator); + + if (MergedUnderlying.isNull()) + return QualType(); + + // If the merged underlying type is the same as one of the original + // underlying types, we can return the original OBT to preserve typedefs. + if (getCanonicalType(MergedUnderlying) == + getCanonicalType(LHSOBT->getUnderlyingType())) + return LHS; + if (getCanonicalType(MergedUnderlying) == + getCanonicalType(RHSOBT->getUnderlyingType())) + return RHS; + + return getOverflowBehaviorType(LHSOBT->getBehaviorKind(), + MergedUnderlying); + } + return mergeTypes(LHSOBT->getUnderlyingType(), RHS, OfBlockPointer, + Unqualified, BlockReturnType, IsConditionalOperator); + } + + return mergeTypes(LHS, RHSOBT->getUnderlyingType(), OfBlockPointer, + Unqualified, BlockReturnType, IsConditionalOperator); +} + QualType ASTContext::mergeTypes(QualType LHS, QualType RHS, bool OfBlockPointer, bool Unqualified, bool BlockReturnType, bool IsConditionalOperator) { @@ -11586,6 +11727,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(); @@ -11708,6 +11854,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: @@ -14227,6 +14374,12 @@ static QualType getCommonNonSugarTypeNode(ASTContext &Ctx, const Type *X, getCommonTypeKeyword(NX, NY), getCommonQualifier(Ctx, NX, NY, /*IsSame=*/true), NX->getIdentifier()); } + case Type::OverflowBehavior: { + // FIXME: Should we consider both types? + const auto *NX = cast(X); + return Ctx.getOverflowBehaviorType(NX->getBehaviorKind(), + NX->getUnderlyingType()); + } case Type::DependentTemplateSpecialization: { const auto *TX = cast(X), *TY = cast(Y); @@ -14315,6 +14468,7 @@ static QualType getCommonSugarTypeNode(ASTContext &Ctx, const Type *X, CANONICAL_TYPE(ObjCInterface) CANONICAL_TYPE(ObjCObject) CANONICAL_TYPE(ObjCObjectPointer) + CANONICAL_TYPE(OverflowBehavior) CANONICAL_TYPE(Pipe) CANONICAL_TYPE(Pointer) CANONICAL_TYPE(Record) diff --git a/clang/lib/AST/ASTDiagnostic.cpp b/clang/lib/AST/ASTDiagnostic.cpp index 2ef0c31ec1bd9..c6bf1db2b592d 100644 --- a/clang/lib/AST/ASTDiagnostic.cpp +++ b/clang/lib/AST/ASTDiagnostic.cpp @@ -79,6 +79,13 @@ QualType clang::desugarForDiagnostic(ASTContext &Context, QualType QT, QT = AT->desugar(); continue; } + // ... or an overflow behavior type. + if (const OverflowBehaviorType *OBT = dyn_cast(Ty)) { + if (!OBT->isSugared()) + break; + QT = OBT->desugar(); + continue; + } // Desugar FunctionType if return type or any parameter type should be // desugared. Preserve nullability attribute on desugared types. diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index 4d3bd985739fb..1ef33f0bda5d6 100644 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -1998,6 +1998,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 3aa6b37844103..9068ebedbd255 100644 --- a/clang/lib/AST/ASTStructuralEquivalence.cpp +++ b/clang/lib/AST/ASTStructuralEquivalence.cpp @@ -1143,6 +1143,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 419dd5dbdc695..6b1ef38fd7395 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -2923,7 +2923,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) @@ -12474,6 +12474,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; @@ -15088,7 +15089,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 5d3b56fc4e713..3fd146c76ce36 100644 --- a/clang/lib/AST/FormatString.cpp +++ b/clang/lib/AST/FormatString.cpp @@ -341,6 +341,10 @@ ArgType::matchesType(ASTContext &C, QualType argTy) const { argTy = PT->getPointeeType(); } + if (const OverflowBehaviorType *OBT = + dyn_cast(argTy.getCanonicalType())) + argTy = OBT->getUnderlyingType(); + switch (K) { case InvalidTy: llvm_unreachable("ArgType must be valid"); diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp index 0520987ce6b3a..ac8949b56d94d 100644 --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -2465,6 +2465,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: @@ -4647,6 +4648,11 @@ void CXXNameMangler::mangleType(const PipeType *T) { Out << "8ocl_pipe"; } +void CXXNameMangler::mangleType(const OverflowBehaviorType *T) { + Out << "U9Obt_"; + mangleType(T->getUnderlyingType()); +} + void CXXNameMangler::mangleType(const BitIntType *T) { // 5.1.5.2 Builtin types // ::= DB _ diff --git a/clang/lib/AST/MicrosoftMangle.cpp b/clang/lib/AST/MicrosoftMangle.cpp index bc47e0506add0..e320d5fe7489a 100644 --- a/clang/lib/AST/MicrosoftMangle.cpp +++ b/clang/lib/AST/MicrosoftMangle.cpp @@ -3782,6 +3782,11 @@ void MicrosoftCXXNameMangler::mangleType(const HLSLInlineSpirvType *T, llvm_unreachable("HLSL uses Itanium name mangling"); } +void MicrosoftCXXNameMangler::mangleType(const OverflowBehaviorType *T, + Qualifiers, SourceRange Range) { + llvm_unreachable("OverflowBehaviorType uses Itanium name mangling"); +} + // ::= | | // // ::= A # private near diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index e5a1ab2ff8906..3cc62e420ba96 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -647,6 +647,10 @@ template <> const CountAttributedType *Type::getAs() const { return getAsSugar(this); } +template <> const OverflowBehaviorType *Type::getAs() const { + return getAsSugar(this); +} + /// getUnqualifiedDesugaredType - Pull any qualifiers and syntactic /// sugar off the given type. This should produce an object of the /// same dynamic type as the canonical type. @@ -1155,6 +1159,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()) @@ -2049,6 +2065,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()); } @@ -2112,10 +2132,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 ET->getDecl()->isComplete(); + if (const OverflowBehaviorType *OBT = + dyn_cast(CanonicalType)) + return OBT->getUnderlyingType()->isIntegralOrEnumerationType(); + } + return isBitIntType(); } @@ -2123,6 +2148,10 @@ bool Type::isIntegralOrUnscopedEnumerationType() const { if (const auto *BT = dyn_cast(CanonicalType)) return BT->isInteger(); + if (const OverflowBehaviorType *OBT = + dyn_cast(CanonicalType)) + return OBT->getUnderlyingType()->isIntegralOrUnscopedEnumerationType(); + if (isBitIntType()) return true; @@ -2225,6 +2254,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; } @@ -2241,6 +2273,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; } @@ -2270,6 +2305,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; } @@ -2286,6 +2324,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; } @@ -2345,6 +2386,10 @@ bool Type::isArithmeticType() const { // false for scoped enumerations since that will disable any // unwanted implicit conversions. return !ET->getDecl()->isScoped() && ET->getDecl()->isComplete(); + + if (isOverflowBehaviorType() && + getAs()->getUnderlyingType()->isArithmeticType()) + return true; return isa(CanonicalType) || isBitIntType(); } @@ -2393,6 +2438,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"); @@ -2733,6 +2780,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: @@ -2934,6 +2982,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::isNoWrapType() const { + if (const auto *OBT = getCanonicalType()->getAs()) + return OBT->getBehaviorKind() == + OverflowBehaviorType::OverflowBehaviorKind::NoWrap; + + return false; +} + QualType::PrimitiveDefaultInitializeKind QualType::isNonTrivialToPrimitiveDefaultInitialize() const { if (const auto *RT = @@ -3032,6 +3096,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())) @@ -3969,6 +4036,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) {} @@ -4770,6 +4843,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"); @@ -4864,6 +4939,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() @@ -5058,6 +5136,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 5c45c596538f8..9d191a24f2753 100644 --- a/clang/lib/AST/TypeLoc.cpp +++ b/clang/lib/AST/TypeLoc.cpp @@ -530,6 +530,10 @@ SourceRange BTFTagAttributedTypeLoc::getLocalSourceRange() const { return getAttr() ? getAttr()->getRange() : SourceRange(); } +SourceRange OverflowBehaviorTypeLoc::getLocalSourceRange() const { + return SourceRange(); +} + void TypeOfTypeLoc::initializeLocal(ASTContext &Context, SourceLocation Loc) { TypeofLikeTypeLoc @@ -737,6 +741,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 818d2139628e3..990ca7aa30517 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; @@ -2057,6 +2058,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: @@ -2134,6 +2136,26 @@ void TypePrinter::printBTFTagAttributedAfter(const BTFTagAttributedType *T, printAfter(T->getWrappedType(), OS); } +void TypePrinter::printOverflowBehaviorBefore(const OverflowBehaviorType *T, + raw_ostream &OS) { + StringRef KindName; + switch (T->getBehaviorKind()) { + case clang::OverflowBehaviorType::OverflowBehaviorKind::Wrap: + KindName = "__wrap"; + break; + case clang::OverflowBehaviorType::OverflowBehaviorKind::NoWrap: + KindName = "__no_wrap"; + break; + } + OS << KindName << " "; + printBefore(T->getUnderlyingType(), OS); +} + +void TypePrinter::printOverflowBehaviorAfter(const OverflowBehaviorType *T, + raw_ostream &OS) { + printAfter(T->getUnderlyingType(), OS); +} + void TypePrinter::printHLSLAttributedResourceBefore( const HLSLAttributedResourceType *T, raw_ostream &OS) { printBefore(T->getWrappedType(), OS); diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp index f97c7b6445984..8f9de41fe0c42 100644 --- a/clang/lib/CodeGen/CGDebugInfo.cpp +++ b/clang/lib/CodeGen/CGDebugInfo.cpp @@ -1126,6 +1126,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; @@ -4038,6 +4043,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 411b2e718da30..1ef094b138bb8 100644 --- a/clang/lib/CodeGen/CGDebugInfo.h +++ b/clang/lib/CodeGen/CGDebugInfo.h @@ -199,6 +199,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 44931d0481e26..1956fa142c2bc 100644 --- a/clang/lib/CodeGen/CGExprScalar.cpp +++ b/clang/lib/CodeGen/CGExprScalar.cpp @@ -189,8 +189,42 @@ static bool IsWidenedIntegerOp(const ASTContext &Ctx, const Expr *E) { return getUnwidenedIntegerType(Ctx, E).has_value(); } +/// Consider OverflowBehaviorType and language options to calculate the final +/// overflow behavior for an expression. There are no language options for +/// unsigned overflow semantics so there is nothing to consider there. +static LangOptions::OverflowBehaviorKind +getOverflowBehaviorConsideringType(const CodeGenFunction &CGF, + const QualType Ty) { + const OverflowBehaviorType *OBT = Ty->getAs(); + /// FIXME: Having two enums named `OverflowBehaviorKind` is not ideal, these + /// should be unified into one coherent enum that supports both unsigned and + /// signed overflow behavior semantics. + if (OBT) { + switch (OBT->getBehaviorKind()) { + case OverflowBehaviorType::OverflowBehaviorKind::Wrap: + return LangOptions::OverflowBehaviorKind::OB_Wrap; + case OverflowBehaviorType::OverflowBehaviorKind::NoWrap: + return LangOptions::OverflowBehaviorKind::OB_NoWrap; + } + llvm_unreachable("Unknown OverflowBehaviorKind"); + } + + if (Ty->isUnsignedIntegerType()) { + return LangOptions::OverflowBehaviorKind::OB_Unset; + } + + switch (CGF.getLangOpts().getSignedOverflowBehavior()) { + case LangOptions::SignedOverflowBehaviorTy::SOB_Defined: + return LangOptions::OverflowBehaviorKind::OB_FWrapv; + case LangOptions::SignedOverflowBehaviorTy::SOB_Undefined: + return LangOptions::OverflowBehaviorKind::OB_Unset; + case LangOptions::SignedOverflowBehaviorTy::SOB_Trapping: + return LangOptions::OverflowBehaviorKind::OB_NoWrap; + } +} + /// Check if we can skip the overflow check for \p Op. -static bool CanElideOverflowCheck(const ASTContext &Ctx, const BinOpInfo &Op) { +static bool CanElideOverflowCheck(ASTContext &Ctx, const BinOpInfo &Op) { assert((isa(Op.E) || isa(Op.E)) && "Expected a unary or binary operator"); @@ -199,6 +233,19 @@ static bool CanElideOverflowCheck(const ASTContext &Ctx, const BinOpInfo &Op) { if (!Op.mayHaveIntegerOverflow()) return true; + const UnaryOperator *UO = dyn_cast(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.isNoWrapType()) + return false; + if (Op.Ty->isSignedIntegerType() && Ctx.isTypeIgnoredBySanitizer(SanitizerKind::SignedIntegerOverflow, Op.Ty)) { @@ -211,24 +258,12 @@ static bool CanElideOverflowCheck(const ASTContext &Ctx, const BinOpInfo &Op) { return true; } - const UnaryOperator *UO = dyn_cast(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; @@ -774,19 +809,28 @@ class ScalarExprEmitter // Binary Operators. Value *EmitMul(const BinOpInfo &Ops) { - if (Ops.Ty->isSignedIntegerOrEnumerationType()) { - switch (CGF.getLangOpts().getSignedOverflowBehavior()) { - case LangOptions::SOB_Defined: - if (!CGF.SanOpts.has(SanitizerKind::SignedIntegerOverflow)) + if (Ops.Ty->isSignedIntegerOrEnumerationType() || + Ops.Ty->isUnsignedIntegerType()) { + const bool isSigned = Ops.Ty->isSignedIntegerOrEnumerationType(); + const bool hasSan = + isSigned ? CGF.SanOpts.has(SanitizerKind::SignedIntegerOverflow) + : CGF.SanOpts.has(SanitizerKind::UnsignedIntegerOverflow); + switch (getOverflowBehaviorConsideringType(CGF, Ops.Ty)) { + case LangOptions::OB_Wrap: + return Builder.CreateMul(Ops.LHS, Ops.RHS, "mul"); + case LangOptions::OB_FWrapv: + if (!hasSan) return Builder.CreateMul(Ops.LHS, Ops.RHS, "mul"); [[fallthrough]]; - case LangOptions::SOB_Undefined: - if (!CGF.SanOpts.has(SanitizerKind::SignedIntegerOverflow)) - return Builder.CreateNSWMul(Ops.LHS, Ops.RHS, "mul"); + case LangOptions::OB_Unset: + if (!hasSan) + return isSigned ? Builder.CreateNSWMul(Ops.LHS, Ops.RHS, "mul") + : Builder.CreateMul(Ops.LHS, Ops.RHS, "mul"); [[fallthrough]]; - case LangOptions::SOB_Trapping: + case LangOptions::OB_NoWrap: if (CanElideOverflowCheck(CGF.getContext(), Ops)) - return Builder.CreateNSWMul(Ops.LHS, Ops.RHS, "mul"); + return isSigned ? Builder.CreateNSWMul(Ops.LHS, Ops.RHS, "mul") + : Builder.CreateMul(Ops.LHS, Ops.RHS, "mul"); return EmitOverflowCheckedBinOp(Ops); } } @@ -808,11 +852,6 @@ class ScalarExprEmitter return MB.CreateScalarMultiply(Ops.LHS, Ops.RHS); } - if (Ops.Ty->isUnsignedIntegerType() && - CGF.SanOpts.has(SanitizerKind::UnsignedIntegerOverflow) && - !CanElideOverflowCheck(CGF.getContext(), Ops)) - return EmitOverflowCheckedBinOp(Ops); - if (Ops.LHS->getType()->isFPOrFPVectorTy()) { // Preserve the old values CodeGenFunction::CGFPOptionsRAII FPOptsRAII(CGF, Ops.FPFeatures); @@ -1162,8 +1201,16 @@ void ScalarExprEmitter::EmitIntegerTruncationCheck(Value *Src, QualType SrcType, SanitizerDebugLocation SanScope(&CGF, {Check.second.second}, CheckHandler); // Does some SSCL ignore this type? - if (CGF.getContext().isTypeIgnoredBySanitizer( - SanitizerMask::bitPosToMask(Check.second.second), DstType)) + const bool ignoredBySanitizer = CGF.getContext().isTypeIgnoredBySanitizer( + SanitizerMask::bitPosToMask(Check.second.second), DstType); + + // Consider OverflowBehaviorTypes which override SSCL type entries for + // truncation sanitizers. + if (const auto *OBT = DstType->getAs()) { + if (OBT->getBehaviorKind() != + OverflowBehaviorType::OverflowBehaviorKind::NoWrap) + return; + } else if (ignoredBySanitizer) return; llvm::Constant *StaticArgs[] = { @@ -2975,43 +3022,37 @@ llvm::Value *ScalarExprEmitter::EmitIncDecConsiderOverflowBehavior( llvm::Value *Amount = llvm::ConstantInt::get(InVal->getType(), IsInc ? 1 : -1, true); StringRef Name = IsInc ? "inc" : "dec"; - switch (CGF.getLangOpts().getSignedOverflowBehavior()) { - case LangOptions::SOB_Defined: - if (!CGF.SanOpts.has(SanitizerKind::SignedIntegerOverflow)) + QualType Ty = E->getType(); + const bool isSigned = Ty->isSignedIntegerOrEnumerationType(); + const bool hasSan = + isSigned ? CGF.SanOpts.has(SanitizerKind::SignedIntegerOverflow) + : CGF.SanOpts.has(SanitizerKind::UnsignedIntegerOverflow); + + switch (getOverflowBehaviorConsideringType(CGF, Ty)) { + case LangOptions::OB_Wrap: + return Builder.CreateAdd(InVal, Amount, Name); + case LangOptions::OB_FWrapv: + 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_NoWrap: + 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 { @@ -3130,9 +3171,6 @@ ScalarExprEmitter::EmitScalarPrePostIncDec(const UnaryOperator *E, LValue LV, QualType promotedType; bool canPerformLossyDemotionCheck = false; - bool excludeOverflowPattern = - matchesPostDecrInWhile(E, isInc, isPre, CGF.getContext()); - if (CGF.getContext().isPromotableIntegerType(type)) { promotedType = CGF.getContext().getPromotedIntegerType(type); assert(promotedType != type && "Shouldn't promote to the same type."); @@ -3189,16 +3227,21 @@ ScalarExprEmitter::EmitScalarPrePostIncDec(const UnaryOperator *E, LValue LV, // Note that signed integer inc/dec with width less than int can't // overflow because of promotion rules; we're just eliding a few steps // here. - } else if (E->canOverflow() && type->isSignedIntegerOrEnumerationType()) { + } else if (type->isSignedIntegerOrEnumerationType() || + type->isUnsignedIntegerType()) { value = EmitIncDecConsiderOverflowBehavior(E, value, isInc); - } else if (E->canOverflow() && type->isUnsignedIntegerType() && - CGF.SanOpts.has(SanitizerKind::UnsignedIntegerOverflow) && - !excludeOverflowPattern && - !CGF.getContext().isTypeIgnoredBySanitizer( - SanitizerKind::UnsignedIntegerOverflow, E->getType())) { - value = EmitOverflowCheckedBinOp(createBinOpInfoFromIncDec( - E, value, isInc, E->getFPFeaturesInEffect(CGF.getLangOpts()))); - } else { + } + // else if (E->canOverflow() && type->isSignedIntegerOrEnumerationType()) { + // value = EmitIncDecConsiderOverflowBehavior(E, value, isInc); + // } else if (E->canOverflow() && type->isUnsignedIntegerType() && + // CGF.SanOpts.has(SanitizerKind::UnsignedIntegerOverflow) && + // !excludeOverflowPattern && + // !CGF.getContext().isTypeIgnoredBySanitizer( + // SanitizerKind::UnsignedIntegerOverflow, E->getType())) { + // value = EmitOverflowCheckedBinOp(createBinOpInfoFromIncDec( + // E, value, isInc, E->getFPFeaturesInEffect(CGF.getLangOpts()))); + // } + else { llvm::Value *amt = llvm::ConstantInt::get(value->getType(), amount, true); value = Builder.CreateAdd(value, amt, isInc ? "inc" : "dec"); } @@ -3976,7 +4019,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 = @@ -4122,14 +4165,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; @@ -4447,19 +4501,30 @@ Value *ScalarExprEmitter::EmitAdd(const BinOpInfo &op) { op.RHS->getType()->isPointerTy()) return emitPointerArithmetic(CGF, op, CodeGenFunction::NotSubtraction); - if (op.Ty->isSignedIntegerOrEnumerationType()) { - switch (CGF.getLangOpts().getSignedOverflowBehavior()) { - case LangOptions::SOB_Defined: - if (!CGF.SanOpts.has(SanitizerKind::SignedIntegerOverflow)) + // FIXME: maybe we want to emit NUWAdd for unsigned non-wrapping types which + // should depend on -fwrapv (-fno-strict-overflow) + if (op.Ty->isSignedIntegerOrEnumerationType() || + op.Ty->isUnsignedIntegerType()) { + const bool isSigned = op.Ty->isSignedIntegerOrEnumerationType(); + const bool hasSan = + isSigned ? CGF.SanOpts.has(SanitizerKind::SignedIntegerOverflow) + : CGF.SanOpts.has(SanitizerKind::UnsignedIntegerOverflow); + switch (getOverflowBehaviorConsideringType(CGF, op.Ty)) { + case LangOptions::OB_Wrap: + return Builder.CreateAdd(op.LHS, op.RHS, "add"); + case LangOptions::OB_FWrapv: + if (!hasSan) return Builder.CreateAdd(op.LHS, op.RHS, "add"); [[fallthrough]]; - case LangOptions::SOB_Undefined: - if (!CGF.SanOpts.has(SanitizerKind::SignedIntegerOverflow)) - return Builder.CreateNSWAdd(op.LHS, op.RHS, "add"); + case LangOptions::OB_Unset: + if (!hasSan) + return isSigned ? Builder.CreateNSWAdd(op.LHS, op.RHS, "add") + : Builder.CreateAdd(op.LHS, op.RHS, "add"); [[fallthrough]]; - case LangOptions::SOB_Trapping: + case LangOptions::OB_NoWrap: if (CanElideOverflowCheck(CGF.getContext(), op)) - return Builder.CreateNSWAdd(op.LHS, op.RHS, "add"); + return isSigned ? Builder.CreateNSWAdd(op.LHS, op.RHS, "add") + : Builder.CreateAdd(op.LHS, op.RHS, "add"); return EmitOverflowCheckedBinOp(op); } } @@ -4478,11 +4543,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"); @@ -4603,19 +4663,28 @@ Value *ScalarExprEmitter::EmitFixedPointBinOp(const BinOpInfo &op) { Value *ScalarExprEmitter::EmitSub(const BinOpInfo &op) { // The LHS is always a pointer if either side is. if (!op.LHS->getType()->isPointerTy()) { - if (op.Ty->isSignedIntegerOrEnumerationType()) { - switch (CGF.getLangOpts().getSignedOverflowBehavior()) { - case LangOptions::SOB_Defined: - if (!CGF.SanOpts.has(SanitizerKind::SignedIntegerOverflow)) + if (op.Ty->isSignedIntegerOrEnumerationType() || + op.Ty->isUnsignedIntegerType()) { + const bool isSigned = op.Ty->isSignedIntegerOrEnumerationType(); + const bool hasSan = + isSigned ? CGF.SanOpts.has(SanitizerKind::SignedIntegerOverflow) + : CGF.SanOpts.has(SanitizerKind::UnsignedIntegerOverflow); + switch (getOverflowBehaviorConsideringType(CGF, op.Ty)) { + case LangOptions::OB_Wrap: + return Builder.CreateSub(op.LHS, op.RHS, "sub"); + case LangOptions::OB_FWrapv: + if (!hasSan) return Builder.CreateSub(op.LHS, op.RHS, "sub"); [[fallthrough]]; - case LangOptions::SOB_Undefined: - if (!CGF.SanOpts.has(SanitizerKind::SignedIntegerOverflow)) - return Builder.CreateNSWSub(op.LHS, op.RHS, "sub"); + case LangOptions::OB_Unset: + if (!hasSan) + return isSigned ? Builder.CreateNSWSub(op.LHS, op.RHS, "sub") + : Builder.CreateSub(op.LHS, op.RHS, "sub"); [[fallthrough]]; - case LangOptions::SOB_Trapping: + case LangOptions::OB_NoWrap: if (CanElideOverflowCheck(CGF.getContext(), op)) - return Builder.CreateNSWSub(op.LHS, op.RHS, "sub"); + return isSigned ? Builder.CreateNSWSub(op.LHS, op.RHS, "sub") + : Builder.CreateSub(op.LHS, op.RHS, "sub"); return EmitOverflowCheckedBinOp(op); } } @@ -4634,11 +4703,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 776a646ceb32f..3a7715a6ed567 100644 --- a/clang/lib/CodeGen/CodeGenFunction.cpp +++ b/clang/lib/CodeGen/CodeGenFunction.cpp @@ -286,6 +286,7 @@ TypeEvaluationKind CodeGenFunction::getEvaluationKind(QualType type) { case Type::BitInt: case Type::HLSLAttributedResource: case Type::HLSLInlineSpirv: + case Type::OverflowBehavior: return TEK_Scalar; // Complexes. @@ -2584,6 +2585,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 c98503e4bbd26..552602e242048 100644 --- a/clang/lib/CodeGen/CodeGenTypes.cpp +++ b/clang/lib/CodeGen/CodeGenTypes.cpp @@ -767,6 +767,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 f4a99467010af..ffec509c12da6 100644 --- a/clang/lib/CodeGen/ItaniumCXXABI.cpp +++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp @@ -3974,6 +3974,8 @@ void ItaniumRTTIBuilder::BuildVTablePointer(const Type *Ty, case Type::HLSLAttributedResource: case Type::HLSLInlineSpirv: llvm_unreachable("HLSL doesn't support virtual functions"); + case Type::OverflowBehavior: + llvm_unreachable("NoSanitize not supported for virtual functions"); } llvm::Constant *VTable = nullptr; @@ -4247,6 +4249,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 fe1865888bdd0..3e05188fecf35 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -6354,6 +6354,9 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, Args.addOptInFlag(CmdArgs, options::OPT_ffixed_point, options::OPT_fno_fixed_point); + Args.addOptInFlag(CmdArgs, options::OPT_foverflow_behavior_types, + options::OPT_fno_overflow_behavior_types); + if (Arg *A = Args.getLastArg(options::OPT_fcxx_abi_EQ)) A->render(Args, CmdArgs); diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index dd5b710d7e1d4..ae1f486d7c0fd 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -8213,6 +8213,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. @@ -10459,6 +10463,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. @@ -10509,6 +10515,8 @@ struct IntRange { T = AT->getValueType().getTypePtr(); if (const EnumType *ET = dyn_cast(T)) T = C.getCanonicalType(ET->getDecl()->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()); @@ -12131,6 +12139,30 @@ void Sema::CheckImplicitConversion(Expr *E, QualType T, SourceLocation CC, } } + // Diagnose potentially problematic implicit casts from an overflow behavior + // type to an integer type. + if (const auto *OBT = Source->getAs()) { + bool DiscardedDuringAssignment = false; + if (const DeclRefExpr *DRE = dyn_cast(E)) + DiscardedDuringAssignment = DRE->isOverflowBehaviorDiscarded(); + + if (Target->isIntegerType() && !Target->isOverflowBehaviorType()) { + // Implicit casts from unsigned wrap types to unsigned types are less + // problematic but still warrant some diagnostic. + if (DiscardedDuringAssignment) + return DiagnoseImpCast( + *this, E, T, CC, + diag::warn_implicitly_discarded_overflow_behavior_assignment); + if (OBT->isUnsignedIntegerType() && OBT->isWrapKind() && + Target->isUnsignedIntegerType()) + return DiagnoseImpCast( + *this, E, T, CC, + diag::warn_implicitly_discarded_overflow_behavior_pedantic); + return DiagnoseImpCast(*this, E, T, CC, + diag::warn_implicitly_discarded_overflow_behavior); + } + } + // If the we're converting a constant to an ObjC BOOL on a platform where BOOL // is a typedef for signed char (macOS), then that constant value has to be 1 // or 0. diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index d7234e269f645..4eba0d5556ab3 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -13643,6 +13643,13 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init, bool DirectInit) { return; } + if (!VDecl->getType()->isOverflowBehaviorType() && + !Init->getType().isNull() && Init->getType()->isOverflowBehaviorType()) { + if (DeclRefExpr *DRE = dyn_cast(Init)) { + DRE->setOverflowBehaviorDiscarded(true); + } + } + // C++11 [decl.spec.auto]p6. Deduce the type which 'auto' stands in for. if (VDecl->getType()->isUndeducedType()) { if (Init->containsErrors()) { diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 728ada33e2e63..17ada5773c041 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -1409,6 +1409,36 @@ static QualType handleComplexIntConversion(Sema &S, ExprResult &LHS, return ComplexType; } +static QualType handleOverflowBehaviorTypeConversion(Sema &S, ExprResult &LHS, + ExprResult &RHS, + bool IsCompAssign) { + QualType LHSType = LHS.get()->getType().getUnqualifiedType(); + QualType RHSType = RHS.get()->getType().getUnqualifiedType(); + + const OverflowBehaviorType *LhsOBT = LHSType->getAs(); + const OverflowBehaviorType *RhsOBT = RHSType->getAs(); + + assert(LHSType->isIntegerType() && RHSType->isIntegerType() && + "Non-integer type conversion not supported for OverflowBehaviorTypes"); + + if (LhsOBT && RhsOBT) { + if (LhsOBT->getBehaviorKind() == RhsOBT->getBehaviorKind()) + return handleIntegerConversion( + S, LHS, RHS, LHSType, RHSType, IsCompAssign); + } + + // NoWrap has precedence over Wrap; eagerly cast Wrap types to NoWrap types + if ((LhsOBT && !RhsOBT) || (LhsOBT && RhsOBT && !RhsOBT->isNoWrapKind())) { + RHS = doIntegralCast(S, RHS.get(), LHSType); + return LHSType; + } + + if (!IsCompAssign) + LHS = doIntegralCast(S, LHS.get(), RHSType); + + return RHSType; +} + /// Return the rank of a given fixed point or integer type. The value itself /// doesn't matter, but the values must be increasing with proper increasing /// rank as described in N1169 4.1.1. @@ -1662,8 +1692,9 @@ QualType Sema::UsualArithmeticConversions(ExprResult &LHS, ExprResult &RHS, LHSType = AtomicLHS->getValueType(); // If both types are identical, no conversion is needed. - if (Context.hasSameType(LHSType, RHSType)) + if (Context.hasSameType(LHSType, RHSType)) { return Context.getCommonSugaredType(LHSType, RHSType); + } // If either side is a non-arithmetic type (e.g. a pointer), we are done. // The caller can deal with this (e.g. pointer + int). @@ -1709,6 +1740,11 @@ QualType Sema::UsualArithmeticConversions(ExprResult &LHS, ExprResult &RHS, if (LHSType->isFixedPointType() || RHSType->isFixedPointType()) return handleFixedPointConversion(*this, LHSType, RHSType); + if (LHSType->isOverflowBehaviorType() || RHSType->isOverflowBehaviorType()) { + return handleOverflowBehaviorTypeConversion( + *this, LHS, RHS, ACK == ArithConvKind::CompAssign); + } + // Finally, we have two differing integer types. return handleIntegerConversion( *this, LHS, RHS, LHSType, RHSType, ACK == ArithConvKind::CompAssign); @@ -1914,16 +1950,33 @@ ExprResult Sema::CreateGenericSelectionExpr( for (unsigned i = 0; i < NumAssocs; ++i) { if (!Types[i]) DefaultIndex = i; - else if (ControllingExpr && - Context.typesAreCompatible( - ControllingExpr->getType().getCanonicalType(), - Types[i]->getType())) - CompatIndices.push_back(i); - else if (ControllingType && - Context.typesAreCompatible( - ControllingType->getType().getCanonicalType(), - Types[i]->getType())) - CompatIndices.push_back(i); + else { + bool Compatible; + QualType ControllingQT = + ControllingExpr ? ControllingExpr->getType().getCanonicalType() + : ControllingType->getType().getCanonicalType(); + QualType AssocQT = Types[i]->getType(); + + const auto *ControllingOBT = ControllingQT->getAs(); + const auto *AssocOBT = + AssocQT.getCanonicalType()->getAs(); + + if (ControllingOBT || AssocOBT) { + if (ControllingOBT && AssocOBT) { + if (ControllingOBT->getBehaviorKind() == AssocOBT->getBehaviorKind()) + Compatible = + Context.typesAreCompatible(ControllingOBT->getUnderlyingType(), + AssocOBT->getUnderlyingType()); + else + Compatible = false; + } else + Compatible = false; + } else + Compatible = Context.typesAreCompatible(ControllingQT, AssocQT); + + if (Compatible) + CompatIndices.push_back(i); + } } auto GetControllingRangeAndType = [](Expr *ControllingExpr, @@ -4535,6 +4588,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: @@ -9078,6 +9132,12 @@ static AssignConvertType checkPointerTypesForAssignment(Sema &S, // C99 6.5.16.1p1 (constraint 3): both operands are pointers to qualified or // unqualified versions of compatible types, ... QualType ltrans = QualType(lhptee, 0), rtrans = QualType(rhptee, 0); + + if (ltrans->isOverflowBehaviorType() || rtrans->isOverflowBehaviorType()) { + if (!S.Context.hasSameType(ltrans, rtrans)) + return AssignConvertType::IncompatiblePointer; + } + if (!S.Context.typesAreCompatible(ltrans, rtrans)) { // Check if the pointee types are compatible ignoring the sign. // We explicitly check for char so that we catch "char" vs @@ -14214,6 +14274,8 @@ static QualType CheckIncrementDecrementOperand(Sema &S, Expr *Op, // C99 6.5.2.4p2, 6.5.6p2 if (!checkArithmeticOpPointerOperand(S, OpLoc, Op)) return QualType(); + } else if (ResType->isOverflowBehaviorType()) { + // OK! } else if (ResType->isObjCObjectPointerType()) { // On modern runtimes, ObjC pointer arithmetic is forbidden. // Otherwise, we just need a complete type. diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp index aa7191d2814fe..7f2012791c5e0 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 1b54628c5e564..3597c74aa76ab 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -117,6 +117,11 @@ CompareQualificationConversions(Sema &S, const StandardConversionSequence& SCS1, const StandardConversionSequence& SCS2); +static ImplicitConversionSequence::CompareKind +CompareOverflowBehaviorConversions(Sema &S, + const StandardConversionSequence &SCS1, + const StandardConversionSequence &SCS2); + static ImplicitConversionSequence::CompareKind CompareDerivedToBaseConversions(Sema &S, SourceLocation Loc, const StandardConversionSequence& SCS1, @@ -2227,6 +2232,12 @@ static bool tryAtomicConversion(Sema &S, Expr *From, QualType ToType, StandardConversionSequence &SCS, bool CStyle); +static bool tryOverflowBehaviorTypeConversion(Sema &S, Expr *From, + QualType ToType, + bool InOverloadResolution, + StandardConversionSequence &SCS, + bool CStyle); + /// IsStandardConversion - Determines whether there is a standard /// conversion sequence (C++ [conv], C++ [over.ics.scs]) from the /// expression From to the type ToType. Standard conversion sequences @@ -2417,9 +2428,16 @@ static bool IsStandardConversion(Sema &S, Expr* From, QualType ToType, // Complex promotion (Clang extension) SCS.Second = ICK_Complex_Promotion; FromType = ToType.getUnqualifiedType(); + } else if (S.IsOverflowBehaviorTypePromotion(FromType, ToType)) { + // OverflowBehaviorType promotions + SCS.Second = ICK_Integral_Promotion; + FromType = ToType.getUnqualifiedType(); + } else if (S.IsOverflowBehaviorTypeConversion(FromType, ToType)) { + // OverflowBehaviorType conversions + SCS.Second = ICK_Integral_Conversion; + FromType = ToType.getUnqualifiedType(); } else if (ToType->isBooleanType() && - (FromType->isArithmeticType() || - FromType->isAnyPointerType() || + (FromType->isArithmeticType() || FromType->isAnyPointerType() || FromType->isBlockPointerType() || FromType->isMemberPointerType())) { // Boolean conversions (C++ 4.12). @@ -2485,6 +2503,9 @@ static bool IsStandardConversion(Sema &S, Expr* From, QualType ToType, // tryAtomicConversion has updated the standard conversion sequence // appropriately. return true; + } else if (tryOverflowBehaviorTypeConversion( + S, From, ToType, InOverloadResolution, SCS, CStyle)) { + return true; } else if (ToType->isEventT() && From->isIntegerConstantExpr(S.getASTContext()) && From->EvaluateKnownConstInt(S.getASTContext()) == 0) { @@ -2834,6 +2855,33 @@ bool Sema::IsComplexPromotion(QualType FromType, QualType ToType) { ToComplex->getElementType()); } +bool Sema::IsOverflowBehaviorTypePromotion(QualType FromType, QualType ToType) { + if (!getLangOpts().OverflowBehaviorTypes) + return false; + + if (!FromType->isOverflowBehaviorType() || !ToType->isOverflowBehaviorType()) + return false; + + return Context.getTypeSize(FromType) < Context.getTypeSize(ToType); +} + +bool Sema::IsOverflowBehaviorTypeConversion(QualType FromType, + QualType ToType) { + if (!getLangOpts().OverflowBehaviorTypes) + return false; + + if (FromType->isOverflowBehaviorType() && !ToType->isOverflowBehaviorType()) + return true; + + if (!FromType->isOverflowBehaviorType() && ToType->isOverflowBehaviorType()) + return true; + + if (FromType->isOverflowBehaviorType() && ToType->isOverflowBehaviorType()) + return Context.getTypeSize(FromType) > Context.getTypeSize(ToType); + + return false; +} + /// BuildSimilarlyQualifiedPointerType - In a pointer conversion from /// the pointer type FromPtr to a pointer to type ToPointee, with the /// same type qualifiers as FromPtr has on its pointee type. ToType, @@ -3867,6 +3915,30 @@ static bool tryAtomicConversion(Sema &S, Expr *From, QualType ToType, return true; } +static bool tryOverflowBehaviorTypeConversion(Sema &S, Expr *From, + QualType ToType, + bool InOverloadResolution, + StandardConversionSequence &SCS, + bool CStyle) { + const OverflowBehaviorType *ToOBT = ToType->getAs(); + if (!ToOBT) + return false; + + StandardConversionSequence InnerSCS; + if (!IsStandardConversion(S, From, ToOBT->getUnderlyingType(), + InOverloadResolution, InnerSCS, CStyle, + /*AllowObjCWritebackConversion=*/false)) + return false; + + SCS.Second = InnerSCS.Second; + SCS.setToType(1, InnerSCS.getToType(1)); + SCS.Third = InnerSCS.Third; + SCS.QualificationIncludesObjCLifetime = + InnerSCS.QualificationIncludesObjCLifetime; + SCS.setToType(2, InnerSCS.getToType(2)); + return true; +} + static bool isFirstArgumentCompatibleWithType(ASTContext &Context, CXXConstructorDecl *Constructor, QualType Type) { @@ -4651,6 +4723,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 @@ -4763,6 +4839,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 b76619fc50268..6cf49f4b9343f 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -6250,6 +6250,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 d09a72b71b805..db2a91e2e37aa 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -2475,6 +2475,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; @@ -7026,6 +7027,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 661746731fdcc..967338c6f9914 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -299,6 +299,14 @@ namespace { return sema.Context.getBTFTagAttributedType(BTFAttr, WrappedType); } + /// Get a OverflowBehaviorType type for the overflow_behavior type + /// attribute. + QualType + getOverflowBehaviorType(OverflowBehaviorType::OverflowBehaviorKind Kind, + QualType UnderlyingType) { + return sema.Context.getOverflowBehaviorType(Kind, UnderlyingType); + } + /// Completely replace the \c auto in \p TypeWithAuto by /// \p Replacement. Also replace \p TypeWithAuto in \c TypeAttrPair if /// necessary. @@ -5881,6 +5889,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); @@ -6136,6 +6147,9 @@ namespace { void VisitBTFTagAttributedTypeLoc(BTFTagAttributedTypeLoc TL) { // nothing } + void VisitOverflowBehaviorTypeLoc(OverflowBehaviorTypeLoc TL) { + // nothing + } void VisitAdjustedTypeLoc(AdjustedTypeLoc TL) { // nothing } @@ -6586,6 +6600,73 @@ static void HandleAddressSpaceTypeAttribute(QualType &Type, } } +static void HandleOverflowBehaviorAttr(QualType &Type, const ParsedAttr &Attr, + TypeProcessingState &State) { + Sema &S = State.getSema(); + + // Check for -foverflow-behavior-types + if (!S.getLangOpts().OverflowBehaviorTypes) { + S.Diag(Attr.getLoc(), diag::warn_overflow_behavior_attribute_disabled) + << Attr << 1; + Attr.setInvalid(); + return; + } + + // Check the number of attribute arguments. + if (Attr.getNumArgs() != 1) { + S.Diag(Attr.getLoc(), diag::err_attribute_wrong_number_arguments) + << Attr << 1; + Attr.setInvalid(); + return; + } + + // Check that the underlying type is an integer type + if (!Type->isIntegerType()) { + S.Diag(Attr.getLoc(), diag::warn_overflow_behavior_non_integer_type) + << Attr << Type.getAsString(); + Attr.setInvalid(); + return; + } + + StringRef KindName = ""; + IdentifierInfo *Ident = nullptr; + + if (Attr.isArgIdent(0)) { + Ident = Attr.getArgAsIdent(0)->getIdentifierInfo(); + KindName = Ident->getName(); + } + + // Support identifier or string argument types. Failure to provide one of + // these two types results in a diagnostic that hints towards using string + // arguments (either "wrap" or "no_wrap") as this is the most common use + // pattern. + if (!Ident) { + auto *Str = dyn_cast(Attr.getArgAsExpr(0)); + if (Str) + KindName = Str->getString(); + else { + S.Diag(Attr.getLoc(), diag::err_attribute_argument_type) + << Attr << AANT_ArgumentString; + Attr.setInvalid(); + return; + } + } + + OverflowBehaviorType::OverflowBehaviorKind Kind; + if (KindName == "wrap") { + Kind = OverflowBehaviorType::OverflowBehaviorKind::Wrap; + } else if (KindName == "no_wrap") { + Kind = OverflowBehaviorType::OverflowBehaviorKind::NoWrap; + } else { + S.Diag(Attr.getLoc(), diag::err_overflow_behavior_unknown_ident) + << KindName << Attr; + Attr.setInvalid(); + return; + } + + Type = State.getOverflowBehaviorType(Kind, Type); +} + /// handleObjCOwnershipTypeAttr - Process an objc_ownership /// attribute on the specified type. /// @@ -8901,6 +8982,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 758012f894a41..2797cdafaa5a5 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -7658,6 +7658,13 @@ QualType TreeTransform::TransformBTFTagAttributedType( llvm_unreachable("Unexpected TreeTransform for BTFTagAttributedType"); } +template +QualType TreeTransform::TransformOverflowBehaviorType( + TypeLocBuilder &TLB, OverflowBehaviorTypeLoc TL) { + // The OverflowBehaviorType is available for C only. + llvm_unreachable("Unexpected TreeTransform for OverflowBehaviorType"); +} + template QualType TreeTransform::TransformHLSLAttributedResourceType( TypeLocBuilder &TLB, HLSLAttributedResourceTypeLoc TL) { diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp index 30e0973149594..f595a79a358c5 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -7338,6 +7338,10 @@ void TypeLocReader::VisitBTFTagAttributedTypeLoc(BTFTagAttributedTypeLoc TL) { // Nothing to do. } +void TypeLocReader::VisitOverflowBehaviorTypeLoc(OverflowBehaviorTypeLoc TL) { + // Nothing to do. +} + void TypeLocReader::VisitHLSLAttributedResourceTypeLoc( HLSLAttributedResourceTypeLoc TL) { // Nothing to do. diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp index 1a20fc9595dce..fda464acccfcc 100644 --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -587,6 +587,10 @@ void TypeLocWriter::VisitBTFTagAttributedTypeLoc(BTFTagAttributedTypeLoc TL) { // Nothing to do. } +void TypeLocWriter::VisitOverflowBehaviorTypeLoc(OverflowBehaviorTypeLoc TL) { + // Nothing to do. +} + void TypeLocWriter::VisitHLSLAttributedResourceTypeLoc( HLSLAttributedResourceTypeLoc TL) { // Nothing to do. diff --git a/clang/test/CodeGen/overflow-behavior-types-extensions.c b/clang/test/CodeGen/overflow-behavior-types-extensions.c new file mode 100644 index 0000000000000..56ef12329d905 --- /dev/null +++ b/clang/test/CodeGen/overflow-behavior-types-extensions.c @@ -0,0 +1,44 @@ +// RUN: %clang_cc1 -triple x86_64-linux-gnu -foverflow-behavior-types -std=c2x %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK + +#define __wrap __attribute__((overflow_behavior(wrap))) +#define __nowrap __attribute__((overflow_behavior(no_wrap))) + +typedef int __wrap w_int; +typedef int __nowrap nw_int; + +// CHECK-LABEL: define {{.*}} @generic_selection_test_nomatch +int generic_selection_test_nomatch(int x) { + // CHECK: ret i32 3 + return _Generic(x, w_int: 1, nw_int: 2, default: 3); +} + +// CHECK-LABEL: define {{.*}} @generic_selection_test_obtmatch +int generic_selection_test_obtmatch(w_int x) { + // CHECK: ret i32 1 + return _Generic(x, w_int: 1, nw_int: 2, default: 3); +} + +// CHECK-LABEL: define {{.*}} @signed_bitint_test +void signed_bitint_test(_BitInt(4) __nowrap a, _BitInt(8) __nowrap b, _BitInt(99) __wrap c) { + // CHECK: call { i4, i1 } @llvm.sadd.with.overflow.i4(i4 + (a + 1); + + // CHECK: call { i8, i1 } @llvm.sadd.with.overflow.i8(i8 + (b + 1); + + // CHECK: add i99 {{.*}}, 1 + (c + 1); +} + +// CHECK-LABEL: define {{.*}} @unsigned_bitint_test +void unsigned_bitint_test(unsigned _BitInt(4) __nowrap a, unsigned _BitInt(8) __nowrap b, unsigned _BitInt(99) __wrap c) { + // CHECK: call { i4, i1 } @llvm.uadd.with.overflow.i4(i4 + (a + 1); + + // CHECK: call { i8, i1 } @llvm.uadd.with.overflow.i8(i8 + (b + 1); + + // CHECK: add i99 {{.*}}, 1 + (c + 1); +} + diff --git a/clang/test/CodeGen/overflow-behavior-types-operators.cpp b/clang/test/CodeGen/overflow-behavior-types-operators.cpp new file mode 100644 index 0000000000000..8049ece3af696 --- /dev/null +++ b/clang/test/CodeGen/overflow-behavior-types-operators.cpp @@ -0,0 +1,70 @@ +// RUN: %clang_cc1 -triple x86_64-linux-gnu %s -foverflow-behavior-types \ +// RUN: -fsanitize=signed-integer-overflow -emit-llvm -o - -std=c++14 | FileCheck %s + +#define __wrap __attribute__((overflow_behavior(wrap))) +#define __nowrap __attribute__((overflow_behavior(no_wrap))) + +typedef int __wrap wrap_int; +typedef int __nowrap nowrap_int; +typedef unsigned int __wrap u_wrap_int; +typedef unsigned int __nowrap u_nowrap_int; + +//===----------------------------------------------------------------------===// +// Compound Assignment Operators +//===----------------------------------------------------------------------===// + +// CHECK-LABEL: define {{.*}} @_Z28compound_assignment_operatorv +void compound_assignment_operator() { + wrap_int a = 1; + // CHECK: add i32 + a += 1; + + nowrap_int b = 1; + // CHECK: llvm.sadd.with.overflow.i32 + b += 1; + + u_wrap_int c = 1; + // CHECK: sub i32 + c -= 1; + + u_nowrap_int d = 1; + // CHECK: llvm.usub.with.overflow.i32 + d -= 1; + + wrap_int e = 2; + // CHECK: mul i32 + e *= 2; + + nowrap_int f = 2; + // CHECK: llvm.smul.with.overflow.i32 + f *= 2; +} + +//===----------------------------------------------------------------------===// +// Bitwise and Shift Operators +//===----------------------------------------------------------------------===// + +// CHECK-LABEL: define {{.*}} @_Z27bitwise_and_shift_operatorsv +void bitwise_and_shift_operators() { + wrap_int a = 1; + // CHECK: shl i32 + // No overflow check for shifts + a <<= 1; + + nowrap_int b = 1; + // CHECK: ashr i32 + // No overflow check for shifts + b >>= 1; + + wrap_int c = 1; + // CHECK: and i32 + c &= 1; + + nowrap_int d = 1; + // CHECK: xor i32 + d ^= 1; + + u_wrap_int e = 1; + // CHECK: or i32 + e |= 1; +} diff --git a/clang/test/CodeGen/overflow-behavior-types-promotions.cpp b/clang/test/CodeGen/overflow-behavior-types-promotions.cpp new file mode 100644 index 0000000000000..fd8622e88b44f --- /dev/null +++ b/clang/test/CodeGen/overflow-behavior-types-promotions.cpp @@ -0,0 +1,50 @@ +// RUN: %clang_cc1 -triple x86_64-linux-gnu %s -foverflow-behavior-types \ +// RUN: -fsanitize=signed-integer-overflow,unsigned-integer-overflow -emit-llvm -o - -std=c++14 | FileCheck %s + +#define __wrap __attribute__((overflow_behavior(wrap))) +#define __nowrap __attribute__((overflow_behavior(no_wrap))) + +typedef int __wrap wrap_int; +typedef char __wrap wrap_char; +typedef int __nowrap nowrap_int; +typedef unsigned int __wrap u_wrap_int; +typedef unsigned int __nowrap u_nowrap_int; + +// CHECK-LABEL: define {{.*}} @_Z30conditional_operator_promotionbU9Obt_cU9Obt_ii +void conditional_operator_promotion(bool cond, wrap_char w, nowrap_int nw, int i) { + // OBT wins over regular integer. + // CHECK: cond.end: + // CHECK-NEXT: %cond1 = phi i8 + // CHECK-NEXT: store i8 %cond1, ptr %r1 + // CHECK-NEXT: %{{.*}} = load i8, ptr %r1 + // CHECK-NEXT: add i8 + auto r1 = cond ? w : i; + (void)(r1 + 2147483647); + + // nowrap wins over wrap. + // CHECK: cond.end6: + // CHECK-NEXT: %cond7 = phi i32 + // CHECK-NEXT: store i32 %cond7, ptr %r2 + // CHECK-NEXT: %{{.*}} = load i32, ptr %r2 + // CHECK-NEXT: call { i32, i1 } @llvm.sadd.with.overflow.i32 + auto r2 = cond ? w : nw; + (void)(r2 + 2147483647); +} + +// CHECK-LABEL: define {{.*}} @_Z20promotion_rules_testU9Obt_iU9Obt_iU9Obt_jU9Obt_j +void promotion_rules_test(wrap_int sw, nowrap_int snw, u_wrap_int uw, u_nowrap_int unw) { + // Unsigned is favored over signed for same-behavior OBTs. + // CHECK: add i32 + auto r1 = sw + uw; + (void)r1; + + // nowrap is favored over wrap. Result is unsigned nowrap. + // CHECK: call { i32, i1 } @llvm.uadd.with.overflow.i32 + auto r2 = sw + unw; + (void)r2; + + // nowrap is favored over wrap. Result is signed nowrap. + // CHECK: call { i32, i1 } @llvm.sadd.with.overflow.i32 + auto r3 = uw + snw; + (void)r3; +} diff --git a/clang/test/CodeGen/overflow-behavior-types-scl.c b/clang/test/CodeGen/overflow-behavior-types-scl.c new file mode 100644 index 0000000000000..2abf0cb2fbbb7 --- /dev/null +++ b/clang/test/CodeGen/overflow-behavior-types-scl.c @@ -0,0 +1,43 @@ +// RUN: rm -rf %t +// RUN: split-file %s %t + +// RUN: %clang_cc1 -triple x86_64-linux-gnu %t/test.c -fsanitize-ignorelist=%t/sio.scl -foverflow-behavior-types -fsanitize=signed-integer-overflow -emit-llvm -o - | FileCheck %s --check-prefix=SIO +// RUN: %clang_cc1 -triple x86_64-linux-gnu %t/test.c -fsanitize-ignorelist=%t/uio.scl -foverflow-behavior-types -fsanitize=unsigned-integer-overflow -emit-llvm -o - | FileCheck %s --check-prefix=UIO + +//--- sio.scl +[signed-integer-overflow] +# ignore signed-integer-overflow instrumentation across all types +type:* + +//--- uio.scl +[unsigned-integer-overflow] +# ignore unsigned-integer-overflow instrumentation across all types +type:* + +//--- test.c +#define __wrap __attribute__((overflow_behavior("wrap"))) +#define __nowrap __attribute__((overflow_behavior("no_wrap"))) + +// SIO-LABEL: define {{.*}} @foo +// UIO-LABEL: define {{.*}} @foo +void foo(void) { + // SIO-LABEL: load volatile i32, ptr @a, align 4 + volatile extern int a; + volatile extern char b; + volatile extern char __nowrap c; // nowrap has precedence over scl entries + + // SIO: add nsw i32 + (a + 1); + // SIO: add nsw i32 + (b + 1); + // SIO: @llvm.sadd.with.overflow.i8 + (c + 1); + + // UIO-LABEL: load volatile i32, ptr @d, align 4 + volatile extern unsigned int d; + volatile extern unsigned short __nowrap e; + // UIO: add i32 + (d + 1); + // UIO: @llvm.uadd.with.overflow.i16 + (e + 1); +} diff --git a/clang/test/CodeGen/overflow-behavior-types.c b/clang/test/CodeGen/overflow-behavior-types.c new file mode 100644 index 0000000000000..bc01f4184fb8d --- /dev/null +++ b/clang/test/CodeGen/overflow-behavior-types.c @@ -0,0 +1,148 @@ +// RUN: %clang_cc1 -triple x86_64-linux-gnu -foverflow-behavior-types %s \ +// RUN: -fsanitize=signed-integer-overflow,unsigned-integer-overflow,implicit-signed-integer-truncation,implicit-unsigned-integer-truncation \ +// RUN: -emit-llvm -o - | FileCheck %s --check-prefix=DEFAULT + +// RUN: %clang_cc1 -triple x86_64-linux-gnu -foverflow-behavior-types %s -ftrapv \ +// RUN: -fsanitize=signed-integer-overflow,unsigned-integer-overflow,implicit-signed-integer-truncation,implicit-unsigned-integer-truncation \ +// RUN: -emit-llvm -o - | FileCheck %s --check-prefix=DEFAULT + +// RUN: %clang_cc1 -triple x86_64-linux-gnu -foverflow-behavior-types %s \ +// RUN: -ftrapv -ftrapv-handler OVERFLOW_HANDLER \ +// RUN: -emit-llvm -o - | FileCheck %s --check-prefix=TRAPV-HANDLER + +// RUN: %clang_cc1 -triple x86_64-linux-gnu -foverflow-behavior-types %s -fwrapv \ +// RUN: -fsanitize=signed-integer-overflow,unsigned-integer-overflow,implicit-signed-integer-truncation,implicit-unsigned-integer-truncation \ +// RUN: -emit-llvm -o - | FileCheck %s --check-prefix=DEFAULT + +// RUN: %clang_cc1 -triple x86_64-linux-gnu -foverflow-behavior-types %s \ +// RUN: -fsanitize-undefined-ignore-overflow-pattern=all \ +// RUN: -fsanitize=signed-integer-overflow,unsigned-integer-overflow,implicit-signed-integer-truncation,implicit-unsigned-integer-truncation \ +// RUN: -emit-llvm -o - | FileCheck %s --check-prefix=EXCL + +// RUN: %clang_cc1 -triple x86_64-linux-gnu -foverflow-behavior-types %s \ +// RUN: -emit-llvm -o - | FileCheck %s --check-prefix=NOSAN + +#define __wrap __attribute__((overflow_behavior("wrap"))) +#define __nowrap __attribute__((overflow_behavior("no_wrap"))) + +// DEFAULT-LABEL: define {{.*}} @test1 +// TRAPV-HANDLER-LABEL: define {{.*}} @test1 +// NOSAN-LABEL: define {{.*}} @test1 +void test1(int __wrap a, int __nowrap b) { + // DEFAULT: add i32 + // TRAPV-HANDLER: add i32 + // NOSAN: add i32 + (a + 1); + + // DEFAULT: llvm.sadd.with.overflow.i32 + // TRAPV-HANDLER: %[[T0:.*]] = load i32, ptr %b + // TRAPV-HANDLER: call {{.*}} @OVERFLOW_HANDLER(i64 %[[T0]] + // NOSAN: %[[T0:.*]] = load i32, ptr %b + // NOSAN-NEXT: %[[T1:.*]] = call {{.*}} @llvm.sadd.with.overflow.i32(i32 %[[T0]] + // NOSAN: %[[OF:.*]] = extractvalue {{.*}} %[[T1]], 1 + // NOSAN-NEXT: %[[XOR:.*]] = xor i1 %[[OF]] + // NOSAN-NEXT: br i1 %[[XOR]]{{.*}}cont, label %[[TRAP:.*]], !prof + // NOSAN: [[TRAP]]: + // NOSAN-NEXT: call void @llvm.ubsantrap + (b + 1); + + // DEFAULT: sub i32 0 + (-a); + + // DEFAULT: llvm.ssub.with.overflow.i32 + (-b); + + // DEFAULT: add i32 + a++; + // DEFAULT: llvm.sadd.with.overflow.i32 + b++; + + // DEFAULT: add i32 + ++a; + // DEFAULT: llvm.sadd.with.overflow.i32 + ++b; + + volatile extern int divisor; + // DEFAULT: %[[T0:.*]] = load i32, ptr %a + // DEFAULT-NEXT: %[[T1:.*]] = load volatile i32, ptr @divisor + // DEFAULT-NOT: br {{.*}} %handler.divrem_overflow + // DEFAULT: sdiv i32 %[[T0]], %[[T1]] + a/divisor; + + // DEFAULT: %[[T0:.*]] = load i32, ptr %b + // DEFAULT-NEXT: %[[T1:.*]] = load volatile i32, ptr @divisor + // DEFAULT: br {{.*}} %handler.divrem_overflow + b/divisor; +} + +// DEFAULT-LABEL: define {{.*}} @test2 +void test2(unsigned char __wrap a, unsigned char __nowrap b) { + // DEFAULT: add i8 + (a + 1); + // DEFAULT: llvm.uadd.with.overflow.i8 + (b + 1); + + // DEFAULT: %[[T0:.*]] = load volatile i64, ptr @big + // DEFAULT: %[[TRUNC1:.*]] = icmp eq i64 {{.*}} %[[T0]] + // DEFAULT-NOT: br i1 %[[TRUNC1]], {{.*}} %handler.implicit_conversion + volatile extern unsigned long long big; + a = big; + + // DEFAULT: %[[T1:.*]] = load volatile i64, ptr @big + // DEFAULT: %[[TRUNC2:.*]] = icmp eq i64 {{.*}} %[[T1]] + // DEFAULT: br i1 %[[TRUNC2]], {{.*}} %handler.implicit_conversion + b = big; +} + +// DEFAULT-LABEL: define {{.*}} @test3 +void test3(void) { + volatile extern char __wrap a; + volatile extern short __wrap b; + // less-than-int arithmetic is possible when one or more wrapping types are + // present. When both operands are wrapping types, the larger of the two + // types should be used as the result of the arithmetic. + + // DEFAULT: add i16 + (a + b); + + // nowrap has precedence over wrap, regardless of bit widths + volatile extern unsigned long long __wrap c; + volatile extern char __nowrap d; + + // DEFAULT: %[[T0:.*]] = load volatile i64, ptr @c + // DEFAULT: %[[TRUNC1:.*]] = icmp eq i64 {{.*}} %[[T0]] + // DEFAULT: br i1 %[[TRUNC1]] + // DEFAULT: %[[T1:.*]] = load volatile i8, ptr @d + // DEFAULT-NEXT: @llvm.sadd.with.overflow.i8 + (c + d); + + volatile extern int __nowrap e; + volatile extern unsigned int __wrap f; + + // DEFAULT: @llvm.ssub.with.overflow.i32 + (e - f); +} + +typedef int __attribute__((overflow_behavior(wrap))) wrap_int; +typedef int __attribute__((overflow_behavior(no_wrap))) nowrap_int; +// DEFAULT-LABEL: define {{.*}} @typedefs +void typedefs(nowrap_int a, wrap_int b) { + // DEFAULT: llvm.sadd.with.overflow.i32 + (a + 100); + + // DEFAULT: add i32 + (b + 100); +} + +// EXCL-LABEL: define {{.*}} @ignored_patterns +void ignored_patterns(unsigned long __attribute__((overflow_behavior(no_wrap))) a) { + // EXCL: %[[T0:.*]] = load i64, ptr %a.addr + // EXCL-NEXT: add i64 %[[T0]], -1 + while (a--) { /*...*/ } + + // EXCL: %[[T1:.*]] = load i64, ptr %a.addr + // EXCL: %[[T2:.*]] = load volatile i64, ptr %b + // EXCL-NEXT: add i64 %[[T1]], %[[T2]] + volatile unsigned long __attribute__((overflow_behavior(no_wrap))) b; + if (a + b < a) { /*...*/ } +} diff --git a/clang/test/CodeGen/overflow-behavior-types.cpp b/clang/test/CodeGen/overflow-behavior-types.cpp new file mode 100644 index 0000000000000..e552a20a91d17 --- /dev/null +++ b/clang/test/CodeGen/overflow-behavior-types.cpp @@ -0,0 +1,68 @@ +// RUN: %clang_cc1 -triple x86_64-linux-gnu %s -foverflow-behavior-types \ +// RUN: -fsanitize=signed-integer-overflow,unsigned-integer-overflow,implicit-signed-integer-truncation,implicit-unsigned-integer-truncation \ +// RUN: -emit-llvm -o - | FileCheck %s --check-prefix=DEFAULT + + +// Test the __attribute__((overflow_behavior())) for C++ + +#define __wrap __attribute__((overflow_behavior(wrap))) +#define __no_wrap __attribute__((overflow_behavior(no_wrap))) + +class Foo { +public: + unsigned long other; + char __wrap a; + + Foo() = delete; + Foo(char _a) : a(_a) {} + + decltype(a) getA() const { return a; } +}; +/* define dso_local void @_Z12test_membersc(i8 noundef signext %some) #0 { +entry: + %some.addr = alloca i8, align 1 + %foo = alloca %class.Foo, align 8 + store i8 %some, ptr %some.addr, align 1 + %0 = load i8, ptr %some.addr, align 1 + call void @_ZN3FooC2Ec(ptr noundef nonnull align 8 dereferenceable(9) %foo, i8 noundef signext %0) + %a = getelementptr inbounds nuw %class.Foo, ptr %foo, i32 0, i32 1 + %1 = load i8, ptr %a, align 8 + %inc = add i8 %1, 1 + store i8 %inc, ptr %a, align 8 + %call = call noundef i8 @_ZNK3Foo4getAEv(ptr noundef nonnull align 8 dereferenceable(9) %foo) + %add = add i8 %call, 1 + ret void +} +*/ + +// DEFAULT-LABEL: define {{.*}} @_Z12test_membersc +void test_members(char some) { + Foo foo{some}; + + // DEFAULT: %[[A:.*]] = getelementptr inbounds nuw %class.Foo, ptr %foo, i32 0, i32 1 + // DEFAULT-NEXT: %[[T1:.*]] = load i8, ptr %[[A]], align 8 + // DEFAULT-NEXT: %inc{{\d*}} = add i8 %[[T1]], 1 + (++foo.a); + + // DEFAULT: %[[CALL:.*]] = call noundef i8 @_ZNK3Foo4getAEv + // DEFAULT-NEXT: add i8 %[[CALL]], 1 + (void)(foo.getA() + 1); +} + +// DEFAULT-LABEL: define {{.*}} @_Z9test_autoU9Obt_c +void test_auto(char __wrap a) { + auto b = a; + + // DEFAULT: %[[T1:.*]] = load i8, ptr %b + // DEFAULT-NEXT: sub i8 %[[T1]], 1 + (b - 1); // no instrumentation +} + + +int overloadme(__no_wrap int a) { return 0; } +int overloadme(int a) { return 1; } // make sure we pick this one +// DEFAULT-LABEL: define {{.*}}test_overload_set_exact_match +int test_overload_set_exact_match(int a) { + // DEFAULT: call {{.*}} @_Z10overloadmei + return overloadme(a); +} diff --git a/clang/test/Misc/pragma-attribute-supported-attributes-list.test b/clang/test/Misc/pragma-attribute-supported-attributes-list.test index 05693538252aa..d60f3d2793103 100644 --- a/clang/test/Misc/pragma-attribute-supported-attributes-list.test +++ b/clang/test/Misc/pragma-attribute-supported-attributes-list.test @@ -165,6 +165,7 @@ // CHECK-NEXT: OpenCLIntelReqdSubGroupSize (SubjectMatchRule_function) // CHECK-NEXT: OpenCLNoSVM (SubjectMatchRule_variable) // CHECK-NEXT: OptimizeNone (SubjectMatchRule_function, SubjectMatchRule_objc_method) +// CHECK-NEXT: OverflowBehavior (SubjectMatchRule_variable, SubjectMatchRule_type_alias, SubjectMatchRule_field) // CHECK-NEXT: Overloadable (SubjectMatchRule_function) // CHECK-NEXT: Owner (SubjectMatchRule_record_not_is_union) // CHECK-NEXT: ParamTypestate (SubjectMatchRule_variable_is_parameter) diff --git a/clang/test/Sema/attr-overflow-behavior-constexpr.cpp b/clang/test/Sema/attr-overflow-behavior-constexpr.cpp new file mode 100644 index 0000000000000..a1ce02a581bb6 --- /dev/null +++ b/clang/test/Sema/attr-overflow-behavior-constexpr.cpp @@ -0,0 +1,44 @@ +// RUN: %clang_cc1 -triple x86_64-linux-gnu %s -foverflow-behavior-types -verify -fsyntax-only -std=c++14 + +#define __wrap __attribute__((overflow_behavior(wrap))) +#define __nowrap __attribute__((overflow_behavior(no_wrap))) + +typedef int __wrap wrap_int; +typedef int __nowrap nowrap_int; + +constexpr wrap_int add(wrap_int a, wrap_int b) { + return a + b; +} + +constexpr nowrap_int sub(nowrap_int a, nowrap_int b) { + return a - b; // expected-note {{-2147483649 is outside the range of representable values}} +} + +void constexpr_test() { + constexpr wrap_int max = 2147483647; + constexpr wrap_int one = 1; + static_assert(add(max, one) == -2147483648, "constexpr wrapping failed"); + + constexpr nowrap_int min = -2147483648; + constexpr nowrap_int one_nw = 1; + // This should fail to compile because of overflow. + constexpr nowrap_int res = sub(min, one_nw); // expected-error {{constexpr variable 'res' must be initialized by a constant expression}} expected-note {{in call to 'sub(-2147483648, 1)'}} +} + +template +void check_deduction_wrap(T) { + static_assert(__is_same(T, wrap_int), "T should be deduced as wrap_int"); +} + +template +void check_deduction_nowrap(T) { + static_assert(__is_same(T, nowrap_int), "T should be deduced as nowrap_int"); +} + +void template_deduction_test() { + wrap_int w = 0; + check_deduction_wrap(w); + + nowrap_int nw = 0; + check_deduction_nowrap(nw); +} diff --git a/clang/test/Sema/attr-overflow-behavior-off.c b/clang/test/Sema/attr-overflow-behavior-off.c new file mode 100644 index 0000000000000..2aac2a887e060 --- /dev/null +++ b/clang/test/Sema/attr-overflow-behavior-off.c @@ -0,0 +1,3 @@ +// RUN: %clang_cc1 %s -Winteger-overflow -Wno-unused-value -verify -fsyntax-only + +typedef int __attribute__((overflow_behavior(wrap))) wrap_int; // expected-warning {{'overflow_behavior' attribute is ignored because it is not enabled;}} diff --git a/clang/test/Sema/attr-overflow-behavior.c b/clang/test/Sema/attr-overflow-behavior.c new file mode 100644 index 0000000000000..53f7bb51b6c7a --- /dev/null +++ b/clang/test/Sema/attr-overflow-behavior.c @@ -0,0 +1,41 @@ +// RUN: %clang_cc1 %s -Winteger-overflow -Wno-unused-value -foverflow-behavior-types -Wimplicitly-discarded-overflow-behavior -verify -fsyntax-only + +typedef int __attribute__((overflow_behavior)) bad_arg_count; // expected-error {{'overflow_behavior' attribute takes one argument}} +typedef int __attribute__((overflow_behavior(not_real))) bad_arg_spec; // expected-error {{'not_real' is not a valid argument to attribute 'overflow_behavior'}} +typedef int __attribute__((overflow_behavior("not_real"))) bad_arg_spec_str; // expected-error {{'not_real' is not a valid argument to attribute 'overflow_behavior'}} +typedef char* __attribute__((overflow_behavior("wrap"))) bad_type; // expected-warning {{'overflow_behavior' attribute cannot be applied to non-integer type 'char *'; attribute ignored}} + +typedef int __attribute__((overflow_behavior(wrap))) ok_wrap; // OK +typedef long __attribute__((overflow_behavior(no_wrap))) ok_nowrap; // OK +typedef unsigned long __attribute__((overflow_behavior("wrap"))) str_ok_wrap; // OK +typedef char __attribute__((overflow_behavior("no_wrap"))) str_ok_nowrap; // OK + +void foo() { + (2147483647 + 100); // expected-warning {{overflow in expression; result is }} + (ok_wrap)2147483647 + 100; // no warn +} + +#define __no_wrap __attribute__((overflow_behavior(no_wrap))) + +void ptr(int a) { + int __no_wrap *p = &a; // expected-warning-re {{incompatible pointer types initializing '__no_wrap int *' {{.*}}of type 'int *'}} +} + +void ptr2(__no_wrap int a) { + int *p = &a; // expected-warning-re {{incompatible pointer types initializing 'int *' {{.*}}of type '__no_wrap int *'}} +} + + +// verify semantics of -Wimplicitly-discarded-overflow-behavior{,-pedantic} +void imp_disc_pedantic(unsigned a) {} +void imp_disc(int a) {} +void imp_disc_test(unsigned __attribute__((overflow_behavior(wrap))) a) { + imp_disc_pedantic(a); // expected-warning {{implicit conversion from '__wrap unsigned int' to 'unsigned int' discards overflow behavior}} + imp_disc(a); // expected-warning {{implicit conversion from '__wrap unsigned int' to 'int' discards overflow behavior}} +} + +// -Wimplicitly-discarded-overflow-behavior-assignment +void assignment_disc_test(unsigned __attribute__((overflow_behavior(wrap))) a) { + int b = a; // expected-warning {{implicit conversion from '__wrap unsigned int' to 'int' during assignment discards overflow behavior}} + int c = (int)a; // OK +} diff --git a/clang/test/Sema/attr-overflow-behavior.cpp b/clang/test/Sema/attr-overflow-behavior.cpp new file mode 100644 index 0000000000000..1eedd736a6b91 --- /dev/null +++ b/clang/test/Sema/attr-overflow-behavior.cpp @@ -0,0 +1,115 @@ +// RUN: %clang_cc1 %s -Winteger-overflow -Wno-unused-value -foverflow-behavior-types -verify -fsyntax-only + +typedef int __attribute__((overflow_behavior)) bad_arg_count; // expected-error {{'overflow_behavior' attribute takes one argument}} +typedef int __attribute__((overflow_behavior(not_real))) bad_arg_spec; // expected-error {{'not_real' is not a valid argument to attribute 'overflow_behavior'}} +typedef int __attribute__((overflow_behavior("not_real"))) bad_arg_spec_str; // expected-error {{'not_real' is not a valid argument to attribute 'overflow_behavior'}} +typedef char* __attribute__((overflow_behavior("wrap"))) bad_type; // expected-warning {{'overflow_behavior' attribute cannot be applied to non-integer type 'char *'; attribute ignored}} + +typedef int __attribute__((overflow_behavior(wrap))) ok_wrap; // OK +typedef long __attribute__((overflow_behavior(no_wrap))) ok_nowrap; // OK +typedef unsigned long __attribute__((overflow_behavior("wrap"))) str_ok_wrap; // OK +typedef char __attribute__((overflow_behavior("no_wrap"))) str_ok_nowrap; // OK + +struct struct_not_allowed { + int i; +} __attribute__((overflow_behavior(wrap))); // expected-warning {{'overflow_behavior' attribute only applies to variables, typedefs, and non-static data members}} + +void foo() { + (2147483647 + 100); // expected-warning {{overflow in expression; result is }} + (ok_wrap)2147483647 + 100; // no warn +} + +// C++ stuff expects no warns +typedef int __attribute__((overflow_behavior(wrap))) wrap_int; + +template +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(no_wrap))) b; // don't consider underlying type an exact match. + add_one(b); // expected-error {{call to 'add_one' is ambiguous}} +} + +#define __no_wrap __attribute__((overflow_behavior(no_wrap))) +void func(__no_wrap int i); +void func(int i); // Overload, not invalid redeclaration + +// TODO: make this diagnostic message more descriptive +template +void func2(__no_wrap Ty i) {} // expected-warning {{'overflow_behavior' attribute cannot be applied to non-integer type 'Ty'; attribute ignored}} + +template +struct S {}; + +template <> +struct S<__no_wrap int> {}; + +template <> +struct S {}; + +void ptr(int a) { + int __no_wrap *p = &a; // expected-error-re {{cannot initialize a variable of type '__no_wrap int *' {{.*}}with an rvalue of type 'int *'}} +} + +void ptr2(__no_wrap int a) { + int *p = &a; // expected-error-re {{cannot initialize a variable of type 'int *' {{.*}}with an rvalue of type '__no_wrap int *'}} +} + +void overloadme(__no_wrap int a); // expected-note {{candidate function}} +void overloadme(short a); // expected-note {{candidate function}} + +void test_overload_ambiguity() { + int a; + overloadme(a); // expected-error {{call to 'overloadme' is ambiguous}} +} + +void f(void) __attribute__((overflow_behavior(wrap))); // expected-warning {{'overflow_behavior' attribute cannot be applied to non-integer type 'void (void)'; attribute ignored}} + +typedef float __attribute__((overflow_behavior(wrap))) wrap_float; // expected-warning {{'overflow_behavior' attribute cannot be applied to non-integer type 'float'; attribute ignored}} + +void pointer_compatibility_test(int* i_ptr) { + __no_wrap int* nowrap_ptr; + + // static_cast should fail. + nowrap_ptr = static_cast<__no_wrap int*>(i_ptr); // expected-error {{static_cast from 'int *' to '__no_wrap int *' is not allowed}} + + // reinterpret_cast should succeed. + nowrap_ptr = reinterpret_cast<__no_wrap int*>(i_ptr); + (void)nowrap_ptr; +} diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp index 9089984fa4a54..3124087119cc9 100644 --- a/clang/tools/libclang/CIndex.cpp +++ b/clang/tools/libclang/CIndex.cpp @@ -1789,6 +1789,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());