From 416505f87a220f55365eea912931902d6464a3f8 Mon Sep 17 00:00:00 2001 From: liuxinyi Date: Thu, 9 Jan 2025 16:37:41 +0800 Subject: [PATCH 01/29] [FatPtr]support fat ptr for bsc compiler and add std library bsc_fat_ptr.hbs 1. add enable-fat-ptr compilation flags to control desugar fat ptr 2. add fat type qualifier and &fat operator 3. desugar fat ptr in FunctionDecl, VarDecl and RecordDecl by TreeTransform framework 4. add type-checking for fat ptr 5. desugar fat ptr in FunctionDecl, VarDecl and RecordDecl by TreeTransform framework. --- clang/include/clang/AST/BSC/WalkerBSC.h | 3 +- clang/include/clang/AST/CanonicalType.h | 4 +- clang/include/clang/AST/OperationKinds.def | 2 + clang/include/clang/AST/StmtVisitor.h | 2 + clang/include/clang/AST/Type.h | 184 +- clang/include/clang/Basic/BSC/BSCAttr.td | 6 + .../clang/Basic/BSC/DiagnosticBSCSemaKinds.td | 25 +- clang/include/clang/Basic/LangOptions.def | 1 + clang/include/clang/Basic/TokenKinds.def | 2 + clang/include/clang/Driver/BSC/BSCOptions.td | 2 + clang/include/clang/Sema/DeclSpec.h | 34 +- clang/include/clang/Sema/Sema.h | 52 +- clang/lib/AST/ASTContext.cpp | 6 +- clang/lib/AST/ASTImporter.cpp | 2 +- clang/lib/AST/BSC/TypeBSC.cpp | 111 ++ clang/lib/AST/Expr.cpp | 1 + clang/lib/AST/ExprConstant.cpp | 16 + clang/lib/AST/StmtPrinter.cpp | 12 +- clang/lib/AST/TypePrinter.cpp | 6 + clang/lib/Analysis/BSCBorrowCheck.cpp | 1 - clang/lib/Analysis/ThreadSafetyCommon.cpp | 3 +- clang/lib/CodeGen/CGCall.cpp | 7 + clang/lib/CodeGen/CGExprAgg.cpp | 5 + clang/lib/CodeGen/CGExprScalar.cpp | 6 + clang/lib/Driver/ToolChains/Clang.cpp | 2 + clang/lib/Headers/CMakeLists.txt | 1 + clang/lib/Headers/bsc_include/bsc_fat_ptr.hbs | 90 + clang/lib/Lex/Lexer.cpp | 14 + clang/lib/Parse/ParseDecl.cpp | 39 +- clang/lib/Parse/ParseExpr.cpp | 1 + clang/lib/Sema/BSC/SemaBSCCoroutine.cpp | 12 +- clang/lib/Sema/BSC/SemaBSCDestructor.cpp | 2 +- clang/lib/Sema/BSC/SemaBSCFatPtr.cpp | 1547 +++++++++++++++++ clang/lib/Sema/BSC/SemaBSCOwnership.cpp | 2 +- clang/lib/Sema/BSC/SemaBSCSafeZone.cpp | 2 +- clang/lib/Sema/BSC/SemaDeclBSC.cpp | 2 +- clang/lib/Sema/CMakeLists.txt | 1 + clang/lib/Sema/DeclSpec.cpp | 13 +- clang/lib/Sema/Sema.cpp | 28 +- clang/lib/Sema/SemaCast.cpp | 63 +- clang/lib/Sema/SemaDecl.cpp | 34 +- clang/lib/Sema/SemaDeclAttr.cpp | 16 + clang/lib/Sema/SemaExpr.cpp | 121 +- clang/lib/Sema/SemaExprMember.cpp | 28 +- clang/lib/Sema/SemaOverload.cpp | 9 +- clang/lib/Sema/SemaType.cpp | 7 +- clang/lib/Serialization/ASTWriterStmt.cpp | 3 +- clang/test/BSC/Negative/FatPtr/addr_fat.cbs | 58 + clang/test/BSC/Negative/FatPtr/fat_cast.cbs | 72 + .../FatPtr/fat_qulifier_only_for_pointer.cbs | 24 + .../global_array/array_out_of_bounds1.cbs | 12 + .../global_array/array_out_of_bounds2.cbs | 14 + .../heap/array_out_of_bounds1.cbs | 11 + .../heap/array_out_of_bounds2.cbs | 11 + .../heap/array_out_of_bounds3.cbs | 12 + .../heap/array_out_of_bounds4.cbs | 13 + .../heap/array_out_of_bounds5.cbs | 13 + .../heap/array_out_of_bounds6.cbs | 11 + .../heap/buffer_overflow.cbs | 21 + .../heap/double_free1.cbs | 13 + .../heap/double_free2.cbs | 14 + .../heap/double_free3.cbs | 16 + .../heap/double_free4.cbs | 18 + .../heap/use_after_free1.cbs | 14 + .../heap/use_after_free2.cbs | 15 + .../heap/use_after_free3.cbs | 17 + .../heap/use_after_free4.cbs | 15 + .../heap/use_after_free5.cbs | 29 + .../heap/use_after_free6.cbs | 14 + .../stack/array_out_of_bounds1.cbs | 12 + .../stack/array_out_of_bounds2.cbs | 13 + .../stack/buffer_overflow.cbs | 22 + .../stack/return_local_address.cbs | 17 + .../test/BSC/Positive/FatPtr/global_array.cbs | 23 + clang/test/BSC/Positive/FatPtr/heap.cbs | 94 + clang/test/BSC/Positive/FatPtr/stack.cbs | 24 + 76 files changed, 2971 insertions(+), 201 deletions(-) create mode 100644 clang/lib/Headers/bsc_include/bsc_fat_ptr.hbs create mode 100644 clang/lib/Sema/BSC/SemaBSCFatPtr.cpp create mode 100644 clang/test/BSC/Negative/FatPtr/addr_fat.cbs create mode 100644 clang/test/BSC/Negative/FatPtr/fat_cast.cbs create mode 100644 clang/test/BSC/Negative/FatPtr/fat_qulifier_only_for_pointer.cbs create mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/global_array/array_out_of_bounds1.cbs create mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/global_array/array_out_of_bounds2.cbs create mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds1.cbs create mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds2.cbs create mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds3.cbs create mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds4.cbs create mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds5.cbs create mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds6.cbs create mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/buffer_overflow.cbs create mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free1.cbs create mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free2.cbs create mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free3.cbs create mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free4.cbs create mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free1.cbs create mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free2.cbs create mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free3.cbs create mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free4.cbs create mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free5.cbs create mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free6.cbs create mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds1.cbs create mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds2.cbs create mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/buffer_overflow.cbs create mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/return_local_address.cbs create mode 100644 clang/test/BSC/Positive/FatPtr/global_array.cbs create mode 100644 clang/test/BSC/Positive/FatPtr/heap.cbs create mode 100644 clang/test/BSC/Positive/FatPtr/stack.cbs diff --git a/clang/include/clang/AST/BSC/WalkerBSC.h b/clang/include/clang/AST/BSC/WalkerBSC.h index 9def7ca98e82..c98e20d67d41 100644 --- a/clang/include/clang/AST/BSC/WalkerBSC.h +++ b/clang/include/clang/AST/BSC/WalkerBSC.h @@ -89,7 +89,8 @@ public: bool VisitQualType(QualType QT) { if (QT.isOwnedQualified() || QT.isBorrowQualified() || - QT->hasBorrowFields() || QT->hasOwnedFields()) { + QT->hasBorrowFields() || QT->hasOwnedFields() || QT.isFatQualified() || + QT->hasFatFields()) { return true; } if (IsDesugaredFromTraitType(QT)) { diff --git a/clang/include/clang/AST/CanonicalType.h b/clang/include/clang/AST/CanonicalType.h index 9b7a1d4efe96..89258bedde76 100644 --- a/clang/include/clang/AST/CanonicalType.h +++ b/clang/include/clang/AST/CanonicalType.h @@ -146,7 +146,9 @@ public: bool isBorrowQualified() const { return Stored.isLocalBorrowQualified(); } - #endif + + bool isFatQualified() const { return Stored.isLocalFatQualified(); } +#endif bool isVolatileQualified() const { return Stored.isLocalVolatileQualified(); diff --git a/clang/include/clang/AST/OperationKinds.def b/clang/include/clang/AST/OperationKinds.def index 0c48c0a51353..b744276b1b46 100644 --- a/clang/include/clang/AST/OperationKinds.def +++ b/clang/include/clang/AST/OperationKinds.def @@ -445,6 +445,8 @@ UNARY_OPERATION(AddrMut, "&mut") UNARY_OPERATION(AddrMutDeref, "&mut *") UNARY_OPERATION(AddrConst, "&const") UNARY_OPERATION(AddrConstDeref, "&const *") +// [BSC Fat pointer] fat pointer operator +UNARY_OPERATION(AddrFat, "&fat") #endif #undef CAST_OPERATION diff --git a/clang/include/clang/AST/StmtVisitor.h b/clang/include/clang/AST/StmtVisitor.h index da654e5a616b..b013581c178b 100644 --- a/clang/include/clang/AST/StmtVisitor.h +++ b/clang/include/clang/AST/StmtVisitor.h @@ -106,6 +106,7 @@ public: case UO_AddrConst: DISPATCH(UnaryAddrConst, UnaryOperator); case UO_AddrMutDeref: DISPATCH(UnaryAddrMutDeref, UnaryOperator); case UO_AddrConstDeref: DISPATCH(UnaryAddrConstDeref, UnaryOperator); + case UO_AddrFat: DISPATCH(UnaryAddrFat, UnaryOperator); #endif } } @@ -177,6 +178,7 @@ public: #if ENABLE_BSC UNARYOP_FALLBACK(AddrMut) UNARYOP_FALLBACK(AddrMutDeref) UNARYOP_FALLBACK(AddrConst) UNARYOP_FALLBACK(AddrConstDeref) + UNARYOP_FALLBACK(AddrFat) #endif #undef UNARYOP_FALLBACK diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h index cdaca83b0f61..22f7e986a5b0 100644 --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -66,11 +66,11 @@ class TemplateParameterList; class Type; enum { - #if ENABLE_BSC - TypeAlignmentInBits = 6, - #else +#if ENABLE_BSC + TypeAlignmentInBits = 7, +#else TypeAlignmentInBits = 4, - #endif +#endif TypeAlignment = 1 << TypeAlignmentInBits }; @@ -154,16 +154,17 @@ using CanQualType = CanQual; class Qualifiers { public: enum TQ { // NOTE: These flags must be kept in sync with DeclSpec::TQ. - Const = 0x1, + Const = 0x1, Restrict = 0x2, Volatile = 0x4, - #if ENABLE_BSC - Owned = 0x8, - Borrow = 0x10, - CVRMask = Const | Volatile | Restrict | Owned | Borrow - #else +#if ENABLE_BSC + Owned = 0x8, + Borrow = 0x10, + Fat = 0x20, + CVRMask = Const | Volatile | Restrict | Owned | Borrow | Fat +#else CVRMask = Const | Volatile | Restrict - #endif +#endif }; enum GC { @@ -195,20 +196,20 @@ public: }; enum { - /// The maximum supported address space number. - /// 21 bits should be enough for anyone. - #if ENABLE_BSC - MaxAddressSpace = 0x1fffffu, - #else +/// The maximum supported address space number. +/// 20 bits should be enough for anyone. +#if ENABLE_BSC + MaxAddressSpace = 0xfffffu, +#else MaxAddressSpace = 0x7fffffu, - #endif +#endif - /// The width of the "fast" qualifier mask. - #if ENABLE_BSC - FastWidth = 5, - #else +/// The width of the "fast" qualifier mask. +#if ENABLE_BSC + FastWidth = 6, +#else FastWidth = 3, - #endif +#endif /// The fast qualifier mask. FastMask = (1 << FastWidth) - 1 @@ -312,7 +313,17 @@ public: Qs.addBorrow(); return Qs; } - #endif + + bool hasFat() const { return Mask & Fat; } + bool hasOnlyFat() const { return Mask == Fat; } + void removeFat() { Mask &= ~Fat; } + void addFat() { Mask |= Fat; } + Qualifiers withFat() const { + Qualifiers Qs = *this; + Qs.addFat(); + return Qs; + } +#endif bool hasVolatile() const { return Mask & Volatile; } bool hasOnlyVolatile() const { return Mask == Volatile; } @@ -652,32 +663,32 @@ public: } private: - // bits: |0 1 2 3 4|5|6 .. 7|8 .. 10|11 ... 31| - // |C R V O B|U|GCAttr|Lifetime|AddressSpace| + // bits: |0 1 2 3 4 5|6|7 .. 8|9 .. 11|12 ... 31| + // |C R V O B F|U|GCAttr|Lifetime|AddressSpace| uint32_t Mask = 0; #if ENABLE_BSC - static const uint32_t UMask = 0x20; - static const uint32_t UShift = 5; - static const uint32_t GCAttrMask = 0xC0; - static const uint32_t GCAttrShift = 6; - static const uint32_t LifetimeMask = 0x700; - static const uint32_t LifetimeShift = 8; - #else + static const uint32_t UMask = 0x40; + static const uint32_t UShift = 6; + static const uint32_t GCAttrMask = 0x180; + static const uint32_t GCAttrShift = 7; + static const uint32_t LifetimeMask = 0xe00; + static const uint32_t LifetimeShift = 9; +#else static const uint32_t UMask = 0x8; static const uint32_t UShift = 3; static const uint32_t GCAttrMask = 0x30; static const uint32_t GCAttrShift = 4; static const uint32_t LifetimeMask = 0x1C0; static const uint32_t LifetimeShift = 6; - #endif +#endif static const uint32_t AddressSpaceMask = ~(CVRMask | UMask | GCAttrMask | LifetimeMask); #if ENABLE_BSC - static const uint32_t AddressSpaceShift = 11; - #else + static const uint32_t AddressSpaceShift = 12; +#else static const uint32_t AddressSpaceShift = 9; - #endif +#endif }; class QualifiersAndAtomic { @@ -696,7 +707,8 @@ public: #if ENABLE_BSC bool hasOwned() const { return Quals.hasOwned(); } bool hasBorrow() const { return Quals.hasBorrow(); } - #endif + bool hasFat() const { return Quals.hasFat(); } +#endif bool hasRestrict() const { return Quals.hasRestrict(); } bool hasAtomic() const { return HasAtomic; } @@ -705,7 +717,8 @@ public: #if ENABLE_BSC void addOwned() { Quals.addOwned(); } void addBorrow() { Quals.addBorrow(); } - #endif + void addFat() { Quals.addFat(); } +#endif void addRestrict() { Quals.addRestrict(); } void addAtomic() { HasAtomic = true; } @@ -714,7 +727,8 @@ public: #if ENABLE_BSC void removeOwned() { Quals.removeOwned(); } void removeBorrow() { Quals.removeBorrow(); } - #endif + void removeFat() { Quals.removeFat(); } +#endif void removeRestrict() { Quals.removeRestrict(); } void removeAtomic() { HasAtomic = false; } @@ -725,7 +739,8 @@ public: #if ENABLE_BSC QualifiersAndAtomic withOwned() { return {Quals.withOwned(), HasAtomic}; } QualifiersAndAtomic withBorrow() { return {Quals.withBorrow(), HasAtomic}; } - #endif + QualifiersAndAtomic withFat() { return {Quals.withFat(), HasAtomic}; } +#endif QualifiersAndAtomic withRestrict() { return {Quals.withRestrict(), HasAtomic}; } @@ -805,7 +820,8 @@ class QualType { // Thankfully, these are efficiently composable. llvm::PointerIntPair, - Qualifiers::FastWidth> Value; // For BSC, FastWidth = 5 + Qualifiers::FastWidth> + Value; // For BSC, FastWidth = 6 const ExtQuals *getExtQualsUnsafe() const { return Value.getPointer().get(); @@ -912,7 +928,18 @@ public: /// Determine whether this type is borrow-qualified. bool isBorrowQualified() const; - #endif + + /// Determine whether this particular QualType instance has the + /// "fat" qualifier set, without looking through typedefs that may have + /// added "fat" at a different level. + bool isLocalFatQualified() const { + return (getLocalFastQualifiers() & Qualifiers::Fat); + } + + /// Determine whether this type is fat-qualified. + bool isFatQualified() const; + bool isFatPtrType() const; +#endif /// Determine whether this particular QualType instance has the /// "restrict" qualifier set, without looking through typedefs that may have @@ -1034,7 +1061,12 @@ public: QualType addConstBorrow(const ASTContext &Context); QualType removeConstForBorrow(const ASTContext &Context); bool hasBorrow() const; - #endif + + /// Add the `fat` type qualifier to this QualType. + void addFat() { addFastQualifiers(Qualifiers::Fat); } + QualType withFat() const { return withFastQualifiers(Qualifiers::Fat); } + bool hasFat() const; +#endif /// Add the `volatile` type qualifier to this QualType. void addVolatile() { @@ -1066,7 +1098,8 @@ public: #if ENABLE_BSC void removeLocalOwned(); void removeLocalBorrow(); - #endif + void removeLocalFat(); +#endif void removeLocalVolatile(); void removeLocalRestrict(); void removeLocalCVRQualifiers(unsigned Mask); @@ -1799,9 +1832,9 @@ protected: /// C++ 8.3.5p4: The return type, the parameter type list and the /// cv-qualifier-seq, [...], are part of the function type. /// - /// After add 'owned, borrow' qualifiers, the FastWidth is now 5(from 3 to 5), - /// then bitNumberOf(FunctionTypeBitFields) is now 65(63 to 65, greater than 8 bytes), - /// then sizeof(Type) is now increased by one byte. + /// After add 'owned, borrow, fat' qualifiers, the FastWidth is now 6(from 3 + /// to 6), then bitNumberOf(FunctionTypeBitFields) is now 66(63 to 66, + /// greater than 8 bytes), then sizeof(Type) is now increased by one byte. unsigned FastTypeQuals : Qualifiers::FastWidth; /// Whether this function has extended Qualifiers. unsigned HasExtQuals : 1; @@ -2039,7 +2072,7 @@ protected: Type(TypeClass tc, QualType canon, TypeDependence Dependence) : ExtQualsTypeCommonBase(this, canon.isNull() ? QualType(this_(), 0) : canon) { - // Add owned, borrow, sizeof(FunctionTypeBitfields) > 8 now. + // Add owned, borrow, fat, sizeof(FunctionTypeBitfields) > 8 now. // sizeof(Type) is larger too. static_assert(sizeof(*this) <= 16 + sizeof(ExtQualsTypeCommonBase), "changing bitfields changed sizeof(Type)!"); @@ -2175,7 +2208,9 @@ public: bool hasOwnedFields() const; bool hasBorrowFields() const; - #endif + + bool hasFatFields() const; +#endif /// Test for a particular builtin type. bool isSpecificBuiltinType(unsigned K) const; @@ -2266,6 +2301,8 @@ public: bool isTraitType() const; bool isTraitPointerType() const; bool hasTraitType() const; + bool isFatPtrRecordType() const; + QualType getFatPtrPointeeType() const; bool isBSCCalculatedTypeInCompileTime() const; bool checkFunctionProtoType(SafeZoneSpecifier SZS) const; bool isOwnedStructureType() const; @@ -2909,7 +2946,9 @@ public: bool hasOwnedFields() const; bool hasBorrowFields() const; - #endif + + bool hasFatFields() const; +#endif }; /// Represents a type which was implicitly adjusted by the semantic @@ -4069,18 +4108,20 @@ public: ExtInfo getExtInfo() const { return ExtInfo(FunctionTypeBits.ExtInfo); } static_assert((~Qualifiers::FastMask & Qualifiers::CVRMask) == 0, - #if ENABLE_BSC - "Const, volatile, restrict, owned and borrow are assumed to be a subset of " - #else +#if ENABLE_BSC + "Const, volatile, restrict, owned, borrow and fat are assumed " + "to be a subset of " +#else "Const, volatile and restrict are assumed to be a subset of " - #endif +#endif "the fast qualifiers."); bool isConst() const { return getFastTypeQuals().hasConst(); } #if ENABLE_BSC bool isOwned() const { return getFastTypeQuals().hasOwned(); } bool isBorrow() const { return getFastTypeQuals().hasBorrow(); } - #endif + bool isFat() const { return getFastTypeQuals().hasFat(); } +#endif bool isVolatile() const { return getFastTypeQuals().hasVolatile(); } bool isRestrict() const { return getFastTypeQuals().hasRestrict(); } @@ -4410,7 +4451,9 @@ public: bool hasOwnedRetOrParams() const; // return true if any 'borrow' here bool hasBorrowRetOrParams() const; - #endif + // return true if any 'fat' here + bool hasFatRetOrParams() const; +#endif /// Return all the available information about this type's exception spec. ExceptionSpecInfo getExceptionSpecInfo() const { @@ -4931,8 +4974,11 @@ protected: withBorrow, withoutBorrow }; + + enum fatStatus{unInitFat, withFat, withoutFat}; mutable ownedStatus hasOwn = ownedStatus::unInitOwned; mutable borrowStatus hasBorrow = borrowStatus::unInitBorrow; + mutable fatStatus hasFat = fatStatus::unInitFat; #endif public: @@ -4952,7 +4998,11 @@ public: bool hasBorrowFields() const; void initBorrowStatus() const; - #endif + + bool hasFatFields() const; + + void initFatStatus() const; +#endif bool isSugared() const { return false; } QualType desugar() const { return QualType(this, 0); } @@ -6986,6 +7036,11 @@ inline bool QualType::isBorrowQualified() const { return isLocalBorrowQualified() || getCommonPtr()->CanonicalType.isLocalBorrowQualified(); } + +inline bool QualType::isFatQualified() const { + return isLocalFatQualified() || + getCommonPtr()->CanonicalType.isLocalFatQualified(); +} #endif inline bool QualType::isRestrictQualified() const { @@ -7009,14 +7064,17 @@ inline QualType QualType::getUnqualifiedType() const { int addOwned = getCanonicalType().isOwnedQualified() ? Qualifiers::Owned : 0; int addBorrow = getCanonicalType().isBorrowQualified() ? Qualifiers::Borrow : 0; - #else + int addFat = getCanonicalType().isFatQualified() ? Qualifiers::Fat : 0; +#else int addOwned = 0; int addBorrow = 0; - #endif + int addFat = 0; +#endif if (!getTypePtr()->getCanonicalTypeInternal().hasLocalQualifiers()) - return QualType(getTypePtr(), addOwned | addBorrow); + return QualType(getTypePtr(), addOwned | addBorrow | addFat); - return QualType(getSplitUnqualifiedTypeImpl(*this).Ty, addOwned | addBorrow); + return QualType(getSplitUnqualifiedTypeImpl(*this).Ty, + addOwned | addBorrow | addFat); } inline SplitQualType QualType::getSplitUnqualifiedType() const { @@ -7039,6 +7097,10 @@ inline void QualType::removeLocalOwned() { inline void QualType::removeLocalBorrow() { removeLocalFastQualifiers(Qualifiers::Borrow); } + +inline void QualType::removeLocalFat() { + removeLocalFastQualifiers(Qualifiers::Fat); +} #endif inline void QualType::removeLocalRestrict() { diff --git a/clang/include/clang/Basic/BSC/BSCAttr.td b/clang/include/clang/Basic/BSC/BSCAttr.td index ee4128dd97b5..5de2437c70fd 100644 --- a/clang/include/clang/Basic/BSC/BSCAttr.td +++ b/clang/include/clang/Basic/BSC/BSCAttr.td @@ -90,3 +90,9 @@ def Operator : InheritableAttr { let Args = [IntArgument<"OperatorKind">]; let Documentation = [Undocumented]; } + +def FatPtrChecked : InheritableAttr { + let Spellings = [GNU<"fat_ptr_checked">]; + let Subjects = SubjectList<[Var]>; + let Documentation = [Undocumented]; +} \ No newline at end of file diff --git a/clang/include/clang/Basic/BSC/DiagnosticBSCSemaKinds.td b/clang/include/clang/Basic/BSC/DiagnosticBSCSemaKinds.td index 61485f99f7c2..c38945739b42 100644 --- a/clang/include/clang/Basic/BSC/DiagnosticBSCSemaKinds.td +++ b/clang/include/clang/Basic/BSC/DiagnosticBSCSemaKinds.td @@ -63,7 +63,7 @@ def err_typecheck_invalid_owned_arrsub : Error< "owned pointer type (%0) do not support ArraySubscript operate">; def err_owned_temporary_memLeak : Error< "memory leak because temporary variable '%0' is owned or indirect owned type, please fix it">; -def err_funcPtr_incompatible : Error< +def err_borrow_funcPtr_incompatible : Error< "incompatible borrow function pointer types, cannot cast %0 to %1">; def err_borrow_on_borrow : Error<"%0 on a 'borrow' quialified type is not allowed">; def err_mut_expr_unmodifiable : Error<"the expression after '&mut' must be modifiable">; @@ -78,7 +78,6 @@ def err_borrow_qualcheck_compare : Error< def err_typecheck_borrow_func : Error<"no borrow qualified type found in the function parameters, the return type is not allowed to be borrow qualified">; def err_typecheck_borrow_subscript : Error<"subscript of borrow pointer is not allowed">; - // BSC trait warnings and errors. def err_variables_not_trait_pointer : Error<"only trait pointer type is allowed to be declared">; def err_trait_impl : Error<"function %0 in %1 is not implemented for %2">; @@ -216,4 +215,24 @@ def err_nullable_cast_nonnull : Error< def err_nullable_pointer_access_member : Error< "cannot access member througn nullable pointer">; def err_nonnull_assigned_by_nullable : Error< - "nonnull pointer cannot be assigned by nullable pointer">; \ No newline at end of file + "nonnull pointer cannot be assigned by nullable pointer">; + +// BSC Fat Pointer errors. +def err_fat_ptr_checked_attr_global_array_var : Error< + "fat_ptr_checked attribute can only used for global or static array variables">; +def err_typecheck_invalid_fat_not_pointer : Error< + "only pointer type can be qualified by fat">; +def err_fat_qualcheck_incompatible : Error< + "incompatible fat types, cannot cast %0 to %1">; +def err_fat_funcPtr_incompatible : Error< + "incompatible fat function pointer types, cannot cast %0 to %1">; +def err_addr_fat_on_no_fat_pointer : Error< + "'&fat' on member of no-fat pointer is not allowed">; +def err_addr_fat_on_global_var_without_checked : Error< + "'&fat' on global variables without fat_ptr_checked attribute is not allowed">; +def err_fat_ptr_type_not_found : Error< + "fat ptr type not found, you need to include %0 before using the 'fat' keyword">; +def err_fat_ptr_func_not_found : Error< + "fat ptr related function not found, you need to include %0">; +def err_fat_ptr_missing_specialization : Error< + "missing definition of template specialization for fat ptr %0">; \ No newline at end of file diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def index 6530a91d7f78..01b65d0b44a6 100644 --- a/clang/include/clang/Basic/LangOptions.def +++ b/clang/include/clang/Basic/LangOptions.def @@ -260,6 +260,7 @@ LANGOPT(RenderScript , 1, 0, "RenderScript") LANGOPT(HLSL, 1, 0, "HLSL") #if ENABLE_BSC LANGOPT(BSC, 1, 0, "BSC") +LANGOPT(EnableFatPtr, 1, 0, "enable fat ptr") LANGOPT(DisableOwnershipCheck, 1, 0, "disable ownership check") ENUM_LANGOPT(NullabilityCheck, NullCheckZone, 2, NC_SAFE, "nullability check") #endif diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def index 0def6903efe1..e2f7b89cff7c 100644 --- a/clang/include/clang/Basic/TokenKinds.def +++ b/clang/include/clang/Basic/TokenKinds.def @@ -251,6 +251,7 @@ PUNCTUATOR(caretcaret, "^^") #if ENABLE_BSC PUNCTUATOR(ampmut, "&mut") PUNCTUATOR(ampconst, "&const") +PUNCTUATOR(ampfat, "&fat") #endif // C99 6.4.1: Keywords. These turn into kw_* tokens. @@ -477,6 +478,7 @@ KEYWORD(async , KEYBSC) KEYWORD(await , KEYBSC) KEYWORD(owned , KEYBSC) KEYWORD(borrow , KEYBSC) +KEYWORD(fat , KEYBSC) KEYWORD(trait , KEYBSC) KEYWORD(safe , KEYBSC) KEYWORD(unsafe , KEYBSC) diff --git a/clang/include/clang/Driver/BSC/BSCOptions.td b/clang/include/clang/Driver/BSC/BSCOptions.td index 1b7d0328d08c..6c782e581767 100644 --- a/clang/include/clang/Driver/BSC/BSCOptions.td +++ b/clang/include/clang/Driver/BSC/BSCOptions.td @@ -4,6 +4,8 @@ def rewrite_bsc : Flag<["-"], "rewrite-bsc">, Flags<[CC1Option]>, Group; def rewrite_bsc_line : Flag<["-"], "line">, Flags<[CC1Option]>, HelpText<"Insert line info when rewrite BSC">; +def enable_fat_ptr : Flag<["-"], "enable-fat-ptr">, Flags<[CC1Option]>, + HelpText<"Enable fat ptr">, MarshallingInfoFlag>; def disable_ownership_check : Flag<["-"], "disable-ownership-check">, Flags<[CC1Option]>, HelpText<"Disable ownership check">, MarshallingInfoFlag>; def nullability_check : Joined<["-"], "nullability-check=">, Flags<[CC1Option]>, diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h index 2f84558ae9fd..bf2f3c3e1bf4 100644 --- a/clang/include/clang/Sema/DeclSpec.h +++ b/clang/include/clang/Sema/DeclSpec.h @@ -337,19 +337,20 @@ public: // type-qualifiers enum TQ { // NOTE: These flags must be kept in sync with Qualifiers::TQ. TQ_unspecified = 0, - TQ_const = 1, - TQ_restrict = 2, - TQ_volatile = 4, + TQ_const = 1, + TQ_restrict = 2, + TQ_volatile = 4, #if ENABLE_BSC - TQ_owned = 8, - TQ_borrow = 16, - TQ_unaligned = 32, + TQ_owned = 8, + TQ_borrow = 16, + TQ_fat = 32, + TQ_unaligned = 64, // This has no corresponding Qualifiers::TQ value, because it's not treated // as a qualifier in our type system. - TQ_atomic = 64 + TQ_atomic = 128 #else - TQ_unaligned = 8, - TQ_atomic = 16 + TQ_unaligned = 8, + TQ_atomic = 16 #endif }; @@ -385,7 +386,7 @@ private: // type-qualifiers #if ENABLE_BSC - unsigned TypeQualifiers : 7; // Bitwise OR of TQ. + unsigned TypeQualifiers : 8; // Bitwise OR of TQ. #else unsigned TypeQualifiers : 5; #endif @@ -456,7 +457,7 @@ private: SourceLocation FriendLoc, ModulePrivateLoc, ConstexprLoc; SourceLocation TQ_pipeLoc; #if ENABLE_BSC - SourceLocation TQ_ownedLoc, TQ_borrowLoc; + SourceLocation TQ_ownedLoc, TQ_borrowLoc, TQ_fatLoc; SourceLocation FS_asyncLoc, FS_safe_zone_loc; bool IsImplTrait = false; // if parsing impl trait decl #endif @@ -631,6 +632,7 @@ public: #if ENABLE_BSC SourceLocation getOwnedSpecLoc() const { return TQ_ownedLoc; } SourceLocation getBorrowSpecLoc() const { return TQ_borrowLoc; } + SourceLocation getFatSpecLoc() const { return TQ_fatLoc; } void setImplTrait() { IsImplTrait = true; } bool getImplTrait() { return IsImplTrait; } llvm::Optional getConditionalCondResult() const { return ConditionalCondResult; } @@ -646,6 +648,7 @@ public: #if ENABLE_BSC TQ_ownedLoc = SourceLocation(); TQ_borrowLoc = SourceLocation(); + TQ_fatLoc = SourceLocation(); #endif TQ_restrictLoc = SourceLocation(); TQ_volatileLoc = SourceLocation(); @@ -1329,7 +1332,7 @@ struct DeclaratorChunk { struct PointerTypeInfo { /// The type qualifiers: const/volatile/restrict/owned/unaligned/atomic. #if ENABLE_BSC - unsigned TypeQuals : 7; + unsigned TypeQuals : 8; #else unsigned TypeQuals : 5; #endif @@ -1343,6 +1346,9 @@ struct DeclaratorChunk { /// The location of the borrow-qualifier, if any. SourceLocation BorrowQualLoc; + + /// The location of the fat-qualifier, if any. + SourceLocation FatQualLoc; #endif /// The location of the volatile-qualifier, if any. @@ -1374,7 +1380,7 @@ struct DeclaratorChunk { /// The type qualifiers for the array: /// const/volatile/restrict/owned/__unaligned/_Atomic. #if ENABLE_BSC - unsigned TypeQuals : 7; + unsigned TypeQuals : 8; #else unsigned TypeQuals : 5; #endif @@ -1678,7 +1684,7 @@ struct DeclaratorChunk { struct MemberPointerTypeInfo { /// The type qualifiers: const/volatile/restrict/owned/__unaligned/_Atomic. #if ENABLE_BSC - unsigned TypeQuals : 7; + unsigned TypeQuals : 8; #else unsigned TypeQuals : 5; #endif diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index f492feaabcf8..29bd39ffb14b 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -1953,11 +1953,7 @@ public: PoppedFunctionScopePtr PopFunctionScopeInfo(const sema::AnalysisBasedWarnings::Policy *WP = nullptr, const Decl *D = nullptr, - QualType BlockType = QualType() - #if ENABLE_BSC - , bool isBSCCoroutine = false - #endif - ); + QualType BlockType = QualType()); sema::FunctionScopeInfo *getCurFunction() const { return FunctionScopes.empty() ? nullptr : FunctionScopes.back(); @@ -3224,6 +3220,19 @@ public: bool IsBSCCompatibleFutureType(QualType Ty); + // BSC Fat Ptr related. + bool CheckFatQualTypeCStyleCast(QualType LHSType, QualType RHSType, + SourceLocation RLoc); + bool CheckFatQualTypeCStyleCast(QualType LHSType, QualType RHSType); + bool CheckFatQualTypeAssignment(QualType LHSType, Expr *RHSExpr); + bool CheckFatQualTypeAssignment(QualType LHSType, QualType RHSType, + SourceLocation RLoc); + bool CheckFatFunctionPointerType(QualType LHSType, Expr *RHSExpr); + void DesugarFunctionDeclWithFatPtr(FunctionDecl *FD); + void DesugarRecordDeclWithFatPtr(RecordDecl *RD); + void DesugarGlobalVarDeclWithFatPtr(VarDecl *VD); + std::pair + BuildAllocationUnitForGlobalArrayVar(VarDecl *VD); // BSC Destructor related. BSCMethodDecl *getOrInsertBSCDestructor(RecordDecl *RD); void HandleBSCDestructorBody(RecordDecl *RD, BSCMethodDecl *Destructor, @@ -12316,20 +12325,25 @@ public: /// object with __weak qualifier. IncompatibleObjCWeakRef, - #if ENABLE_BSC - /// IncompatibleOwnedPointer - The assignment is between a owned qualified pointer - /// type with a unOwned qualified pointer type or two owned qualified pointer type - /// with different base types +#if ENABLE_BSC + /// IncompatibleOwnedPointer - The assignment is between a owned qualified + /// pointer type with a unOwned qualified pointer type or two owned + /// qualified pointer type with different base types IncompatibleOwnedPointer, - /// IncompatibleBorrowPointer - The assignment is between a borrow qualified pointer - /// type with a unBorrow qualified pointer type or two borrow qualified pointer type - /// with different base types + /// IncompatibleBorrowPointer - The assignment is between a borrow qualified + /// pointer type with a unBorrow qualified pointer type or two borrow + /// qualified pointer type with different base types IncompatibleBorrowPointer, + /// IncompatibleFatPointer - The assignment is between a fat qualified + /// pointer type with a unFat qualified pointer type or two fat qualified + /// pointer type with different base types + IncompatibleFatPointer, + /// IncompatibleBSCSafeZone - unsafe convert in the bsc safe zone. IncompatibleBSCSafeZone, - #endif +#endif /// Incompatible - We reject this conversion outright, it is invalid to /// represent it in the AST. @@ -12407,7 +12421,7 @@ public: void CheckOwnedOrIndirectOwnedType(SourceLocation ErrLoc, QualType T, StringRef Env); bool CheckOwnedDecl(SourceLocation ErrLoc, QualType T); bool CheckTemporaryVarMemoryLeak(Expr* E); - void BSCDataflowAnalysis(const Decl *D, bool EnableOwnershipCheck = true, + void BSCDataflowAnalysis(const FunctionDecl *FD, bool EnableOwnershipCheck = true, bool EnableNullabilityCheck = true); bool IsInSafeZone(); bool IsSafeBuiltinTypeConversion(BuiltinType::Kind SourceType, @@ -12442,11 +12456,11 @@ public: bool CheckBorrowQualTypeCompare(QualType LHSType, QualType RHSType); void CheckBorrowOrIndirectBorrowType(SourceLocation ErrLoc, QualType T, StringRef Env); - QualType GetBorrowAddressOperandQualType(QualType resultType, - ExprResult &Input, - const Expr *InputExpr, - UnaryOperatorKind &Opc, - SourceLocation OpLoc); + QualType GetBSCAddressOperandQualType(QualType resultType, + ExprResult &Input, + const Expr *InputExpr, + UnaryOperatorKind &Opc, + SourceLocation OpLoc); OverloadedOperatorKind getOperatorKindByDeclarator(Declarator &D); bool CheckComparisonKindOperatorFunReturnType(FunctionDecl *FnDecl); bool CheckBSCOverloadedOperatorDeclaration(FunctionDecl *FnDecl); diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 11d3138573fd..0d34dfc80377 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -10150,7 +10150,11 @@ bool ASTContext::typesAreCompatible(QualType LHS, QualType RHS, bool CompareUnqualified) { if (getLangOpts().CPlusPlus) return hasSameType(LHS, RHS); - +#if ENABLE_BSC + if (getLangOpts().BSC && getLangOpts().EnableFatPtr && + LHS->isFatPtrRecordType() && RHS->isFatPtrRecordType()) + return true; +#endif return !mergeTypes(LHS, RHS, false, CompareUnqualified).isNull(); } diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index 196d193d5b24..38e841e5c02d 100644 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -1508,7 +1508,7 @@ ExpectedType ASTNodeImporter::VisitInjectedClassNameType( // return Importer.getToContext().getInjectedClassNameType(D, InjType); enum { #if ENABLE_BSC - TypeAlignmentInBits = 6, + TypeAlignmentInBits = 7, #else TypeAlignmentInBits = 4, #endif diff --git a/clang/lib/AST/BSC/TypeBSC.cpp b/clang/lib/AST/BSC/TypeBSC.cpp index bba9b782c748..f7e1cbf52688 100644 --- a/clang/lib/AST/BSC/TypeBSC.cpp +++ b/clang/lib/AST/BSC/TypeBSC.cpp @@ -66,6 +66,52 @@ bool Type::hasBorrowFields() const { return false; } +bool PointerType::hasFatFields() const { + QualType R = getPointeeType(); + if (R.isFatQualified()) { + return true; + } + if (R.getTypePtr()->hasFatFields()) { + return true; + } + return false; +} + +bool Type::hasFatFields() const { + if (const auto *RecTy = dyn_cast(CanonicalType)) { + return RecTy->hasFatFields(); + } else if (const auto *PointerTy = dyn_cast(CanonicalType)) { + return PointerTy->hasFatFields(); + } + return false; +} + +bool Type::isFatPtrRecordType() const { + if (this->isRecordType()) + if (RecordDecl *RD = this->getAs()->getDecl()) + if (RD->getNameAsString() == "_FatPtr") + return true; + return false; +} + +QualType Type::getFatPtrPointeeType() const { + if (this->isFatPtrRecordType()) + if (auto TST = dyn_cast(this)) + if (TST->getNumArgs() == 1) + return TST->begin()->getAsType(); + if (auto PT = dyn_cast(this)) + return PT->getPointeeType(); + return QualType(); +} + +bool QualType::isFatPtrType() const { + if (isFatQualified()) + return true; + if (const Type *ty = getTypePtr()) + return ty->isFatPtrRecordType(); + return false; +} + bool FunctionProtoType::hasOwnedRetOrParams() const { if (getReturnType().isOwnedQualified()) { return true; @@ -90,6 +136,18 @@ bool FunctionProtoType::hasBorrowRetOrParams() const { return false; } +bool FunctionProtoType::hasFatRetOrParams() const { + if (getReturnType().isFatQualified()) { + return true; + } + for (auto ParamType : getParamTypes()) { + if (ParamType.isFatQualified()) { + return true; + } + } + return false; +} + bool Type::checkFunctionProtoType(SafeZoneSpecifier SZS) const { const FunctionProtoType *FPT = nullptr; if (isFunctionType()) { @@ -254,6 +312,53 @@ bool RecordType::hasBorrowFields() const { return false; } +void RecordType::initFatStatus() const { + if (hasFat != fatStatus::unInitFat) + return; + std::vector RecordTypeList; + RecordTypeList.push_back(this); + unsigned NextToCheckIndex = 0; + + while (RecordTypeList.size() > NextToCheckIndex) { + for (FieldDecl *FD : + RecordTypeList[NextToCheckIndex]->getDecl()->fields()) { + QualType FieldTy = FD->getType(); + if (FieldTy.isFatQualified()) { + hasFat = fatStatus::withFat; + return; + } + QualType tempQT = FieldTy; + const Type *tempT = tempQT.getTypePtr(); + while (tempT->isPointerType()) { + tempQT = tempT->getPointeeType(); + if (tempQT.isFatQualified()) { + hasFat = fatStatus::withFat; + return; + } else { + tempQT = tempQT.getCanonicalType(); + tempT = tempQT.getTypePtr(); + } + } + FieldTy = tempQT.getCanonicalType(); + if (const auto *FieldRecTy = FieldTy->getAs()) { + if (llvm::find(RecordTypeList, FieldRecTy) == RecordTypeList.end()) + RecordTypeList.push_back(FieldRecTy); + } + } + ++NextToCheckIndex; + } + hasFat = fatStatus::withoutFat; + return; +} + +bool RecordType::hasFatFields() const { + if (hasFat == fatStatus::unInitFat) + initFatStatus(); + if (hasFat == fatStatus::withFat) + return true; + return false; +} + bool Type::isBSCFutureType() const { if (const auto *RT = getAs()) { RecordDecl *RD = RT->getAsRecordDecl(); @@ -292,6 +397,12 @@ bool QualType::hasBorrow() const { return getTypePtr()->hasBorrowFields(); } +bool QualType::hasFat() const { + if (isFatQualified()) + return true; + return getTypePtr()->hasFatFields(); +} + bool QualType::isConstBorrow() const { QualType QT = QualType(getTypePtr(), getLocalFastQualifiers()); if (QT.isLocalBorrowQualified()) { diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index c55477431d52..025a70a754fa 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -2535,6 +2535,7 @@ bool Expr::isUnusedResultAWarning(const Expr *&WarnE, SourceLocation &Loc, case UO_AddrConst: case UO_AddrMutDeref: case UO_AddrConstDeref: + case UO_AddrFat: #endif case UO_Not: case UO_LNot: diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 0d7aa2caeab1..894692ab2d5c 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -8037,8 +8037,18 @@ public: QualType BaseTy; bool EvalOK; if (E->isArrow()) { +#if ENABLE_BSC + if (this->Info.getLangOpts().BSC && this->Info.getLangOpts().EnableFatPtr && + E->getBase()->getType()->isFatPtrRecordType()) { + EvalOK = true; + BaseTy = E->getBase()->getType()->getFatPtrPointeeType(); + } else { +#endif EvalOK = evaluatePointer(E->getBase(), Result); BaseTy = E->getBase()->getType()->castAs()->getPointeeType(); +#if ENABLE_BSC + } +#endif } else if (E->getBase()->isPRValue()) { assert(E->getBase()->getType()->isRecordType()); EvalOK = EvaluateTemporary(E->getBase(), Result, this->Info); @@ -8484,6 +8494,11 @@ bool LValueExprEvaluator::VisitArraySubscriptExpr(const ArraySubscriptExpr *E) { } bool LValueExprEvaluator::VisitUnaryDeref(const UnaryOperator *E) { +#if ENABLE_BSC + if (Info.getLangOpts().BSC && Info.getLangOpts().EnableFatPtr && + E->getSubExpr()->getType()->isFatPtrRecordType()) + return true; +#endif return evaluatePointer(E->getSubExpr(), Result); } @@ -15533,6 +15548,7 @@ static ICEDiag CheckICE(const Expr* E, const ASTContext &Ctx) { case UO_AddrConst: case UO_AddrMutDeref: case UO_AddrConstDeref: + case UO_AddrFat: #endif case UO_Deref: case UO_Coawait: diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp index 8b829627c235..444b2e161204 100644 --- a/clang/lib/AST/StmtPrinter.cpp +++ b/clang/lib/AST/StmtPrinter.cpp @@ -1158,10 +1158,12 @@ void StmtPrinter::VisitDeclRefExpr(DeclRefExpr *Node) { #if ENABLE_BSC if (Policy.RewriteBSC) { if (VarDecl *VD = dyn_cast(Node->getDecl())) { - APValue *Res = VD->getEvaluatedValue(); - if (VD->isConstexpr() && Res && Res->isInt()) { - OS << Res->getInt(); - return; + if (VD->isConstexpr()) { + APValue *Res = VD->evaluateValue(); + if (Res && Res->isInt()) { + OS << Res->getInt(); + return; + } } } if (auto *BD = dyn_cast(Node->getFoundDecl())) { @@ -1483,7 +1485,7 @@ void StmtPrinter::VisitUnaryOperator(UnaryOperator *Node) { #if ENABLE_BSC if (Policy.RewriteBSC) { if (Node->getOpcode() == UO_AddrConst || - Node->getOpcode() == UO_AddrMut) { + Node->getOpcode() == UO_AddrMut || Node->getOpcode() == UO_AddrFat) { OS << UnaryOperator::getOpcodeStr(UO_AddrOf); } else if (Node->getOpcode() == UO_AddrConstDeref || Node->getOpcode() == UO_AddrMutDeref) { diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp index 0cc42b5cf96f..8279e434ad67 100644 --- a/clang/lib/AST/TypePrinter.cpp +++ b/clang/lib/AST/TypePrinter.cpp @@ -200,6 +200,12 @@ static void AppendTypeQualList(raw_ostream &OS, unsigned TypeQuals, OS << "borrow"; appendSpace = true; } + if (TypeQuals & Qualifiers::Fat && !IsRewriteBSC) { + if (appendSpace) + OS << ' '; + OS << "fat"; + appendSpace = true; + } #endif if (TypeQuals & Qualifiers::Volatile) { if (appendSpace) OS << ' '; diff --git a/clang/lib/Analysis/BSCBorrowCheck.cpp b/clang/lib/Analysis/BSCBorrowCheck.cpp index bc9243999a9c..7b4d8d3d47f8 100644 --- a/clang/lib/Analysis/BSCBorrowCheck.cpp +++ b/clang/lib/Analysis/BSCBorrowCheck.cpp @@ -18,7 +18,6 @@ #include "clang/Analysis/CFG.h" #include #include -#include #include using namespace clang; using namespace std; diff --git a/clang/lib/Analysis/ThreadSafetyCommon.cpp b/clang/lib/Analysis/ThreadSafetyCommon.cpp index e6ab441058d0..89b71e7a37e4 100644 --- a/clang/lib/Analysis/ThreadSafetyCommon.cpp +++ b/clang/lib/Analysis/ThreadSafetyCommon.cpp @@ -465,7 +465,8 @@ til::SExpr *SExprBuilder::translateUnaryOperator(const UnaryOperator *UO, #if ENABLE_BSC case UO_AddrMut: case UO_AddrConst: - #endif + case UO_AddrFat: +#endif if (CapabilityExprMode) { // interpret &Graph::mu_ as an existential. if (const auto *DRE = dyn_cast(UO->getSubExpr())) { diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp index dfa78bf59c65..4ff156690c82 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -4185,6 +4185,13 @@ void CodeGenFunction::EmitCallArgs( CallExpr::const_arg_iterator Arg = ArgRange.begin(); for (QualType Ty : ArgTypes) { assert(Arg != ArgRange.end() && "Running over edge of argument list!"); + if (Ty.getCanonicalType()->isRecordType()) + if (RecordDecl *RD = + Ty.getCanonicalType()->getAs()->getDecl()) + if (RD->getNameAsString() == "_FatPtr") { + ++Arg; + continue; + } assert( (isGenericMethod || Ty->isVariablyModifiedType() || Ty.getNonReferenceType()->isObjCRetainableType() || diff --git a/clang/lib/CodeGen/CGExprAgg.cpp b/clang/lib/CodeGen/CGExprAgg.cpp index 73b05690537d..f5d307974bd6 100644 --- a/clang/lib/CodeGen/CGExprAgg.cpp +++ b/clang/lib/CodeGen/CGExprAgg.cpp @@ -1166,6 +1166,11 @@ static bool isBlockVarRef(const Expr *E) { void AggExprEmitter::VisitBinAssign(const BinaryOperator *E) { // For an assignment to work, the value on the right has // to be compatible with the value on the left. +#if ENABLE_BSC + if (!(CGF.getLangOpts().BSC && CGF.getLangOpts().EnableFatPtr && + E->getLHS()->getType()->isFatPtrRecordType() && + E->getRHS()->getType()->isFatPtrRecordType())) +#endif assert(CGF.getContext().hasSameUnqualifiedType(E->getLHS()->getType(), E->getRHS()->getType()) && "Invalid assignment"); diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp index c689fa310858..9a0fba739fe0 100644 --- a/clang/lib/CodeGen/CGExprScalar.cpp +++ b/clang/lib/CodeGen/CGExprScalar.cpp @@ -637,6 +637,12 @@ public: Value *VisitUnaryAddrConstDeref(const UnaryOperator *E) { return Visit(E->getSubExpr()); } + Value *VisitUnaryAddrFat(const UnaryOperator *E) { + if (isa(E->getType())) // never sugared + return CGF.CGM.getMemberPointerConstant(E); + + return EmitLValue(E->getSubExpr()).getPointer(CGF); + } #endif Value *VisitUnaryDeref(const UnaryOperator *E) { if (E->getType()->isVoidType()) diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index f32da6d90e65..543964111f2c 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -4818,6 +4818,8 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, #if ENABLE_BSC if (Args.getLastArg(options::OPT_opt_string)) Args.AddLastArg(CmdArgs, options::OPT_opt_string); + if (Args.hasArg(options::OPT_enable_fat_ptr)) + CmdArgs.push_back("-enable-fat-ptr"); if (Args.hasArg(options::OPT_disable_ownership_check)) CmdArgs.push_back("-disable-ownership-check"); if (Args.hasArg(options::OPT_nullability_check)) { diff --git a/clang/lib/Headers/CMakeLists.txt b/clang/lib/Headers/CMakeLists.txt index 158dfb759f75..81faf5ebd67c 100644 --- a/clang/lib/Headers/CMakeLists.txt +++ b/clang/lib/Headers/CMakeLists.txt @@ -265,6 +265,7 @@ set(openmp_wrapper_files if (ENABLE_BSC) set(bsc_include_files + bsc_include/bsc_fat_ptr.hbs bsc_include/bsc_type_traits.hbs bsc_include/bsc_conditional.hbs bsc_include/future.hbs diff --git a/clang/lib/Headers/bsc_include/bsc_fat_ptr.hbs b/clang/lib/Headers/bsc_include/bsc_fat_ptr.hbs new file mode 100644 index 000000000000..e0558ee48b36 --- /dev/null +++ b/clang/lib/Headers/bsc_include/bsc_fat_ptr.hbs @@ -0,0 +1,90 @@ +/*===---------- bsc_fat_ptr.hbs - Standard header for fat ptr ----------===*\ + * + * Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. + * See https://llvm.org/LICENSE.txt for license information. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + * +\*===----------------------------------------------------------------------===*/ + +// Define BSC fat ptr type and related functions +#ifndef BSC_FAT_PTR_HBS +#define BSC_FAT_PTR_HBS + +#include +#include +#include +#include +#include + +struct _FatPtr { + T* raw_ptr; + uint32_t key_version; + int32_t offset; +}; + +struct _AllocationUnit { + uint32_t lock_version; + uint32_t size; + uint8_t payload[]; +}; + +void _report_error(const char* msg) { + fprintf(stderr, "%s", msg); + void *callstack[10]; + int num_frames = backtrace(callstack, 10); + char **symbols = backtrace_symbols(callstack, num_frames); + + fprintf(stderr, "Call stack:\n"); + for (int i = 0; i < num_frames; i++) { + fprintf(stderr, "%s\n", symbols[i]); + } + free(symbols); + exit(EXIT_FAILURE); +} + +uint32_t _new_version_number() { + static _Atomic(uint32_t) uuid = 10000; + atomic_fetch_add(&uuid, 1); + return atomic_load(&uuid); +} + +_FatPtr checked_malloc(size_t payload_bytes) { + if (payload_bytes == 0) + _report_error("allocated size is ZERO!\n"); + size_t all_size = payload_bytes + 8; + void *raw = malloc(all_size); + uint32_t *p = (uint32_t*)raw; + uint32_t ver = _new_version_number(); + *p = ver; + *(p + 1) = payload_bytes; + _FatPtr ptr = { .raw_ptr = (void*)(p + 2), .key_version = ver, .offset = 0 }; + return ptr; +} + +void checked_free(_FatPtr ptr) { + uint8_t* head = (uint8_t*)ptr.raw_ptr - ptr.offset - 8; + struct _AllocationUnit* phead = (struct _AllocationUnit *)head; + if (ptr.key_version != phead->lock_version) + _report_error("version number error when free!\n"); + if (ptr.offset != 0) + _report_error("free a pointer which is not point to the head of an allocation!\n"); + memset(phead, 0, phead->size + 8); + free(phead); +} + +void _check_version(_FatPtr ptr) { + const uint8_t *head = (const uint8_t *)ptr.raw_ptr - ptr.offset - 8; + const struct _AllocationUnit *phead = (const struct _AllocationUnit *)head; + if (ptr.key_version != 0 && ptr.key_version != phead->lock_version) + _report_error("version number error!\n"); +} + +void _check_offset(_FatPtr ptr, int32_t bytes) { + const uint8_t *head = (const uint8_t *)ptr.raw_ptr - ptr.offset - 8; + const struct _AllocationUnit *phead = (const struct _AllocationUnit *)head; + int32_t new_offset = ptr.offset + bytes; + if (phead->size > 0 && new_offset > phead->size) + _report_error("pointer offset exceed the allocation size!\n"); +} + +#endif /* BSC_FAT_PTR_HBS */ \ No newline at end of file diff --git a/clang/lib/Lex/Lexer.cpp b/clang/lib/Lex/Lexer.cpp index 27cdfc68c13e..c38f31d4324a 100644 --- a/clang/lib/Lex/Lexer.cpp +++ b/clang/lib/Lex/Lexer.cpp @@ -3914,6 +3914,20 @@ LexNextToken: ConsumeChar(ConsumeChar(CurPtr, SizeTmp, Result), SizeTmp2, Result), SizeTmp3, Result), SizeTmp4, Result), SizeTmp5, Result); } + } else if (LangOpts.BSC && Char == 'f' && + getCharAndSize(CurPtr + SizeTmp, SizeTmp2) == 'a' && + getCharAndSize(CurPtr + SizeTmp + SizeTmp2, SizeTmp3) == 't') { + char After = + getCharAndSize(CurPtr + SizeTmp + SizeTmp2 + SizeTmp3, SizeTmp4); + if ((After >= '0' && After <= '9') || (After >= 'a' && After <= 'z') || + (After >= 'A' && After <= 'Z')) { + Kind = tok::amp; + } else { + Kind = tok::ampfat; + CurPtr = ConsumeChar( + ConsumeChar(ConsumeChar(CurPtr, SizeTmp, Result), SizeTmp2, Result), + SizeTmp3, Result); + } } #endif else { diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index 3faa4ceba9ca..d314128f5ff7 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -2233,12 +2233,26 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS, DeclsInGroup.push_back(FirstDecl); #if ENABLE_BSC - if (FirstDecl) { - FunctionDecl *FD = dyn_cast_or_null(FirstDecl); - if (getLangOpts().BSC && FD && FD->isAsyncSpecified()) { - SmallVector Decls = Actions.ActOnAsyncFunctionDeclaration(FD); - for (auto &D : Decls) { - DeclsInGroup.push_back(D); + if (getLangOpts().BSC && FirstDecl) { + if (FunctionDecl *FD = dyn_cast_or_null(FirstDecl)) { + if (FD->isAsyncSpecified()) { + SmallVector Decls = Actions.ActOnAsyncFunctionDeclaration(FD); + for (auto &D : Decls) { + DeclsInGroup.push_back(D); + } + } + } else if (VarDecl *VD = dyn_cast_or_null(FirstDecl)) { + if (getLangOpts().EnableFatPtr && VD->hasGlobalStorage()) { + QualType QT = VD->getType().getCanonicalType(); + if (VD->hasAttr() && + QT->isConstantArrayType()) { + std::pair Decls = + Actions.BuildAllocationUnitForGlobalArrayVar(VD); + DeclsInGroup.push_back(Decls.first); + DeclsInGroup.push_back(Decls.second); + } else if (QT.isFatQualified() || QT->hasFatFields()) { + Actions.DesugarGlobalVarDeclWithFatPtr(VD); + } } } } @@ -4519,6 +4533,12 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, isInvalid = DS.SetTypeQual(DeclSpec::TQ_borrow, Loc, PrevSpec, DiagID, getLangOpts()); break; + + // fat-qualifier: + case tok::kw_fat: + isInvalid = DS.SetTypeQual(DeclSpec::TQ_fat, Loc, PrevSpec, DiagID, + getLangOpts()); + break; #endif // C++ typename-specifier: @@ -5610,6 +5630,7 @@ bool Parser::isTypeSpecifierQualifier() { #if ENABLE_BSC case tok::kw_owned: case tok::kw_borrow: + case tok::kw_fat: #endif // Debugger support. @@ -5785,6 +5806,7 @@ bool Parser::isDeclarationSpecifier(bool DisambiguatingWithExpression) { #if ENABLE_BSC case tok::kw_owned: case tok::kw_borrow: + case tok::kw_fat: #endif case tok::kw__Sat: @@ -6108,6 +6130,11 @@ void Parser::ParseTypeQualifierListOpt( isInvalid = DS.SetTypeQual(DeclSpec::TQ_borrow, Loc, PrevSpec, DiagID, getLangOpts()); break; + // fat-qualifier: + case tok::kw_fat: + isInvalid = DS.SetTypeQual(DeclSpec::TQ_fat, Loc, PrevSpec, DiagID, + getLangOpts()); + break; #endif case tok::kw__Atomic: if (!AtomicAllowed) diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp index e711ea022f1b..6cc6ad5679e9 100644 --- a/clang/lib/Parse/ParseExpr.cpp +++ b/clang/lib/Parse/ParseExpr.cpp @@ -1427,6 +1427,7 @@ ExprResult Parser::ParseCastExpression( #if ENABLE_BSC case tok::ampmut: case tok::ampconst: + case tok::ampfat: #endif case tok::amp: { // unary-expression: '&' cast-expression if (NotPrimaryExpression) diff --git a/clang/lib/Sema/BSC/SemaBSCCoroutine.cpp b/clang/lib/Sema/BSC/SemaBSCCoroutine.cpp index a67c1c8dfea5..18aa747e27fa 100644 --- a/clang/lib/Sema/BSC/SemaBSCCoroutine.cpp +++ b/clang/lib/Sema/BSC/SemaBSCCoroutine.cpp @@ -917,7 +917,7 @@ static FunctionDecl *buildFutureInitFunctionDefinition(Sema &S, RecordDecl *RD, CompoundStmt::Create(S.Context, Stmts, FPOptionsOverride(), SLoc, ELoc); NewFD->setBody(CS); sema::AnalysisBasedWarnings::Policy *ActivePolicy = nullptr; - S.PopFunctionScopeInfo(ActivePolicy, NewFD, QualType(), true); + S.PopFunctionScopeInfo(ActivePolicy, NewFD, QualType()); return NewFD; } @@ -1094,7 +1094,7 @@ buildFutureStructInitFunctionDefinition(Sema &S, RecordDecl *RD, CompoundStmt::Create(S.Context, Stmts, FPOptionsOverride(), SLoc, ELoc); NewFD->setBody(CS); sema::AnalysisBasedWarnings::Policy *ActivePolicy = nullptr; - S.PopFunctionScopeInfo(ActivePolicy, NewFD, QualType(), true); + S.PopFunctionScopeInfo(ActivePolicy, NewFD, QualType()); return NewFD; } @@ -2414,7 +2414,7 @@ static BSCMethodDecl *buildFreeFunctionDeclaration(Sema &S, RecordDecl *RD, S.PushFunctionScope(); sema::AnalysisBasedWarnings::Policy *ActivePolicy = nullptr; - S.PopFunctionScopeInfo(ActivePolicy, NewFD, QualType(), true); + S.PopFunctionScopeInfo(ActivePolicy, NewFD, QualType()); return NewFD; } @@ -2645,7 +2645,7 @@ static BSCMethodDecl *buildFreeFunctionDefinition(Sema &S, RecordDecl *RD, CompoundStmt::Create(S.Context, Stmts, FPOptionsOverride(), SLoc, ELoc); NewFD->setBody(CS); sema::AnalysisBasedWarnings::Policy *ActivePolicy = nullptr; - S.PopFunctionScopeInfo(ActivePolicy, NewFD, QualType(), true); + S.PopFunctionScopeInfo(ActivePolicy, NewFD, QualType()); return NewFD; } @@ -2706,7 +2706,7 @@ static BSCMethodDecl *buildPollFunctionDeclaration(Sema &S, RecordDecl *RD, NewFD->setType(FuncType); sema::AnalysisBasedWarnings::Policy *ActivePolicy = nullptr; - S.PopFunctionScopeInfo(ActivePolicy, NewFD, QualType(), true); + S.PopFunctionScopeInfo(ActivePolicy, NewFD, QualType()); return NewFD->isInvalidDecl() ? nullptr : NewFD; } @@ -2869,7 +2869,7 @@ static BSCMethodDecl *buildPollFunctionDefinition(Sema &S, RecordDecl *RD, CompoundStmt::Create(S.Context, Stmts, FPOptionsOverride(), SLoc, ELoc); NewFD->setBody(CS); sema::AnalysisBasedWarnings::Policy *ActivePolicy = nullptr; - S.PopFunctionScopeInfo(ActivePolicy, NewFD, QualType(), true); + S.PopFunctionScopeInfo(ActivePolicy, NewFD, QualType()); return NewFD->isInvalidDecl() ? nullptr : NewFD; } diff --git a/clang/lib/Sema/BSC/SemaBSCDestructor.cpp b/clang/lib/Sema/BSC/SemaBSCDestructor.cpp index edc2674d1980..fc88350e2ba8 100644 --- a/clang/lib/Sema/BSC/SemaBSCDestructor.cpp +++ b/clang/lib/Sema/BSC/SemaBSCDestructor.cpp @@ -2,10 +2,10 @@ #include -#include "TreeTransform.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Expr.h" #include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Basic/DiagnosticSema.h" #include "clang/Basic/SourceManager.h" #include "clang/Sema/Scope.h" #include "clang/Sema/Template.h" diff --git a/clang/lib/Sema/BSC/SemaBSCFatPtr.cpp b/clang/lib/Sema/BSC/SemaBSCFatPtr.cpp new file mode 100644 index 000000000000..41a5c1ae7b21 --- /dev/null +++ b/clang/lib/Sema/BSC/SemaBSCFatPtr.cpp @@ -0,0 +1,1547 @@ +//===--- SemaBSCFatPtr.cpp - Semantic Analysis for BSC FatPtr -------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements semantic analysis for BSC FatPtr. +// +//===----------------------------------------------------------------------===// + +#if ENABLE_BSC +#include "TreeTransform.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/StmtVisitor.h" +#include "clang/Basic/DiagnosticSema.h" +#include "llvm/ADT/SmallSet.h" +#include "llvm/ADT/SmallVector.h" +#include + +using namespace clang; +using namespace sema; + +enum AddrFatExprBaseKind { + // `&fat p->a;` p is fat ptr + // `&fat p->arr[1];` p is fat ptr + // `&fat p[1];` p is fat ptr + // `&fat p[1].a;` p is fat ptr + // `&fat s.p[1].a;` s.p is fat ptr + FatPtr, + + // `&fat a;` a is local no-array variable + // `&fat s.a;` s is local no-array variable whose type has no array field + LocalNoArrayVar, + + // `&fat arr[5];` arr is local array variable + // `&fat s.arr[5];` s is local variable whose type has array field + // `&fat s.a;` s is local variable whose type has array field + LocalArrayVar, + + // `&fat garr[5];` garr is global array variable + // `&fat g.arr[5];` g is global variable whose type has array field + // `&fat g.a;` g is global variable whose type has array field + GlobalArrayVar, + + OtherKind, +}; + +// Collect local vars whose address is taken by &fat in current function, +// also classify &fat expr by its base kind. +class AddressFatTakenFinder : public StmtVisitor { + bool IsVisitingAddrFatExpr = false; + Expr *CurrentAddrFatExpr = nullptr; + +public: + bool LocalNoArrayVarAddressIsFatTaken = false; + llvm::SmallSet LocalArrayVarsSet; + + // Map of AddrFatExpr and its base expr and base kind: + // key is AddrFatExpr, value is its base expr and kind. + // For example: + // 1. &fat p->a` where p is fat ptr + // 2. &fat arr[5]` where arr is local array variable + // 3. &fat g.arr[5]` where g is global variable whose type has array field + // the map will be: + // &fat p->a : (FatPtr, p) + // &fat arr[5] : (LocalArrayVar, arr) + // &fat g.arr[5] : (GlobalArrayVar, g) + llvm::DenseMap> + AddrFatExprAndBaseMap; + + bool hasConstantArray(QualType QT) { + QT = QT.getCanonicalType(); + if (QT->isConstantArrayType()) + return true; + else if (auto RT = dyn_cast(QT)) { + if (auto RD = dyn_cast(RT->getDecl())) + for (auto *FD : RD->fields()) + if (hasConstantArray(FD->getType())) + return true; + } + return false; + } + + void VisitStmt(Stmt *S) { + for (auto *C : S->children()) + if (C) + Visit(C); + } + + void VisitUnaryOperator(UnaryOperator *UO) { + if (UO->getOpcode() == UO_AddrFat) { + IsVisitingAddrFatExpr = true; + CurrentAddrFatExpr = UO; + AddrFatExprAndBaseMap[CurrentAddrFatExpr].first = + AddrFatExprBaseKind::OtherKind; + AddrFatExprAndBaseMap[CurrentAddrFatExpr].second = nullptr; + } + Visit(UO->getSubExpr()); + } + + void VisitMemberExpr(MemberExpr *ME) { + if (IsVisitingAddrFatExpr) { + Expr *BaseE = ME->getBase()->IgnoreImpCasts(); + if (BaseE->getType().isFatPtrType()) { + AddrFatExprAndBaseMap[CurrentAddrFatExpr].first = + AddrFatExprBaseKind::FatPtr; + AddrFatExprAndBaseMap[CurrentAddrFatExpr].second = BaseE; + CurrentAddrFatExpr = nullptr; + IsVisitingAddrFatExpr = false; + return; + } + } + Visit(ME->getBase()); + } + + void VisitArraySubscriptExpr(ArraySubscriptExpr *ASE) { + if (IsVisitingAddrFatExpr) { + Expr *BaseE = ASE->getBase()->IgnoreImpCasts(); + if (BaseE->getType().isFatPtrType()) { + AddrFatExprAndBaseMap[CurrentAddrFatExpr].first = + AddrFatExprBaseKind::FatPtr; + AddrFatExprAndBaseMap[CurrentAddrFatExpr].second = BaseE; + CurrentAddrFatExpr = nullptr; + IsVisitingAddrFatExpr = false; + return; + } + } + Visit(ASE->getBase()); + } + + void VisitDeclRefExpr(DeclRefExpr *DRE) { + auto VD = dyn_cast(DRE->getDecl()); + if (VD && IsVisitingAddrFatExpr) { + QualType QT = VD->getType(); + if (VD->hasLocalStorage()) { + if (hasConstantArray(QT)) { + AddrFatExprAndBaseMap[CurrentAddrFatExpr].first = + AddrFatExprBaseKind::LocalArrayVar; + AddrFatExprAndBaseMap[CurrentAddrFatExpr].second = DRE; + LocalArrayVarsSet.insert(VD); + } else { + AddrFatExprAndBaseMap[CurrentAddrFatExpr].first = + AddrFatExprBaseKind::LocalNoArrayVar; + LocalNoArrayVarAddressIsFatTaken = true; + } + } else if (VD->hasGlobalStorage() && hasConstantArray(QT)) { + AddrFatExprAndBaseMap[CurrentAddrFatExpr].first = + AddrFatExprBaseKind::GlobalArrayVar; + AddrFatExprAndBaseMap[CurrentAddrFatExpr].second = DRE; + } + CurrentAddrFatExpr = nullptr; + IsVisitingAddrFatExpr = false; + } + } +}; + +// Desugar fat ptr for FunctionDecl, VarDecl and RecordDecl. +class TransformFatPtr : public TreeTransform { + typedef TreeTransform BaseTransform; + FunctionDecl *FD = nullptr; + + // cache fat ptr related function to avoid frequent lookup. + std::map FatPtrFD; + // cache fat ptr TemplateDecl to avoid frequent lookup. + ClassTemplateDecl *FatPtrTD = nullptr; + + AddressFatTakenFinder Finder; + VarDecl *LocalLockVersionVD = nullptr; + llvm::DenseMap> + AllocationUnitForLocalArrayVarMap; + + // when deref, access member or some pointer calculation, + // fat ptr should be desugared to its ptr member, for example: + // 1. `*p` should be desugared to `*p.raw_ptr` + // 2. `p->a` should be desugared to `p.raw_ptr->a` + // 3. `p1 - p2` should be desugared to `p1.raw_ptr - p2.raw_ptr` + // 4. `(int*)p` should be desugared to `(int*)p.raw_ptr` + // but `int *fat p1 = p2;` p2 should not be desugared, + // and `float *fat p1 = (float *fat)p2;` p2 should not be desugared. + bool DesugarToMemberPtr = false; + bool InsertCheckVersionCall = false; + std::pair InsertCheckOffsetCall = + std::make_pair(false, nullptr); + std::stack CheckCallExpr; + + // when accessing array member through FatPtr, + // such as `p->arr[3]` or `p->a.b.arr[3]` + // we should check offset to avoid out of bounds. + ArraySubscriptExpr *CurrentASEToAccessMemberThroughFatPtr = nullptr; + +public: + TransformFatPtr(Sema &SemaRef) : BaseTransform(SemaRef) {} + + TransformFatPtr(Sema &SemaRef, FunctionDecl *Fd) + : BaseTransform(SemaRef), FD(Fd) { + FatPtrFD["_check_version"] = nullptr; + FatPtrFD["_check_offset"] = nullptr; + FatPtrFD["_new_version_number"] = nullptr; + } + + // Entry to desugar FunctionDecl + void TransformFunctionDecl(); + // Entry to desugar global or local VarDecl + void TransformVarDecl(VarDecl *VD); + // Entry to desugar global or local RecordDecl + void TransformRecordDecl(RecordDecl *RD); + + StmtResult TransformCompoundStmt(CompoundStmt *CS, bool IsStmtExpr); + StmtResult TransformCompoundStmt(CompoundStmt *CS); + StmtResult TransformDeclStmt(DeclStmt *DS); + StmtResult TransformReturnStmt(ReturnStmt *RS); + ExprResult TransformDeclRefExpr(DeclRefExpr *DRE); + ExprResult TransformImplicitCastExpr(ImplicitCastExpr *ICE); + ExprResult TransformParenExpr(ParenExpr *PE); + ExprResult TransformCompoundLiteralExpr(CompoundLiteralExpr *CLE); + ExprResult TransformInitListExpr(InitListExpr *ILE); + ExprResult TransformCallExpr(CallExpr *CE); + ExprResult TransformArraySubscriptExpr(ArraySubscriptExpr *ASE); + ExprResult TransformCStyleCastExpr(CStyleCastExpr *CSCE); + ExprResult TransformUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *UETT); + ExprResult TransformMemberExpr(MemberExpr *ME); + + ExprResult TransformBinaryOperator(BinaryOperator *BO); + ExprResult HandleBOFatPtrAddOrSubInteger(BinaryOperator *BO); + ExprResult HandleBOFatPtrSubFatPtr(BinaryOperator *BO); + + ExprResult TransformUnaryOperator(UnaryOperator *UO); + ExprResult HandleUODerefFatPtr(UnaryOperator *UO); + ExprResult HandleUOAddrFatOnExprWithFatPtrBase(UnaryOperator *UO, Expr *FatPtrBaseE); + ExprResult HandleUOAddrFatOnLocalNoArrayVar(UnaryOperator *UO); + ExprResult HandleUOAddrFatOnArrayVar(UnaryOperator *UO, Expr *VarBaseE); + +private: + // Handle allocation unit for local vars whose address is taken by &fat. + CompoundStmt *HandleLocalAllocationUnit(CompoundStmt *CS, + bool IsTopLevelCompoundStmt); + + std::pair + BuildAllocationUnitHeaderForLocalNoArrayVars(SourceLocation SLoc); + + Expr *BuildLockVersionAssignmentForLocalNoArrayVars(SourceLocation SLoc); + + std::pair + BuildAllocationUnitForLocalArrayVar(VarDecl *VD); + + QualType DesugarFatPtrType(SourceLocation SLoc, QualType T); + MemberExpr *BuildMemberForFatPtr(Expr *FatPtrBaseE, std::string FieldName); + Expr *BuildFatPtrCall(SourceLocation SLoc, std::string FDName, + llvm::SmallVector &Args); + Expr *BuildFatPtrCompoundLiteralExprForRawPtr(Expr *RawPtrE, QualType QT); + Expr *BuildFatPtrCompoundLiteralExpr(Expr *FirstInitE, Expr *SecondInitE, + Expr *ThirdInitE, QualType QT); + bool IsAccessingArrayMemberThroughFatPtr(Expr *E); + void VisitExprToBuildOffsetOfComponent( + Expr *E, SourceLocation SLoc, + SmallVector &Comps); +}; + +void TransformFatPtr::TransformFunctionDecl() { + // Desugar fat qualified return type. + QualType ReturnTy = + DesugarFatPtrType(FD->getBeginLoc(), FD->getReturnType()); + // Desugar fat qualified parameters. + llvm::SmallVector ParamTys; + for (ParmVarDecl *PVD : FD->parameters()) { + QualType ParamTy = + DesugarFatPtrType(PVD->getLocation(), PVD->getType()); + PVD->setType(ParamTy); + ParamTys.push_back(ParamTy); + } + // Set desugared function type. + FD->setType(SemaRef.Context.getFunctionType( + ReturnTy, ParamTys, + FD->getType()->getAs()->getExtProtoInfo())); + + // First traversal: + // find local vars whose address is taken by `&fat` expr. + Finder.VisitStmt(FD->getBody()); + + // Second traversal + CompoundStmt *NewCS = + HandleLocalAllocationUnit(dyn_cast(FD->getBody()), true); + + // Third traversal: desugar stmt and expr of function body. + FD->setBody(TransformStmt(NewCS).get()); +} + +void TransformFatPtr::TransformVarDecl(VarDecl *VD) { + QualType QT = DesugarFatPtrType(VD->getLocation(), VD->getType()); + VD->setType(QT); + if (Expr *Init = VD->getInit()) { + if (QT->isFatPtrRecordType() && isa(Init)) { + // `int *fat p = nullptr;` should be desugared to + // `_FatPtr p = (_FatPtr) { nullptr, 0, 0 };` + VD->setInit(BuildFatPtrCompoundLiteralExprForRawPtr(Init, QT)); + } else { + VD->setInit(BaseTransform::TransformExpr(Init).get()); + } + } +} + +void TransformFatPtr::TransformRecordDecl(RecordDecl *RD) { + // Desugar fat ptr field. + for (FieldDecl *FD : RD->fields()) + FD->setType(DesugarFatPtrType(FD->getLocation(), FD->getType())); +} + +// 1. For all local vars whose type has no array and address is +// taken by &fat, we think they are packed in an allocation unit +// in stack and share a lock_version, so we build 2 VarDecl +// `_local_lock_version` and `_local_size` as allocation unit +// header and insert them to the beginning of function body. +// 2. Before function return, `_local_lock_version` should be set to +// 0, so we build assignment statement before return stmt and in the +// end of function body. +// 3. For local var whose type has array and address is taken by &fat, +// because every array have its own size, so we pack every var into +// a struct as allocation unit. +CompoundStmt * +TransformFatPtr::HandleLocalAllocationUnit(CompoundStmt *CS, + bool IsTopLevelCompoundStmt) { + if (CS == nullptr) + return CS; + llvm::SmallVector Statements; + if (IsTopLevelCompoundStmt && Finder.LocalNoArrayVarAddressIsFatTaken) { + std::pair Headers = + BuildAllocationUnitHeaderForLocalNoArrayVars(CS->getBeginLoc()); + Statements.push_back(Headers.second); + Statements.push_back(Headers.first); + } + if (Finder.LocalArrayVarsSet.size() == 0) { + for (auto *B : CS->body()) + Statements.push_back(B); + } else { + for (auto *B : CS->body()) { + if (auto ChildCS = dyn_cast(B)) { + Statements.push_back(HandleLocalAllocationUnit(ChildCS, false)); + } else if (auto DS = dyn_cast(B)) { + if (DS->isSingleDecl()) { + if (VarDecl *VD = dyn_cast(DS->getSingleDecl())) { + if (Finder.LocalArrayVarsSet.count(VD)) { + std::pair Decls = + BuildAllocationUnitForLocalArrayVar(VD); + Statements.push_back(Decls.first); + Statements.push_back(Decls.second); + continue; + } + } + Statements.push_back(B); + } else { + // TODO: a DeclStmt contains more than one VarDecl, we should + // divide it into several DeclStmts. + Statements.push_back(B); + } + } else if (isa(B) && Finder.LocalNoArrayVarAddressIsFatTaken) { + Statements.push_back( + BuildLockVersionAssignmentForLocalNoArrayVars(B->getBeginLoc())); + Statements.push_back(B); + } else { + Statements.push_back(B); + } + } + } + if (IsTopLevelCompoundStmt && Finder.LocalNoArrayVarAddressIsFatTaken) + Statements.push_back( + BuildLockVersionAssignmentForLocalNoArrayVars(CS->getEndLoc())); + auto NewCS = CompoundStmt::Create( + SemaRef.Context, Statements, FPOptionsOverride(), CS->getLBracLoc(), + CS->getRBracLoc(), CS->getSafeSpecifier(), CS->getSafeSpecifierLoc(), + CS->getCompSafeZoneSpecifier()); + return NewCS; +} + +// Build allocation unit header for all local vars whose +// address is taken by `&fat`: +// @code +// uint32_t _local_size = 0; +// uint32_t _local_lock_version = _new_version_number(); +// @endcode +std::pair +TransformFatPtr::BuildAllocationUnitHeaderForLocalNoArrayVars( + SourceLocation SLoc) { + QualType UInt32Ty = SemaRef.Context.getIntTypeForBitwidth(32, false); + // Build VarDecl: uint32_t _local_lock_version = _new_version_number(); + VarDecl *LockVersionVD = + VarDecl::Create(SemaRef.Context, FD, SLoc, SLoc, + &SemaRef.Context.Idents.get("_local_lock_version"), + UInt32Ty, nullptr, SC_None); + llvm::SmallVector Args; + LockVersionVD->setInit(BuildFatPtrCall(SLoc, "_new_version_number", Args)); + DeclStmt *LockVersionDS = + new (SemaRef.Context) DeclStmt(DeclGroupRef(LockVersionVD), SLoc, SLoc); + LocalLockVersionVD = LockVersionVD; + + // Build VarDecl: uint32_t _local_size = 0; + VarDecl *SizeVD = VarDecl::Create(SemaRef.Context, FD, SLoc, SLoc, + &SemaRef.Context.Idents.get("_local_size"), + UInt32Ty, nullptr, SC_None); + SizeVD->setInit(IntegerLiteral::Create(SemaRef.Context, llvm::APInt(32, 0), + UInt32Ty, SLoc)); + DeclStmt *SizeDS = + new (SemaRef.Context) DeclStmt(DeclGroupRef(SizeVD), SLoc, SLoc); + + return std::make_pair(LockVersionDS, SizeDS); +} + +// Build allocation unit RecordDecl for every local array var whose element +// address is taken by &fat, for example, if we have an array variable whose +// VarDecl is `int arr[3] = {1, 2, 3};`, we desugar this decl to: +// @code +// struct _arr_AllocationUnit { +// uint32_t lock_version; +// uint32_t size; +// int arr[3]; +// }; +// struct _arr_AllocationUnit _arr = +// { _new_version_number(), 12, {1, 2, 3} }; +// @endcode +std::pair +TransformFatPtr::BuildAllocationUnitForLocalArrayVar(VarDecl *VD) { + SourceLocation SLoc = VD->getLocation(); + // Build RecordDecl: struct _arr_AllocationUnit; + RecordDecl *RD = RecordDecl::Create( + SemaRef.Context, TagDecl::TagKind::TTK_Struct, FD, SLoc, SLoc, + &SemaRef.Context.Idents.get("_" + VD->getNameAsString() + + "_AllocationUnit")); + // Add three Field + RD->startDefinition(); + QualType UInt32Ty = SemaRef.Context.getIntTypeForBitwidth(32, false); + FieldDecl *LockVersionField = FieldDecl::Create( + SemaRef.Context, RD, SLoc, SLoc, &SemaRef.Context.Idents.get("lock_version"), + UInt32Ty, SemaRef.Context.getTrivialTypeSourceInfo(UInt32Ty), + nullptr, false, ICIS_NoInit); + FieldDecl *SizeField = FieldDecl::Create( + SemaRef.Context, RD, SLoc, SLoc, &SemaRef.Context.Idents.get("size"), + UInt32Ty, SemaRef.Context.getTrivialTypeSourceInfo(UInt32Ty), + nullptr, false, ICIS_NoInit); + FieldDecl *VDField = FieldDecl::Create( + SemaRef.Context, RD, SLoc, SLoc, + &SemaRef.Context.Idents.get(VD->getNameAsString()), VD->getType(), + SemaRef.Context.getTrivialTypeSourceInfo(VD->getType()), + nullptr, false, ICIS_NoInit); + LockVersionField->setAccess(AS_public); + SizeField->setAccess(AS_public); + VDField->setAccess(AS_public); + RD->addDecl(LockVersionField); + RD->addDecl(SizeField); + RD->addDecl(VDField); + RD->completeDefinition(); + + // Build VarDecl: struct _arr_AllocationUnit _arr; + VarDecl *NewVD = + VarDecl::Create(SemaRef.Context, FD, SLoc, SLoc, + &SemaRef.Context.Idents.get("_" + VD->getNameAsString()), + QualType(RD->getTypeForDecl(), 0), nullptr, SC_None); + // Build InitExpr + // Build first init expr: _ner_version_number() + llvm::SmallVector Args; + Expr *LockVersionInit = BuildFatPtrCall(SLoc, "_new_version_number", Args); + // Build second init expr by array size + Expr *SizeInit = + IntegerLiteral::Create(SemaRef.Context, + llvm::APInt(32, SemaRef.Context.getTypeSize(VD->getType()) / 8), + UInt32Ty, SLoc); + // Build InitListExpr + SmallVector Inits{LockVersionInit, SizeInit}; + if (VD->getInit()) + Inits.push_back(VD->getInit()); + Expr *ILE = SemaRef.BuildInitList(SLoc, Inits, SLoc).get(); + ILE->setType(QualType(RD->getTypeForDecl(), 0)); + NewVD->setInit(ILE); + AllocationUnitForLocalArrayVarMap[VD] = std::make_pair(RD, NewVD); + DeclStmt *RDDS = new (SemaRef.Context) DeclStmt(DeclGroupRef(RD), SLoc, SLoc); + DeclStmt *NewVDDS = new (SemaRef.Context) DeclStmt(DeclGroupRef(NewVD), SLoc, SLoc); + return std::make_pair(RDDS, NewVDDS); +} + +// Build assignment expr: _local_lock_version = 0; +Expr *TransformFatPtr::BuildLockVersionAssignmentForLocalNoArrayVars( + SourceLocation SLoc) { + if (LocalLockVersionVD) { + QualType VQT = LocalLockVersionVD->getType(); + Expr *LHS = + SemaRef.BuildDeclRefExpr(LocalLockVersionVD, VQT, VK_LValue, SLoc); + Expr *RHS = + IntegerLiteral::Create(SemaRef.Context, llvm::APInt(32, 0), VQT, SLoc); + return SemaRef.CreateBuiltinBinOp(SLoc, BO_Assign, LHS, RHS).get(); + } + return nullptr; +} + +StmtResult TransformFatPtr::TransformCompoundStmt(CompoundStmt *CS) { + return this->TransformCompoundStmt(CS, false); +} + +StmtResult TransformFatPtr::TransformCompoundStmt(CompoundStmt *CS, + bool IsStmtExpr) { + if (CS == nullptr) + return CS; + llvm::SmallVector Statements; + for (auto *B : CS->body()) { + StmtResult Result = BaseTransform::TransformStmt(B); + while (!CheckCallExpr.empty()) { + Statements.push_back(CheckCallExpr.top()); + CheckCallExpr.pop(); + } + Statements.push_back(Result.getAs()); + } + auto NewCS = CompoundStmt::Create( + SemaRef.Context, Statements, FPOptionsOverride(), CS->getLBracLoc(), + CS->getRBracLoc(), CS->getSafeSpecifier(), CS->getSafeSpecifierLoc(), + CS->getCompSafeZoneSpecifier()); + return NewCS; +} + +ExprResult TransformFatPtr::TransformDeclRefExpr(DeclRefExpr *DRE) { + SourceLocation SLoc = DRE->getBeginLoc(); + QualType QT = DesugarFatPtrType(SLoc, DRE->getType()); + DRE->setType(QT); + if (QT->isFatPtrRecordType()) { + if (InsertCheckOffsetCall.first) { + // Build CallExpr to check offset. + llvm::SmallVector Args{DRE, InsertCheckOffsetCall.second}; + Expr *CE = BuildFatPtrCall(SLoc, "_check_offset", Args); + CheckCallExpr.push(CE); + InsertCheckOffsetCall.first = false; + InsertCheckOffsetCall.second = nullptr; + } + if (InsertCheckVersionCall) { + // Build CallExpr to check version. + llvm::SmallVector Args{DRE}; + Expr *CE = BuildFatPtrCall(SLoc, "_check_version", Args); + CheckCallExpr.push(CE); + InsertCheckVersionCall = false; + } + if (DesugarToMemberPtr) { + DesugarToMemberPtr = false; + // Desugar p to p.raw_ptr + return BuildMemberForFatPtr(DRE, "raw_ptr"); + } + } else if (auto VD = dyn_cast(DRE->getDecl())) { + if (AllocationUnitForLocalArrayVarMap.count(VD) || + SemaRef.Context.BSCDesugaredMap.count(VD)) { + // Desugar arr to _arr.arr where arr has its owned AllocationUnit. + RecordDecl *AllocationUnitRD; + VarDecl *AllocationUnitVD; + if (AllocationUnitForLocalArrayVarMap.count(VD)) { + AllocationUnitRD = AllocationUnitForLocalArrayVarMap[VD].first; + AllocationUnitVD = AllocationUnitForLocalArrayVarMap[VD].second; + } else { + AllocationUnitRD = + dyn_cast(SemaRef.Context.BSCDesugaredMap[VD][0]); + AllocationUnitVD = + dyn_cast(SemaRef.Context.BSCDesugaredMap[VD][1]); + } + Expr *AllocationUnitVDRef = SemaRef.BuildDeclRefExpr( + AllocationUnitVD, AllocationUnitVD->getType(), VK_LValue, SLoc); + RecordDecl::field_iterator it = AllocationUnitRD->field_begin(); + it++; + it++; + FieldDecl *ArrayFD = *it; + return SemaRef.BuildMemberExpr( + AllocationUnitVDRef, false, SLoc, NestedNameSpecifierLoc(), SLoc, + ArrayFD, DeclAccessPair::make(AllocationUnitRD, ArrayFD->getAccess()), + false, DeclarationNameInfo(), ArrayFD->getType(), VK_LValue, + OK_Ordinary); + } + } + return DRE; +} + +ExprResult TransformFatPtr::TransformImplicitCastExpr(ImplicitCastExpr *ICE) { + QualType QT = ICE->getType(); + if (DesugarToMemberPtr && QT.isFatPtrType()) { + QT = QT->getFatPtrPointeeType(); + ICE->setType(SemaRef.Context.getPointerType(QT)); + ICE->setCastKind(CK_LValueToRValue); + } else { + QT = DesugarFatPtrType(ICE->getBeginLoc(), QT); + ICE->setType(QT); + } + Expr *SubE = ICE->getSubExpr(); + ICE->setSubExpr(BaseTransform::TransformExpr(SubE).get()); + if (ICE->getCastKind() == CK_NoOp && QT->isFatPtrRecordType() && + SubE->getType()->isFatPtrRecordType()) + ICE->setType(SubE->getType()); + return ICE; +} + +ExprResult TransformFatPtr::TransformParenExpr(ParenExpr *PE) { + QualType QT = PE->getType(); + if (DesugarToMemberPtr && QT.isFatPtrType()) { + QT = QT->getFatPtrPointeeType(); + PE->setType(SemaRef.Context.getPointerType(QT)); + } else { + QT = DesugarFatPtrType(PE->getBeginLoc(), QT); + PE->setType(QT); + } + PE->setSubExpr(BaseTransform::TransformExpr(PE->getSubExpr()).get()); + return PE; +} + +StmtResult TransformFatPtr::TransformDeclStmt(DeclStmt *DS) { + for (auto D : DS->decls()) { + if (VarDecl *VD = dyn_cast(D)) + TransformVarDecl(VD); + else if (RecordDecl *RD = dyn_cast(D)) + TransformRecordDecl(RD); + else + BaseTransform::TransformDefinition(D->getLocation(), D); + } + return DS; +} + +ExprResult +TransformFatPtr::TransformCompoundLiteralExpr(CompoundLiteralExpr *CLE) { + CLE->setType(DesugarFatPtrType(CLE->getBeginLoc(), CLE->getType())); + CLE->setInitializer( + BaseTransform::TransformExpr(CLE->getInitializer()).get()); + return CLE; +} + +ExprResult TransformFatPtr::TransformInitListExpr(InitListExpr *ILE) { + ILE->setType(DesugarFatPtrType(ILE->getBeginLoc(), ILE->getType())); + for (unsigned i = 0; i < ILE->getNumInits(); i++) + ILE->setInit(i, BaseTransform::TransformExpr(ILE->getInit(i)).get()); + return ILE; +} + +StmtResult TransformFatPtr::TransformReturnStmt(ReturnStmt *RS) { + Expr *RV = RS->getRetValue(); + QualType ReturnQT = FD->getReturnType(); + if (ReturnQT->isFatPtrRecordType() && isa(RV)) { + // if a function has fat pointer return type, such as `int *fat foo();` + // `return nullptr;` should be desugared to + // `return (_FatPtr){ nullptr, 0, 0 };` + RS->setRetValue(BuildFatPtrCompoundLiteralExprForRawPtr(RV, ReturnQT)); + } else { + RS->setRetValue(BaseTransform::TransformExpr(RV).get()); + } + return RS; +} + +ExprResult TransformFatPtr::TransformCallExpr(CallExpr *CE) { + CE->setType(DesugarFatPtrType(CE->getBeginLoc(), CE->getType())); + FunctionDecl *CalleeFD = CE->getDirectCallee(); + for (unsigned i = 0; i < CalleeFD->getNumParams(); i++) { + QualType ParamQT = CalleeFD->getParamDecl(i)->getType(); + Expr *ArgE = CE->getArg(i); + if (ParamQT->isFatPtrRecordType() && isa(ArgE)) { + // if a function has fat pointer parameter, such as + // `void foo(int *fat p);` + // `foo(nullptr)` should be desugared to + // `foo((_FatPtr){ nullptr, 0, 0 })` + CE->setArg(i, BuildFatPtrCompoundLiteralExprForRawPtr(ArgE, ParamQT)); + } else + CE->setArg(i, BaseTransform::TransformExpr(ArgE).get()); + } + return CE; +} + +ExprResult +TransformFatPtr::TransformArraySubscriptExpr(ArraySubscriptExpr *ASE) { + SourceLocation SLoc = ASE->getBeginLoc(); + QualType QT = DesugarFatPtrType(SLoc, ASE->getType()); + ASE->setType(QT); + + Expr *LHS = ASE->getLHS(); + Expr *RHS = ASE->getRHS(); + QualType LHSQT = LHS->getType(); + if (LHSQT.isFatPtrType()) { + // p[i] should be desugared to `p.raw_ptr[i]` and insert check call expr. + ASE->setRHS(BaseTransform::TransformExpr(RHS).get()); + DesugarToMemberPtr = true; + InsertCheckVersionCall = true; + InsertCheckOffsetCall.first = true; + // Build offset arg expr, for example: + // for p[i] where p[i] is int type, + // offset should be `i * sizeof(int) + sizeof(int)` + QualType Int32Ty = SemaRef.Context.getIntTypeForBitwidth(32, true); + Expr *OffsetExprLHS = + IntegerLiteral::Create(SemaRef.Context, + llvm::APInt(32, SemaRef.Context.getTypeSize(QT) / 8), Int32Ty, SLoc); + OffsetExprLHS = + SemaRef.CreateBuiltinBinOp(SLoc, BO_Mul, ASE->getRHS(), OffsetExprLHS).get(); + Expr *OffsetExprRHS = + IntegerLiteral::Create(SemaRef.Context, + llvm::APInt(32, SemaRef.Context.getTypeSize(QT) / 8), Int32Ty, SLoc); + Expr *OffsetExpr = + SemaRef.CreateBuiltinBinOp(SLoc, BO_Add, OffsetExprLHS, OffsetExprRHS).get(); + InsertCheckOffsetCall.second = OffsetExpr; + ASE->setLHS(BaseTransform::TransformExpr(LHS).get()); + return ASE; + } else if (IsAccessingArrayMemberThroughFatPtr(ASE)) { + // If current ASE is accessing array member through fat ptr, + // such as `p->arr[i]` or `p->b.arr[i]`, in order to calculate + // offset when transform inner MemberEXpr, we record current ASE. + CurrentASEToAccessMemberThroughFatPtr = ASE; + } + ASE->setLHS(BaseTransform::TransformExpr(LHS).get()); + ASE->setRHS(BaseTransform::TransformExpr(RHS).get()); + return ASE; +} + +ExprResult TransformFatPtr::TransformCStyleCastExpr(CStyleCastExpr *CSCE) { + SourceLocation SLoc = CSCE->getBeginLoc(); + QualType QT = CSCE->getType(); + Expr *SubE = CSCE->getSubExpr(); + QualType SubQT = SubE->getType(); + if (QT->isPointerType() && !QT.isFatPtrType() && SubQT.isFatPtrType()) { + // desuagr `(int*)fat_p` to `(int*)fat_p.raw_ptr` + DesugarToMemberPtr = true; + InsertCheckVersionCall = true; + InsertCheckOffsetCall.first = true; + // Build offset arg expr + Expr *OffsetExpr = + IntegerLiteral::Create(SemaRef.Context, llvm::APInt(32, 1), + SemaRef.Context.getIntTypeForBitwidth(32, true), SLoc); + InsertCheckOffsetCall.second = OffsetExpr; + } else if (QT.isFatPtrType() && SubQT->isPointerType() && !SubQT.isFatPtrType()) { + QT = DesugarFatPtrType(SLoc, QT); + // desugar `(int *fat)raw_p` to `(_FatPtr) { raw_p, 0, 0 }` + Expr *SubRawPtrE = BaseTransform::TransformExpr(SubE).get(); + return BuildFatPtrCompoundLiteralExprForRawPtr(SubRawPtrE, QT); + } else if (QT.isFatPtrType() && SubQT.isFatPtrType()) { + return BaseTransform::TransformExpr(SubE).get(); + } + CSCE->setSubExpr(BaseTransform::TransformExpr(SubE).get()); + return CSCE; +} + +ExprResult TransformFatPtr::TransformUnaryExprOrTypeTraitExpr( + UnaryExprOrTypeTraitExpr *UETT) { + SourceLocation SLoc = UETT->getBeginLoc(); + QualType QT = DesugarFatPtrType(SLoc, UETT->getType()); + UETT->setType(QT); + // Recalculate size of fat ptr which is larger than raw ptr: + // `sizeof(int *)` equals to 8, but `sizeof(int *fat)` equals to 16 + if (UETT->isArgumentType()) { + QualType OldQT = UETT->getArgumentType(); + if (OldQT.isFatPtrType()) { + QualType NewQT = DesugarFatPtrType(SLoc, OldQT); + TypeSourceInfo *NewTInfo = + SemaRef.Context.getTrivialTypeSourceInfo(NewQT, SLoc); + return BaseTransform::RebuildUnaryExprOrTypeTrait( + NewTInfo, UETT->getOperatorLoc(), UETT->getKind(), + UETT->getSourceRange()); + } + } + return UETT; +} + +// BinaryOperator includes: +// reassign fat ptr : p1 = p2, p1 = nullptr +// calculation between fat ptr : p + 1, p - 1, p1 - p2 +ExprResult TransformFatPtr::TransformBinaryOperator(BinaryOperator *BO) { + SourceLocation SLoc = BO->getBeginLoc(); + BO->setType(DesugarFatPtrType(SLoc, BO->getType())); + Expr *LHS = BO->getLHS(); + Expr *RHS = BO->getRHS(); + QualType LHSQT = LHS->getType(); + QualType RHSQT = RHS->getType(); + BinaryOperator::Opcode Op = BO->getOpcode(); + if (Op == BO_Sub && LHSQT.isFatPtrType() && RHSQT.isFatPtrType()) { + // Substraction between two fat ptr + // `p1 - p2` should be desugared to `p1.raw_ptr - p2.raw_ptr` + return HandleBOFatPtrSubFatPtr(BO); + } else if (BO->isAdditiveOp() && LHSQT.isFatPtrType() && RHSQT->isIntegerType()) { + // addition or substraction between fat ptr and integer + // `p + 1` should be desugared to + // `(_FatPtr) { p.raw_ptr + 1, p.key_version, p.offset + 1 * sizeof(int) }` + return HandleBOFatPtrAddOrSubInteger(BO); + } else if (BO->isAssignmentOp() && LHSQT.isFatPtrType() && + isa(RHS)) { + // `p = nullptr;` should be desugared to + // `p = (_FatPtr){ nullptr, 0, 0 };` + LHSQT = DesugarFatPtrType(SLoc, LHSQT); + BO->setLHS(BaseTransform::TransformExpr(LHS).get()); + BO->setRHS(BuildFatPtrCompoundLiteralExprForRawPtr(RHS, LHSQT)); + return BO; + } + BO->setLHS(BaseTransform::TransformExpr(LHS).get()); + BO->setRHS(BaseTransform::TransformExpr(RHS).get()); + return BO; +} + +ExprResult TransformFatPtr::HandleBOFatPtrSubFatPtr(BinaryOperator *BO) { + DesugarToMemberPtr = true; + BO->setLHS(BaseTransform::TransformExpr(BO->getLHS()).get()); + DesugarToMemberPtr = true; + BO->setRHS(BaseTransform::TransformExpr(BO->getRHS()).get()); + return BO; +} + +// addition or substraction between fat ptr and integer +// `p + 1` should be desugared to +// `(_FatPtr) { p.ptr + 1, p.key_version, p.offset + 1 * sizeof(int) }` +ExprResult TransformFatPtr::HandleBOFatPtrAddOrSubInteger(BinaryOperator *BO) { + SourceLocation SLoc = BO->getBeginLoc(); + QualType QT = BO->getType(); + BinaryOperator::Opcode Op = BO->getOpcode(); + Expr *FatPtrLHS = BO->getLHS()->IgnoreImpCasts(); + DesugarToMemberPtr = true; + Expr *LHS = BaseTransform::TransformExpr(BO->getLHS()).get(); + Expr *RHS = BaseTransform::TransformExpr(BO->getRHS()).get(); + Expr *FirstInitE = + SemaRef.CreateBuiltinBinOp(SLoc, Op, LHS, RHS).get(); + Expr *SecondInitE = BuildMemberForFatPtr(FatPtrLHS, "key_version"); + + Expr *ThirdInitLHS = BuildMemberForFatPtr(FatPtrLHS, "offset"); + QualType PointeeQT = FatPtrLHS->getType()->getFatPtrPointeeType(); + Expr *SizeOfExpr = + SemaRef + .CreateUnaryExprOrTypeTraitExpr( + SemaRef.Context.getTrivialTypeSourceInfo(PointeeQT), + SLoc, UETT_SizeOf, SourceRange()) + .get(); + Expr *ThirdInitRHS = + SemaRef.CreateBuiltinBinOp(SLoc, BO_Mul, RHS, SizeOfExpr).get(); + Expr *ThirdInitE = + SemaRef.CreateBuiltinBinOp(SLoc, Op, ThirdInitLHS, ThirdInitRHS).get(); + return BuildFatPtrCompoundLiteralExpr(FirstInitE, SecondInitE, ThirdInitE, QT); +} + +ExprResult TransformFatPtr::TransformUnaryOperator(UnaryOperator *UO) { + QualType QT = DesugarFatPtrType(UO->getBeginLoc(), UO->getType()); + UO->setType(QT); + + UnaryOperator::Opcode Op = UO->getOpcode(); + Expr *SubE = UO->getSubExpr(); + QualType SubQT = SubE->getType(); + if (Op == UO_Deref && SubQT.isFatPtrType()) { + // handle *p if p is fat ptr. + return HandleUODerefFatPtr(UO); + } else if (Op == UO_AddrFat) { + // handle &fat expr. + AddrFatExprBaseKind BaseKind = Finder.AddrFatExprAndBaseMap[UO].first; + switch (BaseKind) { + case AddrFatExprBaseKind::FatPtr: + return HandleUOAddrFatOnExprWithFatPtrBase(UO, Finder.AddrFatExprAndBaseMap[UO].second); + case AddrFatExprBaseKind::LocalNoArrayVar: + return HandleUOAddrFatOnLocalNoArrayVar(UO); + case AddrFatExprBaseKind::LocalArrayVar: + case AddrFatExprBaseKind::GlobalArrayVar: + return HandleUOAddrFatOnArrayVar(UO, Finder.AddrFatExprAndBaseMap[UO].second); + default: + break; + } + } + UO->setSubExpr(BaseTransform::TransformExpr(SubE).get()); + return UO; +} + +ExprResult TransformFatPtr::HandleUODerefFatPtr(UnaryOperator *UO) { + Expr *SubE = UO->getSubExpr(); + DesugarToMemberPtr = true; + InsertCheckVersionCall = true; + InsertCheckOffsetCall.first = true; + // Build offset arg expr, for example: + // for *p, offset should be `sizeof(int)` + Expr *OffsetExpr = + SemaRef + .CreateUnaryExprOrTypeTraitExpr( + SemaRef.Context.getTrivialTypeSourceInfo(UO->getType()), + UO->getBeginLoc(), UETT_SizeOf, SourceRange()) + .get(); + InsertCheckOffsetCall.second = OffsetExpr; + UO->setSubExpr(BaseTransform::TransformExpr(SubE).get()); + return UO; +} + +// if p is fat ptr of struct G, +// 1. `int *fat p1 = &fat p->a;` should be desugared to +// @code +// _FatPtr p1 = (_FatPtr) { &p.ptr->a, p.key_version, +// (int32_t)((uint8_t *)&p.ptr->a - (uint8_t *)p.ptr + p.offset }; +// @endcode +// 2. `struct G *fat p1 = &fat p[i];` should be desugared to +// @code +// _FatPtr p1 = (_FatPtr) { &p.ptr[i], p.key_version, +// (int32_t)((uint8_t *)&p.ptr[i] - (uint8_t *)p.ptr) + p.offset }; +// @endcode +ExprResult TransformFatPtr::HandleUOAddrFatOnExprWithFatPtrBase( + UnaryOperator *UO, Expr *FatPtrBaseE) { + SourceLocation SLoc = UO->getBeginLoc(); + QualType QT = UO->getType(); + Expr *SubE = BaseTransform::TransformExpr(UO->getSubExpr()).get(); + + // Build first init expr: &p.raw_ptr->a + Expr *FirstInitE = SemaRef.CreateBuiltinUnaryOp(SLoc, UO_AddrOf, SubE).get(); + // Build second init expr: p.key_version + Expr *SecondInitE = BuildMemberForFatPtr(FatPtrBaseE, "key_version"); + // Build third init expr: + // `(int32_t)((uint8_t *)&p.ptr->a - (uint8_t *)p.ptr + p.offset` + QualType UInt8PointerTy = SemaRef.Context.getPointerType( + SemaRef.Context.getIntTypeForBitwidth(8, false)); + Expr *ThirdInitLHSLHS = + SemaRef + .BuildCStyleCastExpr( + SLoc, SemaRef.Context.getTrivialTypeSourceInfo(UInt8PointerTy), + SLoc, FirstInitE) + .get(); + Expr *ThirdInitLHSRHS = + SemaRef + .BuildCStyleCastExpr( + SLoc, SemaRef.Context.getTrivialTypeSourceInfo(UInt8PointerTy), + SLoc, BuildMemberForFatPtr(FatPtrBaseE, "raw_ptr")) + .get(); + Expr *ThirdInitLHS = + SemaRef + .CreateBuiltinBinOp(SLoc, BO_Sub, ThirdInitLHSLHS, ThirdInitLHSRHS) + .get(); + QualType Int32Ty = SemaRef.Context.getIntTypeForBitwidth(32, true); + ThirdInitLHS = + SemaRef + .BuildCStyleCastExpr( + SLoc, SemaRef.Context.getTrivialTypeSourceInfo(Int32Ty), SLoc, + ThirdInitLHS) + .get(); + Expr *ThirdInitRHS = BuildMemberForFatPtr(FatPtrBaseE, "offset"); + Expr *ThirdInitE = + SemaRef.CreateBuiltinBinOp(SLoc, BO_Add, ThirdInitLHS, ThirdInitRHS).get(); + return BuildFatPtrCompoundLiteralExpr(FirstInitE, SecondInitE, ThirdInitE, QT); +} + +// if s is local variable whose type has no array, +// 1.`struct S *fat p = &fat s;` should be desugared to +// @code +// _FatPtr p = (_FatPtr) { &s, _local_lock_version, +// (int32_t)((uint8_t *)&a - (uint8_t *)&_local_lock_version) - 8 };` +// @endcode +// 2. `int *fat p = &fat s.a;` should be desugared to +// @code +// _FatPtr p = (_FatPtr) { &s.a, _local_lock_version, +// (int32_t)((uint8_t *)&s.a - (uint8_t *)&_local_lock_version) - 8 };` +// @endcode +ExprResult +TransformFatPtr::HandleUOAddrFatOnLocalNoArrayVar(UnaryOperator *UO) { + SourceLocation SLoc = UO->getBeginLoc(); + QualType QT = UO->getType(); + Expr *SubE = BaseTransform::TransformExpr(UO->getSubExpr()).get(); + // Build first init expr: &a + Expr *FirstInitE = SemaRef.CreateBuiltinUnaryOp(SLoc, UO_AddrOf, SubE).get(); + // Build second init expr: _local_version_lock + Expr *SecondInitE = SemaRef.BuildDeclRefExpr( + LocalLockVersionVD, LocalLockVersionVD->getType(), VK_LValue, SLoc); + // Build third init expr: + // `(int32_t)((uint8_t *)&a - (uint8_t *)&_local_lock_version) - 8` + QualType UInt8PointerTy = SemaRef.Context.getPointerType( + SemaRef.Context.getIntTypeForBitwidth(8, false)); + Expr *ThirdInitLHSLHS = + SemaRef + .BuildCStyleCastExpr( + SLoc, SemaRef.Context.getTrivialTypeSourceInfo(UInt8PointerTy), + SLoc, FirstInitE) + .get(); + Expr *ThirdInitLHSRHS = + SemaRef.CreateBuiltinUnaryOp(SLoc, UO_AddrOf, SecondInitE).get(); + ThirdInitLHSRHS = + SemaRef + .BuildCStyleCastExpr( + SLoc, SemaRef.Context.getTrivialTypeSourceInfo(UInt8PointerTy), + SLoc, ThirdInitLHSRHS) + .get(); + Expr *ThirdInitLHS = + SemaRef + .CreateBuiltinBinOp(SLoc, BO_Sub, ThirdInitLHSLHS,ThirdInitLHSRHS) + .get(); + QualType Int32Ty = SemaRef.Context.getIntTypeForBitwidth(32, true); + ThirdInitLHS = + SemaRef + .BuildCStyleCastExpr( + SLoc, SemaRef.Context.getTrivialTypeSourceInfo(Int32Ty), SLoc, + ThirdInitLHS) + .get(); + Expr *ThirdInitRHS = IntegerLiteral::Create( + SemaRef.Context, llvm::APInt(32, 8), Int32Ty, SLoc); + Expr *ThirdInitE = + SemaRef + .CreateBuiltinBinOp(SLoc, BO_Sub, ThirdInitLHS, ThirdInitRHS) + .get(); + return BuildFatPtrCompoundLiteralExpr(FirstInitE, SecondInitE, ThirdInitE, QT); +} + +// 1. if arr is array variable, +// `int *fat p = &fat arr[5]` should be desugared to +// @code +// _FatPtr p = (_FatPtr) { &_arr.arr[5], _arr.lock_version, +// (int32_t)((uint8_t *)&_arr.arr[5] - (uint8_t *)&_arr.lock_version) - 8 }; +// @endcode +// 2. if s is variable whose type is `struct G { int a; int arr[3]; }` +// `int *fat p = &fat s.a` should be desugared to +// @code +// _FatPtr p = (_FatPtr) { &_s.s.a, _s.lock_version, +// (int32_t)((uint8_t *)&_s.s.a - (uint8_t *)&_s.lock_version) - 8 }; +// @endcode +ExprResult TransformFatPtr::HandleUOAddrFatOnArrayVar( + UnaryOperator *UO, Expr *VarBaseE) { + SourceLocation SLoc = UO->getBeginLoc(); + QualType QT = UO->getType(); + Expr *SubE = BaseTransform::TransformExpr(UO->getSubExpr()).get(); + // Build first init expr: &_arr.arr[5] + Expr *FirstInitE = + SemaRef.CreateBuiltinUnaryOp(SLoc, UO_AddrOf, SubE).get(); + // Build second init expr: _arr.lock_version + RecordDecl *AllocationUnitRD = nullptr; + VarDecl *AllocationUnitVD = nullptr; + auto DRE = dyn_cast(VarBaseE); + auto VD = dyn_cast(DRE->getDecl()); + if (AllocationUnitForLocalArrayVarMap.count(VD)) { + // VD is local var + AllocationUnitRD = AllocationUnitForLocalArrayVarMap[VD].first; + AllocationUnitVD = AllocationUnitForLocalArrayVarMap[VD].second; + } else if (SemaRef.Context.BSCDesugaredMap.count(VD)) { + // VD is global var + AllocationUnitRD = + dyn_cast(SemaRef.Context.BSCDesugaredMap[VD][0]); + AllocationUnitVD = + dyn_cast(SemaRef.Context.BSCDesugaredMap[VD][1]); + } + FieldDecl *LockVersionFD = *(AllocationUnitRD->field_begin()); + Expr *AllocationUnitVDRef = SemaRef.BuildDeclRefExpr( + AllocationUnitVD, AllocationUnitVD->getType(), VK_LValue, SLoc); + Expr *SecondInitE = SemaRef.BuildMemberExpr( + AllocationUnitVDRef, false, SLoc, NestedNameSpecifierLoc(), SLoc, LockVersionFD, + DeclAccessPair::make(AllocationUnitRD, LockVersionFD->getAccess()), false, + DeclarationNameInfo(), LockVersionFD->getType(), VK_LValue, OK_Ordinary); + // Build third init expr: 5 * size(int); + QualType UInt8PointerTy = SemaRef.Context.getPointerType( + SemaRef.Context.getIntTypeForBitwidth(8, false)); + Expr *ThirdInitLHSLHS = + SemaRef + .BuildCStyleCastExpr( + SLoc, SemaRef.Context.getTrivialTypeSourceInfo(UInt8PointerTy), + SLoc, FirstInitE) + .get(); + Expr *ThirdInitLHSRHS = + SemaRef.CreateBuiltinUnaryOp(SLoc, UO_AddrOf, SecondInitE).get(); + ThirdInitLHSRHS = + SemaRef + .BuildCStyleCastExpr( + SLoc, SemaRef.Context.getTrivialTypeSourceInfo(UInt8PointerTy), + SLoc, ThirdInitLHSRHS) + .get(); + Expr *ThirdInitLHS = + SemaRef.CreateBuiltinBinOp(SLoc, BO_Sub, ThirdInitLHSLHS, ThirdInitLHSRHS).get(); + QualType Int32Ty = SemaRef.Context.getIntTypeForBitwidth(32, true); + ThirdInitLHS = + SemaRef + .BuildCStyleCastExpr( + SLoc, SemaRef.Context.getTrivialTypeSourceInfo(Int32Ty), SLoc, + ThirdInitLHS) + .get(); + Expr *ThirdInitRHS = IntegerLiteral::Create( + SemaRef.Context, llvm::APInt(32, 8), Int32Ty, SLoc); + Expr *ThirdInitE = + SemaRef.CreateBuiltinBinOp(SLoc, BO_Sub, ThirdInitLHS, ThirdInitRHS).get(); + return BuildFatPtrCompoundLiteralExpr(FirstInitE, SecondInitE, ThirdInitE, QT); +} + +ExprResult TransformFatPtr::TransformMemberExpr(MemberExpr *ME) { + SourceLocation SLoc = ME->getBeginLoc(); + QualType QT = DesugarFatPtrType(SLoc, ME->getType()); + ME->setType(QT); + + MemberExpr *MemberPtr = nullptr; + if (QT->isFatPtrRecordType()) { + if (InsertCheckOffsetCall.first) { + llvm::SmallVector Args{ME, InsertCheckOffsetCall.second}; + Expr *CE = BuildFatPtrCall(SLoc, "_check_offset", Args); + CheckCallExpr.push(CE); + InsertCheckOffsetCall.first = false; + InsertCheckOffsetCall.second = nullptr; + } + if (InsertCheckVersionCall) { + llvm::SmallVector Args{ME}; + Expr *CE = BuildFatPtrCall(SLoc, "_check_version", Args); + CheckCallExpr.push(CE); + InsertCheckVersionCall = false; + } + if (DesugarToMemberPtr) { + // Desugar s.p to s.p.raw_ptr + MemberPtr = BuildMemberForFatPtr(ME, "raw_ptr"); + DesugarToMemberPtr = false; + } + } + QualType BaseType = ME->getBase()->getType(); + if (ME->isArrow() && BaseType.isFatPtrType()) { + DesugarToMemberPtr = true; + InsertCheckVersionCall = true; + InsertCheckOffsetCall.first = true; + // Build offset arg expr, for example: + // 1. for p->arr[3] where p is fat ptr and p->arr[3] is int type, + // offset is `__builtin_offsetof(struct S, arr[3]) + sizeof(int)` + // 2. for p->a, where p is fat ptr and p->a is int type, + // offset is `__builtin_offsetof(struct S, a) + sizeof(int)` + QualType PointeeQT = BaseType->getFatPtrPointeeType(); + TypeSourceInfo *PointeeTInfo = + SemaRef.Context.getTrivialTypeSourceInfo(PointeeQT, SLoc); + QualType ElementType; + SmallVector Comps; + if (CurrentASEToAccessMemberThroughFatPtr) { + VisitExprToBuildOffsetOfComponent( + CurrentASEToAccessMemberThroughFatPtr, SLoc, Comps); + ElementType = CurrentASEToAccessMemberThroughFatPtr->getType(); + CurrentASEToAccessMemberThroughFatPtr = nullptr; + } else { + VisitExprToBuildOffsetOfComponent(ME, SLoc, Comps); + ElementType = QT; + } + Expr *OffsetExprLHS = + SemaRef.BuildBuiltinOffsetOf(SLoc, PointeeTInfo, Comps, SLoc).get(); + QualType Int32Ty = SemaRef.Context.getIntTypeForBitwidth(32, true); + Expr *OffsetExprRHS = + IntegerLiteral::Create(SemaRef.Context, + llvm::APInt(32, SemaRef.Context.getTypeSize(ElementType) / 8), Int32Ty, SLoc); + InsertCheckOffsetCall.second = + SemaRef.CreateBuiltinBinOp(SLoc, BO_Add, OffsetExprLHS, OffsetExprRHS).get(); + } + ME->setBase(BaseTransform::TransformExpr(ME->getBase()).get()); + return MemberPtr ? MemberPtr : ME; +} + +// Desugar fat qualified QualType ,for example: +// 1. `int *fat` will be desugared to `_FatPtr`; +// 2. `struct S *fat` will be desugared to `_FatPtr`; +// 3. `int *fat *` will be desugared to `_FatPtr *`; +// 4. `int *fat *fat` will be desugared to `_FatPtr<_FatPtr>`; +QualType +TransformFatPtr::DesugarFatPtrType(SourceLocation SLoc, QualType T) { + T = T.getCanonicalType(); + if (!T.isFatQualified() && !T->hasFatFields()) + return T; + if (!FatPtrTD) { + DeclContext::lookup_result Decls = + SemaRef.Context.getTranslationUnitDecl()->lookup( + DeclarationName(&SemaRef.Context.Idents.get("_FatPtr"))); + if (Decls.isSingleResult()) + if (ClassTemplateDecl *CTD = dyn_cast(Decls.front())) + FatPtrTD = CTD; + } + if (FatPtrTD) { + if (auto PT = dyn_cast(T)) { + QualType PointeeT = DesugarFatPtrType(SLoc, PT->getPointeeType()); + if (T.isFatQualified()) { + TemplateArgumentListInfo Args(SLoc, SLoc); + Args.addArgument(TemplateArgumentLoc( + TemplateArgument(PointeeT), + SemaRef.Context.getTrivialTypeSourceInfo(PointeeT, SLoc))); + QualType FatPtrRecordType = + SemaRef.CheckTemplateIdType(TemplateName(FatPtrTD), SLoc, Args); + if (!FatPtrRecordType.isNull() && + !SemaRef.RequireCompleteType(SLoc, FatPtrRecordType, + diag::err_fat_ptr_missing_specialization)) + return FatPtrRecordType; + } else + return SemaRef.Context.getPointerType(PointeeT); + } + return T; + } + SemaRef.Diag(SLoc, diag::err_fat_ptr_type_not_found) << ""; + return T; +} + +Expr *TransformFatPtr::BuildFatPtrCall(SourceLocation SLoc, std::string FDName, + llvm::SmallVector &Args) { + if (!FatPtrFD[FDName]) { + DeclContext::lookup_result Decls = + SemaRef.Context.getTranslationUnitDecl()->lookup( + DeclarationName(&SemaRef.Context.Idents.get(FDName))); + if (Decls.isSingleResult()) + FatPtrFD[FDName] = dyn_cast(Decls.front()); + } + if (FunctionDecl *FD = FatPtrFD[FDName]) { + DeclRefExpr *FDRef = + SemaRef.BuildDeclRefExpr(FD, FD->getType(), VK_LValue, SLoc); + CallExpr *CE = + dyn_cast(SemaRef.BuildCallExpr(nullptr, FDRef, SLoc, Args, SLoc).get()); + // Handle first arg of _check_version and _check_version. + if (CE && CE->getNumArgs() > 0 && + CE->getArg(0)->getType()->isFatPtrRecordType()) + if (auto ICE = dyn_cast(CE->getArg(0))) + ICE->setType(ICE->getSubExpr()->getType()); + return CE; + } + SemaRef.Diag(SLoc, diag::err_fat_ptr_func_not_found) << ""; + return nullptr; +} + +MemberExpr *TransformFatPtr::BuildMemberForFatPtr(Expr *FatPtrBaseE, + std::string FieldName) { + QualType FatPtrQT = + DesugarFatPtrType(FatPtrBaseE->getBeginLoc(), FatPtrBaseE->getType()); + if (FatPtrQT->isRecordType()) + if (RecordDecl *FatPtrRD = FatPtrQT->getAs()->getDecl()) + if (FatPtrRD->getNameAsString() == "_FatPtr") { + RecordDecl::field_iterator it = FatPtrRD->field_begin(); + if (FieldName == "key_version") { + it++; + } else if (FieldName == "offset") { + it++; + it++; + } + FieldDecl *FD = *it; + return SemaRef.BuildMemberExpr( + FatPtrBaseE, false, FatPtrBaseE->getBeginLoc(), + NestedNameSpecifierLoc(), FatPtrBaseE->getBeginLoc(), FD, + DeclAccessPair::make(FatPtrRD, FD->getAccess()), false, + DeclarationNameInfo(), FD->getType(), VK_LValue, OK_Ordinary); + } + return nullptr; +} + +Expr *TransformFatPtr::BuildFatPtrCompoundLiteralExprForRawPtr( + Expr *RawPtrE, QualType QT) { + SourceLocation SLoc = RawPtrE->getBeginLoc(); + llvm::APInt Zero(32, 0); + QualType UInt32Ty = SemaRef.Context.getIntTypeForBitwidth(32, false); + QualType Int32Ty = SemaRef.Context.getIntTypeForBitwidth(32, true); + Expr *SecondInitE = + IntegerLiteral::Create(SemaRef.Context, Zero, UInt32Ty, SLoc); + Expr *ThirdInitE = + IntegerLiteral::Create(SemaRef.Context, Zero, Int32Ty, SLoc); + return BuildFatPtrCompoundLiteralExpr(RawPtrE, SecondInitE, ThirdInitE, QT); +} + +Expr *TransformFatPtr::BuildFatPtrCompoundLiteralExpr( + Expr *FirstInitE, Expr *SecondInitE, Expr *ThirdInitE, QualType QT) { + SourceLocation SLoc = FirstInitE->getBeginLoc(); + SmallVector Inits{FirstInitE, SecondInitE, ThirdInitE}; + Expr *ILE = SemaRef.BuildInitList(SLoc, Inits, SLoc).get(); + ILE->setType(QT); + return SemaRef + .BuildCompoundLiteralExpr( + SLoc, SemaRef.Context.getTrivialTypeSourceInfo(QT), SLoc, ILE) + .get(); +} + +// return true when accessing array member through FatPtr, +// such as `p->arr[3]`, `p->a.b.arr[3]` +bool +TransformFatPtr::IsAccessingArrayMemberThroughFatPtr(Expr *E) { + if (!CurrentASEToAccessMemberThroughFatPtr) { + if (auto ME = dyn_cast(E)) { + if (ME->getBase()->getType().isFatPtrType()) { + return true; + } else { + return IsAccessingArrayMemberThroughFatPtr(ME->getBase()); + } + } else if (auto ASE = dyn_cast(E)) { + return IsAccessingArrayMemberThroughFatPtr(ASE->getBase()); + } else if (auto ICE = dyn_cast(E)) { + return IsAccessingArrayMemberThroughFatPtr(ICE->getSubExpr()); + } else if (auto PE = dyn_cast(E)) { + return IsAccessingArrayMemberThroughFatPtr(PE->getSubExpr()); + } + } + return false; +} + +void TransformFatPtr::VisitExprToBuildOffsetOfComponent( + Expr *E, SourceLocation SLoc, + SmallVector &Comps) { + if (E->getType().isFatPtrType()) { + return; + } else if (auto ASE = dyn_cast(E)) { + VisitExprToBuildOffsetOfComponent(ASE->getBase(), SLoc, Comps); + Comps.push_back(Sema::OffsetOfComponent()); + Comps.back().isBrackets = true; + Comps.back().U.E = ASE->getIdx(); + Comps.back().LocStart = Comps.back().LocEnd = SLoc; + } else if (auto ME = dyn_cast(E)) { + VisitExprToBuildOffsetOfComponent(ME->getBase(), SLoc, Comps); + Comps.push_back(Sema::OffsetOfComponent()); + Comps.back().isBrackets = false; + Comps.back().U.IdentInfo = ME->getMemberDecl()->getIdentifier(); + Comps.front().LocStart = Comps.back().LocEnd = SLoc; + } else if (auto ICE = dyn_cast(E)) { + VisitExprToBuildOffsetOfComponent(ICE->getSubExpr(), SLoc, Comps); + } else if (auto PE = dyn_cast(E)) { + VisitExprToBuildOffsetOfComponent(PE->getSubExpr(), SLoc, Comps); + } +} + +void Sema::DesugarFunctionDeclWithFatPtr(FunctionDecl *FD) { + if (Diags.hasErrorOccurred() || + FD->getTemplatedKind() == FunctionDecl::TK_FunctionTemplate) + return; + // Skip function template and class template. + if (auto RD = dyn_cast(FD->getParent())) { + if (RD->getDescribedClassTemplate()) + return; + } + + // Desugar fat ptr in function. + TransformFatPtr TFP(*this, FD); + TFP.TransformFunctionDecl(); +} + +void Sema::DesugarGlobalVarDeclWithFatPtr(VarDecl *VD) { + if (Diags.hasErrorOccurred()) + return; + + TransformFatPtr TFP(*this); + TFP.TransformVarDecl(VD); +} + +void Sema::DesugarRecordDeclWithFatPtr(RecordDecl *RD) { + if (Diags.hasErrorOccurred() || RD->getDescribedClassTemplate()) + return; + + TransformFatPtr TFP(*this); + TFP.TransformRecordDecl(RD); +} + +bool Sema::CheckFatQualTypeCStyleCast(QualType LHSType, QualType RHSType, + SourceLocation RLoc) { + if (!CheckFatQualTypeCStyleCast(LHSType, RHSType)) { + Diag(RLoc, diag::err_fat_qualcheck_incompatible) + << CompleteTraitType(RHSType) << CompleteTraitType(LHSType); + return false; + } + return true; +} + +bool Sema::CheckFatQualTypeCStyleCast(QualType LHSType, QualType RHSType) { + QualType RHSCanType = RHSType.getCanonicalType(); + QualType LHSCanType = LHSType.getCanonicalType(); + bool IsSameType = (LHSCanType.getTypePtr() == RHSCanType.getTypePtr()); + const auto *LHSPtrType = LHSType->getAs(); + const auto *RHSPtrType = RHSType->getAs(); + bool IsPointer = LHSPtrType && RHSPtrType; + QualType RHSRawType = RHSCanType.getUnqualifiedType(); + QualType LHSRawType = LHSCanType.getUnqualifiedType(); + + if (IsSameType) { + return true; + } + if ((LHSCanType.isFatQualified() || LHSCanType->isFatPtrRecordType()) && + (RHSCanType.isFatQualified() || RHSCanType->isFatPtrRecordType())) { + return true; + } + if (IsPointer) { + if (Context.hasSameType(LHSRawType, RHSRawType)) { + return true; + } else if (LHSCanType.isFatQualified() && RHSCanType.isFatQualified()) { + // T1 *fat is allowed to cast to T2 *fat + return true; + } else if (TryDesugarTrait(RHSType)) { + return true; + } else { + return CheckFatQualTypeCStyleCast(LHSPtrType->getPointeeType(), + RHSPtrType->getPointeeType()); + } + } + return false; +} + +bool Sema::CheckFatQualTypeAssignment(QualType LHSType, QualType RHSType, + SourceLocation RLoc) { + QualType RHSCanType = RHSType.getCanonicalType(); + QualType LHSCanType = LHSType.getCanonicalType(); + const auto *LHSPtrType = LHSType->getAs(); + const auto *RHSPtrType = RHSType->getAs(); + bool IsPointer = LHSPtrType && RHSPtrType; + + if (LHSCanType.isFatQualified() == RHSCanType.isFatQualified()) { + if (TraitDecl *TD = TryDesugarTrait(LHSCanType)) { + if (TD->getTypeImpledVarDecl(RHSCanType->getPointeeType())) + return true; + } + if (Context.hasSameType(LHSCanType, RHSCanType)) { + return true; + } + if (LHSCanType->isVoidPointerType()) { + if (LHSCanType->getPointeeType().isConstQualified() == + RHSCanType->getPointeeType().isConstQualified()) + return true; + } + if (!IsPointer) { + return false; + } else { + return CheckFatQualTypeAssignment(LHSPtrType->getPointeeType(), + RHSPtrType->getPointeeType(), RLoc); + } + } + return false; +} + +bool Sema::CheckFatQualTypeAssignment(QualType LHSType, Expr *RHSExpr) { + QualType LHSCanType = LHSType.getCanonicalType(); + QualType RHSCanType = RHSExpr->getType().getCanonicalType(); + SourceLocation ExprLoc = RHSExpr->getBeginLoc(); + bool Res = true; + + if (LHSCanType.isFatQualified() || RHSCanType.isFatQualified()) { + if (TraitDecl *TD = TryDesugarTrait(LHSCanType)) { + if (RHSCanType->isPointerType()) { + QualType ImplType = RHSCanType->getPointeeType() + .getUnqualifiedType() + .getCanonicalType(); + ImplType.removeLocalOwned(); + if (TD->getTypeImpledVarDecl(ImplType)) + return true; + } + // trait T* fat <-> trait T* fat // legal + if (TD) { + QualType QT = + DesugarTraitToStructTrait(TD, LHSCanType, RHSExpr->getExprLoc()); + if (QT.getCanonicalType() == RHSCanType) { + return true; + } + } + } + + // Fat pointer can be inited by nullptr. + if (LHSCanType.isFatQualified() && isa(RHSExpr)) + return true; + + if (LHSCanType->isVoidPointerType() && RHSCanType->isPointerType()) { + if (LHSCanType->getPointeeType().isConstQualified() == + RHSCanType->getPointeeType().isConstQualified()) + return true; + } + + if (!Context.hasSameType(LHSCanType, RHSCanType)) + Res = false; + } else { + Res = CheckFatQualTypeAssignment(LHSType, RHSCanType, ExprLoc); + } + if ((LHSCanType.isFatQualified() || LHSCanType->isFatPtrRecordType()) && + (RHSCanType.isFatQualified() || RHSCanType->isFatPtrRecordType())) + return true; + if (!Res) { + Diag(ExprLoc, diag::err_fat_qualcheck_incompatible) + << CompleteTraitType(RHSExpr->getType()) << CompleteTraitType(LHSType); + } + return Res; +} + +bool Sema::CheckFatFunctionPointerType(QualType LHSType, Expr *RHSExpr) { + const FunctionProtoType *LHSFuncType = LHSType->getAs() + ->getPointeeType() + ->getAs(); + const FunctionProtoType *RHSFuncType = + RHSExpr->getType()->isFunctionPointerType() + ? RHSExpr->getType() + ->getAs() + ->getPointeeType() + ->getAs() + : RHSExpr->getType()->getAs(); + SourceLocation ExprLoc = RHSExpr->getBeginLoc(); + + // return if no 'fat' in both side + if (!LHSFuncType->hasFatRetOrParams() && !RHSFuncType->hasFatRetOrParams()) { + return true; + } + + if (!Context.hasSameType(LHSFuncType, RHSFuncType)) { + Diag(ExprLoc, diag::err_fat_funcPtr_incompatible) + << LHSType << RHSExpr->getType(); + return false; + } + for (unsigned i = 0; i < LHSFuncType->getNumParams(); i++) { + QualType LHSParamType = LHSFuncType->getParamType(i); + QualType RHSParamType = RHSFuncType->getParamType(i); + if (!Context.hasSameType(LHSParamType, RHSParamType)) { + Diag(ExprLoc, diag::err_fat_funcPtr_incompatible) + << LHSType << RHSExpr->getType(); + return false; + } + } + return true; +} + +// Build allocation unit RecordDecl for every global array var +// which has attribute fat_ptr_checked, for example: +// @code +// __attribute((fat_ptr_checked)) int arr[3] = {1, 2, 3}; +// @endcode +// We desugar this decl to: +// @code +// struct _arr_AllocationUnit { +// uint32_t lock_version; +// uint32_t size; +// int arr[3]; +// }; +// struct _arr_AllocationUnit _arr = { 0, 12, {1, 2, 3} }; +// @endcode +std::pair +Sema::BuildAllocationUnitForGlobalArrayVar(VarDecl *VD) { + SourceLocation SLoc = VD->getLocation(); + // Build RecordDecl + RecordDecl *RD = RecordDecl::Create( + Context, TagDecl::TagKind::TTK_Struct, Context.getTranslationUnitDecl(), + SLoc, SLoc, + &Context.Idents.get("_" + VD->getNameAsString() + "_AllocationUnit")); + // Add three Field + RD->startDefinition(); + QualType UInt32Ty = Context.getIntTypeForBitwidth(32, false); + FieldDecl *LockVersionField = FieldDecl::Create( + Context, RD, SLoc, SLoc, &Context.Idents.get("lock_version"), UInt32Ty, + Context.getTrivialTypeSourceInfo(UInt32Ty), nullptr, false, ICIS_NoInit); + FieldDecl *SizeField = FieldDecl::Create( + Context, RD, SLoc, SLoc, &Context.Idents.get("size"), UInt32Ty, + Context.getTrivialTypeSourceInfo(UInt32Ty), nullptr, false, ICIS_NoInit); + FieldDecl *VDTypeField = FieldDecl::Create( + Context, RD, SLoc, SLoc, &Context.Idents.get(VD->getNameAsString()), + VD->getType(), Context.getTrivialTypeSourceInfo(VD->getType()), nullptr, + false, ICIS_NoInit); + LockVersionField->setAccess(AS_public); + SizeField->setAccess(AS_public); + VDTypeField->setAccess(AS_public); + RD->addDecl(LockVersionField); + RD->addDecl(SizeField); + RD->addDecl(VDTypeField); + RD->completeDefinition(); + PushOnScopeChains(RD, TUScope); + + // Build VarDecl + VarDecl *NewVD = + VarDecl::Create(Context, Context.getTranslationUnitDecl(), SLoc, SLoc, + &Context.Idents.get("_" + VD->getNameAsString()), + QualType(RD->getTypeForDecl(), 0), nullptr, SC_None); + // Build InitExpr + // Build first init expr : 0 + Expr *LockVersionInit = + IntegerLiteral::Create(Context, llvm::APInt(32, 0), UInt32Ty, SLoc); + // Build second init expr : array size + Expr *SizeInit = IntegerLiteral::Create( + Context, llvm::APInt(32, Context.getTypeSize(VD->getType()) / 8), + UInt32Ty, SLoc); + // Build InitListExpr + SmallVector Inits{LockVersionInit, SizeInit}; + if (VD->getInit()) + Inits.push_back(VD->getInit()); + Expr *ILE = BuildInitList(SLoc, Inits, SLoc).get(); + ILE->setType(QualType(RD->getTypeForDecl(), 0)); + NewVD->setInit(ILE); + PushOnScopeChains(NewVD, TUScope); + Context.BSCDesugaredMap[VD].push_back(RD); + Context.BSCDesugaredMap[VD].push_back(NewVD); + return std::make_pair(RD, NewVD); +} +#endif // ENABLE_BSC \ No newline at end of file diff --git a/clang/lib/Sema/BSC/SemaBSCOwnership.cpp b/clang/lib/Sema/BSC/SemaBSCOwnership.cpp index c96493b7f583..229f30a2f5ad 100644 --- a/clang/lib/Sema/BSC/SemaBSCOwnership.cpp +++ b/clang/lib/Sema/BSC/SemaBSCOwnership.cpp @@ -399,7 +399,7 @@ bool Sema::CheckBorrowFunctionPointerType(QualType LHSType, Expr *RHSExpr) { return true; } if (!Context.hasSameType(LSHFuncType, RSHFuncType)) { - Diag(ExprLoc, diag::err_funcPtr_incompatible) + Diag(ExprLoc, diag::err_borrow_funcPtr_incompatible) << LHSType << RHSExpr->getType(); return false; } diff --git a/clang/lib/Sema/BSC/SemaBSCSafeZone.cpp b/clang/lib/Sema/BSC/SemaBSCSafeZone.cpp index cad99765f657..f3813e181333 100644 --- a/clang/lib/Sema/BSC/SemaBSCSafeZone.cpp +++ b/clang/lib/Sema/BSC/SemaBSCSafeZone.cpp @@ -233,7 +233,7 @@ bool Sema::IsSafeConversion(QualType DestType, Expr *Expr) { // Init a owned or borrow pointer by nullptr is allowed in the safe zone if ((DestType.isOwnedQualified() && DestType->isPointerType()) || - DestType.isBorrowQualified()) { + DestType.isBorrowQualified() || DestType.isFatQualified()) { if (isa(Expr)) return true; } diff --git a/clang/lib/Sema/BSC/SemaDeclBSC.cpp b/clang/lib/Sema/BSC/SemaDeclBSC.cpp index 3dc080a812ee..979b2db10f44 100644 --- a/clang/lib/Sema/BSC/SemaDeclBSC.cpp +++ b/clang/lib/Sema/BSC/SemaDeclBSC.cpp @@ -166,7 +166,7 @@ bool Sema::HasSafeZoneInFunction(const FunctionDecl* FnDecl) { /// ==================================================================== void Sema::BSCDataflowAnalysis(const Decl *D, bool EnableOwnershipCheck, bool EnableNullabilityCheck) { - AnalysisDeclContext AC(/* AnalysisDeclContextManager */ nullptr, D); + AnalysisDeclContext AC(/* AnalysisDeclContextManager */ nullptr, FD); // TODO: understand how these parameters affect the CFG. AC.getCFGBuildOptions().PruneTriviallyFalseEdges = true; diff --git a/clang/lib/Sema/CMakeLists.txt b/clang/lib/Sema/CMakeLists.txt index a665edbb358a..e408444fa565 100644 --- a/clang/lib/Sema/CMakeLists.txt +++ b/clang/lib/Sema/CMakeLists.txt @@ -16,6 +16,7 @@ add_clang_library(clangSema BSC/SemaBSCOverload.cpp BSC/SemaBSCOwnedStruct.cpp BSC/SemaBSCCoroutine.cpp + BSC/SemaBSCFatPtr.cpp BSC/SemaBSCOwnership.cpp BSC/SemaBSCSafeZone.cpp BSC/SemaBSCTrait.cpp diff --git a/clang/lib/Sema/DeclSpec.cpp b/clang/lib/Sema/DeclSpec.cpp index 7226987274eb..a8dee67b7dc6 100644 --- a/clang/lib/Sema/DeclSpec.cpp +++ b/clang/lib/Sema/DeclSpec.cpp @@ -438,7 +438,9 @@ void DeclSpec::forEachCVRUQualifier( Handle(TQ_owned, "owned", TQ_ownedLoc); if (TypeQualifiers & TQ_borrow) Handle(TQ_borrow, "borrow", TQ_borrowLoc); - #endif + if (TypeQualifiers & TQ_fat) + Handle(TQ_fat, "fat", TQ_fatLoc); +#endif if (TypeQualifiers & TQ_volatile) Handle(TQ_volatile, "volatile", TQ_volatileLoc); if (TypeQualifiers & TQ_restrict) @@ -635,7 +637,9 @@ const char *DeclSpec::getSpecifierName(TQ T) { #if ENABLE_BSC case DeclSpec::TQ_owned: return "owned"; case DeclSpec::TQ_borrow: return "borrow"; - #endif + case DeclSpec::TQ_fat: + return "fat"; +#endif case DeclSpec::TQ_restrict: return "restrict"; case DeclSpec::TQ_volatile: return "volatile"; case DeclSpec::TQ_atomic: return "_Atomic"; @@ -1011,7 +1015,10 @@ bool DeclSpec::SetTypeQual(TQ T, SourceLocation Loc) { case TQ_borrow: TQ_borrowLoc = Loc; return false; - #endif + case TQ_fat: + TQ_fatLoc = Loc; + return false; +#endif case TQ_restrict: TQ_restrictLoc = Loc; return false; case TQ_volatile: TQ_volatileLoc = Loc; return false; case TQ_unaligned: TQ_unalignedLoc = Loc; return false; diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index 7f55f3a83696..e0780e8c796b 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -2196,11 +2196,7 @@ static void markEscapingByrefs(const FunctionScopeInfo &FSI, Sema &S) { /// \param BlockType The type of the block expression, if D is a BlockDecl. Sema::PoppedFunctionScopePtr Sema::PopFunctionScopeInfo(const AnalysisBasedWarnings::Policy *WP, - const Decl *D, QualType BlockType - #if ENABLE_BSC - , bool isBSCCoroutine - #endif - ) { + const Decl *D, QualType BlockType) { assert(!FunctionScopes.empty() && "mismatched push/pop!"); markEscapingByrefs(*FunctionScopes.back(), *this); @@ -2211,28 +2207,6 @@ Sema::PopFunctionScopeInfo(const AnalysisBasedWarnings::Policy *WP, if (LangOpts.OpenMP) popOpenMPFunctionRegion(Scope.get()); -#if ENABLE_BSC - // FIXME: Rename EnableOwnershipCheck, maybe EnableBorrowCheck is a better name. - bool EnableOwnershipCheck = LangOpts.BSC ? (!(getLangOpts().DisableOwnershipCheck)) : false; - // If NullabilityCheck is in Mode: {SafeOnly, All}, we should build CFG. - bool EnableNullabilityCheck = LangOpts.BSC ? (getLangOpts().getNullabilityCheck() != LangOptions::NC_NONE) : false; - if (EnableOwnershipCheck || EnableNullabilityCheck) { - if (!isBSCCoroutine && - (getDiagnostics().getNumErrors() == - getDiagnostics().getNumOwnershipErrors() + - getDiagnostics().getNumBorrowCheckErrors() + - getDiagnostics().getNumNullabilityCheckErrors()) && - D) - if (const auto *const CastReturn = dyn_cast_or_null(D)) { - auto md = dyn_cast_or_null(D); - // Don't check ownership rules of destructor parameters - if (!md || !md->isDestructor()) - BSCDataflowAnalysis(D, EnableOwnershipCheck, - EnableNullabilityCheck); - } - } -#endif - // Issue any analysis-based warnings. if (WP && D) AnalysisWarnings.IssueWarnings(*WP, Scope.get(), D, BlockType); diff --git a/clang/lib/Sema/SemaCast.cpp b/clang/lib/Sema/SemaCast.cpp index b174416b2a54..741617c32050 100644 --- a/clang/lib/Sema/SemaCast.cpp +++ b/clang/lib/Sema/SemaCast.cpp @@ -2879,43 +2879,70 @@ void CastOperation::CheckCStyleCast() { SrcExpr = ExprError(); return; } + QualType SrcType = SrcExpr.get()->getType(); + QualType SrcCanType = SrcType.getCanonicalType(); + QualType DestCanType = DestType.getCanonicalType(); // bsc owned type CStyleCast - if (SrcExpr.get()->getType().getCanonicalType().isOwnedQualified() || - DestType.getCanonicalType().isOwnedQualified()) { - if (!Self.CheckOwnedQualTypeCStyleCast(DestType, SrcExpr.get()->getType(), SrcExpr.get()->getExprLoc())) { + if (SrcCanType.isOwnedQualified() || DestCanType.isOwnedQualified()) { + if (!Self.CheckOwnedQualTypeCStyleCast(DestType, SrcType, + SrcExpr.get()->getExprLoc())) { SrcExpr = ExprError(); return; } } // bsc borrow type CStyleCast - if (SrcExpr.get()->getType().getCanonicalType().isBorrowQualified() || - DestType.getCanonicalType().isBorrowQualified()) { - if (!Self.CheckBorrowQualTypeCStyleCast(DestType, SrcExpr.get()->getType(), SrcExpr.get()->getExprLoc())) { + if (SrcCanType.isBorrowQualified() || DestCanType.isBorrowQualified()) { + if (!Self.CheckBorrowQualTypeCStyleCast(DestType, SrcType, + SrcExpr.get()->getExprLoc())) { + SrcExpr = ExprError(); + return; + } + } + // bsc fat type CStyleCast + if (SrcCanType.isFatQualified() || DestCanType.isFatQualified()) { + if (!Self.CheckFatQualTypeCStyleCast(DestType, SrcType, + SrcExpr.get()->getExprLoc())) { SrcExpr = ExprError(); return; } } if (const auto *LHSPtrType = DestType->getAs()) { - if (const auto *RHSPtrType = SrcExpr.get()->getType()->getAs()) { + if (const auto *RHSPtrType = SrcType->getAs()) { if (LHSPtrType->hasOwnedFields() || RHSPtrType->hasOwnedFields()) { - if (!Self.CheckOwnedQualTypeCStyleCast(DestType, SrcExpr.get()->getType(), SrcExpr.get()->getExprLoc())) { + if (!Self.CheckOwnedQualTypeCStyleCast(DestType, SrcType, + SrcExpr.get()->getExprLoc())) { + SrcExpr = ExprError(); + return; + } + } + if (LHSPtrType->hasBorrowFields() || RHSPtrType->hasBorrowFields()) { + if (!Self.CheckBorrowQualTypeCStyleCast( + DestType, SrcType, SrcExpr.get()->getExprLoc())) { + SrcExpr = ExprError(); + return; + } + } + if (LHSPtrType->hasFatFields() || RHSPtrType->hasFatFields()) { + if (!Self.CheckFatQualTypeCStyleCast(DestType, SrcType, + SrcExpr.get()->getExprLoc())) { SrcExpr = ExprError(); return; } } } } - } - if (Self.getLangOpts().BSC && Self.IsTraitExpr(SrcExpr.get()) && - DestType->isPointerType()) { - if (!DestType->hasTraitType()) - SrcExpr = Self.ActOnTraitPointerCast(SrcExpr.get()); - if (DestType->isVoidPointerType()) { - Kind = CK_NoOp; - } else { - Kind = CK_BitCast; + if (Self.IsTraitExpr(SrcExpr.get()) && DestType->isPointerType()) { + if (!DestType->hasTraitType()) + SrcExpr = Self.ActOnTraitPointerCast(SrcExpr.get()); + if (DestType->isVoidPointerType()) { + Kind = CK_NoOp; + } else { + Kind = CK_BitCast; + } + return; + } else if (SrcCanType->isFatPtrRecordType() && DestType.isFatQualified()) { + return; } - return; } #endif diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 27347cea4684..3dd0f99dbb13 100755 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -15986,12 +15986,37 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body, // the declaration context below. Otherwise, we're unable to transform // 'this' expressions when transforming immediate context functions. +#if ENABLE_BSC + if (LangOpts.BSC) { + if (auto FD = dyn_cast_or_null(dcl)) { + // FIXME: Rename EnableOwnershipCheck, maybe EnableBorrowCheck is a better name. + bool EnableOwnershipCheck = !getLangOpts().DisableOwnershipCheck; + // If NullabilityCheck is in Mode: {SafeOnly, All}, we should build CFG. + bool EnableNullabilityCheck = getLangOpts().getNullabilityCheck() != LangOptions::NC_NONE; + if (EnableOwnershipCheck || EnableNullabilityCheck) { + if (getDiagnostics().getNumErrors() == + getDiagnostics().getNumOwnershipErrors() + + getDiagnostics().getNumBorrowCheckErrors() + + getDiagnostics().getNumNullabilityCheckErrors()) { + auto md = dyn_cast_or_null(D); + // Don't check ownership rules of destructor parameters + if (!md || !md->isDestructor()) + BSCDataflowAnalysis(D, EnableOwnershipCheck, + EnableNullabilityCheck); + } + } + // Desugar BSC Function. + DesugarDestructorCall(FD); + if (getLangOpts().EnableFatPtr) + DesugarFunctionDeclWithFatPtr(FD); + } + } +#endif + if (!IsInstantiation) PopDeclContext(); PopFunctionScopeInfo(ActivePolicy, dcl); - if (auto *FD = dyn_cast(dcl)) - DesugarDestructorCall(FD); // If any errors have occurred, clear out any temporaries that may have // been leftover. This ensures that these temporaries won't be picked up for // deletion in some later function. @@ -18986,6 +19011,11 @@ void Sema::ActOnFields(Scope *S, SourceLocation RecLoc, Decl *EnclosingDecl, if (!Completed) Record->completeDefinition(); +#if ENABLE_BSC + if (getLangOpts().BSC && getLangOpts().EnableFatPtr) + DesugarRecordDeclWithFatPtr(Record); +#endif + // Handle attributes before checking the layout. ProcessDeclAttributeList(S, Record, Attrs); diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index d3cd9eb36026..dd35eac8f173 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -8688,6 +8688,18 @@ static void handleOperatorAttr(Sema &S, Decl *D, const ParsedAttr &Attrs) { D->addAttr(::new (S.Context) OperatorAttr( S.Context, Attrs, Attrs.getOperatorTypeBuffer().Kind)); } + +static void handleFatPtrCheckedAttr(Sema &S, Decl *D, const ParsedAttr &Attrs) { + if (auto VD = dyn_cast(D)) { + if (VD->hasGlobalStorage() && VD->getType()->isConstantArrayType()) { + D->addAttr(::new (S.Context) FatPtrCheckedAttr(S.Context, Attrs)); + return; + } + } + S.Diag(Attrs.getLoc(), diag::err_fat_ptr_checked_attr_global_array_var); + Attrs.setInvalid(); + return; +} #endif //===----------------------------------------------------------------------===// @@ -9562,6 +9574,10 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL, case ParsedAttr::AT_Operator: handleOperatorAttr(S, D, AL); break; + + case ParsedAttr::AT_FatPtrChecked: + handleFatPtrCheckedAttr(S, D, AL); + break; #endif } } diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 246582275fef..1f10537f65c5 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -5895,6 +5895,13 @@ Sema::CreateBuiltinArraySubscriptExpr(Expr *Base, SourceLocation LLoc, BaseExpr = LHSExp; IndexExpr = RHSExp; ResultType = PTy->getPointeeType(); +#if ENABLE_BSC + } else if (getLangOpts().BSC && getLangOpts().EnableFatPtr && + LHSTy->isFatPtrRecordType()) { + BaseExpr = LHSExp; + IndexExpr = RHSExp; + ResultType = LHSTy->getFatPtrPointeeType(); +#endif } else if (const ObjCObjectPointerType *PTy = LHSTy->getAs()) { BaseExpr = LHSExp; @@ -10440,6 +10447,37 @@ static bool IsTraitEqualExpr(Sema &S, QualType DstType, QualType SrcType, } return false; } + +static bool IsFatPtrEqualExpr(Sema &S, QualType DstType, QualType SrcType) { + if (DstType.getCanonicalType().isFatQualified()) + if (SrcType.getCanonicalType()->isRecordType()) + if (RecordDecl *RD = + SrcType.getCanonicalType()->getAs()->getDecl()) + if (RD->getNameAsString() == "_FatPtr") + return true; + if (SrcType.getCanonicalType().isFatQualified()) + if (DstType.getCanonicalType()->isRecordType()) + if (RecordDecl *RD = + DstType.getCanonicalType()->getAs()->getDecl()) + if (RD->getNameAsString() == "_FatPtr") + return true; + if (DstType.getCanonicalType().isFatQualified()) + if (SrcType.getCanonicalType()->isRecordType()) + if (RecordDecl *RD = + SrcType.getCanonicalType()->getAs()->getDecl()) + if (RD->getNameAsString() == "_FatPtr") + return true; + if (SrcType.getCanonicalType()->isRecordType() && + DstType.getCanonicalType()->isRecordType()) + if (RecordDecl *DstRD = + DstType.getCanonicalType()->getAs()->getDecl()) + if (DstRD->getNameAsString() == "_FatPtr") + if (RecordDecl *SrcRD = + SrcType.getCanonicalType()->getAs()->getDecl()) + if (SrcRD->getNameAsString() == "_FatPtr") + return true; + return false; +} #endif Sema::AssignConvertType @@ -10473,10 +10511,21 @@ Sema::CheckSingleAssignmentConstraints(QualType LHSType, ExprResult &CallerRHS, if (!IsSafeConversion(LHSType, RHS.get())) { return IncompatibleBSCSafeZone; } - if (RHS.get()->getType().getCanonicalType().isOwnedQualified() || LHSType.getCanonicalType().isOwnedQualified()) { + QualType LHSCanType = LHSType.getCanonicalType(); + QualType RHSCanType = RHS.get()->getType().getCanonicalType(); + if (RHSCanType.isOwnedQualified() || LHSCanType.isOwnedQualified()) { if (!CheckOwnedQualTypeAssignment(LHSType, RHS.get())) return IncompatibleOwnedPointer; } + if (RHSCanType.isBorrowQualified() || LHSCanType.isBorrowQualified()) { + if (!CheckBorrowQualTypeAssignment(LHSType, RHS.get())) + return IncompatibleBorrowPointer; + } + if ((RHSCanType.isFatQualified() || RHSCanType->isFatPtrRecordType()) && + (LHSCanType.isFatQualified() || LHSCanType->isFatPtrRecordType())) { + if (!CheckFatQualTypeAssignment(LHSType, RHS.get())) + return IncompatibleFatPointer; + } if (const auto *LHSPtrType = LHSType->getAs()) { if (const auto *RHSPtrType = RHS.get()->getType()->getAs()) { if (LHSPtrType->hasOwnedFields() || RHSPtrType->hasOwnedFields()) { @@ -10484,18 +10533,14 @@ Sema::CheckSingleAssignmentConstraints(QualType LHSType, ExprResult &CallerRHS, return IncompatibleOwnedPointer; } } - } - } - - if (RHS.get()->getType().getCanonicalType().isBorrowQualified() || LHSType.getCanonicalType().isBorrowQualified()) { - if (!CheckBorrowQualTypeAssignment(LHSType, RHS.get())) - return IncompatibleOwnedPointer; - } - if (const auto *LHSPtrType = LHSType->getAs()) { - if (const auto *RHSPtrType = RHS.get()->getType()->getAs()) { if (LHSPtrType->hasBorrowFields() || RHSPtrType->hasBorrowFields()) { if (!CheckBorrowQualTypeAssignment(LHSType, RHS.get())) { - return IncompatibleOwnedPointer; + return IncompatibleBorrowPointer; + } + } + if (LHSPtrType->hasFatFields() || RHSPtrType->hasFatFields()) { + if (!CheckFatQualTypeAssignment(LHSType, RHS.get())) { + return IncompatibleFatPointer; } } } @@ -10507,6 +10552,8 @@ Sema::CheckSingleAssignmentConstraints(QualType LHSType, ExprResult &CallerRHS, return IncompatibleOwnedPointer; if (!CheckBorrowFunctionPointerType(LHSType, RHS.get())) return IncompatibleBorrowPointer; + if (!CheckFatFunctionPointerType(LHSType, RHS.get())) + return IncompatibleFatPointer; } } #endif @@ -14909,11 +14956,12 @@ bool Sema::IsAddrBorrowDerefOp(ExprResult &OrigOp) { return false; } -QualType Sema::GetBorrowAddressOperandQualType(QualType resultType, - ExprResult &Input, - const Expr *InputExpr, - UnaryOperatorKind &Opc, - SourceLocation OpLoc) { +// Handle BSC AddressOperand, Include &mut, &const, &mut*, &const*, &fat. +QualType Sema::GetBSCAddressOperandQualType(QualType resultType, + ExprResult &Input, + const Expr *InputExpr, + UnaryOperatorKind &Opc, + SourceLocation OpLoc) { if (Opc == UO_AddrMut || Opc == UO_AddrMutDeref) { if (Opc == UO_AddrMut && IsAddrBorrowDerefOp(Input)) { Opc = UO_AddrMutDeref; @@ -14967,6 +15015,28 @@ QualType Sema::GetBorrowAddressOperandQualType(QualType resultType, resultType = resultType.addConstBorrow(Context); } } + } else if (Opc == UO_AddrFat && !resultType.isNull()) { + if (auto ME = dyn_cast(InputExpr)) { + // if p is no-fat pointer, `&fat p->a` is not allowed. + if (ME->isArrow() && !ME->getBase()->getType().isFatQualified()) + Diag(OpLoc, diag::err_addr_fat_on_no_fat_pointer) + << InputExpr->getSourceRange(); + InputExpr = ME->getBase()->IgnoreImpCasts(); + } else if (auto ASE = dyn_cast(InputExpr)) { + // if p is no-fat pointer, `&fat p[5]` is not allowed. + QualType BaseQT = ASE->getBase()->IgnoreImpCasts()->getType(); + if (BaseQT->isPointerType() && !BaseQT.isFatQualified()) + Diag(OpLoc, diag::err_addr_fat_on_no_fat_pointer) + << InputExpr->getSourceRange(); + InputExpr = ASE->getBase()->IgnoreImpCasts(); + } + if (auto DRE = dyn_cast(InputExpr)) { + if (auto VD = dyn_cast(DRE->getDecl())) + if (VD->hasGlobalStorage() && !VD->hasAttr()) + Diag(OpLoc, diag::err_addr_fat_on_global_var_without_checked) + << InputExpr->getSourceRange(); + } + resultType.addFat(); } return resultType; } @@ -15241,6 +15311,10 @@ static QualType CheckIndirectionOperand(Sema &S, Expr *Op, ExprValueKind &VK, else if (const ObjCObjectPointerType *OPT = OpTy->getAs()) Result = OPT->getPointeeType(); +#if ENABLE_BSC + else if (S.getLangOpts().BSC && S.getLangOpts().EnableFatPtr && OpTy->isFatPtrRecordType()) + Result = OpTy->getFatPtrPointeeType(); +#endif else { ExprResult PR = S.CheckPlaceholderExpr(Op); if (PR.isInvalid()) return QualType(); @@ -15342,6 +15416,9 @@ static inline UnaryOperatorKind ConvertTokenKindToUnaryOpcode( #if ENABLE_BSC case tok::ampmut: Opc = UO_AddrMut; break; case tok::ampconst: Opc = UO_AddrConst; break; + case tok::ampfat: + Opc = UO_AddrFat; + break; #endif } return Opc; @@ -16379,19 +16456,20 @@ ExprResult Sema::CreateBuiltinUnaryOp(SourceLocation OpLoc, #if ENABLE_BSC case UO_AddrMutDeref: case UO_AddrConstDeref: - resultType = GetBorrowAddressOperandQualType(Input.get()->getType(), Input, - InputExpr, Opc, OpLoc); + resultType = GetBSCAddressOperandQualType(Input.get()->getType(), Input, + InputExpr, Opc, OpLoc); break; case UO_AddrMut: case UO_AddrConst: + case UO_AddrFat: #endif case UO_AddrOf: resultType = CheckAddressOfOperand(Input, OpLoc); CheckAddressOfNoDeref(InputExpr); RecordModifiableNonNullParam(*this, InputExpr); #if ENABLE_BSC - resultType = GetBorrowAddressOperandQualType(resultType, Input, InputExpr, - Opc, OpLoc); + resultType = + GetBSCAddressOperandQualType(resultType, Input, InputExpr, Opc, OpLoc); #endif break; case UO_Deref: { @@ -17907,6 +17985,8 @@ bool Sema::DiagnoseAssignmentResult(AssignConvertType ConvTy, isInvalid = true; break; case Incompatible: + if (getLangOpts().BSC && IsFatPtrEqualExpr(*this, DstType, SrcType)) + return false; if (maybeDiagnoseAssignmentToFunction(*this, DstType, SrcExpr)) { if (Complained) *Complained = true; @@ -17922,6 +18002,7 @@ bool Sema::DiagnoseAssignmentResult(AssignConvertType ConvTy, #if ENABLE_BSC case IncompatibleOwnedPointer: case IncompatibleBorrowPointer: + case IncompatibleFatPointer: return false; case IncompatibleBSCSafeZone: return true; diff --git a/clang/lib/Sema/SemaExprMember.cpp b/clang/lib/Sema/SemaExprMember.cpp index 112c767a1b04..4d6becec6f43 100644 --- a/clang/lib/Sema/SemaExprMember.cpp +++ b/clang/lib/Sema/SemaExprMember.cpp @@ -1075,8 +1075,17 @@ Sema::BuildMemberReferenceExpr(Expr *BaseExpr, QualType BaseExprType, ActOnMemberAccessExtraArgs *ExtraArgs) { QualType BaseType = BaseExprType; if (IsArrow) { +#if ENABLE_BSC + if (getLangOpts().BSC && getLangOpts().EnableFatPtr && + BaseType->isFatPtrRecordType()) + BaseType = BaseType->getFatPtrPointeeType(); + else { +#endif assert(BaseType->isPointerType()); BaseType = BaseType->castAs()->getPointeeType(); +#if ENABLE_BSC + } +#endif } R.setBaseObjectType(BaseType); @@ -1496,6 +1505,11 @@ static ExprResult LookupMemberExpr(Sema &S, LookupResult &R, else if (const ObjCObjectPointerType *Ptr = BaseType->getAs()) BaseType = Ptr->getPointeeType(); +# if ENABLE_BSC + else if (S.getLangOpts().BSC && S.getLangOpts().EnableFatPtr && + BaseType->isFatPtrRecordType()) + BaseType = BaseType->getFatPtrPointeeType(); +# endif else if (BaseType->isRecordType()) { // Recover from arrow accesses to records, e.g.: // struct MyRecord foo; @@ -2106,8 +2120,18 @@ Sema::BuildFieldReferenceExpr(Expr *BaseExpr, bool IsArrow, VK = VK_LValue; } else { QualType BaseType = BaseExpr->getType(); - if (IsArrow) BaseType = BaseType->castAs()->getPointeeType(); - + if (IsArrow) +# if ENABLE_BSC + { + if (getLangOpts().BSC && getLangOpts().EnableFatPtr && + BaseType->isFatPtrRecordType()) + BaseType = BaseType->getFatPtrPointeeType(); + else +# endif + BaseType = BaseType->castAs()->getPointeeType(); +# if ENABLE_BSC + } +# endif Qualifiers BaseQuals = BaseType.getQualifiers(); // GC attributes are never picked up by members. diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index 8244126a2fac..5599d29754fe 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -8041,7 +8041,8 @@ BuiltinCandidateTypeSet::AddPointerWithMoreQualifiedTypeVariants(QualType Ty, #if ENABLE_BSC bool hasOwned = VisibleQuals.hasOwned(); bool hasBorrow = VisibleQuals.hasBorrow(); - #endif + bool hasFat = VisibleQuals.hasFat(); +#endif // Iterate through all strict supersets of BaseCVR. for (unsigned CVR = BaseCVR+1; CVR <= Qualifiers::CVRMask; ++CVR) { @@ -8055,7 +8056,11 @@ BuiltinCandidateTypeSet::AddPointerWithMoreQualifiedTypeVariants(QualType Ty, // Skip over borrow if no borrow found anywhere in the types. if ((CVR & Qualifiers::Borrow) && !hasBorrow) continue; - #endif + + // Skip over fat if no fat found anywhere in the types. + if ((CVR & Qualifiers::Fat) && !hasFat) + continue; +#endif // Skip over restrict if no restrict found anywhere in the types, or if // the type cannot be restrict-qualified. diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index f04325d2063b..5882e115de86 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -1999,10 +1999,13 @@ QualType Sema::BuildQualifiedType(QualType T, SourceLocation Loc, Qs.removeVolatile(); } #if ENABLE_BSC - if (getLangOpts().BSC && Qs.hasBorrow()) { - if (!T->isPointerType()) + if (getLangOpts().BSC && !T->isPointerType()) { + if (Qs.hasBorrow()) Diag(DS ? DS->getBorrowSpecLoc() : Loc, diag::err_typecheck_invalid_borrow_not_pointer) << T; + if (Qs.hasFat()) + Diag(DS ? DS->getFatSpecLoc() : Loc, + diag::err_typecheck_invalid_fat_not_pointer); } #endif diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp index cb19ab6202fa..3dac2d46a7cf 100644 --- a/clang/lib/Serialization/ASTWriterStmt.cpp +++ b/clang/lib/Serialization/ASTWriterStmt.cpp @@ -758,7 +758,8 @@ void ASTStmtWriter::VisitUnaryOperator(UnaryOperator *E) { Record.push_back(HasFPFeatures); Record.AddStmt(E->getSubExpr()); #if ENABLE_BSC - if (E->getOpcode() == UO_AddrMut || E->getOpcode() == UO_AddrConst) + if (E->getOpcode() == UO_AddrMut || E->getOpcode() == UO_AddrConst || + E->getOpcode() == UO_AddrFat) Record.push_back(UO_AddrOf); else #endif diff --git a/clang/test/BSC/Negative/FatPtr/addr_fat.cbs b/clang/test/BSC/Negative/FatPtr/addr_fat.cbs new file mode 100644 index 000000000000..09c1b718876a --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/addr_fat.cbs @@ -0,0 +1,58 @@ +// RUN: %clang_cc1 -verify %s + +struct S { int a; }; +void test1() { + struct S *fat p1; + struct S *p2; + int *fat p3 = &fat p1->a; + int *fat p4 = &fat p2->a;// expected-error {{'&fat' on member of no-fat pointer is not allowed}} + float *fat p5 = &fat p1->a;// expected-error {{incompatible fat types, cannot cast 'int *fat' to 'float *fat'}} + float *fat p6 = (float *fat)&fat p1->a; + + int *fat p7; + int *p8; + int *fat p9 = &fat p7[1]; + int *fat p10 = &fat p8[1]; // expected-error {{'&fat' on member of no-fat pointer is not allowed}} + float *fat p11 = &fat p7[1];// expected-error {{incompatible fat types, cannot cast 'int *fat' to 'float *fat'}} + float *fat p12 = (float *fat)&fat p7[1]; + + struct S s; + int arr[10]; + int *fat p13 = &fat s.a; + float *fat p14 = &fat s.a; // expected-error {{incompatible fat types, cannot cast 'int *fat' to 'float *fat'}} + float *fat p15 = (float *fat)&fat s.a; + int *fat p16 = &fat arr[5]; + float *fat p17 = &fat arr[5];// expected-error {{incompatible fat types, cannot cast 'int *fat' to 'float *fat'}} + float *fat p18 = (float *fat)&fat arr[5]; +} + +__attribute__((fat_ptr_checked)) int arr1[5]; +int arr2[5]; +__attribute__((fat_ptr_checked)) static int arr3[5]; +static int arr4[5]; +void test2() { + __attribute__((fat_ptr_checked)) static int arr5[5]; + static int arr6[5]; + int *fat p1 = &fat arr1[0]; + int *fat p2 = &fat arr2[0]; // expected-error {{'&fat' on global variables without fat_ptr_checked attribute is not allowed}} + int *fat p3 = &fat arr3[0]; + int *fat p4 = &fat arr4[0]; // expected-error {{'&fat' on global variables without fat_ptr_checked attribute is not allowed}} + int *fat p5 = &fat arr5[0]; + int *fat p6 = &fat arr6[0]; // expected-error {{'&fat' on global variables without fat_ptr_checked attribute is not allowed}} +} + +__attribute__((fat_ptr_checked)) int g1 = 10; // expected-error {{fat_ptr_checked attribute can only used for global or static array variables}} +__attribute__((fat_ptr_checked)) static int g2 = 10; // expected-error {{fat_ptr_checked attribute can only used for global or static array variables}} +extern __attribute__((fat_ptr_checked)) int g3; // expected-error {{fat_ptr_checked attribute can only used for global or static array variables}} +int g4 = 10; +static int g5 = 10; +extern int g6; +void test3() { + int *fat p1 = &fat g4; // expected-error {{'&fat' on global variables without fat_ptr_checked attribute is not allowed}} + int *fat p2 = &fat g5; // expected-error {{'&fat' on global variables without fat_ptr_checked attribute is not allowed}} + int *fat p3 = &fat g6; // expected-error {{'&fat' on global variables without fat_ptr_checked attribute is not allowed}} + + __attribute__((fat_ptr_checked)) static int x1 = 10; // expected-error {{fat_ptr_checked attribute can only used for global or static array variables}} + static int x2 = 10; + int *fat p4 = &fat x2; // expected-error {{'&fat' on global variables without fat_ptr_checked attribute is not allowed}} +} \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/fat_cast.cbs b/clang/test/BSC/Negative/FatPtr/fat_cast.cbs new file mode 100644 index 000000000000..50008ac52448 --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/fat_cast.cbs @@ -0,0 +1,72 @@ +// RUN: %clang_cc1 -verify %s +void foo1(int *fat p); +void foo2(int *p); +int *fat foo3(); +int * foo4(); + +typedef void (*PF1)(int *fat p); +typedef void (*PF2)(int *p); +typedef int *fat (*PF3)(); +typedef int * (*PF4)(); + +void test1() { //cast between function pointer and function + PF1 p1 = foo1; + PF1 p2 = foo2;// expected-error {{incompatible fat function pointer types, cannot cast 'PF1' (aka 'void (*)(int *fat)') to 'void (int *)'}} + PF2 p3 = foo1;// expected-error {{incompatible fat function pointer types, cannot cast 'PF2' (aka 'void (*)(int *)') to 'void (int *fat)'}} + PF2 p4 = foo2; + PF3 p5 = foo3; + PF3 p6 = foo4;// expected-error {{incompatible fat function pointer types, cannot cast 'PF3' (aka 'int *fat (*)(void)') to 'int *(void)'}} + PF4 p7 = foo3;// expected-error {{incompatible fat function pointer types, cannot cast 'PF4' (aka 'int *(*)(void)') to 'int *fat (void)'}} + PF4 p8 = foo4; +} + +void test2() { //cast between zero and fat pointer + int * fat b1 = 0; // expected-error {{incompatible fat types, cannot cast 'int' to 'int *fat'}} + int * fat b2 = {0};// expected-error {{incompatible fat types, cannot cast 'int' to 'int *fat'}} + foo1(0); // expected-error {{incompatible fat types, cannot cast 'int' to 'int *fat'}} +} + +void test3() { //cast between raw pointer and fat ptr + int a = 1; + // explicit cast between raw pointer and fat ptr is allowed. + int *fat p1 = nullptr; + const int *fat p2 = nullptr; + int *p3 = &a; + const int *p4 = &a; + // T * --> T *fat + p1 = (int *fat)&a; + p2 = (const int *fat)&a; + p1 = (int *fat)p3; + p2 = (const int *fat)p4; + p1 = (int *fat)foo4(); + foo1((int *fat)p3); + // T *fat --> T * + p3 = (int *)p1; + p4 = (const int *)p2; + p3 = (int *)foo3(); + foo2((int *)p1); + // implicit cast between raw pointer and fat ptr is not allowed. + // T * --> T *fat + p1 = p3; // expected-error {{incompatible fat types, cannot cast 'int *' to 'int *fat'}} + p2 = p4; // expected-error {{incompatible fat types, cannot cast 'const int *' to 'const int *fat'}} + p1 = foo4();// expected-error {{incompatible fat types, cannot cast 'int *' to 'int *fat'}} + foo1(p3);// expected-error {{incompatible fat types, cannot cast 'int *' to 'int *fat'}} + // T *fat --> T * + p3 = p1; // expected-error {{incompatible fat types, cannot cast 'int *fat' to 'int *'}} + p4 = p2; // expected-error {{incompatible fat types, cannot cast 'const int *fat' to 'const int *'}} + p3 = foo3();// expected-error {{incompatible fat types, cannot cast 'int *fat' to 'int *'}} + foo2(p1);// expected-error {{incompatible fat types, cannot cast 'int *fat' to 'int *'}} + + safe { + int *fat p1 = nullptr; + const int *fat p2 = nullptr; + int *p3 = (int *)p1; // expected-error {{conversion from type 'int *fat' to 'int *' is forbidden in the safe zone}} + const int *p4 = (const int *)p2; // expected-error {{conversion from type 'const int *fat' to 'const int *' is forbidden in the safe zone}} + // T * --> T *fat + p1 = (int *fat)p3; // expected-error {{conversion from type 'int *' to 'int *fat' is forbidden in the safe zone}} + p2 = (const int *fat)p4; // expected-error {{conversion from type 'const int *' to 'const int *fat' is forbidden in the safe zone}} + // T *fat --> T * + p3 = (int *)p1; // expected-error {{conversion from type 'int *fat' to 'int *' is forbidden in the safe zone}} + p4 = (const int *)p2; // expected-error {{conversion from type 'const int *fat' to 'const int *' is forbidden in the safe zone}} + } +} \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/fat_qulifier_only_for_pointer.cbs b/clang/test/BSC/Negative/FatPtr/fat_qulifier_only_for_pointer.cbs new file mode 100644 index 000000000000..3006aa21235d --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/fat_qulifier_only_for_pointer.cbs @@ -0,0 +1,24 @@ +// RUN: %clang_cc1 -verify %s +struct R { + int fat p1; // expected-error {{only pointer type can be qualified by fat}} + const int fat p2; // expected-error {{only pointer type can be qualified by fat}} +}; +struct V { + T fat p1; // expected-error {{only pointer type can be qualified by fat}} + const T fat p2; // expected-error {{only pointer type can be qualified by fat}} +}; + +void f1(int fat a); // expected-error {{only pointer type can be qualified by fat}} +void f2(int fat a) {} // expected-error {{only pointer type can be qualified by fat}} +void f3(T fat a) {} // expected-error {{only pointer type can be qualified by fat}} + +void f4(int *fat a) {} +void f5(T *fat a) {} + + +void test() { + int fat b1; // expected-error {{only pointer type can be qualified by fat}} + const int fat b2; // expected-error {{only pointer type can be qualified by fat}} + int fat * b3; // expected-error {{only pointer type can be qualified by fat}} + const int fat * b4; // expected-error {{only pointer type can be qualified by fat}} +} \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/global_array/array_out_of_bounds1.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/global_array/array_out_of_bounds1.cbs new file mode 100644 index 000000000000..5594d6b6d211 --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/global_array/array_out_of_bounds1.cbs @@ -0,0 +1,12 @@ +// RUN: %clang -enable-fat-ptr %s -o %t.output +// RUN: not %t.output 2>&1 | FileCheck %s + +#include + +__attribute__((fat_ptr_checked)) int arr[3]; +int main() { + int *fat p = &fat arr[3]; + *p = 10; + return 0; +} +// CHECK: pointer offset exceed the allocation size! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/global_array/array_out_of_bounds2.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/global_array/array_out_of_bounds2.cbs new file mode 100644 index 000000000000..2d8b7aae4bb6 --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/global_array/array_out_of_bounds2.cbs @@ -0,0 +1,14 @@ +// RUN: %clang -enable-fat-ptr %s -o %t.output +// RUN: not %t.output 2>&1 | FileCheck %s + +#include + +__attribute__((fat_ptr_checked)) int arr[3]; + +int main() { + int *fat p = &fat arr[2]; + int *fat p1 = p + 1; + *p1 = 10; + return 0; +} +// CHECK: pointer offset exceed the allocation size! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds1.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds1.cbs new file mode 100644 index 000000000000..43d004ddc475 --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds1.cbs @@ -0,0 +1,11 @@ +// RUN: %clang -enable-fat-ptr %s -o %t.output +// RUN: not %t.output 2>&1 | FileCheck %s + +#include + +int main() { + int *fat p = checked_malloc(3 * sizeof(int)); + int a = p[3]; + return 0; +} +// CHECK: pointer offset exceed the allocation size! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds2.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds2.cbs new file mode 100644 index 000000000000..c3b97993ee12 --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds2.cbs @@ -0,0 +1,11 @@ +// RUN: %clang -enable-fat-ptr %s -o %t.output +// RUN: not %t.output 2>&1 | FileCheck %s + +#include +struct S { int *fat p; }; +int main() { + struct S s = { checked_malloc(3 * sizeof(int)) }; + int a = s.p[3]; + return 0; +} +// CHECK: pointer offset exceed the allocation size! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds3.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds3.cbs new file mode 100644 index 000000000000..910359d28473 --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds3.cbs @@ -0,0 +1,12 @@ +// RUN: %clang -enable-fat-ptr %s -o %t.output +// RUN: not %t.output 2>&1 | FileCheck %s + +#include + +int main() { + int *fat p = checked_malloc(3 * sizeof(int)); + int *fat p1 = p + 3; + int a = *p1; + return 0; +} +// CHECK: pointer offset exceed the allocation size! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds4.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds4.cbs new file mode 100644 index 000000000000..9a6cbe7ba543 --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds4.cbs @@ -0,0 +1,13 @@ +// RUN: %clang -enable-fat-ptr %s -o %t.output +// RUN: not %t.output 2>&1 | FileCheck %s + +#include + +struct S { int a; }; +int main() { + struct S *fat p = checked_malloc(sizeof(struct S)); + struct S *fat p1 = p + 1; + p1->a = 10; + return 0; +} +// CHECK: pointer offset exceed the allocation size! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds5.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds5.cbs new file mode 100644 index 000000000000..8267ee6635cd --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds5.cbs @@ -0,0 +1,13 @@ +// RUN: %clang -enable-fat-ptr %s -o %t.output +// RUN: not %t.output 2>&1 | FileCheck %s + +#include + +struct S { int a; int arr[3]; }; +int main() { + struct S *fat p1 = checked_malloc(sizeof(struct S) * 2); + struct S *fat p2 = p1 + 1; + int a = p2->arr[3]; + return 0; +} +// CHECK: pointer offset exceed the allocation size! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds6.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds6.cbs new file mode 100644 index 000000000000..dedb1e9f8d70 --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds6.cbs @@ -0,0 +1,11 @@ +// RUN: %clang -enable-fat-ptr %s -o %t.output +// RUN: not %t.output 2>&1 | FileCheck %s + +#include + +struct S { int arr[3]; }; +int main() { + struct S *fat p = checked_malloc(sizeof(struct S)); + int a = p->arr[3]; +} +// CHECK: pointer offset exceed the allocation size! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/buffer_overflow.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/buffer_overflow.cbs new file mode 100644 index 000000000000..474e6c77a4ab --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/buffer_overflow.cbs @@ -0,0 +1,21 @@ +// RUN: %clang -enable-fat-ptr %s -o %t.output +// RUN: not %t.output longstring 2>&1 | FileCheck %s + +#include +#define BUFSIZE 8 + +char *fat fat_strcpy(char *fat dest, const char *src) { + while (*src != '\0') { + *dest = *src; + dest = dest + 1; + src = src + 1; + } + return dest; +} + +int main(int argc, char **argv) { + char *fat buf = checked_malloc(sizeof(char)*BUFSIZE); + fat_strcpy(buf, argv[1]); + return 0; +} +// CHECK: pointer offset exceed the allocation size! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free1.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free1.cbs new file mode 100644 index 000000000000..b7cf5d4ce5e7 --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free1.cbs @@ -0,0 +1,13 @@ +// RUN: %clang -enable-fat-ptr %s -o %t.output +// RUN: not %t.output 2>&1 | FileCheck %s + +#include + +int main() { + int *fat p = checked_malloc(sizeof(int)); + checked_free(p); + checked_free(p); + return 0; +} + +// CHECK: version number error when free! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free2.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free2.cbs new file mode 100644 index 000000000000..adb31c07c81e --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free2.cbs @@ -0,0 +1,14 @@ +// RUN: %clang -enable-fat-ptr %s -o %t.output +// RUN: not %t.output 2>&1 | FileCheck %s + +#include + +int main() { + int *fat p1 = checked_malloc(sizeof(int)); + int *fat p2 = p1; + checked_free(p1); + checked_free(p2); + return 0; +} + +// CHECK: version number error when free! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free3.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free3.cbs new file mode 100644 index 000000000000..a5a4b818647f --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free3.cbs @@ -0,0 +1,16 @@ +// RUN: %clang -enable-fat-ptr %s -o %t.output +// RUN: not %t.output 2>&1 | FileCheck %s + +#include + +struct G { int *fat a; }; +int main() { + struct G g = { .a = checked_malloc(sizeof(int)) }; + struct G *fat p = checked_malloc(sizeof(struct G)); + p->a = g.a; + checked_free(p->a); + checked_free(g.a); + return 0; +} + +// CHECK: version number error when free! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free4.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free4.cbs new file mode 100644 index 000000000000..f9ddbb207a9c --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free4.cbs @@ -0,0 +1,18 @@ +// RUN: %clang -enable-fat-ptr %s -o %t.output +// RUN: not %t.output 2>&1 | FileCheck %s + +#include + +int *fat test() { + int *fat p = checked_malloc(sizeof(int)); + checked_free(p); + return p; +} + +int main() { + int *fat p = test(); + checked_free(p); + return 0; +} + +// CHECK: version number error when free! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free1.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free1.cbs new file mode 100644 index 000000000000..42fc9676d354 --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free1.cbs @@ -0,0 +1,14 @@ +// RUN: %clang -enable-fat-ptr %s -o %t.output +// RUN: not %t.output 2>&1 | FileCheck %s + +#include + +int main() { + int *fat p = checked_malloc(sizeof(int)); + *p = 10; + checked_free(p); + *p = 10; + return 0; +} + +// CHECK: version number error! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free2.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free2.cbs new file mode 100644 index 000000000000..31ff1217ea98 --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free2.cbs @@ -0,0 +1,15 @@ +// RUN: %clang -enable-fat-ptr %s -o %t.output +// RUN: not %t.output 2>&1 | FileCheck %s + +#include + +struct S { int a; }; +int main() { + struct S *fat p = checked_malloc(sizeof(struct S)); + p->a = 10; + checked_free(p); + p->a = 10; + return 0; +} + +// CHECK: version number error! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free3.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free3.cbs new file mode 100644 index 000000000000..3c9ef85f86d0 --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free3.cbs @@ -0,0 +1,17 @@ +// RUN: %clang -enable-fat-ptr %s -o %t.output +// RUN: not %t.output 2>&1 | FileCheck %s + +#include + +int *fat test() __attribute__((fat_ptr_checked)) { + int *fat p = checked_malloc(sizeof(int)); + checked_free(p); + return p; +} + +int main() { + int *fat p = test(); + *p = 10; + return 0; +} +// CHECK: version number error! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free4.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free4.cbs new file mode 100644 index 000000000000..e0227f63c4db --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free4.cbs @@ -0,0 +1,15 @@ +// RUN: %clang -enable-fat-ptr %s -o %t.output +// RUN: not %t.output 2>&1 | FileCheck %s + +#include + +struct G { int *fat a; }; +int main() { + struct G g = { .a = checked_malloc(sizeof(int)) }; + struct G *fat p = checked_malloc(sizeof(struct G)); + p->a = g.a; + checked_free(p->a); + *g.a = 10; + return 0; +} +// CHECK: version number error! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free5.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free5.cbs new file mode 100644 index 000000000000..0c420fce218d --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free5.cbs @@ -0,0 +1,29 @@ +// RUN: %clang -enable-fat-ptr %s -o %t.output +// RUN: not %t.output 2>&1 | FileCheck %s + +#include +#define BUFSIZE 32 + +char *fat fat_strncpy(char *fat dest, const char *src, size_t n) { + size_t i; + for (i = 0; i < n && src[i] != '\0'; i++) { + dest[i] = src[i]; + } + for (; i < n; i++) { + dest[i] = '\0'; + } + return dest; +} + +int main() { + char *fat buf1 = checked_malloc(BUFSIZE); + checked_free(buf1); + + char *fat buf2 = checked_malloc(BUFSIZE); + memset((char *)buf2, 0, BUFSIZE); + + fat_strncpy(buf1, "hack", 5); + checked_free(buf2); + return 0; +} +// CHECK: version number error! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free6.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free6.cbs new file mode 100644 index 000000000000..4b00fec29811 --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free6.cbs @@ -0,0 +1,14 @@ +// RUN: %clang -enable-fat-ptr %s -o %t.output +// RUN: not %t.output 2>&1 | FileCheck %s + +#include + +void pass_fat(int *p) {} + +int main() { + int *fat p = checked_malloc(sizeof(int)); + checked_free(p); + pass_fat((int *)p); + return 0; +} +// CHECK: version number error! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds1.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds1.cbs new file mode 100644 index 000000000000..07ea7ca2204c --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds1.cbs @@ -0,0 +1,12 @@ +// RUN: %clang -enable-fat-ptr %s -o %t.output +// RUN: not %t.output 2>&1 | FileCheck %s + +#include + +int main() { + int arr[3] = { 1, 2, 3 }; + int *fat p = &fat arr[3]; + *p = 10; + return 0; +} +// CHECK: pointer offset exceed the allocation size! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds2.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds2.cbs new file mode 100644 index 000000000000..80280bf71a33 --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds2.cbs @@ -0,0 +1,13 @@ +// RUN: %clang -enable-fat-ptr %s -o %t.output +// RUN: not %t.output 2>&1 | FileCheck %s + +#include + +int main() { + int arr[3] = { 1, 2, 3 }; + int *fat p = &fat arr[2]; + int *fat p1 = p + 1; + *p1 = 10; + return 0; +} +// CHECK: pointer offset exceed the allocation size! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/buffer_overflow.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/buffer_overflow.cbs new file mode 100644 index 000000000000..d4230082860d --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/buffer_overflow.cbs @@ -0,0 +1,22 @@ +// RUN: %clang -enable-fat-ptr %s -o %t.output +// RUN: not %t.output longstring 2>&1 | FileCheck %s + +#include +#define BUFSIZE 8 + +char *fat fat_strcpy(char *fat dest, const char *src) { + while (*src != '\0') { + *dest = *src; + dest = dest + 1; + src = src + 1; + } + return dest; +} + +int main(int argc, char **argv) { + char buf[BUFSIZE]; + char *fat p = &fat buf[0]; + fat_strcpy(p, argv[1]); + return 0; +} +// CHECK: pointer offset exceed the allocation size! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/return_local_address.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/return_local_address.cbs new file mode 100644 index 000000000000..c39bf785d13b --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/return_local_address.cbs @@ -0,0 +1,17 @@ +// RUN: %clang -enable-fat-ptr %s -o %t.output +// RUN: not %t.output 2>&1 | FileCheck %s + +#include + +int *fat test() { + int local; + int *fat p = &fat local; + return p; +} + +int main() { + int *fat p = test(); + *p = 10; + return 0; +} +// CHECK: version number error! \ No newline at end of file diff --git a/clang/test/BSC/Positive/FatPtr/global_array.cbs b/clang/test/BSC/Positive/FatPtr/global_array.cbs new file mode 100644 index 000000000000..7f4893eab5f0 --- /dev/null +++ b/clang/test/BSC/Positive/FatPtr/global_array.cbs @@ -0,0 +1,23 @@ +// RUN: %clang %s -enable-fat-ptr -o %t.output +// RUN: %t.output +// expected-no-diagnostics + +#include + +__attribute__((fat_ptr_checked)) int arr[3]; +void test1() { + int *fat p = &fat arr[2]; + *p = 10; +} + +void test2() { + int *fat p = &fat arr[1]; + int *fat p1 = p + 1; + *p1 = 10; +} + +int main() { + test1(); + test2(); + return 0; +} \ No newline at end of file diff --git a/clang/test/BSC/Positive/FatPtr/heap.cbs b/clang/test/BSC/Positive/FatPtr/heap.cbs new file mode 100644 index 000000000000..5c8a83374c7e --- /dev/null +++ b/clang/test/BSC/Positive/FatPtr/heap.cbs @@ -0,0 +1,94 @@ +// RUN: %clang %s -enable-fat-ptr -o %t.output +// RUN: %t.output +// expected-no-diagnostics + +#include +struct S { int a; }; +void test1() { + int *fat p1 = checked_malloc(sizeof(int)); + *p1 = 10; + checked_free(p1); + + struct S *fat p2 = checked_malloc(sizeof(struct S)); + p2->a = 10; + checked_free(p2); +} + +struct G { int *fat a; }; +void test2() { + struct G g = { .a = checked_malloc(sizeof(int)) }; + struct G *fat p = checked_malloc(sizeof(struct G)); + *p = g; + *p->a = 10; + checked_free(p->a); + checked_free(p); +} + +void test3() { + int *fat p1 = checked_malloc(sizeof(int)); + int *fat p2 = checked_malloc(sizeof(int)); + *p1 = *p2; + checked_free(p1); + checked_free(p2); +} + +void test4() { + int *fat p1 = checked_malloc(sizeof(int)); + int *fat p2 = checked_malloc(sizeof(int)); + p1 = p2; + checked_free(p1); //only free once +} + +void test5() { + int *fat p = checked_malloc(sizeof(int) * 3); + int a = p[2]; + checked_free(p); +} + +void test6() { + int *fat p1 = checked_malloc(sizeof(int)); + int *fat p2 = p1; + int *p3 = (int *)p1; + float *fat p4 = (float *fat)p1; + checked_free(p1); +} + +void test7() { + int *fat p = checked_malloc(3 * sizeof(int)); + int *fat p1 = p + 2; + int a = *p1; +} + +void test8(int *fat p1, int *fat p2) { + p1 = checked_malloc(3 * sizeof(int)); + p2 = p1 + 2; + int a = *p1; + int b = *p2; +} + +struct K { int arr[3]; }; +void test9() { + struct K *fat p1 = checked_malloc(sizeof(struct K) * 2); + int a = p1->arr[3]; + int *fat p2 = &fat p1->arr[3]; + *p2 = 10; + + struct K *fat p3 = p1 + 1; + int b = p3->arr[2]; + int *fat p4 = &fat p3->arr[2]; + *p4 = 10; +} + +int main() { + test1(); + test2(); + test3(); + test4(); + test5(); + test6(); + test7(); + int *fat p1; + int *fat p2; + test8(p1, p2); + return 0; +} \ No newline at end of file diff --git a/clang/test/BSC/Positive/FatPtr/stack.cbs b/clang/test/BSC/Positive/FatPtr/stack.cbs new file mode 100644 index 000000000000..c44dab32adca --- /dev/null +++ b/clang/test/BSC/Positive/FatPtr/stack.cbs @@ -0,0 +1,24 @@ +// RUN: %clang %s -enable-fat-ptr -o %t.output +// RUN: %t.output +// expected-no-diagnostics + +#include + +void test1() { + int arr[3] = { 1, 2, 3 }; + int *fat p = &fat arr[2]; + *p = 10; +} + +void test2() { + int arr[3] = { 1, 2, 3 }; + int *fat p = &fat arr[1]; + int *fat p1 = p + 1; + *p1 = 10; +} + +int main() { + test1(); + test2(); + return 0; +} \ No newline at end of file -- Gitee From fcdc22c9e6a16a8ff2a67b98ef99111763aafd70 Mon Sep 17 00:00:00 2001 From: xiaoyu Date: Tue, 31 Dec 2024 17:05:03 +0800 Subject: [PATCH 02/29] [FatPtr] Desugar the function type of the self-call Expr 1. For self-call Expr, desugar the function type of the calling expression 2. nullptr auto cast in fat ptr 3. fix type check when assignment 4. Transform a classic binary sorting algorithm: Bisort in Olden case 2 tree add --- clang/include/clang/Sema/Sema.h | 1 + clang/lib/Sema/BSC/SemaBSCDestructor.cpp | 15 + clang/lib/Sema/BSC/SemaBSCFatPtr.cpp | 65 +++- clang/lib/Sema/SemaExpr.cpp | 45 +-- .../FatPtr/nullptr_cast/nullptr_cast.cbs | 41 +++ .../perform_anaysis/deref_loop/deref_loop.cbs | 39 +++ .../perform_anaysis/olden/bisort/bitonic.c | 303 +++++++++++++++++ .../olden/bisort_bsc/bitonic.cbs | 304 ++++++++++++++++++ .../perform_anaysis/olden/treeadd/node.c | 233 ++++++++++++++ .../perform_anaysis/olden/treeadd/tree.h | 25 ++ .../olden/treeadd_bsc/node.cbs | 193 +++++++++++ .../olden/treeadd_bsc/tree.hbs | 29 ++ .../destructor_call/destructor_call.cbs | 21 ++ 13 files changed, 1268 insertions(+), 46 deletions(-) create mode 100644 clang/test/BSC/Positive/FatPtr/nullptr_cast/nullptr_cast.cbs create mode 100644 clang/test/BSC/Positive/FatPtr/perform_anaysis/deref_loop/deref_loop.cbs create mode 100644 clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/bisort/bitonic.c create mode 100644 clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/bisort_bsc/bitonic.cbs create mode 100644 clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd/node.c create mode 100644 clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd/tree.h create mode 100644 clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd_bsc/node.cbs create mode 100644 clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd_bsc/tree.hbs create mode 100644 clang/test/BSC/Positive/SafeZone/destructor_call/destructor_call.cbs diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 29bd39ffb14b..0c647044f968 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -3234,6 +3234,7 @@ public: std::pair BuildAllocationUnitForGlobalArrayVar(VarDecl *VD); // BSC Destructor related. + bool IsCallDestructorExpr(Expr *E); BSCMethodDecl *getOrInsertBSCDestructor(RecordDecl *RD); void HandleBSCDestructorBody(RecordDecl *RD, BSCMethodDecl *Destructor, std::stack InstanceFields); diff --git a/clang/lib/Sema/BSC/SemaBSCDestructor.cpp b/clang/lib/Sema/BSC/SemaBSCDestructor.cpp index fc88350e2ba8..7ab37e21fc0d 100644 --- a/clang/lib/Sema/BSC/SemaBSCDestructor.cpp +++ b/clang/lib/Sema/BSC/SemaBSCDestructor.cpp @@ -734,6 +734,21 @@ public: } }; +bool Sema::IsCallDestructorExpr(Expr *E) { + Expr *NakedE = E->IgnoreParens(); + if (auto *CastExpr = llvm::dyn_cast(NakedE)) { + if (Expr *SubExpr = CastExpr->getSubExpr()) { + if (auto *DRE = llvm::dyn_cast(SubExpr)) { + NamedDecl *NDecl = DRE->getDecl(); + if (auto *MD = dyn_cast(NDecl)) { + return MD->isDestructor(); + } + } + } + } + return false; +} + void Sema::DesugarDestructorCall(FunctionDecl *FD) { if (!getLangOpts().BSC) return; diff --git a/clang/lib/Sema/BSC/SemaBSCFatPtr.cpp b/clang/lib/Sema/BSC/SemaBSCFatPtr.cpp index 41a5c1ae7b21..e789857a4099 100644 --- a/clang/lib/Sema/BSC/SemaBSCFatPtr.cpp +++ b/clang/lib/Sema/BSC/SemaBSCFatPtr.cpp @@ -200,12 +200,14 @@ public: FatPtrFD["_new_version_number"] = nullptr; } + bool IsNullPtrExpr(Expr *E); // Entry to desugar FunctionDecl void TransformFunctionDecl(); // Entry to desugar global or local VarDecl void TransformVarDecl(VarDecl *VD); // Entry to desugar global or local RecordDecl void TransformRecordDecl(RecordDecl *RD); + void TransformSelfCallExprFunctionType(CallExpr *CE); StmtResult TransformCompoundStmt(CompoundStmt *CS, bool IsStmtExpr); StmtResult TransformCompoundStmt(CompoundStmt *CS); @@ -258,6 +260,13 @@ private: SmallVector &Comps); }; +bool TransformFatPtr::IsNullPtrExpr (Expr *E) { + if (auto *CastExpr = dyn_cast(E)) { + E = CastExpr->getSubExpr(); + } + return isa(E); +} + void TransformFatPtr::TransformFunctionDecl() { // Desugar fat qualified return type. QualType ReturnTy = @@ -291,7 +300,7 @@ void TransformFatPtr::TransformVarDecl(VarDecl *VD) { QualType QT = DesugarFatPtrType(VD->getLocation(), VD->getType()); VD->setType(QT); if (Expr *Init = VD->getInit()) { - if (QT->isFatPtrRecordType() && isa(Init)) { + if (QT->isFatPtrRecordType() && IsNullPtrExpr(Init)) { // `int *fat p = nullptr;` should be desugared to // `_FatPtr p = (_FatPtr) { nullptr, 0, 0 };` VD->setInit(BuildFatPtrCompoundLiteralExprForRawPtr(Init, QT)); @@ -632,7 +641,7 @@ ExprResult TransformFatPtr::TransformInitListExpr(InitListExpr *ILE) { StmtResult TransformFatPtr::TransformReturnStmt(ReturnStmt *RS) { Expr *RV = RS->getRetValue(); QualType ReturnQT = FD->getReturnType(); - if (ReturnQT->isFatPtrRecordType() && isa(RV)) { + if (ReturnQT->isFatPtrRecordType() && IsNullPtrExpr(RV)) { // if a function has fat pointer return type, such as `int *fat foo();` // `return nullptr;` should be desugared to // `return (_FatPtr){ nullptr, 0, 0 };` @@ -643,18 +652,37 @@ StmtResult TransformFatPtr::TransformReturnStmt(ReturnStmt *RS) { return RS; } +void TransformFatPtr::TransformSelfCallExprFunctionType(CallExpr *CE) { + QualType FQT = FD->getType(); + Expr *CalleeExpr = CE->getCallee(); + if (auto *ImplCastExpr = dyn_cast(CalleeExpr)) { + ImplCastExpr->setType(SemaRef.Context.getPointerType(FQT)); + if (Expr *subExpr = ImplCastExpr->getSubExpr()) { + if (auto *declRefExpr = dyn_cast(subExpr)) { + declRefExpr->setType(FQT); + } + } + } +} + ExprResult TransformFatPtr::TransformCallExpr(CallExpr *CE) { CE->setType(DesugarFatPtrType(CE->getBeginLoc(), CE->getType())); FunctionDecl *CalleeFD = CE->getDirectCallee(); + // For self-call Expr, desugar the function type of the calling expression + if (CalleeFD == FD) { + TransformSelfCallExprFunctionType(CE); + } for (unsigned i = 0; i < CalleeFD->getNumParams(); i++) { QualType ParamQT = CalleeFD->getParamDecl(i)->getType(); Expr *ArgE = CE->getArg(i); - if (ParamQT->isFatPtrRecordType() && isa(ArgE)) { + if (ParamQT->isFatPtrRecordType() && IsNullPtrExpr(ArgE)) { // if a function has fat pointer parameter, such as // `void foo(int *fat p);` // `foo(nullptr)` should be desugared to // `foo((_FatPtr){ nullptr, 0, 0 })` - CE->setArg(i, BuildFatPtrCompoundLiteralExprForRawPtr(ArgE, ParamQT)); + Expr *CLE = BuildFatPtrCompoundLiteralExprForRawPtr(ArgE, ParamQT); + CLE = SemaRef.DefaultFunctionArrayLvalueConversion(CLE).get(); + CE->setArg(i, CLE); } else CE->setArg(i, BaseTransform::TransformExpr(ArgE).get()); } @@ -773,13 +801,18 @@ ExprResult TransformFatPtr::TransformBinaryOperator(BinaryOperator *BO) { // `(_FatPtr) { p.raw_ptr + 1, p.key_version, p.offset + 1 * sizeof(int) }` return HandleBOFatPtrAddOrSubInteger(BO); } else if (BO->isAssignmentOp() && LHSQT.isFatPtrType() && - isa(RHS)) { + IsNullPtrExpr(RHS)) { // `p = nullptr;` should be desugared to // `p = (_FatPtr){ nullptr, 0, 0 };` LHSQT = DesugarFatPtrType(SLoc, LHSQT); BO->setLHS(BaseTransform::TransformExpr(LHS).get()); BO->setRHS(BuildFatPtrCompoundLiteralExprForRawPtr(RHS, LHSQT)); return BO; + } else if (BO->isEqualityOp() && LHSQT.isFatPtrType() && IsNullPtrExpr(RHS)) { + // `p1 == p2` should be desugared to `p1.ptr == nullptr` + DesugarToMemberPtr = true; + BO->setLHS(BaseTransform::TransformExpr(BO->getLHS()).get()); + return BO; } BO->setLHS(BaseTransform::TransformExpr(LHS).get()); BO->setRHS(BaseTransform::TransformExpr(RHS).get()); @@ -1215,6 +1248,11 @@ MemberExpr *TransformFatPtr::BuildMemberForFatPtr(Expr *FatPtrBaseE, Expr *TransformFatPtr::BuildFatPtrCompoundLiteralExprForRawPtr( Expr *RawPtrE, QualType QT) { + if (auto *ICE = dyn_cast(RawPtrE)) { + if (isa(ICE->getSubExpr())) { + RawPtrE = ICE->getSubExpr(); + } + } SourceLocation SLoc = RawPtrE->getBeginLoc(); llvm::APInt Zero(32, 0); QualType UInt32Ty = SemaRef.Context.getIntTypeForBitwidth(32, false); @@ -1338,8 +1376,7 @@ bool Sema::CheckFatQualTypeCStyleCast(QualType LHSType, QualType RHSType) { if (IsSameType) { return true; } - if ((LHSCanType.isFatQualified() || LHSCanType->isFatPtrRecordType()) && - (RHSCanType.isFatQualified() || RHSCanType->isFatPtrRecordType())) { + if (LHSCanType.isFatPtrType() && RHSCanType.isFatPtrType()) { return true; } if (IsPointer) { @@ -1395,6 +1432,10 @@ bool Sema::CheckFatQualTypeAssignment(QualType LHSType, Expr *RHSExpr) { SourceLocation ExprLoc = RHSExpr->getBeginLoc(); bool Res = true; + // Fat pointer can be inited by nullptr. + if (LHSCanType.isFatPtrType() && isa(RHSExpr)) + return true; + if (LHSCanType.isFatQualified() || RHSCanType.isFatQualified()) { if (TraitDecl *TD = TryDesugarTrait(LHSCanType)) { if (RHSCanType->isPointerType()) { @@ -1414,24 +1455,18 @@ bool Sema::CheckFatQualTypeAssignment(QualType LHSType, Expr *RHSExpr) { } } } - - // Fat pointer can be inited by nullptr. - if (LHSCanType.isFatQualified() && isa(RHSExpr)) - return true; - if (LHSCanType->isVoidPointerType() && RHSCanType->isPointerType()) { if (LHSCanType->getPointeeType().isConstQualified() == RHSCanType->getPointeeType().isConstQualified()) return true; } - if (!Context.hasSameType(LHSCanType, RHSCanType)) Res = false; } else { Res = CheckFatQualTypeAssignment(LHSType, RHSCanType, ExprLoc); } - if ((LHSCanType.isFatQualified() || LHSCanType->isFatPtrRecordType()) && - (RHSCanType.isFatQualified() || RHSCanType->isFatPtrRecordType())) + if ((LHSCanType.isFatPtrType() && RHSCanType.isFatPtrType()) && + !(LHSCanType.isFatQualified() && RHSCanType.isFatQualified())) return true; if (!Res) { Diag(ExprLoc, diag::err_fat_qualcheck_incompatible) diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 1f10537f65c5..30381de78101 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -7128,7 +7128,7 @@ ExprResult Sema::BuildCallExpr(Scope *Scope, Expr *Fn, SourceLocation LParenLoc, #if ENABLE_BSC if (getLangOpts().BSC) { // unsafe function call is forbidden in the safe zone - if ((IsInSafeZone()) && + if (!IsCallDestructorExpr(Fn) && IsInSafeZone() && (Fn->getType()->checkFunctionProtoType(SZ_None) || Fn->getType()->checkFunctionProtoType(SZ_Unsafe))) { Diag(Fn->getBeginLoc(), diag::err_unsafe_action) @@ -10449,33 +10449,16 @@ static bool IsTraitEqualExpr(Sema &S, QualType DstType, QualType SrcType, } static bool IsFatPtrEqualExpr(Sema &S, QualType DstType, QualType SrcType) { - if (DstType.getCanonicalType().isFatQualified()) - if (SrcType.getCanonicalType()->isRecordType()) - if (RecordDecl *RD = - SrcType.getCanonicalType()->getAs()->getDecl()) - if (RD->getNameAsString() == "_FatPtr") - return true; - if (SrcType.getCanonicalType().isFatQualified()) - if (DstType.getCanonicalType()->isRecordType()) - if (RecordDecl *RD = - DstType.getCanonicalType()->getAs()->getDecl()) - if (RD->getNameAsString() == "_FatPtr") - return true; - if (DstType.getCanonicalType().isFatQualified()) - if (SrcType.getCanonicalType()->isRecordType()) - if (RecordDecl *RD = - SrcType.getCanonicalType()->getAs()->getDecl()) - if (RD->getNameAsString() == "_FatPtr") - return true; - if (SrcType.getCanonicalType()->isRecordType() && - DstType.getCanonicalType()->isRecordType()) - if (RecordDecl *DstRD = - DstType.getCanonicalType()->getAs()->getDecl()) - if (DstRD->getNameAsString() == "_FatPtr") - if (RecordDecl *SrcRD = - SrcType.getCanonicalType()->getAs()->getDecl()) - if (SrcRD->getNameAsString() == "_FatPtr") - return true; + if (auto *Typedef = dyn_cast(DstType)) { + DstType = Typedef->getDecl()->getUnderlyingType(); + } + if (auto *Typedef = dyn_cast(SrcType)) { + SrcType = Typedef->getDecl()->getUnderlyingType(); + } + if (DstType.isFatPtrType() && + (SrcType.isFatPtrType() || SrcType->isNullPtrType())) { + return true; + } return false; } #endif @@ -10521,8 +10504,7 @@ Sema::CheckSingleAssignmentConstraints(QualType LHSType, ExprResult &CallerRHS, if (!CheckBorrowQualTypeAssignment(LHSType, RHS.get())) return IncompatibleBorrowPointer; } - if ((RHSCanType.isFatQualified() || RHSCanType->isFatPtrRecordType()) && - (LHSCanType.isFatQualified() || LHSCanType->isFatPtrRecordType())) { + if (RHSCanType.isFatPtrType() || LHSCanType.isFatPtrType()) { if (!CheckFatQualTypeAssignment(LHSType, RHS.get())) return IncompatibleFatPointer; } @@ -17843,7 +17825,8 @@ bool Sema::DiagnoseAssignmentResult(AssignConvertType ConvTy, case IncompatiblePointer: #if ENABLE_BSC // int a = 1; trait F* f = &a; - if (getLangOpts().BSC && IsTraitEqualExpr(*this, DstType, SrcType, Loc)) + if (getLangOpts().BSC && (IsTraitEqualExpr(*this, DstType, SrcType, Loc) || + IsFatPtrEqualExpr(*this, DstType, SrcType))) return false; #endif if (Action == AA_Passing_CFAudited) { diff --git a/clang/test/BSC/Positive/FatPtr/nullptr_cast/nullptr_cast.cbs b/clang/test/BSC/Positive/FatPtr/nullptr_cast/nullptr_cast.cbs new file mode 100644 index 000000000000..9a47ca5dac2b --- /dev/null +++ b/clang/test/BSC/Positive/FatPtr/nullptr_cast/nullptr_cast.cbs @@ -0,0 +1,41 @@ +// RUN: %clang %s -enable-fat-ptr -o %t.output +// RUN: %t.output +// expected-no-diagnostics + +#include +#include + +typedef struct node HANDLE; + +struct node { + int value; + HANDLE *fat left; +}; + +#define NIL ((HANDLE *) 0) +typedef int * fat myFatPtr; + +myFatPtr test() { + return nullptr; +} + +myFatPtr foo(int *fat p) { + return p; +} + +int main() { + HANDLE * fat a = nullptr; + myFatPtr b = foo(nullptr); + myFatPtr c = test(); + + if (a == nullptr) { + printf("a == nullptr\n"); + } + if (b == nullptr) { + printf("b == nullptr\n"); + } + if (c == nullptr) { + printf("c == nullptr\n"); + } + return 0; +} \ No newline at end of file diff --git a/clang/test/BSC/Positive/FatPtr/perform_anaysis/deref_loop/deref_loop.cbs b/clang/test/BSC/Positive/FatPtr/perform_anaysis/deref_loop/deref_loop.cbs new file mode 100644 index 000000000000..ac7ec4d457e9 --- /dev/null +++ b/clang/test/BSC/Positive/FatPtr/perform_anaysis/deref_loop/deref_loop.cbs @@ -0,0 +1,39 @@ +// RUN: %clang %s -enable-fat-ptr -o %test.output +// RUN: %test.output 20 + +#include + +long dealwithargs(int argc, char *argv[]) +{ + int num = 0; + if (argc > 2) + num = atoi(argv[2]); + return 1L << num; +} + +void test_raw(long n) { + long *p = malloc(sizeof(long)); + while (n > 0) { + *p = n; + n--; + } +} + +void test_fat(long n) { + long *fat p = checked_malloc(sizeof(long)); + while (n > 0) { + *p = n; + n--; + } +} + +int main(int argc, char *argv[]) { + long n = dealwithargs(argc, argv); + int flag = atoi(argv[1]); + if (flag == 0) { + test_raw(n); + } else { + test_fat(n); + } + return 0; +} diff --git a/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/bisort/bitonic.c b/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/bisort/bitonic.c new file mode 100644 index 000000000000..cc3f2cb278aa --- /dev/null +++ b/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/bisort/bitonic.c @@ -0,0 +1,303 @@ +// RUN: %clang %s -o %test.output +// RUN: %test.output 300 + +#include +#include + +#define CONST_m1 10000 +#define CONST_b 31415821 +#define RANGE 100 + +typedef struct node HANDLE; + +struct node { + int value; + HANDLE* left; + HANDLE *right; +}; + +typedef struct future_cell_int{ + HANDLE *value; +} future_cell_int; + +extern void *malloc(unsigned long); + +#define NIL ((HANDLE *) 0) + +int NumNodes, NDim; + +int random_c(int); + +int flag=0,foo=0; + +#define LocalNewNode(h,v) \ +{ \ + h = (HANDLE *) malloc(sizeof(struct node)); \ + h->value = v; \ + h->left = NIL; \ + h->right = NIL; \ + }; + +#define NewNode(h,v,procid) LocalNewNode(h,v) + +int mylog(int num) { + int j=0,k=1; + + while(k 3) + flag = atoi(argv[3]); + else + flag = 1; + + if (argc > 2) + NumNodes = atoi(argv[2]); + else + NumNodes = 4; + + if (argc > 1) + size = atoi(argv[1]); + else + size = 1 << 15; + + NDim = mylog(NumNodes); + return size; +} + +void InOrder(HANDLE *h) { + HANDLE *l, *r; + if ((h != NIL)) { + l = h->left; + r = h->right; + InOrder(l); + static unsigned char counter = 0; + if (counter++ == 0) /* reduce IO */ + printf("%d @ 0x%x\n",h->value, 0); + InOrder(r); + } +} + +int mult(int p, int q) { + int p1, p0, q1, q0; + + p1 = p/CONST_m1; p0 = p%CONST_m1; + q1 = q/CONST_m1; q0 = q%CONST_m1; + return ((p0*q1+p1*q0) % CONST_m1)*CONST_m1+p0*q0; +} + +/* Generate the nth random_c # */ +int skiprand(int seed, int n) { + for (; n; n--) seed=random_c(seed); + return seed; +} + +int random_c(int seed) { + return mult(seed,CONST_b)+1; +} + +HANDLE* RandTree(int n, int seed, int node, int level) { + int next_val,my_name; + future_cell_int f_left, f_right; + HANDLE *h; + my_name=foo++; + if (n > 1) { + int newnode; + if (levelleft = f_left.value; + h->right = f_right.value; + } else { + h = 0; + } + return h; +} + +void SwapValue(HANDLE *l, HANDLE *r) { + int temp,temp2; + + temp = l->value; + temp2 = r->value; + r->value = temp; + l->value = temp2; +} + +void +/***********/ +SwapValLeft(HANDLE *l, HANDLE *r, HANDLE *ll, HANDLE *rl, int lval, int rval) +/***********/ +{ + r->value = lval; + r->left = ll; + l->left = rl; + l->value = rval; +} + + +void +/************/ +SwapValRight(HANDLE *l, HANDLE *r, HANDLE *lr, HANDLE *rr, int lval, int rval) +/************/ +{ + r->value = lval; + r->right = lr; + l->right = rr; + l->value = rval; + /*printf("Swap Val Right l 0x%x,r 0x%x val: %d %d\n",l,r,lval,rval);*/ +} + +int +/********************/ +Bimerge(HANDLE *root, int spr_val, int dir) +/********************/ +{ int rightexchange; + int elementexchange; + HANDLE *pl,*pll,*plr; + HANDLE *pr,*prl,*prr; + HANDLE *rl; + HANDLE *rr; + int rv,lv; + + + /*printf("enter bimerge %x\n", root);*/ + rv = root->value; + + pl = root->left; + pr = root->right; + rightexchange = ((rv > spr_val) ^ dir); + if (rightexchange) + { + root->value = spr_val; + spr_val = rv; + } + + while ((pl != NIL)) + { + /*printf("pl = 0x%x,pr = 0x%x\n",pl,pr);*/ + lv = pl->value; /* <------- 8.2% load penalty */ + pll = pl->left; + plr = pl->right; /* <------- 1.35% load penalty */ + rv = pr->value; /* <------ 57% load penalty */ + prl = pr->left; /* <------ 7.6% load penalty */ + prr = pr->right; /* <------ 7.7% load penalty */ + elementexchange = ((lv > rv) ^ dir); + if (rightexchange) + if (elementexchange) + { + SwapValRight(pl,pr,plr,prr,lv,rv); + pl = pll; + pr = prl; + } + else + { pl = plr; + pr = prr; + } + else + if (elementexchange) + { + SwapValLeft(pl,pr,pll,prl,lv,rv); + pl = plr; + pr = prr; + } + else + { pl = pll; + pr = prl; + } + } + if ((root->left != NIL)) + { + int value; + rl = root->left; + rr = root->right; + value = root->value; + + root->value=Bimerge(rl,value,dir); + spr_val=Bimerge(rr,spr_val,dir); + } + /*printf("exit bimerge %x\n", root);*/ + return spr_val; +} + +int +/*******************/ +Bisort(HANDLE *root, int spr_val, int dir) +/*******************/ +{ HANDLE *l; + HANDLE *r; + int val; + /*printf("bisort %x\n", root);*/ + if (root->left == NIL) /* <---- 8.7% load penalty */ + { + if (((root->value > spr_val) ^ dir)) + { + val = spr_val; + spr_val = root->value; + root->value =val; + } + } + else + { + int ndir; + l = root->left; + r = root->right; + val = root->value; + /*printf("root 0x%x, l 0x%x, r 0x%x\n", root,l,r);*/ + root->value=Bisort(l,val,dir); + ndir = !dir; + spr_val=Bisort(r,spr_val,ndir); + spr_val=Bimerge(root,spr_val,dir); + } + /*printf("exit bisort %x\n", root);*/ + return spr_val; +} + +int main(int argc, char **argv) { + HANDLE *h; + int sval; + int n; + + n = dealwithargs(argc,argv); + + printf("Bisort with %d size of dim %d\n", n, NDim); + + h = RandTree(n,12345768,0,0); + sval = random_c(245867) % RANGE; + if (flag) { + InOrder(h); + printf("%d\n",sval); + } + printf("**************************************\n"); + printf("BEGINNING BITONIC SORT ALGORITHM HERE\n"); + printf("**************************************\n"); + + sval=Bisort(h,sval,0); + + if (flag) { + printf("Sorted Tree:\n"); + InOrder(h); + printf("%d\n",sval); + } + + sval=Bisort(h,sval,1); + + if (flag) { + printf("Sorted Tree:\n"); + InOrder(h); + printf("%d\n",sval); + } + + return 0; +} diff --git a/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/bisort_bsc/bitonic.cbs b/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/bisort_bsc/bitonic.cbs new file mode 100644 index 000000000000..4535551ee5b1 --- /dev/null +++ b/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/bisort_bsc/bitonic.cbs @@ -0,0 +1,304 @@ +// RUN: %clang %s -enable-fat-ptr -o %test.output +// RUN: %test.output 3000 + +#include +#include +#include + +#define CONST_m1 10000 +#define CONST_b 31415821 +#define RANGE 100 + +typedef struct node HANDLE; + +struct node { + int value; + HANDLE *fat left; + HANDLE *fat right; +}; + +typedef struct future_cell_int{ + HANDLE *fat value; +} future_cell_int; + +#define NIL ((HANDLE *) 0) + +int NumNodes, NDim; + +int random_c(int); + +int flag=0,foo=0; + +//FIXME +#define LocalNewNode(h,v) \ +{ \ + h = checked_malloc(sizeof(HANDLE)); \ + h->value = v; \ + h->left = (HANDLE *fat)NIL; \ + h->right = (HANDLE *fat)NIL; \ + }; + +#define NewNode(h,v,procid) LocalNewNode(h,v) + +int mylog(int num) { + int j=0,k=1; + + while(k 3) + flag = atoi(argv[3]); + else + flag = 1; + + if (argc > 2) + NumNodes = atoi(argv[2]); + else + NumNodes = 4; + + if (argc > 1) + size = atoi(argv[1]); + else + size = 1 << 15; + + NDim = mylog(NumNodes); + return size; +} + +void InOrder(HANDLE *fat h) { + HANDLE *fat l, *fat r; + if ((h != nullptr)) { + l = h->left; + r = h->right; + InOrder(l); + static unsigned char counter = 0; + if (counter++ == 0) /* reduce IO */ + // printf("%d @ 0x%x\n",h->value, 0); + InOrder(r); + } +} + +int mult(int p, int q) { + int p1, p0, q1, q0; + + p1 = p/CONST_m1; p0 = p%CONST_m1; + q1 = q/CONST_m1; q0 = q%CONST_m1; + return ((p0*q1+p1*q0) % CONST_m1)*CONST_m1+p0*q0; +} + +/* Generate the nth random_c # */ +int skiprand(int seed, int n) { + for (; n; n--) seed=random_c(seed); + return seed; +} + +int random_c(int seed) { + return mult(seed, CONST_b) + 1; +} + +HANDLE *fat RandTree(int n, int seed, int node, int level) { + int next_val,my_name; + future_cell_int f_left, f_right; + HANDLE *fat h; + my_name=foo++; + if (n > 1) { + int newnode; + if (levelleft = f_left.value; + h->right = f_right.value; + } else { + h = checked_malloc(sizeof(HANDLE)); //FIXME + } + return h; +} + +void SwapValue(HANDLE *fat l, HANDLE *fat r) { + int temp,temp2; + + temp = l->value; + temp2 = r->value; + r->value = temp; + l->value = temp2; +} + +void +/***********/ +SwapValLeft(HANDLE *fat l, HANDLE *fat r, HANDLE *fat ll, HANDLE *fat rl, int lval, int rval) +/***********/ +{ + r->value = lval; + r->left = ll; + l->left = rl; + l->value = rval; +} + + +void +/************/ +SwapValRight(HANDLE *fat l, HANDLE *fat r, HANDLE *fat lr, HANDLE *fat rr, int lval, int rval) +/************/ +{ + r->value = lval; + r->right = lr; + l->right = rr; + l->value = rval; + /*printf("Swap Val Right l 0x%x,r 0x%x val: %d %d\n",l,r,lval,rval);*/ +} + +int +/********************/ +Bimerge(HANDLE *fat root, int spr_val, int dir) +/********************/ +{ int rightexchange; + int elementexchange; + HANDLE *fat pl,*fat pll,*fat plr; + HANDLE *fat pr,*fat prl,*fat prr; + HANDLE *fat rl; + HANDLE *fat rr; + int rv,lv; + + + /*printf("enter bimerge %x\n", root);*/ + rv = root->value; + + pl = root->left; + pr = root->right; + rightexchange = ((rv > spr_val) ^ dir); + if (rightexchange) + { + root->value = spr_val; + spr_val = rv; + } + + while ((pl != nullptr)) + { + /*printf("pl = 0x%x,pr = 0x%x\n",pl,pr);*/ + lv = pl->value; /* <------- 8.2% load penalty */ + pll = pl->left; + plr = pl->right; /* <------- 1.35% load penalty */ + rv = pr->value; /* <------ 57% load penalty */ + prl = pr->left; /* <------ 7.6% load penalty */ + prr = pr->right; /* <------ 7.7% load penalty */ + elementexchange = ((lv > rv) ^ dir); + if (rightexchange) + if (elementexchange) + { + SwapValRight(pl,pr,plr,prr,lv,rv); + pl = pll; + pr = prl; + } + else + { pl = plr; + pr = prr; + } + else + if (elementexchange) + { + SwapValLeft(pl,pr,pll,prl,lv,rv); + pl = plr; + pr = prr; + } + else + { pl = pll; + pr = prl; + } + } + HANDLE *fat root_left = root->left; + if (root_left != nullptr) + { + int value; + rl = root->left; + rr = root->right; + value = root->value; + + root->value=Bimerge(rl,value,dir); + spr_val=Bimerge(rr,spr_val,dir); + } + /*printf("exit bimerge %x\n", root);*/ + return spr_val; +} + +int +/*******************/ +Bisort(HANDLE *fat root, int spr_val, int dir) +/*******************/ +{ HANDLE *fat l; + HANDLE *fat r; + int val; + /*printf("bisort %x\n", root);*/ + HANDLE *fat root_left = root->left; + if (root_left == nullptr) /* <---- 8.7% load penalty */ + { + if (((root->value > spr_val) ^ dir)){ + val = spr_val; + spr_val = root->value; + root->value =val; + } + } + else + { + int ndir; + l = root->left; + r = root->right; + val = root->value; + /*printf("root 0x%x, l 0x%x, r 0x%x\n", root,l,r);*/ + root->value=Bisort(l,val,dir); + ndir = !dir; + spr_val=Bisort(r,spr_val,ndir); + spr_val=Bimerge(root,spr_val,dir); + } + /*printf("exit bisort %x\n", root);*/ + return spr_val; +} + +int main(int argc, char **argv) { + HANDLE *fat h; + int sval; + int n; + + n = dealwithargs(argc,argv); + + printf("Bisort with %d size of dim %d\n", n, NDim); + + h = RandTree(n,12345768,0,0); + sval = random_c(245867) % RANGE; + if (flag) { + InOrder(h); + printf("%d\n",sval); + } + printf("**************************************\n"); + printf("BEGINNING BITONIC SORT ALGORITHM HERE\n"); + printf("**************************************\n"); + + sval=Bisort(h,sval,0); + + if (flag) { + printf("Sorted Tree:\n"); + InOrder(h); + printf("%d\n",sval); + } + + sval=Bisort(h,sval,1); + + if (flag) { + printf("Sorted Tree:\n"); + InOrder(h); + printf("%d\n",sval); + } + + return 0; +} diff --git a/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd/node.c b/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd/node.c new file mode 100644 index 000000000000..c8af4371bf4b --- /dev/null +++ b/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd/node.c @@ -0,0 +1,233 @@ +// RUN: %clang %s -DTORONTO -o %test.output +// RUN: %test.output 16 + +/* For copyright information, see olden_v1.0/COPYRIGHT */ + +/* node.c + */ +#ifndef TORONTO +#include +#include +#include "mem-ref.h" +#endif + +#ifdef FUTURES +#include "future-cell.h" +#endif + +#include +#include "tree.h" + +#ifdef TORONTO +int NumNodes; +#else +int __NumNodes; +#endif + +typedef struct { + long level; +} startmsg_t; + +#ifndef TORONTO +void filestuff() +{ + CMMD_fset_io_mode(stdout, CMMD_independent); + fcntl(fileno(stdout), F_SETFL, O_APPEND); + if (CMMD_self_address()) exit(0); + __InitRegs(0); +} +#endif + +int dealwithargs(int argc, char *argv[]) +{ + int level; + +#ifdef TORONTO + if (argc > 2) + NumNodes = atoi(argv[2]); + else + NumNodes = 4; +#else + if (argc > 2) + __NumNodes = atoi(argv[2]); + else + __NumNodes = 4; +#endif + + if (argc > 1) + level = atoi(argv[1]); + else + level = 16; + + return level; +} + +tree_t *TreeAlloc (int level, int lo, int proc) { + if (level == 0) + return NULL; + else { + struct tree *new, *right, *left; + new = (struct tree *) malloc(sizeof(tree_t)); + left = TreeAlloc(level -1, lo+proc/2, proc/2); + right=TreeAlloc(level-1,lo,proc/2); + new->val = 1; + new->left = (struct tree *) left; + new->right = (struct tree *) right; + return new; + } +} + +/* TreeAdd: + */ +int TreeAdd (tree_t *t) +{ + if (t == NULL) { + return 0; + } + else { +#ifdef FUTURES + future_cell_int leftval; + int rightval; + tree_t *tleft, *tright; + int value; + + tleft = t->left; + RPC(tleft, tleft,TreeAdd,&(leftval)); + NOTEST(); + tright = t->right; + rightval = TreeAdd(tright); + RTOUCH(&leftval); + /*chatting("after touch @ 0x%x\n",t);*/ + value = t->val; + /*chatting("returning from treeadd %d\n",*/ + /*leftval.value + rightval.value + value);*/ + return leftval.value + rightval + value; + RETEST(); +#else + int leftval; + int rightval; + tree_t *tleft, *tright; + int value; + + tleft = t->left; /* <---- 57% load penalty */ + leftval = TreeAdd(tleft); + tright = t->right; /* <---- 11.4% load penalty */ + rightval = TreeAdd(tright); + /*chatting("after touch\n");*/ + value = t->val; + /*chatting("returning from treeadd %d\n",*/ + /*leftval.value + rightval.value + value);*/ + return leftval + rightval + value; +#endif + } +} /* end of TreeAdd */ + +int main (int argc, char *argv[]) +{ + tree_t *root; + int level,result; + +#ifdef FUTURES + level = SPMDInit(argc,argv); +#else +#ifndef TORONTO + filestuff(); +#endif + level = dealwithargs(argc, argv); +#endif +#ifndef TORONTO + CMMD_node_timer_clear(0); + CMMD_node_timer_clear(1); +#endif +#ifdef TORONTO + chatting("Treeadd with %d levels on %d processors \n", + level, NumNodes); +#else + chatting("Treeadd with %d levels on %d processors \n", + level, __NumNodes); +#endif + /* only processor 0 will continue here. */ + chatting("About to enter TreeAlloc\n"); +#ifndef TORONTO + CMMD_node_timer_start(0); +#endif + +#ifdef TORONTO + root = TreeAlloc (level, 0, NumNodes); +#else + root = TreeAlloc (level, 0, __NumNodes); +#endif + +#ifndef TORONTO + CMMD_node_timer_stop(0); +#endif + chatting("About to enter TreeAdd\n"); + +#ifndef PLAIN + ClearAllStats(); +#endif +#ifndef TORONTO + CMMD_node_timer_start(1); +#endif +{ int i; for (i = 0; i < 100; ++i) + result = TreeAdd (root); +} +#ifndef TORONTO + CMMD_node_timer_stop(1); +#endif + chatting("Received result of %d\n",result); + +#ifndef TORONTO + chatting("Alloc Time = %f seconds\n", CMMD_node_timer_elapsed(0)); + chatting(/* TreeAdd: + */ +int TreeAdd (tree_t *t) +{ + if (t == NULL) { + return 0; + } + else { +#ifdef FUTURES + future_cell_int leftval; + int rightval; + tree_t *tleft, *tright; + int value; + + tleft = t->left; + RPC(tleft, tleft,TreeAdd,&(leftval)); + NOTEST(); + tright = t->right; + rightval = TreeAdd(tright); + RTOUCH(&leftval); + /*chatting("after touch @ 0x%x\n",t);*/ + value = t->val; + /*chatting("returning from treeadd %d\n",*/ + /*leftval.value + rightval.value + value);*/ + return leftval.value + rightval + value; + RETEST(); +#else + int leftval; + int rightval; + tree_t *tleft, *tright; + int value; + + tleft = t->left; /* <---- 57% load penalty */ + leftval = TreeAdd(tleft); + tright = t->right; /* <---- 11.4% load penalty */ + rightval = TreeAdd(tright); + /*chatting("after touch\n");*/ + value = t->val; + /*chatting("returning from treeadd %d\n",*/ + /*leftval.value + rightval.value + value);*/ + return leftval + rightval + value; +#endif + } +} /* end of TreeAdd */ +"Add Time = %f seconds\n", CMMD_node_timer_elapsed(1)); +#endif + +#ifdef FUTURES + __ShutDown(); +#endif + exit(0); +} diff --git a/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd/tree.h b/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd/tree.h new file mode 100644 index 000000000000..b8d075e41cb0 --- /dev/null +++ b/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd/tree.h @@ -0,0 +1,25 @@ +/* For copyright information, see olden_v1.0/COPYRIGHT */ + +/* tree.h + */ + +#ifdef TORONTO +#include +#define chatting printf +#define PLAIN +#endif + +typedef struct tree { + int val; + struct tree *left, *right; +} tree_t; + +tree_t *TreeAlloc (int level, int lo, int hi); +int TreeAdd (tree_t *t); + + + + + + + diff --git a/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd_bsc/node.cbs b/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd_bsc/node.cbs new file mode 100644 index 000000000000..f859e368b7ae --- /dev/null +++ b/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd_bsc/node.cbs @@ -0,0 +1,193 @@ +// RUN: %clang %s -DTORONTO -enable-fat-ptr -o %test.output +// RUN: %test.output 16 + +#ifndef TORONTO +#include +#include +#endif + +#ifdef FUTURES +#include "future-cell.h" +#endif + +#ifndef TORONTO +#include "mem-ref.h" +#endif + +#include +#include "tree.hbs" + +#ifdef TORONTO +int NumNodes; +#else +int __NumNodes; +#endif + +extern int atoi(const char *); + +#ifndef TORONTO +void filestuff() +{ + CMMD_fset_io_mode(stdout, CMMD_independent); + fcntl(fileno(stdout), F_SETFL, O_APPEND); + if (CMMD_self_address()) exit(0); + __InitRegs(0); +} +#endif + +int dealwithargs(int argc, char *argv[]) +{ + int level; + +#ifdef TORONTO + if (argc > 2) + NumNodes = atoi(argv[2]); + else + NumNodes = 4; +#else + if (argc > 2) + __NumNodes = atoi(argv[2]); + else + __NumNodes = 4; +#endif + + if (argc > 1) + level = atoi(argv[1]); + else + level = 16; + + return level; +} + +typedef struct { + long level; +} startmsg_t; + +tree_t *fat TreeAlloc (int level, int lo, int proc) { + if (level == 0) + return nullptr; + else { + tree_t *fat new; + tree_t *fat right; + tree_t *fat left; + new = checked_malloc(sizeof(tree_t)); + left = TreeAlloc(level -1, lo+proc/2, proc/2); + right = TreeAlloc(level-1,lo,proc/2); + new->val = 1; + new->left = left; + new->right = right; + return new; + } +} + +/* TreeAdd: + */ +int TreeAdd (tree_t *fat t) +{ + if (t == nullptr) { + return 0; + } + else { +#ifdef FUTURES + future_cell_int leftval; + int rightval; + tree_t *fat tleft, *fat tright; + int value; + + tleft = t->left; + RPC(tleft, tleft,TreeAdd,&(leftval)); + NOTEST(); + tright = t->right; + rightval = TreeAdd(tright); + RTOUCH(&leftval); + /*chatting("after touch @ 0x%x\n",t);*/ + value = t->val; + /*chatting("returning from treeadd %d\n",*/ + /*leftval.value + rightval.value + value);*/ + return leftval.value + rightval + value; + RETEST(); +#else + int leftval; + int rightval; + tree_t *fat tleft; + tree_t *fat tright; + int value; + + tleft = t->left; /* <---- 57% load penalty */ + leftval = TreeAdd(tleft); + tright = t->right; /* <---- 11.4% load penalty */ + rightval = TreeAdd(tright); + /*chatting("after touch\n");*/ + value = t->val; + /*chatting("returning from treeadd %d\n",*/ + /*leftval.value + rightval.value + value);*/ + return leftval + rightval + value; +#endif + } +} /* end of TreeAdd */ + +int main (int argc, char *argv[]) +{ + tree_t *fat root; + int level, result; + +#ifdef FUTURES + level = SPMDInit(argc,argv); +#else +#ifndef TORONTO + filestuff(); +#endif + level = dealwithargs(argc, argv); +#endif +#ifndef TORONTO + CMMD_node_timer_clear(0); + CMMD_node_timer_clear(1); +#endif +#ifdef TORONTO + chatting("Treeadd with %d levels on %d processors \n", + level, NumNodes); +#else + chatting("Treeadd with %d levels on %d processors \n", + level, __NumNodes); +#endif + /* only processor 0 will continue here. */ + chatting("About to enter TreeAlloc\n"); +#ifndef TORONTO + CMMD_node_timer_start(0); +#endif + int a0 = 0; +#ifdef TORONTO + root = TreeAlloc (level, a0, NumNodes); +#else + root = TreeAlloc (level, a0, __NumNodes); +#endif + +#ifndef TORONTO + CMMD_node_timer_stop(0); +#endif + chatting("About to enter TreeAdd\n"); + +#ifndef PLAIN + ClearAllStats(); +#endif +#ifndef TORONTO + CMMD_node_timer_start(1); +#endif +{ int i; for (i = 0; i < 100; ++i) + result = TreeAdd(root); +} +#ifndef TORONTO + CMMD_node_timer_stop(1); +#endif + chatting("Received result of %d\n",result); + +#ifndef TORONTO + chatting("Alloc Time = %f seconds\n", CMMD_node_timer_elapsed(0)); + chatting("Add Time = %f seconds\n", CMMD_node_timer_elapsed(1)); +#endif + +#ifdef FUTURES + __ShutDown(); +#endif + exit(0); +} diff --git a/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd_bsc/tree.hbs b/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd_bsc/tree.hbs new file mode 100644 index 000000000000..0b8014a09f3d --- /dev/null +++ b/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd_bsc/tree.hbs @@ -0,0 +1,29 @@ +/* For copyright information, see olden_v1.0/COPYRIGHT */ + +/* tree.h + */ + +#ifdef TORONTO +#include +#define chatting printf +#define PLAIN +#endif + +#include +typedef struct tree tree_t; + +struct tree { + int val; + tree_t *fat left; + tree_t *fat right; +}; + +tree_t *fat TreeAlloc (int level, int lo, int hi); +int TreeAdd (tree_t *fat t); + + + + + + + diff --git a/clang/test/BSC/Positive/SafeZone/destructor_call/destructor_call.cbs b/clang/test/BSC/Positive/SafeZone/destructor_call/destructor_call.cbs new file mode 100644 index 000000000000..9c5c2136db11 --- /dev/null +++ b/clang/test/BSC/Positive/SafeZone/destructor_call/destructor_call.cbs @@ -0,0 +1,21 @@ +// RUN: %clang %s -o %t.output +// RUN: %t.output +// expected-no-diagnostics + +owned struct RawVec { +public: + T a; + ~RawVec(This this) {} +}; + +safe int test(void) { + unsafe { + RawVec rv = { .a = 1}; + } + return 0; +} + +int main() { + int a = test(); + return 0; +} -- Gitee From db4fff3e09788cdc1fa50281abfb356f3d5bceee Mon Sep 17 00:00:00 2001 From: liuxinyi Date: Mon, 17 Feb 2025 11:38:28 +0800 Subject: [PATCH 03/29] [FatPtr] optimize bsc_fat_ptr.hbs and bugfix 1. optimize error report, when check_version or check_offset report an error, also print line number, function name and file name 2. optimize bsc_fat_ptr.hbs (1) to avoid type-checking error after rewrite to C: 1) modify checked_malloc and checked_free as generic function 2) divide FatPtr parameter of check_version and check_offset into its three fields (2) mark function in bsc_fat_ptr.hbs `static` to avoid link error when where are several source file include bsc_fat_ptr.hbs at the same time (3) define macro CHECKED for __attribut__((fat_ptr_checked)) (4) when check_free a fat ptr, report error if raw_ptr is nullptr 3. some refactor (1) refactor type checking when CStyleCast or assignment between two fat ptr type (2) add a preprcocess stage before desugar. 4. some bugfix: (1) if CondExpr of branchStmt or loopStmt have fat ptr deref operation, we insert check_version and check_offset for CondExpr. (2) desugar some fat ptr operation: if(p), if(!p), p1 == p2, p ? p1 : p2, p++, ++p... (3) insert _local_lock_version = 0 before every ReturnStmt (4) fat ptr as arg of variadic function, such as printf --- clang/include/clang/AST/ASTContext.h | 4 +- clang/include/clang/AST/BSC/WalkerBSC.h | 3 +- clang/include/clang/AST/Type.h | 17 +- .../clang/Basic/BSC/DiagnosticBSCSemaKinds.td | 8 +- clang/include/clang/Sema/Sema.h | 9 +- clang/lib/AST/ASTContext.cpp | 44 +- clang/lib/AST/BSC/TypeBSC.cpp | 158 +- clang/lib/AST/ExprConstant.cpp | 28 +- clang/lib/CodeGen/CGExprAgg.cpp | 4 +- clang/lib/Headers/bsc_include/bsc_fat_ptr.hbs | 96 +- clang/lib/Parse/ParseDecl.cpp | 4 +- clang/lib/Sema/BSC/SemaBSCFatPtr.cpp | 1431 +++++++++++++---- clang/lib/Sema/SemaCast.cpp | 15 +- clang/lib/Sema/SemaDecl.cpp | 25 +- clang/lib/Sema/SemaExpr.cpp | 399 +++-- clang/lib/Sema/SemaExprMember.cpp | 12 +- clang/lib/Sema/SemaOverload.cpp | 7 + clang/lib/Sema/SemaStmt.cpp | 7 + clang/lib/Sema/SemaType.cpp | 21 +- clang/test/BSC/Negative/FatPtr/addr_fat.cbs | 8 +- .../{fat_cast.cbs => fat_explicit_cast.cbs} | 61 +- .../FatPtr/fat_func_decl_several_times.cbs | 29 + .../BSC/Negative/FatPtr/fat_implicit_cast.cbs | 73 + .../FatPtr/fat_qulifier_only_for_pointer.cbs | 1 - .../BSC/Negative/FatPtr/func_ptr_has_fat.cbs | 23 + .../FatPtr/self_inc_or_dec_of_fat_ptr.cbs | 22 + .../global_array/array_out_of_bounds1.cbs | 5 +- .../global_array/array_out_of_bounds2.cbs | 3 +- .../heap/array_out_of_bounds1.cbs | 5 +- .../heap/array_out_of_bounds10.cbs | 16 + .../heap/array_out_of_bounds11.cbs | 17 + .../heap/array_out_of_bounds12.cbs | 15 + .../heap/array_out_of_bounds13.cbs | 16 + .../heap/array_out_of_bounds14.cbs | 13 + .../heap/array_out_of_bounds15.cbs | 15 + .../heap/array_out_of_bounds16.cbs | 14 + .../heap/array_out_of_bounds17.cbs | 18 + .../heap/array_out_of_bounds18.cbs | 18 + .../heap/array_out_of_bounds19.cbs | 19 + .../heap/array_out_of_bounds2.cbs | 5 +- .../heap/array_out_of_bounds3.cbs | 5 +- .../heap/array_out_of_bounds4.cbs | 5 +- .../heap/array_out_of_bounds5.cbs | 5 +- .../heap/array_out_of_bounds6.cbs | 5 +- .../heap/array_out_of_bounds7.cbs | 17 + .../heap/array_out_of_bounds8.cbs | 17 + .../heap/array_out_of_bounds9.cbs | 15 + .../heap/buffer_overflow.cbs | 5 +- .../heap/double_free1.cbs | 8 +- .../heap/double_free2.cbs | 8 +- .../heap/double_free3.cbs | 10 +- .../heap/double_free4.cbs | 8 +- .../heap/use_after_free1.cbs | 7 +- .../heap/use_after_free2.cbs | 7 +- .../heap/use_after_free3.cbs | 9 +- .../heap/use_after_free4.cbs | 9 +- .../heap/use_after_free5.cbs | 11 +- .../heap/use_after_free6.cbs | 7 +- .../heap/use_after_free7.cbs | 15 + .../heap/use_after_free8.cbs | 17 + .../stack/array_out_of_bounds1.cbs | 3 +- .../stack/array_out_of_bounds2.cbs | 3 +- .../stack/array_out_of_bounds3.cbs | 14 + .../stack/array_out_of_bounds4.cbs | 14 + .../stack/buffer_overflow.cbs | 3 +- .../stack/return_local_address.cbs | 3 +- clang/test/BSC/Positive/FatPtr/heap.cbs | 66 +- .../Positive/FatPtr/multi_level_pointer.cbs | 29 + .../perform_anaysis/deref_loop/deref_loop.cbs | 2 +- .../olden/bisort_bsc/bitonic.cbs | 14 +- .../olden/treeadd_bsc/node.cbs | 2 +- .../BSC/Positive/FatPtr/string_literal.cbs | 27 + 72 files changed, 2253 insertions(+), 805 deletions(-) rename clang/test/BSC/Negative/FatPtr/{fat_cast.cbs => fat_explicit_cast.cbs} (40%) create mode 100644 clang/test/BSC/Negative/FatPtr/fat_func_decl_several_times.cbs create mode 100644 clang/test/BSC/Negative/FatPtr/fat_implicit_cast.cbs create mode 100644 clang/test/BSC/Negative/FatPtr/func_ptr_has_fat.cbs create mode 100644 clang/test/BSC/Negative/FatPtr/self_inc_or_dec_of_fat_ptr.cbs create mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds10.cbs create mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds11.cbs create mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds12.cbs create mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds13.cbs create mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds14.cbs create mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds15.cbs create mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds16.cbs create mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds17.cbs create mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds18.cbs create mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds19.cbs create mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds7.cbs create mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds8.cbs create mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds9.cbs create mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free7.cbs create mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free8.cbs create mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds3.cbs create mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds4.cbs create mode 100644 clang/test/BSC/Positive/FatPtr/multi_level_pointer.cbs create mode 100644 clang/test/BSC/Positive/FatPtr/string_literal.cbs diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index 328205520b01..64d6e5ec52a9 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -2564,7 +2564,9 @@ public: bool hasSameType(const Type *T1, const Type *T2) const { return getCanonicalType(T1) == getCanonicalType(T2); } - +#if ENABLE_BSC + bool hasSameFatPtrType(QualType LHSType, QualType RHSType); +#endif /// Return this type as a completely-unqualified array type, /// capturing the qualifiers in \p Quals. /// diff --git a/clang/include/clang/AST/BSC/WalkerBSC.h b/clang/include/clang/AST/BSC/WalkerBSC.h index c98e20d67d41..fd5868207d95 100644 --- a/clang/include/clang/AST/BSC/WalkerBSC.h +++ b/clang/include/clang/AST/BSC/WalkerBSC.h @@ -89,8 +89,7 @@ public: bool VisitQualType(QualType QT) { if (QT.isOwnedQualified() || QT.isBorrowQualified() || - QT->hasBorrowFields() || QT->hasOwnedFields() || QT.isFatQualified() || - QT->hasFatFields()) { + QT->hasBorrowFields() || QT->hasOwnedFields() || QT.hasFat()) { return true; } if (IsDesugaredFromTraitType(QT)) { diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h index 22f7e986a5b0..c189a56d6331 100644 --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -939,6 +939,9 @@ public: /// Determine whether this type is fat-qualified. bool isFatQualified() const; bool isFatPtrType() const; + bool isFatPtrRecordType() const; + QualType getFatPtrPointeeType() const; + bool hasFat() const; #endif /// Determine whether this particular QualType instance has the @@ -1065,7 +1068,6 @@ public: /// Add the `fat` type qualifier to this QualType. void addFat() { addFastQualifiers(Qualifiers::Fat); } QualType withFat() const { return withFastQualifiers(Qualifiers::Fat); } - bool hasFat() const; #endif /// Add the `volatile` type qualifier to this QualType. @@ -2209,7 +2211,6 @@ public: bool hasBorrowFields() const; - bool hasFatFields() const; #endif /// Test for a particular builtin type. @@ -2301,8 +2302,6 @@ public: bool isTraitType() const; bool isTraitPointerType() const; bool hasTraitType() const; - bool isFatPtrRecordType() const; - QualType getFatPtrPointeeType() const; bool isBSCCalculatedTypeInCompileTime() const; bool checkFunctionProtoType(SafeZoneSpecifier SZS) const; bool isOwnedStructureType() const; @@ -2942,12 +2941,10 @@ public: } static bool classof(const Type *T) { return T->getTypeClass() == Pointer; } - #if ENABLE_BSC - bool hasOwnedFields() const; +#if ENABLE_BSC + bool hasOwnedFields() const; bool hasBorrowFields() const; - - bool hasFatFields() const; #endif }; @@ -4975,10 +4972,8 @@ protected: withoutBorrow }; - enum fatStatus{unInitFat, withFat, withoutFat}; mutable ownedStatus hasOwn = ownedStatus::unInitOwned; mutable borrowStatus hasBorrow = borrowStatus::unInitBorrow; - mutable fatStatus hasFat = fatStatus::unInitFat; #endif public: @@ -5000,8 +4995,6 @@ public: void initBorrowStatus() const; bool hasFatFields() const; - - void initFatStatus() const; #endif bool isSugared() const { return false; } diff --git a/clang/include/clang/Basic/BSC/DiagnosticBSCSemaKinds.td b/clang/include/clang/Basic/BSC/DiagnosticBSCSemaKinds.td index c38945739b42..fad65b1fe493 100644 --- a/clang/include/clang/Basic/BSC/DiagnosticBSCSemaKinds.td +++ b/clang/include/clang/Basic/BSC/DiagnosticBSCSemaKinds.td @@ -222,8 +222,10 @@ def err_fat_ptr_checked_attr_global_array_var : Error< "fat_ptr_checked attribute can only used for global or static array variables">; def err_typecheck_invalid_fat_not_pointer : Error< "only pointer type can be qualified by fat">; +def err_typecheck_invalid_fat_not_function_pointer : Error< + "function pointer type cannot be qualified by fat">; def err_fat_qualcheck_incompatible : Error< - "incompatible fat types, cannot cast %0 to %1">; + "incompatible fat types, cannot %select{implicitly|explicitly}0 cast %1 to %2">; def err_fat_funcPtr_incompatible : Error< "incompatible fat function pointer types, cannot cast %0 to %1">; def err_addr_fat_on_no_fat_pointer : Error< @@ -235,4 +237,6 @@ def err_fat_ptr_type_not_found : Error< def err_fat_ptr_func_not_found : Error< "fat ptr related function not found, you need to include %0">; def err_fat_ptr_missing_specialization : Error< - "missing definition of template specialization for fat ptr %0">; \ No newline at end of file + "missing definition of template specialization for fat ptr %0">; +def err_no_inc_or_dec_of_fat_ptr : Error< + "increment or decrement operation of fat pointer is not allowed here">; \ No newline at end of file diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 0c647044f968..4af40cb08a0c 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -3221,16 +3221,17 @@ public: bool IsBSCCompatibleFutureType(QualType Ty); // BSC Fat Ptr related. - bool CheckFatQualTypeCStyleCast(QualType LHSType, QualType RHSType, - SourceLocation RLoc); + bool CheckFatQualTypeCStyleCast(QualType LHSType, Expr *RHSExpr); bool CheckFatQualTypeCStyleCast(QualType LHSType, QualType RHSType); bool CheckFatQualTypeAssignment(QualType LHSType, Expr *RHSExpr); - bool CheckFatQualTypeAssignment(QualType LHSType, QualType RHSType, - SourceLocation RLoc); + bool CheckFatQualTypeAssignment(QualType LHSType, QualType RHSType); bool CheckFatFunctionPointerType(QualType LHSType, Expr *RHSExpr); + bool HasDiffFatTypeAtBothFunction(QualType LHSType, QualType RHSType); + bool CheckSelfIncOrDecOfFatPtr(Expr *E); void DesugarFunctionDeclWithFatPtr(FunctionDecl *FD); void DesugarRecordDeclWithFatPtr(RecordDecl *RD); void DesugarGlobalVarDeclWithFatPtr(VarDecl *VD); + void DesugarTypedefNameDeclWithFatPtr(TypedefNameDecl *TND); std::pair BuildAllocationUnitForGlobalArrayVar(VarDecl *VD); // BSC Destructor related. diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 0d34dfc80377..d730b1638a11 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -3247,6 +3247,45 @@ bool ASTContext::hasSameFunctionTypeIgnoringPtrSizes(QualType T, QualType U) { getFunctionTypeWithoutPtrSizes(U)); } +#if ENABLE_BSC +bool ASTContext::hasSameFatPtrType(QualType LHSType, QualType RHSType) { + if (LHSType.hasFat() != RHSType.hasFat()) + return false; + if (LHSType->isFunctionProtoType() && RHSType->isFunctionProtoType()) { + const FunctionProtoType *LHSFuncType = LHSType->getAs(); + const FunctionProtoType *RHSFuncType = RHSType->getAs(); + if (LHSFuncType->hasFatRetOrParams() != RHSFuncType->hasFatRetOrParams() || + !hasSameFatPtrType(LHSFuncType->getReturnType(), + RHSFuncType->getReturnType()) || + LHSFuncType->getNumParams() != RHSFuncType->getNumParams()) { + return false; + } + for (unsigned i = 0; i < LHSFuncType->getNumParams(); i++) { + if (!hasSameFatPtrType(LHSFuncType->getParamType(i), + RHSFuncType->getParamType(i))) { + return false; + } + } + return true; + } + bool LHSIsFatPtrType = LHSType.isFatPtrType(); + bool RHSIsFatPtrType = RHSType.isFatPtrType(); + if (LHSIsFatPtrType && RHSIsFatPtrType) { + // _FatPtr and T *fat are the same type. + return hasSameFatPtrType(LHSType.getFatPtrPointeeType(), + RHSType.getFatPtrPointeeType()); + } else if (LHSIsFatPtrType != RHSIsFatPtrType) { + return false; + } else if (LHSType->isPointerType() && RHSType->isPointerType()) { + // _FatPtr * and T *fat * are the same type. + return hasSameFatPtrType(LHSType->getPointeeType(), + RHSType->getPointeeType()); + } else { + return hasSameType(LHSType, RHSType); + } +} +#endif + void ASTContext::adjustExceptionSpec( FunctionDecl *FD, const FunctionProtoType::ExceptionSpecInfo &ESI, bool AsWritten) { @@ -10151,9 +10190,8 @@ bool ASTContext::typesAreCompatible(QualType LHS, QualType RHS, if (getLangOpts().CPlusPlus) return hasSameType(LHS, RHS); #if ENABLE_BSC - if (getLangOpts().BSC && getLangOpts().EnableFatPtr && - LHS->isFatPtrRecordType() && RHS->isFatPtrRecordType()) - return true; + if (getLangOpts().BSC && (LHS.hasFat() || RHS.hasFat())) + return hasSameFatPtrType(LHS, RHS); #endif return !mergeTypes(LHS, RHS, false, CompareUnqualified).isNull(); } diff --git a/clang/lib/AST/BSC/TypeBSC.cpp b/clang/lib/AST/BSC/TypeBSC.cpp index f7e1cbf52688..5f51c1388ca9 100644 --- a/clang/lib/AST/BSC/TypeBSC.cpp +++ b/clang/lib/AST/BSC/TypeBSC.cpp @@ -66,49 +66,91 @@ bool Type::hasBorrowFields() const { return false; } -bool PointerType::hasFatFields() const { - QualType R = getPointeeType(); - if (R.isFatQualified()) { - return true; +// T *fat, T *fat *, T *fat[n], struct S which has fat fields... +bool QualType::hasFat() const { + auto Ty = getTypePtr(); + // getCanonicalType from FunctionProtoType will lose fat qualifier + // of return type or parameter types, from ArrayType will lose fat + // qualifier of element type, so we handle these two kinds type first. + if (Ty->isFunctionPointerType()) { + auto FPT = (*this) + ->getAs() + ->getPointeeType() + ->getAs(); + return FPT->hasFatRetOrParams(); } - if (R.getTypePtr()->hasFatFields()) { - return true; + if (Ty->isFunctionProtoType()) { + auto FPT = (*this)->getAs(); + return FPT->hasFatRetOrParams(); + } + if (Ty->isArrayType()) { + if (auto AT = dyn_cast(Ty)) + return AT->getElementType().hasFat(); } + auto CanQT = getCanonicalType(); + if (CanQT.isFatPtrType()) + return true; + if (auto RT = dyn_cast(CanQT)) + if (RT->hasFatFields()) + return true; + if (auto PT = dyn_cast(CanQT)) + return PT->getPointeeType().hasFat(); return false; } -bool Type::hasFatFields() const { - if (const auto *RecTy = dyn_cast(CanonicalType)) { - return RecTy->hasFatFields(); - } else if (const auto *PointerTy = dyn_cast(CanonicalType)) { - return PointerTy->hasFatFields(); - } - return false; +// return true T *fat, _FatPtr, ... +bool QualType::isFatPtrType() const { + if (getTypePtr()->isArrayType()) + return false; + QualType CanQT = getCanonicalType(); + return CanQT.isLocalFatQualified() || CanQT.isFatPtrRecordType(); } -bool Type::isFatPtrRecordType() const { - if (this->isRecordType()) - if (RecordDecl *RD = this->getAs()->getDecl()) - if (RD->getNameAsString() == "_FatPtr") - return true; +bool QualType::isFatPtrRecordType() const { + if (auto RT = dyn_cast(getCanonicalType())) + if (auto CTSD = dyn_cast(RT->getDecl())) + return CTSD->getNameAsString() == "_FatPtr" && + CTSD->getTemplateArgs().size() == 1; return false; } -QualType Type::getFatPtrPointeeType() const { - if (this->isFatPtrRecordType()) - if (auto TST = dyn_cast(this)) - if (TST->getNumArgs() == 1) - return TST->begin()->getAsType(); - if (auto PT = dyn_cast(this)) +QualType QualType::getFatPtrPointeeType() const { + QualType CanQT = getCanonicalType(); + assert(CanQT.isFatPtrType() && "Can only get pointee type from fat ptr type"); + if (auto RT = dyn_cast(CanQT)) { + if (auto CTSD = dyn_cast(RT->getDecl())) { + const TemplateArgumentList &Args = CTSD->getTemplateArgs(); + assert( + CTSD->getNameAsString() == "_FatPtr" && Args.size() == 1 && + Args.get(0).getKind() == TemplateArgument::Type && + "Fat pointer type is illegal, you need to include "); + return Args.get(0).getAsType(); + } + } + if (auto PT = dyn_cast(CanQT)) return PT->getPointeeType(); return QualType(); } -bool QualType::isFatPtrType() const { - if (isFatQualified()) - return true; - if (const Type *ty = getTypePtr()) - return ty->isFatPtrRecordType(); +bool RecordType::hasFatFields() const { + std::vector RecordTypeList; + RecordTypeList.push_back(this); + unsigned NextToCheckIndex = 0; + + while (RecordTypeList.size() > NextToCheckIndex) { + for (FieldDecl *FD : + RecordTypeList[NextToCheckIndex]->getDecl()->fields()) { + QualType FieldTy = FD->getType(); + if (FieldTy.isFatPtrType()) + return true; + if (const auto *FieldRecTy = + FieldTy.getCanonicalType()->getAs()) { + if (!llvm::is_contained(RecordTypeList, FieldRecTy)) + RecordTypeList.push_back(FieldRecTy); + } + } + ++NextToCheckIndex; + } return false; } @@ -137,11 +179,12 @@ bool FunctionProtoType::hasBorrowRetOrParams() const { } bool FunctionProtoType::hasFatRetOrParams() const { - if (getReturnType().isFatQualified()) { + QualType ReturnType = getReturnType(); + if (ReturnType.hasFat()) { return true; } for (auto ParamType : getParamTypes()) { - if (ParamType.isFatQualified()) { + if (ParamType.hasFat()) { return true; } } @@ -312,53 +355,6 @@ bool RecordType::hasBorrowFields() const { return false; } -void RecordType::initFatStatus() const { - if (hasFat != fatStatus::unInitFat) - return; - std::vector RecordTypeList; - RecordTypeList.push_back(this); - unsigned NextToCheckIndex = 0; - - while (RecordTypeList.size() > NextToCheckIndex) { - for (FieldDecl *FD : - RecordTypeList[NextToCheckIndex]->getDecl()->fields()) { - QualType FieldTy = FD->getType(); - if (FieldTy.isFatQualified()) { - hasFat = fatStatus::withFat; - return; - } - QualType tempQT = FieldTy; - const Type *tempT = tempQT.getTypePtr(); - while (tempT->isPointerType()) { - tempQT = tempT->getPointeeType(); - if (tempQT.isFatQualified()) { - hasFat = fatStatus::withFat; - return; - } else { - tempQT = tempQT.getCanonicalType(); - tempT = tempQT.getTypePtr(); - } - } - FieldTy = tempQT.getCanonicalType(); - if (const auto *FieldRecTy = FieldTy->getAs()) { - if (llvm::find(RecordTypeList, FieldRecTy) == RecordTypeList.end()) - RecordTypeList.push_back(FieldRecTy); - } - } - ++NextToCheckIndex; - } - hasFat = fatStatus::withoutFat; - return; -} - -bool RecordType::hasFatFields() const { - if (hasFat == fatStatus::unInitFat) - initFatStatus(); - if (hasFat == fatStatus::withFat) - return true; - return false; -} - bool Type::isBSCFutureType() const { if (const auto *RT = getAs()) { RecordDecl *RD = RT->getAsRecordDecl(); @@ -397,12 +393,6 @@ bool QualType::hasBorrow() const { return getTypePtr()->hasBorrowFields(); } -bool QualType::hasFat() const { - if (isFatQualified()) - return true; - return getTypePtr()->hasFatFields(); -} - bool QualType::isConstBorrow() const { QualType QT = QualType(getTypePtr(), getLocalFastQualifiers()); if (QT.isLocalBorrowQualified()) { diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 894692ab2d5c..0c9154cfe4ea 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -8038,10 +8038,11 @@ public: bool EvalOK; if (E->isArrow()) { #if ENABLE_BSC - if (this->Info.getLangOpts().BSC && this->Info.getLangOpts().EnableFatPtr && - E->getBase()->getType()->isFatPtrRecordType()) { + if (this->Info.getLangOpts().BSC && + this->Info.getLangOpts().EnableFatPtr && + E->getBase()->getType().isFatPtrRecordType()) { EvalOK = true; - BaseTy = E->getBase()->getType()->getFatPtrPointeeType(); + BaseTy = E->getBase()->getType().getFatPtrPointeeType(); } else { #endif EvalOK = evaluatePointer(E->getBase(), Result); @@ -8481,6 +8482,11 @@ bool LValueExprEvaluator::VisitArraySubscriptExpr(const ArraySubscriptExpr *E) { // C++17's rules require us to evaluate the LHS first, regardless of which // side is the base. for (const Expr *SubExpr : {E->getLHS(), E->getRHS()}) { +#if ENABLE_BSC + if (Info.getLangOpts().BSC && Info.getLangOpts().EnableFatPtr && + SubExpr->getType().isFatPtrRecordType()) + return true; +#endif if (SubExpr == E->getBase() ? !evaluatePointer(SubExpr, Result) : !EvaluateInteger(SubExpr, Index, Info)) { if (!Info.noteFailure()) @@ -8496,7 +8502,7 @@ bool LValueExprEvaluator::VisitArraySubscriptExpr(const ArraySubscriptExpr *E) { bool LValueExprEvaluator::VisitUnaryDeref(const UnaryOperator *E) { #if ENABLE_BSC if (Info.getLangOpts().BSC && Info.getLangOpts().EnableFatPtr && - E->getSubExpr()->getType()->isFatPtrRecordType()) + E->getSubExpr()->getType().isFatPtrRecordType()) return true; #endif return evaluatePointer(E->getSubExpr(), Result); @@ -8825,7 +8831,11 @@ bool PointerExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) { if (E->getOpcode() != BO_Add && E->getOpcode() != BO_Sub) return ExprEvaluatorBaseTy::VisitBinaryOperator(E); - +#if ENABLE_BSC + if (Info.getLangOpts().BSC && Info.getLangOpts().EnableFatPtr && + E->getLHS()->getType().isFatPtrRecordType()) + return true; +#endif const Expr *PExp = E->getLHS(); const Expr *IExp = E->getRHS(); if (IExp->getType()->isPointerType()) @@ -14833,8 +14843,12 @@ static bool Evaluate(APValue &Result, EvalInfo &Info, const Expr *E) { LValue LV; APValue &Value = Info.CurrentCall->createTemporary(E, T, ScopeKind::FullExpression, LV); - if (!EvaluateRecord(E, LV, Value, Info)) - return false; +#if ENABLE_BSC + if (!(Info.getLangOpts().BSC && Info.getLangOpts().EnableFatPtr && + T.isFatPtrRecordType())) +#endif + if (!EvaluateRecord(E, LV, Value, Info)) + return false; Result = Value; } else if (T->isVoidType()) { if (!Info.getLangOpts().CPlusPlus11) diff --git a/clang/lib/CodeGen/CGExprAgg.cpp b/clang/lib/CodeGen/CGExprAgg.cpp index f5d307974bd6..c9d21233d4ea 100644 --- a/clang/lib/CodeGen/CGExprAgg.cpp +++ b/clang/lib/CodeGen/CGExprAgg.cpp @@ -1168,8 +1168,8 @@ void AggExprEmitter::VisitBinAssign(const BinaryOperator *E) { // to be compatible with the value on the left. #if ENABLE_BSC if (!(CGF.getLangOpts().BSC && CGF.getLangOpts().EnableFatPtr && - E->getLHS()->getType()->isFatPtrRecordType() && - E->getRHS()->getType()->isFatPtrRecordType())) + E->getLHS()->getType().isFatPtrRecordType() && + E->getRHS()->getType().isFatPtrRecordType())) #endif assert(CGF.getContext().hasSameUnqualifiedType(E->getLHS()->getType(), E->getRHS()->getType()) diff --git a/clang/lib/Headers/bsc_include/bsc_fat_ptr.hbs b/clang/lib/Headers/bsc_include/bsc_fat_ptr.hbs index e0558ee48b36..e973722e54b0 100644 --- a/clang/lib/Headers/bsc_include/bsc_fat_ptr.hbs +++ b/clang/lib/Headers/bsc_include/bsc_fat_ptr.hbs @@ -16,6 +16,9 @@ #include #include +// only CHECKED marked global array var can be taked address by &fat. +#define CHECKED __attribute__((fat_ptr_checked)) + struct _FatPtr { T* raw_ptr; uint32_t key_version; @@ -28,8 +31,37 @@ struct _AllocationUnit { uint8_t payload[]; }; -void _report_error(const char* msg) { - fprintf(stderr, "%s", msg); +enum _FatPtrErrorKind { + AllocateZero = 0, + FreeNotHeadPointer, + DoubleFree, + UseAfterFree, + OutOfBoundsAccess +}; + +static void _report_error(enum _FatPtrErrorKind kind, char *file_name, + int line_no, char *func_name) { + char *msg; + switch (kind) { + case AllocateZero: + msg = "Allocated size cannot be ZERO"; + break; + case FreeNotHeadPointer: + msg = "Cannot free this pointer because it does not point to the head of an allocation"; + break; + case DoubleFree: + msg = "Cannot free this pointer because the allocation may have been freed"; + break; + case UseAfterFree: + msg = "Cannot use this pointer because the allocation may have been freed or reseored"; + break; + case OutOfBoundsAccess: + msg = "Pointer offset exceeds the allocation size"; + break; + } + fprintf(stderr, "Error: %s!\n", msg); + if (kind == UseAfterFree || kind == OutOfBoundsAccess) + fprintf(stderr, "Error at: %s:%d in %s\n", file_name, line_no, func_name); void *callstack[10]; int num_frames = backtrace(callstack, 10); char **symbols = backtrace_symbols(callstack, num_frames); @@ -42,49 +74,71 @@ void _report_error(const char* msg) { exit(EXIT_FAILURE); } -uint32_t _new_version_number() { +static uint32_t _new_version_number() { static _Atomic(uint32_t) uuid = 10000; atomic_fetch_add(&uuid, 1); return atomic_load(&uuid); } -_FatPtr checked_malloc(size_t payload_bytes) { +_FatPtr checked_malloc(size_t payload_bytes) { if (payload_bytes == 0) - _report_error("allocated size is ZERO!\n"); + _report_error(AllocateZero, 0, 0, 0); size_t all_size = payload_bytes + 8; void *raw = malloc(all_size); uint32_t *p = (uint32_t*)raw; uint32_t ver = _new_version_number(); *p = ver; *(p + 1) = payload_bytes; - _FatPtr ptr = { .raw_ptr = (void*)(p + 2), .key_version = ver, .offset = 0 }; + _FatPtr ptr = { .raw_ptr = (T*)(p + 2), .key_version = ver, .offset = 0 }; return ptr; } -void checked_free(_FatPtr ptr) { +void checked_free(_FatPtr ptr) { + if (ptr.raw_ptr == nullptr) + return; + if (ptr.key_version == 0) { + free((uint8_t*)ptr.raw_ptr - 8); + return; + } uint8_t* head = (uint8_t*)ptr.raw_ptr - ptr.offset - 8; struct _AllocationUnit* phead = (struct _AllocationUnit *)head; - if (ptr.key_version != phead->lock_version) - _report_error("version number error when free!\n"); + if (ptr.key_version != 0 && ptr.key_version != phead->lock_version) { + _report_error(DoubleFree, 0, 0, 0); + } if (ptr.offset != 0) - _report_error("free a pointer which is not point to the head of an allocation!\n"); + _report_error(FreeNotHeadPointer, 0, 0, 0); memset(phead, 0, phead->size + 8); free(phead); } -void _check_version(_FatPtr ptr) { - const uint8_t *head = (const uint8_t *)ptr.raw_ptr - ptr.offset - 8; - const struct _AllocationUnit *phead = (const struct _AllocationUnit *)head; - if (ptr.key_version != 0 && ptr.key_version != phead->lock_version) - _report_error("version number error!\n"); +static void _check_version( + void *raw_ptr, uint32_t key_version, int32_t offset, + char *file_name, int line_no, char *func_name) { + // if key_version == 0, means this fat ptr is from a raw ptr, + // if key_version == 1, means this fat ptr points to a global array, + // we should skip check version. + if (key_version != 0 && key_version != 1) { + const uint8_t *head = (const uint8_t *)raw_ptr - offset - 8; + const struct _AllocationUnit *phead = (const struct _AllocationUnit *)head; + if (key_version != phead->lock_version) { + _report_error(UseAfterFree, file_name, line_no, func_name); + } + } } -void _check_offset(_FatPtr ptr, int32_t bytes) { - const uint8_t *head = (const uint8_t *)ptr.raw_ptr - ptr.offset - 8; - const struct _AllocationUnit *phead = (const struct _AllocationUnit *)head; - int32_t new_offset = ptr.offset + bytes; - if (phead->size > 0 && new_offset > phead->size) - _report_error("pointer offset exceed the allocation size!\n"); +static void _check_offset( + int32_t bytes, void *raw_ptr, uint32_t key_version, int32_t offset, + char *file_name, int line_no, char *func_name) { + // if key_version == 0, means this fat ptr is from a raw ptr, + // we should skip check offset. + if (key_version != 0) { + const uint8_t *head = (const uint8_t *)raw_ptr - offset - 8; + const struct _AllocationUnit *phead = (const struct _AllocationUnit *)head; + int32_t new_offset = offset + bytes; + if (phead->size != 0 && new_offset > phead->size) { + _report_error(OutOfBoundsAccess, file_name, line_no, func_name); + } + } } #endif /* BSC_FAT_PTR_HBS */ \ No newline at end of file diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index d314128f5ff7..a577b8ff8cc1 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -2235,6 +2235,8 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS, #if ENABLE_BSC if (getLangOpts().BSC && FirstDecl) { if (FunctionDecl *FD = dyn_cast_or_null(FirstDecl)) { + if (getLangOpts().EnableFatPtr) + Actions.DesugarFunctionDeclWithFatPtr(FD); if (FD->isAsyncSpecified()) { SmallVector Decls = Actions.ActOnAsyncFunctionDeclaration(FD); for (auto &D : Decls) { @@ -2250,7 +2252,7 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS, Actions.BuildAllocationUnitForGlobalArrayVar(VD); DeclsInGroup.push_back(Decls.first); DeclsInGroup.push_back(Decls.second); - } else if (QT.isFatQualified() || QT->hasFatFields()) { + } else if (QT.hasFat()) { Actions.DesugarGlobalVarDeclWithFatPtr(VD); } } diff --git a/clang/lib/Sema/BSC/SemaBSCFatPtr.cpp b/clang/lib/Sema/BSC/SemaBSCFatPtr.cpp index e789857a4099..8c3494be693d 100644 --- a/clang/lib/Sema/BSC/SemaBSCFatPtr.cpp +++ b/clang/lib/Sema/BSC/SemaBSCFatPtr.cpp @@ -156,7 +156,280 @@ public: } }; -// Desugar fat ptr for FunctionDecl, VarDecl and RecordDecl. +// Preprocess FunctionDecl before desugar +// 1. a DeclStmt may contain more than one VarDecl, we should +// split it into several DeclStmts, for example: +// int a, *fat p = nullptr; -> int a; int *fat p = nullptr; +// 2. if return type of a CallExpr is fat ptr type, build new +// temporary VarDecl to avoid duplicate call. If we don't +// build such a temporary VarDecl, `int *fat p = foo() + 1;` +// will be desugared to: +// @code +// _FatPtr p = (_FatPtr) { +// foo().ptr + 1, +// foo().key_version, +// foo().offset + sizeof(int) }; +// @endcode +// we can see foo() is called for third times which may +// cause error, so we build a temp VarDecl for foo() and +// replace original CallExpr by this temp VarDecl. +// For example, we preprocess `int *fat p = foo() + 1;` to: +// @code +// int *fat temp = foo(); +// int *fat p = temp + 1; +// @endcode +// 3. If body of IfStmt/WhileStmt/DoStmt/ForStmt is not a +// CompoundStmt, build CompoundStmt as its body. +// For example: +// 1) while (*p > 0) foo(); -> while (*p > 0) { foo(); } +// 2) if (*p > 0 ) foo(); else foo(); +// ->if (*p > 0) { foo(); } else { foo(); } +// 4. for SelfIncExpr or SelfDecExpr of FatPtr, we split it +// into two expr. For example: +// 1) int a = *p++; -> int a = *p; p = p + 1; +// 2) int a = *++p; -> p = p + 1; int a = *p; +// It should be noted that in this preprocess stage, we don't +// desugar fat ptr pointer to _FatPtr struct, but only do some +// stmt replacement. +class FatPtrPreprocessor : public TreeTransform { + typedef TreeTransform BaseTransform; + + // whether need to create a temporary variable to replace + // the current CallExpr. + bool NeedToReplace = false; + + // The first element of pair records if current SelfIncExpr or + // SelfDecExpr is postfix or not: + // 1) true means `p++` or `p--`, then the second element of pair + // will be inserted behind this SelfIncExpr or SelfDecExpr; + // 2) false means `++p` or `--p`, then the second element of pair + // will be inserted before this SelfIncExpr or SelfDecExpr. + std::pair IncOrDecFatPtrExpr = std::make_pair(false, nullptr); + +public: + FatPtrPreprocessor(Sema &SemaRef) : BaseTransform(SemaRef) {} + StmtResult TransformCompoundStmt(CompoundStmt *CS); + StmtResult TransformCompoundStmt(CompoundStmt *CS, bool IsStmtExpr); + StmtResult TransformIfStmt(IfStmt *IS); + StmtResult TransformWhileStmt(WhileStmt *WS); + StmtResult TransformDoStmt(DoStmt *DS); + StmtResult TransformForStmt(ForStmt *FS); + StmtResult TransformSwitchStmt(SwitchStmt *SS); + StmtResult TransformCaseStmt(CaseStmt *CS); + StmtResult TransformDefaultStmt(DefaultStmt *DS); + StmtResult TransformDeclStmt(DeclStmt *DS); + ExprResult TransformInitListExpr(InitListExpr *ILE); + ExprResult TransformConditionalOperator(ConditionalOperator *CO); + ExprResult TransformUnaryOperator(UnaryOperator *UO); + ExprResult TransformBinaryOperator(BinaryOperator *BO); + ExprResult TransformImplicitCastExpr(ImplicitCastExpr *ICE); + +private: + Stmt *BuildCompoundStmtForIfOrLoopStmtBody(Stmt *Body); +}; + +StmtResult FatPtrPreprocessor::TransformCompoundStmt(CompoundStmt *CS) { + return TransformCompoundStmt(CS, false); +} + +StmtResult FatPtrPreprocessor::TransformCompoundStmt(CompoundStmt *CS, + bool IsStmtExpr) { + if (CS == nullptr) + return CS; + llvm::SmallVector Stmts; + for (Stmt *S : CS->body()) { + if (auto DS = dyn_cast(S)) { + if (!DS->isSingleDecl()) { + // a DeclStmt may contain more than one VarDecl, we + // split it into several DeclStmts. + for (auto D : DS->decls()) { + DeclStmt *NewDS = new (SemaRef.Context) + DeclStmt(DeclGroupRef(D), D->getLocation(), D->getLocation()); + Stmt *NewS = BaseTransform::TransformStmt(NewDS).get(); + if (IncOrDecFatPtrExpr.second) { + if (IncOrDecFatPtrExpr.first) { + Stmts.push_back(NewS); + Stmts.push_back(IncOrDecFatPtrExpr.second); + } else { + Stmts.push_back(IncOrDecFatPtrExpr.second); + Stmts.push_back(NewS); + } + IncOrDecFatPtrExpr.second = nullptr; + } else { + Stmts.push_back(NewS); + } + } + continue; + } + } + Stmt *NewS = BaseTransform::TransformStmt(S).get(); + if (IncOrDecFatPtrExpr.second) { + if (IncOrDecFatPtrExpr.first) { + Stmts.push_back(NewS); + Stmts.push_back(IncOrDecFatPtrExpr.second); + } else { + Stmts.push_back(IncOrDecFatPtrExpr.second); + Stmts.push_back(NewS); + } + IncOrDecFatPtrExpr.second = nullptr; + } else { + Stmts.push_back(NewS); + } + } + CompoundStmt *NewCS = CompoundStmt::Create( + SemaRef.Context, Stmts, FPOptionsOverride(), CS->getLBracLoc(), + CS->getRBracLoc(), CS->getSafeSpecifier(), CS->getSafeSpecifierLoc(), + CS->getCompSafeZoneSpecifier()); + return NewCS; +} + +StmtResult FatPtrPreprocessor::TransformIfStmt(IfStmt *IS) { + IS->setCond(BaseTransform::TransformExpr(IS->getCond()).get()); + IS->setThen(BaseTransform::TransformStmt( + BuildCompoundStmtForIfOrLoopStmtBody(IS->getThen())) + .get()); + if (IS->getElse()) + IS->setElse(BaseTransform::TransformStmt( + BuildCompoundStmtForIfOrLoopStmtBody(IS->getElse())) + .get()); + return IS; +} + +StmtResult FatPtrPreprocessor::TransformWhileStmt(WhileStmt *WS) { + WS->setCond(BaseTransform::TransformExpr(WS->getCond()).get()); + WS->setBody(BaseTransform::TransformStmt( + BuildCompoundStmtForIfOrLoopStmtBody(WS->getBody())) + .get()); + return WS; +} + +StmtResult FatPtrPreprocessor::TransformDoStmt(DoStmt *DS) { + DS->setCond(BaseTransform::TransformExpr(DS->getCond()).get()); + DS->setBody(BaseTransform::TransformStmt( + BuildCompoundStmtForIfOrLoopStmtBody(DS->getBody())) + .get()); + return DS; +} + +StmtResult FatPtrPreprocessor::TransformForStmt(ForStmt *FS) { + FS->setCond(BaseTransform::TransformExpr(FS->getCond()).get()); + FS->setInc(BaseTransform::TransformExpr(FS->getInc()).get()); + FS->setBody(BaseTransform::TransformStmt( + BuildCompoundStmtForIfOrLoopStmtBody(FS->getBody())) + .get()); + return FS; +} + +StmtResult FatPtrPreprocessor::TransformSwitchStmt(SwitchStmt *SS) { + SS->setCond(BaseTransform::TransformExpr(SS->getCond()).get()); + SS->setBody(BaseTransform::TransformStmt(SS->getBody()).get()); + return SS; +} + +StmtResult FatPtrPreprocessor::TransformCaseStmt(CaseStmt *CS) { + CS->setLHS(BaseTransform::TransformExpr(CS->getLHS()).get()); + Expr *RHS = CS->getRHS(); + if (RHS) + CS->setRHS(BaseTransform::TransformExpr(RHS).get()); + CS->setSubStmt(BaseTransform::TransformStmt(CS->getSubStmt()).get()); + return CS; +} + +StmtResult FatPtrPreprocessor::TransformDefaultStmt(DefaultStmt *DS) { + DS->setSubStmt(BaseTransform::TransformStmt(DS->getSubStmt()).get()); + return DS; +} + +StmtResult FatPtrPreprocessor::TransformDeclStmt(DeclStmt *DS) { + assert(DS->isSingleDecl() && "DeclStmt can only contain a Decl here"); + Decl *D = DS->getSingleDecl(); + if (VarDecl *VD = dyn_cast(D)) { + if (VD->getInit()) + VD->setInit(BaseTransform::TransformExpr(VD->getInit()).get()); + } + return DS; +} + +ExprResult FatPtrPreprocessor::TransformInitListExpr(InitListExpr *ILE) { + for (unsigned i = 0; i < ILE->getNumInits(); i++) + ILE->setInit(i, BaseTransform::TransformExpr(ILE->getInit(i)).get()); + return ILE; +} + +ExprResult +FatPtrPreprocessor::TransformConditionalOperator(ConditionalOperator *CO) { + Expr *CondExpr = BaseTransform::TransformExpr(CO->getCond()).get(); + Expr *LHS = BaseTransform::TransformExpr(CO->getLHS()).get(); + Expr *RHS = BaseTransform::TransformExpr(CO->getRHS()).get(); + return BaseTransform::RebuildConditionalOperator( + CondExpr, CO->getQuestionLoc(), LHS, CO->getColonLoc(), RHS); +} + +ExprResult FatPtrPreprocessor::TransformUnaryOperator(UnaryOperator *UO) { + // 1) int a = *p++; -> int a = *p; p = p + 1; + // 2) int a = *++p; -> p = p + 1; int a = *p; + SourceLocation SLoc = UO->getBeginLoc(); + Expr *SubE = UO->getSubExpr(); + if ((UO->isPrefix() || UO->isPostfix()) && SubE->getType().isFatPtrType()) { + // Build p = p + 1 for p++/++p, build p = p - 1 for p--/--p + Expr *InitRHS = IntegerLiteral::Create(SemaRef.Context, llvm::APInt(32, 1), + SemaRef.Context.IntTy, SLoc); + Expr *InitE = + SemaRef + .CreateBuiltinBinOp(SLoc, UO->isIncrementOp() ? BO_Add : BO_Sub, + SubE, InitRHS) + .get(); + IncOrDecFatPtrExpr.first = UO->isPostfix(); + IncOrDecFatPtrExpr.second = + SemaRef.CreateBuiltinBinOp(SLoc, BO_Assign, SubE, InitE).get(); + return ImplicitCastExpr::Create(SemaRef.Context, SubE->getType(), + CK_LValueToRValue, SubE, nullptr, + VK_PRValue, FPOptionsOverride()); + } + UO->setSubExpr(BaseTransform::TransformExpr(SubE).get()); + return UO; +} + +ExprResult FatPtrPreprocessor::TransformBinaryOperator(BinaryOperator *BO) { + Expr *LHS = BO->getLHS(); + Expr *RHS = BO->getRHS(); + BO->setLHS(BaseTransform::TransformExpr(LHS).get()); + BO->setRHS(BaseTransform::TransformExpr(RHS).get()); + return BO; +} + +ExprResult +FatPtrPreprocessor::TransformImplicitCastExpr(ImplicitCastExpr *ICE) { + ICE->setSubExpr(BaseTransform::TransformExpr(ICE->getSubExpr()).get()); + return ICE; +} + +// If body of IfStmt/WhileStmt/DoStmt/ForStmt is not CompoundStmt, +// we build CompoundStmt and add original body into it. +Stmt *FatPtrPreprocessor::BuildCompoundStmtForIfOrLoopStmtBody(Stmt *Body) { + if (isa(Body) && !IncOrDecFatPtrExpr.second) + return Body; + llvm::SmallVector Statements; + if (auto CS = dyn_cast(Body)) { + for (Stmt *S : CS->body()) + Statements.push_back(S); + } else if (!isa(Body)) { + Statements.push_back(Body); + } + + // IncExpr of ForStmt maybe SelfIncExpr or SelfDecExpr of fat ptr, + // we add it in the end of ForStmt body, for example: + // for (;;p++) {} -> for (;;p) { p = p + 1; } + if (IncOrDecFatPtrExpr.second) { + Statements.push_back(IncOrDecFatPtrExpr.second); + IncOrDecFatPtrExpr.second = nullptr; + } + + return CompoundStmt::Create(SemaRef.Context, Statements, FPOptionsOverride(), + Body->getBeginLoc(), Body->getEndLoc()); +} + +// Desugar fat ptr for FunctionDecl, VarDecl, RecordDecl and TypedefNameDecl class TransformFatPtr : public TreeTransform { typedef TreeTransform BaseTransform; FunctionDecl *FD = nullptr; @@ -183,7 +456,23 @@ class TransformFatPtr : public TreeTransform { bool InsertCheckVersionCall = false; std::pair InsertCheckOffsetCall = std::make_pair(false, nullptr); - std::stack CheckCallExpr; + + CompoundStmt *CurrentCompoundStmt = nullptr; + + // Key is every original CompoundStmt in function, + // value is desugared statements of this CompoundStmt. + llvm::DenseMap> + DesugaredStatements; + + // Use stack to store check calls when traversing. + // When traverse a complex expr, such as `*p->a` where both p and p->a + // are fat ptr, we build check call for p->a firstly and p secondly, + // but we should insert check call for p firstly and p->a secondly. + // So we use stack to store check call and pop them after ending + // traversing this complex expr. + // First element of pair is fat ptr which should be checked, + // second element is check calls should be inserted to AST. + std::stack> CheckCallExpr; // when accessing array member through FatPtr, // such as `p->arr[3]` or `p->a.b.arr[3]` @@ -200,17 +489,25 @@ public: FatPtrFD["_new_version_number"] = nullptr; } - bool IsNullPtrExpr(Expr *E); // Entry to desugar FunctionDecl void TransformFunctionDecl(); // Entry to desugar global or local VarDecl void TransformVarDecl(VarDecl *VD); // Entry to desugar global or local RecordDecl void TransformRecordDecl(RecordDecl *RD); - void TransformSelfCallExprFunctionType(CallExpr *CE); + // Entry to desugar global or local TypedefNameDecl, + // including TypedefDecl and TypeAliasDecl. + void TransformTypedefNameDecl(TypedefNameDecl *TND); StmtResult TransformCompoundStmt(CompoundStmt *CS, bool IsStmtExpr); StmtResult TransformCompoundStmt(CompoundStmt *CS); + StmtResult TransformIfStmt(IfStmt *IS); + StmtResult TransformSwitchStmt(SwitchStmt *SS); + StmtResult TransformCaseStmt(CaseStmt *CS); + StmtResult TransformDefaultStmt(DefaultStmt *DS); + StmtResult TransformWhileStmt(WhileStmt *WS); + StmtResult TransformDoStmt(DoStmt *DS); + StmtResult TransformForStmt(ForStmt *FS); StmtResult TransformDeclStmt(DeclStmt *DS); StmtResult TransformReturnStmt(ReturnStmt *RS); ExprResult TransformDeclRefExpr(DeclRefExpr *DRE); @@ -223,10 +520,11 @@ public: ExprResult TransformCStyleCastExpr(CStyleCastExpr *CSCE); ExprResult TransformUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *UETT); ExprResult TransformMemberExpr(MemberExpr *ME); + ExprResult TransformConditionalOperator(ConditionalOperator *CO); + ExprResult TransformCompoundAssignOperator(CompoundAssignOperator *CAO); ExprResult TransformBinaryOperator(BinaryOperator *BO); ExprResult HandleBOFatPtrAddOrSubInteger(BinaryOperator *BO); - ExprResult HandleBOFatPtrSubFatPtr(BinaryOperator *BO); ExprResult TransformUnaryOperator(UnaryOperator *UO); ExprResult HandleUODerefFatPtr(UnaryOperator *UO); @@ -251,6 +549,7 @@ private: MemberExpr *BuildMemberForFatPtr(Expr *FatPtrBaseE, std::string FieldName); Expr *BuildFatPtrCall(SourceLocation SLoc, std::string FDName, llvm::SmallVector &Args); + MemberExpr *BuildMemberRawPtrAndInsertCheckCallForFatPtr(Expr *FatPtrBaseE); Expr *BuildFatPtrCompoundLiteralExprForRawPtr(Expr *RawPtrE, QualType QT); Expr *BuildFatPtrCompoundLiteralExpr(Expr *FirstInitE, Expr *SecondInitE, Expr *ThirdInitE, QualType QT); @@ -260,13 +559,6 @@ private: SmallVector &Comps); }; -bool TransformFatPtr::IsNullPtrExpr (Expr *E) { - if (auto *CastExpr = dyn_cast(E)) { - E = CastExpr->getSubExpr(); - } - return isa(E); -} - void TransformFatPtr::TransformFunctionDecl() { // Desugar fat qualified return type. QualType ReturnTy = @@ -284,26 +576,43 @@ void TransformFatPtr::TransformFunctionDecl() { ReturnTy, ParamTys, FD->getType()->getAs()->getExtProtoInfo())); - // First traversal: + if (!FD->getBody()) + return; + + // First traversal: do preprocess + FatPtrPreprocessor Preprocessor(SemaRef); + FD->setBody(Preprocessor.TransformStmt(FD->getBody()).get()); + + // Second traversal: // find local vars whose address is taken by `&fat` expr. Finder.VisitStmt(FD->getBody()); - // Second traversal + // Third traversal CompoundStmt *NewCS = HandleLocalAllocationUnit(dyn_cast(FD->getBody()), true); - // Third traversal: desugar stmt and expr of function body. + // the last traversal: desugar stmt and expr of function body. FD->setBody(TransformStmt(NewCS).get()); } void TransformFatPtr::TransformVarDecl(VarDecl *VD) { QualType QT = DesugarFatPtrType(VD->getLocation(), VD->getType()); VD->setType(QT); + VD->setTypeSourceInfo(SemaRef.Context.getTrivialTypeSourceInfo(QT)); if (Expr *Init = VD->getInit()) { - if (QT->isFatPtrRecordType() && IsNullPtrExpr(Init)) { - // `int *fat p = nullptr;` should be desugared to - // `_FatPtr p = (_FatPtr) { nullptr, 0, 0 };` - VD->setInit(BuildFatPtrCompoundLiteralExprForRawPtr(Init, QT)); + if (QT.isFatPtrRecordType()) { + if (Init->isNullExpr(SemaRef.Context)) { + // `int *fat p = nullptr;` should be desugared to + // `_FatPtr p = (_FatPtr) { nullptr, 0, 0 };` + VD->setInit(BuildFatPtrCompoundLiteralExprForRawPtr(Init, QT)); + } else { + VD->setInit(BaseTransform::TransformExpr(Init).get()); + } + if (VD->hasGlobalStorage() && isa(VD->getInit())) { + VD->setInit(ImplicitCastExpr::Create(SemaRef.Context, QT, CK_NoOp, + VD->getInit(), nullptr, VK_PRValue, + FPOptionsOverride())); + } } else { VD->setInit(BaseTransform::TransformExpr(Init).get()); } @@ -316,6 +625,12 @@ void TransformFatPtr::TransformRecordDecl(RecordDecl *RD) { FD->setType(DesugarFatPtrType(FD->getLocation(), FD->getType())); } +void TransformFatPtr::TransformTypedefNameDecl(TypedefNameDecl *TND) { + SourceLocation SLoc = TND->getTypeSourceInfo()->getTypeLoc().getBeginLoc(); + QualType QT = DesugarFatPtrType(SLoc, TND->getUnderlyingType()); + TND->setTypeSourceInfo(SemaRef.Context.getTrivialTypeSourceInfo(QT)); +} + // 1. For all local vars whose type has no array and address is // taken by &fat, we think they are packed in an allocation unit // in stack and share a lock_version, so we build 2 VarDecl @@ -330,7 +645,8 @@ void TransformFatPtr::TransformRecordDecl(RecordDecl *RD) { CompoundStmt * TransformFatPtr::HandleLocalAllocationUnit(CompoundStmt *CS, bool IsTopLevelCompoundStmt) { - if (CS == nullptr) + if (CS == nullptr || (!Finder.LocalNoArrayVarAddressIsFatTaken && + Finder.LocalArrayVarsSet.size() == 0)) return CS; llvm::SmallVector Statements; if (IsTopLevelCompoundStmt && Finder.LocalNoArrayVarAddressIsFatTaken) { @@ -339,37 +655,29 @@ TransformFatPtr::HandleLocalAllocationUnit(CompoundStmt *CS, Statements.push_back(Headers.second); Statements.push_back(Headers.first); } - if (Finder.LocalArrayVarsSet.size() == 0) { - for (auto *B : CS->body()) + for (auto *B : CS->body()) { + if (auto ChildCS = dyn_cast(B)) { + Statements.push_back(HandleLocalAllocationUnit(ChildCS, false)); + } else if (isa(B) && Finder.LocalNoArrayVarAddressIsFatTaken) { + Statements.push_back( + BuildLockVersionAssignmentForLocalNoArrayVars(B->getBeginLoc())); Statements.push_back(B); - } else { - for (auto *B : CS->body()) { - if (auto ChildCS = dyn_cast(B)) { - Statements.push_back(HandleLocalAllocationUnit(ChildCS, false)); - } else if (auto DS = dyn_cast(B)) { - if (DS->isSingleDecl()) { - if (VarDecl *VD = dyn_cast(DS->getSingleDecl())) { - if (Finder.LocalArrayVarsSet.count(VD)) { - std::pair Decls = - BuildAllocationUnitForLocalArrayVar(VD); - Statements.push_back(Decls.first); - Statements.push_back(Decls.second); - continue; - } - } - Statements.push_back(B); - } else { - // TODO: a DeclStmt contains more than one VarDecl, we should - // divide it into several DeclStmts. - Statements.push_back(B); + } else if (auto DS = dyn_cast(B)) { + assert(DS->isSingleDecl() && "DeclStmt can only contain a Decl here"); + bool NeedToReplace = false; + if (VarDecl *VD = dyn_cast(DS->getSingleDecl())) { + if (Finder.LocalArrayVarsSet.count(VD)) { + std::pair Decls = + BuildAllocationUnitForLocalArrayVar(VD); + Statements.push_back(Decls.first); + Statements.push_back(Decls.second); + NeedToReplace = true; } - } else if (isa(B) && Finder.LocalNoArrayVarAddressIsFatTaken) { - Statements.push_back( - BuildLockVersionAssignmentForLocalNoArrayVars(B->getBeginLoc())); - Statements.push_back(B); - } else { - Statements.push_back(B); } + if (!NeedToReplace) + Statements.push_back(B); + } else { + Statements.push_back(B); } } if (IsTopLevelCompoundStmt && Finder.LocalNoArrayVarAddressIsFatTaken) @@ -469,10 +777,10 @@ TransformFatPtr::BuildAllocationUnitForLocalArrayVar(VarDecl *VD) { llvm::SmallVector Args; Expr *LockVersionInit = BuildFatPtrCall(SLoc, "_new_version_number", Args); // Build second init expr by array size - Expr *SizeInit = - IntegerLiteral::Create(SemaRef.Context, - llvm::APInt(32, SemaRef.Context.getTypeSize(VD->getType()) / 8), - UInt32Ty, SLoc); + auto ArraySize = + SemaRef.Context.getTypeSize(DesugarFatPtrType(SLoc, VD->getType())) / 8; + Expr *SizeInit = IntegerLiteral::Create( + SemaRef.Context, llvm::APInt(32, ArraySize), UInt32Ty, SLoc); // Build InitListExpr SmallVector Inits{LockVersionInit, SizeInit}; if (VD->getInit()) @@ -508,48 +816,208 @@ StmtResult TransformFatPtr::TransformCompoundStmt(CompoundStmt *CS, bool IsStmtExpr) { if (CS == nullptr) return CS; - llvm::SmallVector Statements; + + // If current CompoundStmt is the body of WhileStmt, DoStmt and + // ForStmt, and there is fat ptr deref operation in CondExpr, + // after we have treetransformed CondExpr, check calls are added + // into DesugaredStatements[CurrentCompoundStmt] and these check + // calls should be added in the end of current CompoundStmt. + llvm::SmallVector TempStatements; + if (DesugaredStatements.count(CS)) { + TempStatements = DesugaredStatements[CS]; + DesugaredStatements[CS].clear(); + } for (auto *B : CS->body()) { + CurrentCompoundStmt = CS; StmtResult Result = BaseTransform::TransformStmt(B); while (!CheckCallExpr.empty()) { - Statements.push_back(CheckCallExpr.top()); + DesugaredStatements[CS].push_back(CheckCallExpr.top().second); CheckCallExpr.pop(); } - Statements.push_back(Result.getAs()); + DesugaredStatements[CS].push_back(Result.getAs()); + } + for (auto S : TempStatements) { + DesugaredStatements[CS].push_back(S); } auto NewCS = CompoundStmt::Create( - SemaRef.Context, Statements, FPOptionsOverride(), CS->getLBracLoc(), - CS->getRBracLoc(), CS->getSafeSpecifier(), CS->getSafeSpecifierLoc(), - CS->getCompSafeZoneSpecifier()); + SemaRef.Context, DesugaredStatements[CS], FPOptionsOverride(), + CS->getLBracLoc(), CS->getRBracLoc(), CS->getSafeSpecifier(), + CS->getSafeSpecifierLoc(), CS->getCompSafeZoneSpecifier()); return NewCS; } +// If there is fat ptr deref operation in CondExpr of IfStmt or +// SwitchStmt, we insert check call before this Stmt, for example: +// `if (*p > 0) { xxxx; }` where p is fat ptr should be desugared to: +// @code +// _check_version(p); +// _check_offset(p); +// if (*p.raw_ptr > 0) { xxxx; } +// @endcode +StmtResult TransformFatPtr::TransformIfStmt(IfStmt *IS) { + Expr *CondExpr = IS->getCond(); + if (CondExpr->getType().isFatPtrType()) + // `if (p)` where p is fat ptr should be resugared to `if (p.raw_ptr)` + DesugarToMemberPtr = true; + IS->setCond(BaseTransform::TransformExpr(CondExpr).get()); + while (!CheckCallExpr.empty()) { + DesugaredStatements[CurrentCompoundStmt].push_back( + CheckCallExpr.top().second); + CheckCallExpr.pop(); + } + IS->setThen(BaseTransform::TransformStmt(IS->getThen()).get()); + if (IS->getElse()) + IS->setElse(BaseTransform::TransformStmt(IS->getElse()).get()); + return IS; +} + +StmtResult TransformFatPtr::TransformSwitchStmt(SwitchStmt *SS) { + Expr *CondExpr = SS->getCond(); + if (CondExpr->getType().isFatPtrType()) + DesugarToMemberPtr = true; + SS->setCond(BaseTransform::TransformExpr(CondExpr).get()); + while (!CheckCallExpr.empty()) { + DesugaredStatements[CurrentCompoundStmt].push_back( + CheckCallExpr.top().second); + CheckCallExpr.pop(); + } + SS->setBody(BaseTransform::TransformStmt(SS->getBody()).get()); + return SS; +} + +StmtResult TransformFatPtr::TransformCaseStmt(CaseStmt *CS) { + CS->setLHS(BaseTransform::TransformExpr(CS->getLHS()).get()); + Expr *RHS = CS->getRHS(); + if (RHS) + CS->setRHS(BaseTransform::TransformExpr(RHS).get()); + CS->setSubStmt(BaseTransform::TransformStmt(CS->getSubStmt()).get()); + return CS; +} + +StmtResult TransformFatPtr::TransformDefaultStmt(DefaultStmt *DS) { + DS->setSubStmt(BaseTransform::TransformStmt(DS->getSubStmt()).get()); + return DS; +} + +// If there is fat ptr deref operation in CondExpr of WhileStmt, +// we insert check call before this Stmt and in the end of Stmt body, +// for example: +// `while (*p > 0) { xxxx; }` where p is fat ptr should be desugared to: +// @code +// _check_version(p); +// _check_offset(p); +// while (*p.raw_ptr > 0) { +// xxxx; +// _check_version(p); +// _check_offset(p); +// } +// @endcode +StmtResult TransformFatPtr::TransformWhileStmt(WhileStmt *WS) { + CompoundStmt *BodyCS = dyn_cast(WS->getBody()); + assert(BodyCS && "WhileStmt must have CompoundStmt as body"); + + Expr *CondExpr = WS->getCond(); + if (CondExpr->getType().isFatPtrType()) + DesugarToMemberPtr = true; + WS->setCond(BaseTransform::TransformExpr(CondExpr).get()); + if (CheckCallExpr.empty()) { + WS->setBody(BaseTransform::TransformStmt(BodyCS).get()); + return WS; + } + while (!CheckCallExpr.empty()) { + DesugaredStatements[CurrentCompoundStmt].push_back( + CheckCallExpr.top().second); + DesugaredStatements[BodyCS].push_back(CheckCallExpr.top().second); + CheckCallExpr.pop(); + } + WS->setBody(BaseTransform::TransformStmt(BodyCS).get()); + return WS; +} + +// If there is fat ptr deref operation in CondExpr of DoStmt, +// we insert check call in the end of Stmt body, for example: +// `do { xxxx; } while (*p > 0);` where p is fat ptr +// should be desugared to: +// @code +// do { +// xxxx; +// _check_version(p); +// _check_offset(p); +// } while (*p.raw_ptr > 0); +// @endcode +StmtResult TransformFatPtr::TransformDoStmt(DoStmt *DS) { + CompoundStmt *BodyCS = dyn_cast(DS->getBody()); + assert(BodyCS && "DoStmt must have CompoundStmt as body"); + + Expr *CondExpr = DS->getCond(); + if (CondExpr->getType().isFatPtrType()) + DesugarToMemberPtr = true; + DS->setCond(BaseTransform::TransformExpr(CondExpr).get()); + if (CheckCallExpr.empty()) { + DS->setBody(BaseTransform::TransformStmt(BodyCS).get()); + return DS; + } + while (!CheckCallExpr.empty()) { + DesugaredStatements[BodyCS].push_back(CheckCallExpr.top().second); + CheckCallExpr.pop(); + } + DS->setBody(BaseTransform::TransformStmt(BodyCS).get()); + return DS; +} + +// If there is fat ptr deref operation in CondExpr of ForStmt, +// we insert check call before this Stmt and in the end of Stmt body. +// for example: +// `for (; *p < 5;) { xxxx; }` where p is fat ptr +// should be desugared to: +// @code +// _check_version(p); +// _check_offset(p); +// for (; *p.ptr < 5;) +// xxxx; +// _check_version(p); +// _check_offset(p); +// } +// @endcode +StmtResult TransformFatPtr::TransformForStmt(ForStmt *FS) { + CompoundStmt *BodyCS = dyn_cast(FS->getBody()); + assert(BodyCS && "ForStmt must have CompoundStmt as body"); + + FS->setInit(BaseTransform::TransformStmt(FS->getInit()).get()); + while (!CheckCallExpr.empty()) { + DesugaredStatements[CurrentCompoundStmt].push_back( + CheckCallExpr.top().second); + CheckCallExpr.pop(); + } + FS->setInc(BaseTransform::TransformExpr(FS->getInc()).get()); + Expr *CondExpr = FS->getCond(); + if (CondExpr->getType().isFatPtrType()) + DesugarToMemberPtr = true; + FS->setCond(BaseTransform::TransformExpr(CondExpr).get()); + if (CheckCallExpr.empty()) { + FS->setBody(BaseTransform::TransformStmt(BodyCS).get()); + return FS; + } + while (!CheckCallExpr.empty()) { + DesugaredStatements[CurrentCompoundStmt].push_back( + CheckCallExpr.top().second); + DesugaredStatements[BodyCS].push_back(CheckCallExpr.top().second); + CheckCallExpr.pop(); + } + FS->setBody(BaseTransform::TransformStmt(BodyCS).get()); + return FS; +} + ExprResult TransformFatPtr::TransformDeclRefExpr(DeclRefExpr *DRE) { SourceLocation SLoc = DRE->getBeginLoc(); QualType QT = DesugarFatPtrType(SLoc, DRE->getType()); DRE->setType(QT); - if (QT->isFatPtrRecordType()) { - if (InsertCheckOffsetCall.first) { - // Build CallExpr to check offset. - llvm::SmallVector Args{DRE, InsertCheckOffsetCall.second}; - Expr *CE = BuildFatPtrCall(SLoc, "_check_offset", Args); - CheckCallExpr.push(CE); - InsertCheckOffsetCall.first = false; - InsertCheckOffsetCall.second = nullptr; - } - if (InsertCheckVersionCall) { - // Build CallExpr to check version. - llvm::SmallVector Args{DRE}; - Expr *CE = BuildFatPtrCall(SLoc, "_check_version", Args); - CheckCallExpr.push(CE); - InsertCheckVersionCall = false; - } - if (DesugarToMemberPtr) { - DesugarToMemberPtr = false; - // Desugar p to p.raw_ptr - return BuildMemberForFatPtr(DRE, "raw_ptr"); - } - } else if (auto VD = dyn_cast(DRE->getDecl())) { + + if (QT.isFatPtrRecordType() && DesugarToMemberPtr) + // Desugar p to p.raw_ptr + return BuildMemberRawPtrAndInsertCheckCallForFatPtr(DRE); + + if (auto VD = dyn_cast(DRE->getDecl())) { if (AllocationUnitForLocalArrayVarMap.count(VD) || SemaRef.Context.BSCDesugaredMap.count(VD)) { // Desugar arr to _arr.arr where arr has its owned AllocationUnit. @@ -581,30 +1049,32 @@ ExprResult TransformFatPtr::TransformDeclRefExpr(DeclRefExpr *DRE) { } ExprResult TransformFatPtr::TransformImplicitCastExpr(ImplicitCastExpr *ICE) { + SourceLocation SLoc = ICE->getBeginLoc(); QualType QT = ICE->getType(); if (DesugarToMemberPtr && QT.isFatPtrType()) { - QT = QT->getFatPtrPointeeType(); + QT = DesugarFatPtrType(SLoc, QT.getFatPtrPointeeType()); ICE->setType(SemaRef.Context.getPointerType(QT)); ICE->setCastKind(CK_LValueToRValue); } else { - QT = DesugarFatPtrType(ICE->getBeginLoc(), QT); + QT = DesugarFatPtrType(SLoc, QT); ICE->setType(QT); } Expr *SubE = ICE->getSubExpr(); ICE->setSubExpr(BaseTransform::TransformExpr(SubE).get()); - if (ICE->getCastKind() == CK_NoOp && QT->isFatPtrRecordType() && - SubE->getType()->isFatPtrRecordType()) + if (ICE->getCastKind() == CK_NoOp && QT.isFatPtrRecordType() && + SubE->getType().isFatPtrRecordType()) ICE->setType(SubE->getType()); return ICE; } ExprResult TransformFatPtr::TransformParenExpr(ParenExpr *PE) { + SourceLocation SLoc = PE->getBeginLoc(); QualType QT = PE->getType(); if (DesugarToMemberPtr && QT.isFatPtrType()) { - QT = QT->getFatPtrPointeeType(); + QT = DesugarFatPtrType(SLoc, QT.getFatPtrPointeeType()); PE->setType(SemaRef.Context.getPointerType(QT)); } else { - QT = DesugarFatPtrType(PE->getBeginLoc(), QT); + QT = DesugarFatPtrType(SLoc, QT); PE->setType(QT); } PE->setSubExpr(BaseTransform::TransformExpr(PE->getSubExpr()).get()); @@ -612,14 +1082,14 @@ ExprResult TransformFatPtr::TransformParenExpr(ParenExpr *PE) { } StmtResult TransformFatPtr::TransformDeclStmt(DeclStmt *DS) { - for (auto D : DS->decls()) { - if (VarDecl *VD = dyn_cast(D)) - TransformVarDecl(VD); - else if (RecordDecl *RD = dyn_cast(D)) - TransformRecordDecl(RD); - else - BaseTransform::TransformDefinition(D->getLocation(), D); - } + assert(DS->isSingleDecl() && "DeclStmt can only contain a Decl here"); + Decl *D = DS->getSingleDecl(); + if (VarDecl *VD = dyn_cast(D)) + TransformVarDecl(VD); + else if (RecordDecl *RD = dyn_cast(D)) + TransformRecordDecl(RD); + else + BaseTransform::TransformDefinition(D->getLocation(), D); return DS; } @@ -641,7 +1111,7 @@ ExprResult TransformFatPtr::TransformInitListExpr(InitListExpr *ILE) { StmtResult TransformFatPtr::TransformReturnStmt(ReturnStmt *RS) { Expr *RV = RS->getRetValue(); QualType ReturnQT = FD->getReturnType(); - if (ReturnQT->isFatPtrRecordType() && IsNullPtrExpr(RV)) { + if (ReturnQT.isFatPtrRecordType() && RV->isNullExpr(SemaRef.Context)) { // if a function has fat pointer return type, such as `int *fat foo();` // `return nullptr;` should be desugared to // `return (_FatPtr){ nullptr, 0, 0 };` @@ -652,41 +1122,78 @@ StmtResult TransformFatPtr::TransformReturnStmt(ReturnStmt *RS) { return RS; } -void TransformFatPtr::TransformSelfCallExprFunctionType(CallExpr *CE) { - QualType FQT = FD->getType(); - Expr *CalleeExpr = CE->getCallee(); - if (auto *ImplCastExpr = dyn_cast(CalleeExpr)) { - ImplCastExpr->setType(SemaRef.Context.getPointerType(FQT)); - if (Expr *subExpr = ImplCastExpr->getSubExpr()) { - if (auto *declRefExpr = dyn_cast(subExpr)) { - declRefExpr->setType(FQT); +ExprResult TransformFatPtr::TransformCallExpr(CallExpr *CE) { + SourceLocation SLoc = CE->getBeginLoc(); + QualType QT = DesugarFatPtrType(SLoc, CE->getType()); + CE->setType(QT); + + Expr *MemberRawPtr = nullptr; + if (QT.isFatPtrRecordType() && DesugarToMemberPtr) + // Desugar foo() to foo().raw_ptr + MemberRawPtr = BuildMemberRawPtrAndInsertCheckCallForFatPtr(CE); + + Decl *CalleeD = CE->getCalleeDecl(); + if (CalleeD == FD) { + // For self-call Expr, desugar the callee function type + if (auto CalleeICE = dyn_cast(CE->getCallee())) { + CalleeICE->setType(SemaRef.Context.getPointerType(FD->getType())); + if (auto CalleeDRE = dyn_cast(CalleeICE->getSubExpr())) { + CalleeDRE->setType(FD->getType()); } } } -} - -ExprResult TransformFatPtr::TransformCallExpr(CallExpr *CE) { - CE->setType(DesugarFatPtrType(CE->getBeginLoc(), CE->getType())); - FunctionDecl *CalleeFD = CE->getDirectCallee(); - // For self-call Expr, desugar the function type of the calling expression - if (CalleeFD == FD) { - TransformSelfCallExprFunctionType(CE); - } - for (unsigned i = 0; i < CalleeFD->getNumParams(); i++) { - QualType ParamQT = CalleeFD->getParamDecl(i)->getType(); - Expr *ArgE = CE->getArg(i); - if (ParamQT->isFatPtrRecordType() && IsNullPtrExpr(ArgE)) { - // if a function has fat pointer parameter, such as - // `void foo(int *fat p);` - // `foo(nullptr)` should be desugared to - // `foo((_FatPtr){ nullptr, 0, 0 })` - Expr *CLE = BuildFatPtrCompoundLiteralExprForRawPtr(ArgE, ParamQT); - CLE = SemaRef.DefaultFunctionArrayLvalueConversion(CLE).get(); - CE->setArg(i, CLE); - } else - CE->setArg(i, BaseTransform::TransformExpr(ArgE).get()); - } - return CE; + if (auto CalleeFD = dyn_cast(CalleeD)) { + for (unsigned i = 0; i < CE->getNumArgs(); i++) { + Expr *ArgE = CE->getArg(i); + if (i < CalleeFD->getNumParams() && CalleeFD->getParamDecl(i) && + CalleeFD->getParamDecl(i)->getType().isFatPtrRecordType() && + ArgE->isNullExpr(SemaRef.Context)) { + // if a function has fat pointer parameter, such as + // `void foo(int *fat p);` + // `foo(nullptr)` should be desugared to + // `foo((_FatPtr){ nullptr, 0, 0 })` + Expr *CLE = BuildFatPtrCompoundLiteralExprForRawPtr( + ArgE, CalleeFD->getParamDecl(i)->getType()); + CE->setArg(i, SemaRef.DefaultFunctionArrayLvalueConversion(CLE).get()); + } else { + CE->setArg(i, BaseTransform::TransformExpr(ArgE).get()); + if (auto CLE = dyn_cast(CE->getArg(i))) + CE->setArg(i, + SemaRef.DefaultFunctionArrayLvalueConversion(CLE).get()); + } + } + } else if (auto CalleeVD = dyn_cast(CalleeD)) { + // call a function by a function pointer VarDecl, for example: + // @code + // typedef void (*Foo) (int *fat); + // void bar(int *fat p) {} + // Foo foo = bar; + // void test(int *fat p) { + // foo(p); //foo is a function pointer VarDecl + // } + // @endcode + QualType FuncQT = CalleeVD->getType().getCanonicalType(); + if (FuncQT->isFunctionPointerType()) { + auto FuncType = FuncQT->getPointeeType()->getAs(); + for (unsigned i = 0; i < CE->getNumArgs(); i++) { + Expr *ArgE = CE->getArg(i); + if (i < FuncType->getNumParams() && + FuncType->getParamType(i).isFatPtrRecordType() && + ArgE->isNullExpr(SemaRef.Context)) { + Expr *CLE = BuildFatPtrCompoundLiteralExprForRawPtr( + ArgE, FuncType->getParamType(i)); + CE->setArg(i, + SemaRef.DefaultFunctionArrayLvalueConversion(CLE).get()); + } else { + CE->setArg(i, BaseTransform::TransformExpr(ArgE).get()); + if (auto CLE = dyn_cast(CE->getArg(i))) + CE->setArg(i, + SemaRef.DefaultFunctionArrayLvalueConversion(CLE).get()); + } + } + } + } + return MemberRawPtr ? MemberRawPtr : CE; } ExprResult @@ -695,6 +1202,11 @@ TransformFatPtr::TransformArraySubscriptExpr(ArraySubscriptExpr *ASE) { QualType QT = DesugarFatPtrType(SLoc, ASE->getType()); ASE->setType(QT); + Expr *MemberRawPtr = nullptr; + if (QT.isFatPtrRecordType() && DesugarToMemberPtr) + // Desugar arr[i] to arr[i].raw_ptr + MemberRawPtr = BuildMemberRawPtrAndInsertCheckCallForFatPtr(ASE); + Expr *LHS = ASE->getLHS(); Expr *RHS = ASE->getRHS(); QualType LHSQT = LHS->getType(); @@ -720,16 +1232,16 @@ TransformFatPtr::TransformArraySubscriptExpr(ArraySubscriptExpr *ASE) { SemaRef.CreateBuiltinBinOp(SLoc, BO_Add, OffsetExprLHS, OffsetExprRHS).get(); InsertCheckOffsetCall.second = OffsetExpr; ASE->setLHS(BaseTransform::TransformExpr(LHS).get()); - return ASE; + return MemberRawPtr ? MemberRawPtr : ASE; } else if (IsAccessingArrayMemberThroughFatPtr(ASE)) { // If current ASE is accessing array member through fat ptr, // such as `p->arr[i]` or `p->b.arr[i]`, in order to calculate - // offset when transform inner MemberEXpr, we record current ASE. + // offset when transform inner MemberExpr, we record current ASE. CurrentASEToAccessMemberThroughFatPtr = ASE; } ASE->setLHS(BaseTransform::TransformExpr(LHS).get()); ASE->setRHS(BaseTransform::TransformExpr(RHS).get()); - return ASE; + return MemberRawPtr ? MemberRawPtr : ASE; } ExprResult TransformFatPtr::TransformCStyleCastExpr(CStyleCastExpr *CSCE) { @@ -739,6 +1251,7 @@ ExprResult TransformFatPtr::TransformCStyleCastExpr(CStyleCastExpr *CSCE) { QualType SubQT = SubE->getType(); if (QT->isPointerType() && !QT.isFatPtrType() && SubQT.isFatPtrType()) { // desuagr `(int*)fat_p` to `(int*)fat_p.raw_ptr` + CSCE->setCastKind(CK_NoOp); DesugarToMemberPtr = true; InsertCheckVersionCall = true; InsertCheckOffsetCall.first = true; @@ -751,7 +1264,9 @@ ExprResult TransformFatPtr::TransformCStyleCastExpr(CStyleCastExpr *CSCE) { QT = DesugarFatPtrType(SLoc, QT); // desugar `(int *fat)raw_p` to `(_FatPtr) { raw_p, 0, 0 }` Expr *SubRawPtrE = BaseTransform::TransformExpr(SubE).get(); - return BuildFatPtrCompoundLiteralExprForRawPtr(SubRawPtrE, QT); + Expr *FatPtrE = BuildFatPtrCompoundLiteralExprForRawPtr(SubRawPtrE, QT); + return ImplicitCastExpr::Create(SemaRef.Context, QT, CK_NoOp, FatPtrE, + nullptr, VK_PRValue, FPOptionsOverride()); } else if (QT.isFatPtrType() && SubQT.isFatPtrType()) { return BaseTransform::TransformExpr(SubE).get(); } @@ -780,38 +1295,142 @@ ExprResult TransformFatPtr::TransformUnaryExprOrTypeTraitExpr( return UETT; } +ExprResult +TransformFatPtr::TransformConditionalOperator(ConditionalOperator *CO) { + SourceLocation SLoc = CO->getBeginLoc(); + QualType QT = DesugarFatPtrType(SLoc, CO->getType()); + CO->setType(QT); + + Expr *CondExpr = CO->getCond(); + if (CondExpr->getType().isFatPtrType()) + DesugarToMemberPtr = true; + CondExpr = BaseTransform::TransformExpr(CondExpr).get(); + Expr *LHS = CO->getLHS(); + Expr *RHS = CO->getRHS(); + if (QT.isFatPtrRecordType() && LHS->isNullExpr(SemaRef.Context)) { + RHS = BaseTransform::TransformExpr(RHS).get(); + LHS = BuildFatPtrCompoundLiteralExprForRawPtr(LHS, QT); + } else if (QT.isFatPtrRecordType() && RHS->isNullExpr(SemaRef.Context)) { + LHS = BaseTransform::TransformExpr(LHS).get(); + RHS = BuildFatPtrCompoundLiteralExprForRawPtr(RHS, QT); + } else { + LHS = BaseTransform::TransformExpr(LHS).get(); + RHS = BaseTransform::TransformExpr(RHS).get(); + } + // ConditionalOperator class has not setCond/setLHS/setRHS method, + // so we build it. + return BaseTransform::RebuildConditionalOperator( + CondExpr, CO->getQuestionLoc(), LHS, CO->getColonLoc(), RHS); +} + +ExprResult +TransformFatPtr::TransformCompoundAssignOperator(CompoundAssignOperator *CAO) { + SourceLocation SLoc = CAO->getBeginLoc(); + QualType QT = DesugarFatPtrType(SLoc, CAO->getType()); + CAO->setType(QT); + BinaryOperator::Opcode Op = CAO->getOpcode(); + CAO->setLHS(BaseTransform::TransformExpr(CAO->getLHS()).get()); + CAO->setRHS(BaseTransform::TransformExpr(CAO->getRHS()).get()); + if (QT.isFatPtrType() && (Op == BO_AddAssign || Op == BO_SubAssign)) { + // `p += 1` should be desugared to + // `p = (_FatPtr) { p.raw_ptr + 1, p.key_version, p.offset + 1 * + // sizeof(int) }` + Expr *LHS = CAO->getLHS(); + Expr *RHS = CAO->getRHS(); + bool OpIsAddAssign = Op == BO_AddAssign ? true : false; + Expr *FirstInitE = + SemaRef + .CreateBuiltinBinOp(SLoc, OpIsAddAssign ? BO_Add : BO_Sub, + BuildMemberForFatPtr(LHS, "raw_ptr"), RHS) + .get(); + Expr *SecondInitE = BuildMemberForFatPtr(LHS, "key_version"); + Expr *ThirdInitLHS = BuildMemberForFatPtr(LHS, "offset"); + QualType PointeeQT = LHS->getType().getFatPtrPointeeType(); + QualType Int32Ty = SemaRef.Context.getIntTypeForBitwidth(32, true); + Expr *ThirdInitRHSRHS = IntegerLiteral::Create( + SemaRef.Context, + llvm::APInt(32, SemaRef.Context.getTypeSize(PointeeQT) / 8), Int32Ty, + SLoc); + Expr *ThirdInitRHS = + SemaRef.CreateBuiltinBinOp(SLoc, BO_Mul, RHS, ThirdInitRHSRHS).get(); + Expr *ThirdInitE = + SemaRef + .CreateBuiltinBinOp(SLoc, OpIsAddAssign ? BO_Add : BO_Sub, + ThirdInitLHS, ThirdInitRHS) + .get(); + RHS = + BuildFatPtrCompoundLiteralExpr(FirstInitE, SecondInitE, ThirdInitE, QT); + return SemaRef.CreateBuiltinBinOp(SLoc, BO_Assign, LHS, RHS); + } + return CAO; +} + // BinaryOperator includes: // reassign fat ptr : p1 = p2, p1 = nullptr +// comparison between fat ptr : p1 != p2, p == nullptr, p1 < p2 // calculation between fat ptr : p + 1, p - 1, p1 - p2 ExprResult TransformFatPtr::TransformBinaryOperator(BinaryOperator *BO) { SourceLocation SLoc = BO->getBeginLoc(); - BO->setType(DesugarFatPtrType(SLoc, BO->getType())); + QualType QT = DesugarFatPtrType(SLoc, BO->getType()); + BO->setType(QT); + + MemberExpr *MemberRawPtr = nullptr; + if (QT.isFatPtrRecordType() && DesugarToMemberPtr) + // Desugar p + 1 to (p + 1).raw_ptr + MemberRawPtr = BuildMemberRawPtrAndInsertCheckCallForFatPtr(BO); + Expr *LHS = BO->getLHS(); Expr *RHS = BO->getRHS(); QualType LHSQT = LHS->getType(); QualType RHSQT = RHS->getType(); BinaryOperator::Opcode Op = BO->getOpcode(); - if (Op == BO_Sub && LHSQT.isFatPtrType() && RHSQT.isFatPtrType()) { + if ((Op == BO_Sub || BO->isComparisonOp()) && LHSQT.isFatPtrType() && + RHSQT.isFatPtrType()) { // Substraction between two fat ptr // `p1 - p2` should be desugared to `p1.raw_ptr - p2.raw_ptr` - return HandleBOFatPtrSubFatPtr(BO); - } else if (BO->isAdditiveOp() && LHSQT.isFatPtrType() && RHSQT->isIntegerType()) { + // Comparison between two fat ptr + // `p1 == p2` should be desugared to `p1.raw_ptr == p2.raw_ptr` + DesugarToMemberPtr = true; + BO->setLHS(BaseTransform::TransformExpr(LHS).get()); + DesugarToMemberPtr = true; + BO->setRHS(BaseTransform::TransformExpr(RHS).get()); + return BO; + } else if (BO->isAdditiveOp() && LHSQT.isFatPtrType() && + RHSQT->isIntegerType()) { // addition or substraction between fat ptr and integer // `p + 1` should be desugared to // `(_FatPtr) { p.raw_ptr + 1, p.key_version, p.offset + 1 * sizeof(int) }` - return HandleBOFatPtrAddOrSubInteger(BO); + Expr *DesugaredE = HandleBOFatPtrAddOrSubInteger(BO).get(); + if (MemberRawPtr) { + MemberRawPtr->setBase(DesugaredE); + std::stack> CheckCallExprCopy = CheckCallExpr; + while (!CheckCallExprCopy.empty()) { + if (CheckCallExprCopy.top().first == BO) { + for (auto ArgE : dyn_cast(CheckCallExprCopy.top().second) + ->arguments()) { + if (auto ME = dyn_cast(ArgE->IgnoreImpCasts())) { + ME->setBase(DesugaredE); + } + } + } + CheckCallExprCopy.pop(); + } + return MemberRawPtr; + } + return DesugaredE; } else if (BO->isAssignmentOp() && LHSQT.isFatPtrType() && - IsNullPtrExpr(RHS)) { + RHS->isNullExpr(SemaRef.Context)) { // `p = nullptr;` should be desugared to // `p = (_FatPtr){ nullptr, 0, 0 };` LHSQT = DesugarFatPtrType(SLoc, LHSQT); BO->setLHS(BaseTransform::TransformExpr(LHS).get()); BO->setRHS(BuildFatPtrCompoundLiteralExprForRawPtr(RHS, LHSQT)); return BO; - } else if (BO->isEqualityOp() && LHSQT.isFatPtrType() && IsNullPtrExpr(RHS)) { - // `p1 == p2` should be desugared to `p1.ptr == nullptr` + } else if (BO->isEqualityOp() && LHSQT.isFatPtrType() && + RHS->isNullExpr(SemaRef.Context)) { + // `p == nullptr` should be desugared to `p.raw_ptr == nullptr` DesugarToMemberPtr = true; - BO->setLHS(BaseTransform::TransformExpr(BO->getLHS()).get()); + BO->setLHS(BaseTransform::TransformExpr(LHS).get()); return BO; } BO->setLHS(BaseTransform::TransformExpr(LHS).get()); @@ -819,14 +1438,6 @@ ExprResult TransformFatPtr::TransformBinaryOperator(BinaryOperator *BO) { return BO; } -ExprResult TransformFatPtr::HandleBOFatPtrSubFatPtr(BinaryOperator *BO) { - DesugarToMemberPtr = true; - BO->setLHS(BaseTransform::TransformExpr(BO->getLHS()).get()); - DesugarToMemberPtr = true; - BO->setRHS(BaseTransform::TransformExpr(BO->getRHS()).get()); - return BO; -} - // addition or substraction between fat ptr and integer // `p + 1` should be desugared to // `(_FatPtr) { p.ptr + 1, p.key_version, p.offset + 1 * sizeof(int) }` @@ -834,56 +1445,89 @@ ExprResult TransformFatPtr::HandleBOFatPtrAddOrSubInteger(BinaryOperator *BO) { SourceLocation SLoc = BO->getBeginLoc(); QualType QT = BO->getType(); BinaryOperator::Opcode Op = BO->getOpcode(); - Expr *FatPtrLHS = BO->getLHS()->IgnoreImpCasts(); DesugarToMemberPtr = true; Expr *LHS = BaseTransform::TransformExpr(BO->getLHS()).get(); Expr *RHS = BaseTransform::TransformExpr(BO->getRHS()).get(); Expr *FirstInitE = SemaRef.CreateBuiltinBinOp(SLoc, Op, LHS, RHS).get(); - Expr *SecondInitE = BuildMemberForFatPtr(FatPtrLHS, "key_version"); - - Expr *ThirdInitLHS = BuildMemberForFatPtr(FatPtrLHS, "offset"); - QualType PointeeQT = FatPtrLHS->getType()->getFatPtrPointeeType(); - Expr *SizeOfExpr = - SemaRef - .CreateUnaryExprOrTypeTraitExpr( - SemaRef.Context.getTrivialTypeSourceInfo(PointeeQT), - SLoc, UETT_SizeOf, SourceRange()) - .get(); - Expr *ThirdInitRHS = - SemaRef.CreateBuiltinBinOp(SLoc, BO_Mul, RHS, SizeOfExpr).get(); - Expr *ThirdInitE = - SemaRef.CreateBuiltinBinOp(SLoc, Op, ThirdInitLHS, ThirdInitRHS).get(); - return BuildFatPtrCompoundLiteralExpr(FirstInitE, SecondInitE, ThirdInitE, QT); + if (auto MemberFatPtr = dyn_cast(LHS->IgnoreImpCasts())) { + Expr *FatPtrBase = MemberFatPtr->getBase(); + Expr *SecondInitE = BuildMemberForFatPtr(FatPtrBase, "key_version"); + Expr *ThirdInitLHS = BuildMemberForFatPtr(FatPtrBase, "offset"); + QualType PointeeQT = FatPtrBase->getType().getFatPtrPointeeType(); + QualType Int32Ty = SemaRef.Context.getIntTypeForBitwidth(32, true); + Expr *ThirdInitRHSRHS = IntegerLiteral::Create( + SemaRef.Context, + llvm::APInt(32, SemaRef.Context.getTypeSize(PointeeQT) / 8), Int32Ty, + SLoc); + Expr *ThirdInitRHS = + SemaRef.CreateBuiltinBinOp(SLoc, BO_Mul, RHS, ThirdInitRHSRHS).get(); + Expr *ThirdInitE = + SemaRef.CreateBuiltinBinOp(SLoc, Op, ThirdInitLHS, ThirdInitRHS).get(); + return BuildFatPtrCompoundLiteralExpr(FirstInitE, SecondInitE, ThirdInitE, + QT); + } + return BO; } ExprResult TransformFatPtr::TransformUnaryOperator(UnaryOperator *UO) { QualType QT = DesugarFatPtrType(UO->getBeginLoc(), UO->getType()); UO->setType(QT); + MemberExpr *MemberRawPtr = nullptr; + if (QT.isFatPtrRecordType() && DesugarToMemberPtr) + // Desugar *p to (*p).raw_ptr + // Desugar p++ to p.raw_ptr and p = p + 1 + // Desugar &fat a to (&fat a).raw_ptr + MemberRawPtr = BuildMemberRawPtrAndInsertCheckCallForFatPtr(UO); + UnaryOperator::Opcode Op = UO->getOpcode(); Expr *SubE = UO->getSubExpr(); QualType SubQT = SubE->getType(); if (Op == UO_Deref && SubQT.isFatPtrType()) { // handle *p if p is fat ptr. + if (MemberRawPtr) { + MemberRawPtr->setBase(HandleUODerefFatPtr(UO).get()); + return MemberRawPtr; + } return HandleUODerefFatPtr(UO); } else if (Op == UO_AddrFat) { // handle &fat expr. AddrFatExprBaseKind BaseKind = Finder.AddrFatExprAndBaseMap[UO].first; switch (BaseKind) { case AddrFatExprBaseKind::FatPtr: - return HandleUOAddrFatOnExprWithFatPtrBase(UO, Finder.AddrFatExprAndBaseMap[UO].second); + if (MemberRawPtr) { + MemberRawPtr->setBase(HandleUOAddrFatOnExprWithFatPtrBase( + UO, Finder.AddrFatExprAndBaseMap[UO].second) + .get()); + return MemberRawPtr; + } + return HandleUOAddrFatOnExprWithFatPtrBase( + UO, Finder.AddrFatExprAndBaseMap[UO].second); case AddrFatExprBaseKind::LocalNoArrayVar: + if (MemberRawPtr) { + MemberRawPtr->setBase(HandleUOAddrFatOnLocalNoArrayVar(UO).get()); + return MemberRawPtr; + } return HandleUOAddrFatOnLocalNoArrayVar(UO); case AddrFatExprBaseKind::LocalArrayVar: case AddrFatExprBaseKind::GlobalArrayVar: + if (MemberRawPtr) { + MemberRawPtr->setBase(HandleUOAddrFatOnArrayVar( + UO, Finder.AddrFatExprAndBaseMap[UO].second) + .get()); + return MemberRawPtr; + } return HandleUOAddrFatOnArrayVar(UO, Finder.AddrFatExprAndBaseMap[UO].second); default: break; } + } else if (Op == UO_LNot && SubQT.isFatPtrType()) { + // `!p` where p is fat ptr should be desugared to `!p.raw_ptr` + DesugarToMemberPtr = true; } UO->setSubExpr(BaseTransform::TransformExpr(SubE).get()); - return UO; + return MemberRawPtr ? (Expr *)MemberRawPtr : UO; } ExprResult TransformFatPtr::HandleUODerefFatPtr(UnaryOperator *UO) { @@ -893,14 +1537,21 @@ ExprResult TransformFatPtr::HandleUODerefFatPtr(UnaryOperator *UO) { InsertCheckOffsetCall.first = true; // Build offset arg expr, for example: // for *p, offset should be `sizeof(int)` - Expr *OffsetExpr = - SemaRef - .CreateUnaryExprOrTypeTraitExpr( - SemaRef.Context.getTrivialTypeSourceInfo(UO->getType()), - UO->getBeginLoc(), UETT_SizeOf, SourceRange()) - .get(); + QualType Int32Ty = SemaRef.Context.getIntTypeForBitwidth(32, true); + Expr *OffsetExpr = IntegerLiteral::Create( + SemaRef.Context, + llvm::APInt(32, SemaRef.Context.getTypeSize(UO->getType()) / 8), Int32Ty, + UO->getBeginLoc()); InsertCheckOffsetCall.second = OffsetExpr; - UO->setSubExpr(BaseTransform::TransformExpr(SubE).get()); + SubE = BaseTransform::TransformExpr(SubE).get(); + if (auto ICE = dyn_cast(SubE)) { + if (ICE->getCastKind() == CK_LValueToRValue) { + return UO; + } + } + UO->setSubExpr(ImplicitCastExpr::Create(SemaRef.Context, SubE->getType(), + CK_LValueToRValue, SubE, nullptr, + VK_PRValue, FPOptionsOverride())); return UO; } @@ -1098,27 +1749,11 @@ ExprResult TransformFatPtr::TransformMemberExpr(MemberExpr *ME) { QualType QT = DesugarFatPtrType(SLoc, ME->getType()); ME->setType(QT); - MemberExpr *MemberPtr = nullptr; - if (QT->isFatPtrRecordType()) { - if (InsertCheckOffsetCall.first) { - llvm::SmallVector Args{ME, InsertCheckOffsetCall.second}; - Expr *CE = BuildFatPtrCall(SLoc, "_check_offset", Args); - CheckCallExpr.push(CE); - InsertCheckOffsetCall.first = false; - InsertCheckOffsetCall.second = nullptr; - } - if (InsertCheckVersionCall) { - llvm::SmallVector Args{ME}; - Expr *CE = BuildFatPtrCall(SLoc, "_check_version", Args); - CheckCallExpr.push(CE); - InsertCheckVersionCall = false; - } - if (DesugarToMemberPtr) { - // Desugar s.p to s.p.raw_ptr - MemberPtr = BuildMemberForFatPtr(ME, "raw_ptr"); - DesugarToMemberPtr = false; - } - } + Expr *MemberRawPtr = nullptr; + if (QT.isFatPtrRecordType() && DesugarToMemberPtr) + // Desugar s.p to s.p.raw_ptr + MemberRawPtr = BuildMemberRawPtrAndInsertCheckCallForFatPtr(ME); + QualType BaseType = ME->getBase()->getType(); if (ME->isArrow() && BaseType.isFatPtrType()) { DesugarToMemberPtr = true; @@ -1129,7 +1764,7 @@ ExprResult TransformFatPtr::TransformMemberExpr(MemberExpr *ME) { // offset is `__builtin_offsetof(struct S, arr[3]) + sizeof(int)` // 2. for p->a, where p is fat ptr and p->a is int type, // offset is `__builtin_offsetof(struct S, a) + sizeof(int)` - QualType PointeeQT = BaseType->getFatPtrPointeeType(); + QualType PointeeQT = BaseType.getFatPtrPointeeType(); TypeSourceInfo *PointeeTInfo = SemaRef.Context.getTrivialTypeSourceInfo(PointeeQT, SLoc); QualType ElementType; @@ -1146,14 +1781,15 @@ ExprResult TransformFatPtr::TransformMemberExpr(MemberExpr *ME) { Expr *OffsetExprLHS = SemaRef.BuildBuiltinOffsetOf(SLoc, PointeeTInfo, Comps, SLoc).get(); QualType Int32Ty = SemaRef.Context.getIntTypeForBitwidth(32, true); - Expr *OffsetExprRHS = - IntegerLiteral::Create(SemaRef.Context, - llvm::APInt(32, SemaRef.Context.getTypeSize(ElementType) / 8), Int32Ty, SLoc); + Expr *OffsetExprRHS = IntegerLiteral::Create( + SemaRef.Context, + llvm::APInt(32, SemaRef.Context.getTypeSize(ElementType) / 8), Int32Ty, + SLoc); InsertCheckOffsetCall.second = SemaRef.CreateBuiltinBinOp(SLoc, BO_Add, OffsetExprLHS, OffsetExprRHS).get(); } ME->setBase(BaseTransform::TransformExpr(ME->getBase()).get()); - return MemberPtr ? MemberPtr : ME; + return MemberRawPtr ? MemberRawPtr : ME; } // Desugar fat qualified QualType ,for example: @@ -1163,8 +1799,34 @@ ExprResult TransformFatPtr::TransformMemberExpr(MemberExpr *ME) { // 4. `int *fat *fat` will be desugared to `_FatPtr<_FatPtr>`; QualType TransformFatPtr::DesugarFatPtrType(SourceLocation SLoc, QualType T) { - T = T.getCanonicalType(); - if (!T.isFatQualified() && !T->hasFatFields()) + if (T->isFunctionPointerType()) { + const FunctionProtoType *FuncType = + T->getAs()->getPointeeType()->getAs(); + if (!FuncType->hasFatRetOrParams()) + return T; + // Desugar fat qualified return type. + QualType ReturnTy = DesugarFatPtrType(SLoc, FuncType->getReturnType()); + // Desugar fat qualified parameters. + llvm::SmallVector ParamTys; + for (auto ParamTy : FuncType->getParamTypes()) { + ParamTys.push_back(DesugarFatPtrType(SLoc, ParamTy)); + } + return SemaRef.Context.getPointerType(SemaRef.Context.getFunctionType( + ReturnTy, ParamTys, FuncType->getExtProtoInfo())); + } + if (auto AT = dyn_cast(T)) { + QualType ElementT = DesugarFatPtrType(SLoc, AT->getElementType()); + if (auto CAT = dyn_cast(T)) { + return SemaRef.Context.getConstantArrayType( + ElementT, CAT->getSize(), CAT->getSizeExpr(), CAT->getSizeModifier(), + CAT->getIndexTypeCVRQualifiers()); + } else if (auto VAT = dyn_cast(T)) { + return SemaRef.Context.getVariableArrayType( + ElementT, VAT->getSizeExpr(), VAT->getSizeModifier(), + VAT->getIndexTypeCVRQualifiers(), VAT->getBracketsRange()); + } + } + if (!T.hasFat()) return T; if (!FatPtrTD) { DeclContext::lookup_result Decls = @@ -1175,21 +1837,22 @@ TransformFatPtr::DesugarFatPtrType(SourceLocation SLoc, QualType T) { FatPtrTD = CTD; } if (FatPtrTD) { - if (auto PT = dyn_cast(T)) { + T = T.getCanonicalType(); + if (T.isFatPtrType()) { + QualType PointeeT = DesugarFatPtrType(SLoc, T.getFatPtrPointeeType()); + TemplateArgumentListInfo Args(SLoc, SLoc); + Args.addArgument(TemplateArgumentLoc( + TemplateArgument(PointeeT), + SemaRef.Context.getTrivialTypeSourceInfo(PointeeT, SLoc))); + QualType FatPtrRecordType = + SemaRef.CheckTemplateIdType(TemplateName(FatPtrTD), SLoc, Args); + if (!FatPtrRecordType.isNull() && + !SemaRef.RequireCompleteType( + SLoc, FatPtrRecordType, diag::err_fat_ptr_missing_specialization)) + return FatPtrRecordType; + } else if (auto PT = dyn_cast(T)) { QualType PointeeT = DesugarFatPtrType(SLoc, PT->getPointeeType()); - if (T.isFatQualified()) { - TemplateArgumentListInfo Args(SLoc, SLoc); - Args.addArgument(TemplateArgumentLoc( - TemplateArgument(PointeeT), - SemaRef.Context.getTrivialTypeSourceInfo(PointeeT, SLoc))); - QualType FatPtrRecordType = - SemaRef.CheckTemplateIdType(TemplateName(FatPtrTD), SLoc, Args); - if (!FatPtrRecordType.isNull() && - !SemaRef.RequireCompleteType(SLoc, FatPtrRecordType, - diag::err_fat_ptr_missing_specialization)) - return FatPtrRecordType; - } else - return SemaRef.Context.getPointerType(PointeeT); + return SemaRef.Context.getPointerType(PointeeT); } return T; } @@ -1197,6 +1860,7 @@ TransformFatPtr::DesugarFatPtrType(SourceLocation SLoc, QualType T) { return T; } +// Build CallExpr for _check_version, _check_offset or _new_version_number. Expr *TransformFatPtr::BuildFatPtrCall(SourceLocation SLoc, std::string FDName, llvm::SmallVector &Args) { if (!FatPtrFD[FDName]) { @@ -1206,17 +1870,45 @@ Expr *TransformFatPtr::BuildFatPtrCall(SourceLocation SLoc, std::string FDName, if (Decls.isSingleResult()) FatPtrFD[FDName] = dyn_cast(Decls.front()); } - if (FunctionDecl *FD = FatPtrFD[FDName]) { - DeclRefExpr *FDRef = - SemaRef.BuildDeclRefExpr(FD, FD->getType(), VK_LValue, SLoc); - CallExpr *CE = - dyn_cast(SemaRef.BuildCallExpr(nullptr, FDRef, SLoc, Args, SLoc).get()); - // Handle first arg of _check_version and _check_version. - if (CE && CE->getNumArgs() > 0 && - CE->getArg(0)->getType()->isFatPtrRecordType()) - if (auto ICE = dyn_cast(CE->getArg(0))) - ICE->setType(ICE->getSubExpr()->getType()); - return CE; + if (FunctionDecl *CalleeFD = FatPtrFD[FDName]) { + // Handle arg for _check_version and _check_offset: + // 1. split the first _FatPtr argument into three fields argument + // 2. add file name, line number and function name to report error + if (FDName != "_new_version_number") { + Expr *FatPtrE = Args.pop_back_val(); + Args.push_back(BuildMemberForFatPtr(FatPtrE, "raw_ptr")); + Args.push_back(BuildMemberForFatPtr(FatPtrE, "key_version")); + Args.push_back(BuildMemberForFatPtr(FatPtrE, "offset")); + // Build argE for file name. + StringRef FileName = + SemaRef.Context.getSourceManager().getBufferName(SLoc); + QualType FileNameStrType = SemaRef.Context.getConstantArrayType( + SemaRef.Context.CharTy, llvm::APInt(32, FileName.size() + 1), nullptr, + ArrayType::Normal, 0); + Expr *FileNameE = StringLiteral::Create(SemaRef.Context, FileName, + StringLiteral::Ordinary, false, + FileNameStrType, SLoc); + Args.push_back(FileNameE); + // Build argE for line number. + unsigned LineNo = + SemaRef.Context.getSourceManager().getSpellingLineNumber(SLoc); + Expr *LineNoE = + IntegerLiteral::Create(SemaRef.Context, llvm::APInt(32, LineNo), + SemaRef.Context.IntTy, SLoc); + Args.push_back(LineNoE); + // Build argE for function name. + StringRef FuncName = FD->getName(); + QualType FuncNameStrType = SemaRef.Context.getConstantArrayType( + SemaRef.Context.CharTy, llvm::APInt(32, FuncName.size() + 1), nullptr, + ArrayType::Normal, 0); + Expr *FuncNameE = StringLiteral::Create(SemaRef.Context, FuncName, + StringLiteral::Ordinary, false, + FuncNameStrType, SLoc); + Args.push_back(FuncNameE); + } + DeclRefExpr *CalleeFDRef = SemaRef.BuildDeclRefExpr( + CalleeFD, CalleeFD->getType(), VK_LValue, SLoc); + return SemaRef.BuildCallExpr(nullptr, CalleeFDRef, SLoc, Args, SLoc).get(); } SemaRef.Diag(SLoc, diag::err_fat_ptr_func_not_found) << ""; return nullptr; @@ -1239,13 +1931,36 @@ MemberExpr *TransformFatPtr::BuildMemberForFatPtr(Expr *FatPtrBaseE, FieldDecl *FD = *it; return SemaRef.BuildMemberExpr( FatPtrBaseE, false, FatPtrBaseE->getBeginLoc(), - NestedNameSpecifierLoc(), FatPtrBaseE->getBeginLoc(), FD, + NestedNameSpecifierLoc(), SourceLocation(), FD, DeclAccessPair::make(FatPtrRD, FD->getAccess()), false, DeclarationNameInfo(), FD->getType(), VK_LValue, OK_Ordinary); } return nullptr; } +MemberExpr *TransformFatPtr::BuildMemberRawPtrAndInsertCheckCallForFatPtr( + Expr *FatPtrBaseE) { + SourceLocation SLoc = FatPtrBaseE->getBeginLoc(); + if (InsertCheckOffsetCall.first) { + // Build CallExpr to check offset. + llvm::SmallVector Args{InsertCheckOffsetCall.second, + FatPtrBaseE}; + Expr *CE = BuildFatPtrCall(SLoc, "_check_offset", Args); + CheckCallExpr.push(std::make_pair(FatPtrBaseE, CE)); + InsertCheckOffsetCall.first = false; + InsertCheckOffsetCall.second = nullptr; + } + if (InsertCheckVersionCall) { + // Build CallExpr to check version. + llvm::SmallVector Args{FatPtrBaseE}; + Expr *CE = BuildFatPtrCall(SLoc, "_check_version", Args); + CheckCallExpr.push(std::make_pair(FatPtrBaseE, CE)); + InsertCheckVersionCall = false; + } + DesugarToMemberPtr = false; + return BuildMemberForFatPtr(FatPtrBaseE, "raw_ptr"); +} + Expr *TransformFatPtr::BuildFatPtrCompoundLiteralExprForRawPtr( Expr *RawPtrE, QualType QT) { if (auto *ICE = dyn_cast(RawPtrE)) { @@ -1353,126 +2068,155 @@ void Sema::DesugarRecordDeclWithFatPtr(RecordDecl *RD) { TFP.TransformRecordDecl(RD); } -bool Sema::CheckFatQualTypeCStyleCast(QualType LHSType, QualType RHSType, - SourceLocation RLoc) { +void Sema::DesugarTypedefNameDeclWithFatPtr(TypedefNameDecl *TND) { + if (Diags.hasErrorOccurred()) + return; + + TransformFatPtr TFP(*this); + TFP.TransformTypedefNameDecl(TND); +} + +bool Sema::CheckFatQualTypeCStyleCast(QualType LHSType, Expr *RHSExpr) { + // nullptr can be explicitly cast to T *fat or T *fat * + if ((LHSType.isFatPtrType() || LHSType->isPointerType()) && + isa(RHSExpr)) + return true; + + // string literal can be cast to char *fat, such as `(char *fat)"xxxx";` + if (LHSType.isFatPtrType() && LHSType.getFatPtrPointeeType()->isCharType() && + isa(RHSExpr)) + return true; + + QualType RHSType = RHSExpr->getType(); if (!CheckFatQualTypeCStyleCast(LHSType, RHSType)) { - Diag(RLoc, diag::err_fat_qualcheck_incompatible) - << CompleteTraitType(RHSType) << CompleteTraitType(LHSType); + Diag(RHSExpr->getBeginLoc(), diag::err_fat_qualcheck_incompatible) + << 1 << RHSType << LHSType; return false; } return true; } bool Sema::CheckFatQualTypeCStyleCast(QualType LHSType, QualType RHSType) { - QualType RHSCanType = RHSType.getCanonicalType(); QualType LHSCanType = LHSType.getCanonicalType(); - bool IsSameType = (LHSCanType.getTypePtr() == RHSCanType.getTypePtr()); - const auto *LHSPtrType = LHSType->getAs(); - const auto *RHSPtrType = RHSType->getAs(); - bool IsPointer = LHSPtrType && RHSPtrType; - QualType RHSRawType = RHSCanType.getUnqualifiedType(); - QualType LHSRawType = LHSCanType.getUnqualifiedType(); - - if (IsSameType) { - return true; - } - if (LHSCanType.isFatPtrType() && RHSCanType.isFatPtrType()) { - return true; - } - if (IsPointer) { - if (Context.hasSameType(LHSRawType, RHSRawType)) { - return true; - } else if (LHSCanType.isFatQualified() && RHSCanType.isFatQualified()) { - // T1 *fat is allowed to cast to T2 *fat - return true; - } else if (TryDesugarTrait(RHSType)) { - return true; - } else { - return CheckFatQualTypeCStyleCast(LHSPtrType->getPointeeType(), - RHSPtrType->getPointeeType()); - } + QualType RHSCanType = RHSType.getCanonicalType(); + + // Array Type can be cast to fat pointer type + if (LHSCanType.isFatPtrType() && RHSCanType->isArrayType()) { + QualType LHSPointeeType = LHSCanType.getFatPtrPointeeType(); + QualType RHSElementType = + Context.getAsArrayType(RHSCanType)->getElementType(); + if (LHSPointeeType.isFatPtrType() != RHSElementType.isFatPtrType()) + return false; } - return false; -} -bool Sema::CheckFatQualTypeAssignment(QualType LHSType, QualType RHSType, - SourceLocation RLoc) { - QualType RHSCanType = RHSType.getCanonicalType(); - QualType LHSCanType = LHSType.getCanonicalType(); - const auto *LHSPtrType = LHSType->getAs(); - const auto *RHSPtrType = RHSType->getAs(); - bool IsPointer = LHSPtrType && RHSPtrType; + if ((LHSCanType.isFatPtrType() && RHSCanType->isIntegerType()) || + (RHSCanType.isFatPtrType() && LHSCanType->isIntegerType())) + return false; - if (LHSCanType.isFatQualified() == RHSCanType.isFatQualified()) { - if (TraitDecl *TD = TryDesugarTrait(LHSCanType)) { - if (TD->getTypeImpledVarDecl(RHSCanType->getPointeeType())) - return true; - } - if (Context.hasSameType(LHSCanType, RHSCanType)) { - return true; - } - if (LHSCanType->isVoidPointerType()) { - if (LHSCanType->getPointeeType().isConstQualified() == - RHSCanType->getPointeeType().isConstQualified()) + // LHSType and RHSType are both pointer type + if ((LHSCanType.isFatPtrType() || LHSCanType->isPointerType()) && + (RHSCanType.isFatPtrType() || RHSCanType->isPointerType())) { + QualType LHSPointeeType = + LHSCanType.isFatPtrType() + ? LHSCanType.getFatPtrPointeeType() + : LHSCanType->getAs()->getPointeeType(); + QualType RHSPointeeType = + RHSCanType.isFatPtrType() + ? RHSCanType.getFatPtrPointeeType() + : RHSCanType->getAs()->getPointeeType(); + + if (LHSPointeeType.isFatPtrType() != RHSPointeeType.isFatPtrType()) { + // T1 *fat * <-> T2 * is OK + if ((LHSPointeeType.isFatPtrType() && !RHSPointeeType->isPointerType()) || + (RHSPointeeType.isFatPtrType() && !LHSPointeeType->isPointerType())) return true; - } - if (!IsPointer) { + // T1 *fat *fat <-> T2 **fat is ERROR + // T1 *fat * <-> T2 ** is ERROR + // T1 *fat *fat <-> T2 ** is ERROR return false; - } else { - return CheckFatQualTypeAssignment(LHSPtrType->getPointeeType(), - RHSPtrType->getPointeeType(), RLoc); } + + // T1 *fat <-> T2 *fat is OK + // T1 *fat <-> T2 * is OK + // T1 *fat *fat <-> T2 *fat * is OK + // T1 ** fat <-> T2 ** is OK + return true; } - return false; + + return true; } bool Sema::CheckFatQualTypeAssignment(QualType LHSType, Expr *RHSExpr) { + // T *fat or T *fat * can be inited by nullptr. + if ((LHSType.isFatPtrType() || LHSType->isPointerType()) && + isa(RHSExpr)) + return true; + + QualType RHSType = RHSExpr->getType(); + if (!CheckFatQualTypeAssignment(LHSType, RHSType)) { + Diag(RHSExpr->getBeginLoc(), diag::err_fat_qualcheck_incompatible) + << 0 << RHSType << LHSType; + return false; + } + return true; +} + +bool Sema::CheckFatQualTypeAssignment(QualType LHSType, QualType RHSType) { QualType LHSCanType = LHSType.getCanonicalType(); - QualType RHSCanType = RHSExpr->getType().getCanonicalType(); - SourceLocation ExprLoc = RHSExpr->getBeginLoc(); - bool Res = true; + QualType RHSCanType = RHSType.getCanonicalType(); + if (LHSCanType.isFatPtrType() && RHSCanType.isFatPtrType()) { + if (Context.hasSameFatPtrType(LHSCanType, RHSCanType)) + return true; - // Fat pointer can be inited by nullptr. - if (LHSCanType.isFatPtrType() && isa(RHSExpr)) - return true; + QualType LHSPointeeType = LHSCanType.getFatPtrPointeeType(); + QualType RHSPointeeType = RHSCanType.getFatPtrPointeeType(); - if (LHSCanType.isFatQualified() || RHSCanType.isFatQualified()) { - if (TraitDecl *TD = TryDesugarTrait(LHSCanType)) { - if (RHSCanType->isPointerType()) { - QualType ImplType = RHSCanType->getPointeeType() - .getUnqualifiedType() - .getCanonicalType(); - ImplType.removeLocalOwned(); - if (TD->getTypeImpledVarDecl(ImplType)) - return true; - } - // trait T* fat <-> trait T* fat // legal - if (TD) { - QualType QT = - DesugarTraitToStructTrait(TD, LHSCanType, RHSExpr->getExprLoc()); - if (QT.getCanonicalType() == RHSCanType) { - return true; - } - } - } - if (LHSCanType->isVoidPointerType() && RHSCanType->isPointerType()) { - if (LHSCanType->getPointeeType().isConstQualified() == - RHSCanType->getPointeeType().isConstQualified()) + // const T *fat -> T *fat is ERROR + if (!LHSPointeeType.isLocalConstQualified() && + RHSPointeeType.isLocalConstQualified()) + return false; + + if (LHSPointeeType.isLocalConstQualified()) { + LHSPointeeType.removeLocalConst(); + if (RHSPointeeType.isLocalConstQualified()) + RHSPointeeType.removeLocalConst(); + // T *fat -> const T *fat is OK + if (Context.hasSameFatPtrType(LHSPointeeType, RHSPointeeType)) return true; + // T *fat -> const void *fat is OK + if (LHSPointeeType->isVoidType()) + return true; + // T1 *fat -> const T2 *fat is ERROR } - if (!Context.hasSameType(LHSCanType, RHSCanType)) - Res = false; - } else { - Res = CheckFatQualTypeAssignment(LHSType, RHSCanType, ExprLoc); + return false; } - if ((LHSCanType.isFatPtrType() && RHSCanType.isFatPtrType()) && - !(LHSCanType.isFatQualified() && RHSCanType.isFatQualified())) - return true; - if (!Res) { - Diag(ExprLoc, diag::err_fat_qualcheck_incompatible) - << CompleteTraitType(RHSExpr->getType()) << CompleteTraitType(LHSType); + + // T *fat -> T * is ERROR + // T * -> T *fat is ERROR + if (LHSCanType.isFatPtrType() != RHSCanType.isFatPtrType()) + return false; + + if (!LHSCanType.isFatPtrType() && !RHSCanType.isFatPtrType() && + LHSCanType->isPointerType() && RHSCanType->isPointerType()) { + if (LHSCanType->isVoidPointerType() || RHSCanType->isVoidPointerType()) + return true; + QualType LHSPointeeType = + LHSCanType->getAs()->getPointeeType(); + QualType RHSPointeeType = + RHSCanType->getAs()->getPointeeType(); + return CheckFatQualTypeAssignment(LHSPointeeType, RHSPointeeType); } - return Res; + return true; +} + +bool Sema::HasDiffFatTypeAtBothFunction(QualType LHSType, QualType RHSType) { + if (LHSType.isNull() || RHSType.isNull()) + return false; + const FunctionProtoType *LHSFuncType = LHSType->getAs(); + const FunctionProtoType *RHSFuncType = RHSType->getAs(); + if (!LHSFuncType || !RHSFuncType) + return false; + return !Context.hasSameFatPtrType(LHSType, RHSType); } bool Sema::CheckFatFunctionPointerType(QualType LHSType, Expr *RHSExpr) { @@ -1487,26 +2231,37 @@ bool Sema::CheckFatFunctionPointerType(QualType LHSType, Expr *RHSExpr) { ->getAs() : RHSExpr->getType()->getAs(); SourceLocation ExprLoc = RHSExpr->getBeginLoc(); - - // return if no 'fat' in both side - if (!LHSFuncType->hasFatRetOrParams() && !RHSFuncType->hasFatRetOrParams()) { - return true; - } - - if (!Context.hasSameType(LHSFuncType, RHSFuncType)) { + if (HasDiffFatTypeAtBothFunction(QualType(LHSFuncType, 0), + QualType(RHSFuncType, 0))) { Diag(ExprLoc, diag::err_fat_funcPtr_incompatible) << LHSType << RHSExpr->getType(); return false; } - for (unsigned i = 0; i < LHSFuncType->getNumParams(); i++) { - QualType LHSParamType = LHSFuncType->getParamType(i); - QualType RHSParamType = RHSFuncType->getParamType(i); - if (!Context.hasSameType(LHSParamType, RHSParamType)) { - Diag(ExprLoc, diag::err_fat_funcPtr_incompatible) - << LHSType << RHSExpr->getType(); + return true; +} + +// In some context, it is hard to desugar SelfIncExpr or +// SelfDecExpr of fat ptr, so we report error when: +// 1) the CondExpr of IfStmt/WhileStmt/DoStmt/ForStmt/ +// SwitchStmt/ConditionalOperator has SelfIncExpr or +// SelfDecExpr of fat ptr, such as `while (*p++ > 0)`, +// `*p++ > 0 ? : *p : *q;` +// 2) the LHS or RHS of ConditionalOperator has SelfIncExpr +// or SelfDecExpr of fat ptr, such as `*p ? : p++ : q++;` +bool Sema::CheckSelfIncOrDecOfFatPtr(Expr *E) { + if (auto UO = dyn_cast(E)) { + if ((UO->isPrefix() || UO->isPostfix()) && + UO->getSubExpr()->getType().isFatPtrType()) { + Diag(UO->getBeginLoc(), diag::err_no_inc_or_dec_of_fat_ptr); return false; } } + for (auto C : E->children()) { + if (auto ChildE = dyn_cast_or_null(C)) { + if (!CheckSelfIncOrDecOfFatPtr(ChildE)) + return false; + } + } return true; } @@ -1522,7 +2277,7 @@ bool Sema::CheckFatFunctionPointerType(QualType LHSType, Expr *RHSExpr) { // uint32_t size; // int arr[3]; // }; -// struct _arr_AllocationUnit _arr = { 0, 12, {1, 2, 3} }; +// struct _arr_AllocationUnit _arr = { 1, 12, {1, 2, 3} }; // @endcode std::pair Sema::BuildAllocationUnitForGlobalArrayVar(VarDecl *VD) { @@ -1560,9 +2315,9 @@ Sema::BuildAllocationUnitForGlobalArrayVar(VarDecl *VD) { &Context.Idents.get("_" + VD->getNameAsString()), QualType(RD->getTypeForDecl(), 0), nullptr, SC_None); // Build InitExpr - // Build first init expr : 0 + // Build first init expr : 1 Expr *LockVersionInit = - IntegerLiteral::Create(Context, llvm::APInt(32, 0), UInt32Ty, SLoc); + IntegerLiteral::Create(Context, llvm::APInt(32, 1), UInt32Ty, SLoc); // Build second init expr : array size Expr *SizeInit = IntegerLiteral::Create( Context, llvm::APInt(32, Context.getTypeSize(VD->getType()) / 8), diff --git a/clang/lib/Sema/SemaCast.cpp b/clang/lib/Sema/SemaCast.cpp index 741617c32050..1df1d1a28d4b 100644 --- a/clang/lib/Sema/SemaCast.cpp +++ b/clang/lib/Sema/SemaCast.cpp @@ -2899,9 +2899,8 @@ void CastOperation::CheckCStyleCast() { } } // bsc fat type CStyleCast - if (SrcCanType.isFatQualified() || DestCanType.isFatQualified()) { - if (!Self.CheckFatQualTypeCStyleCast(DestType, SrcType, - SrcExpr.get()->getExprLoc())) { + if (SrcType.hasFat() || DestType.hasFat()) { + if (!Self.CheckFatQualTypeCStyleCast(DestType, SrcExpr.get())) { SrcExpr = ExprError(); return; } @@ -2922,13 +2921,6 @@ void CastOperation::CheckCStyleCast() { return; } } - if (LHSPtrType->hasFatFields() || RHSPtrType->hasFatFields()) { - if (!Self.CheckFatQualTypeCStyleCast(DestType, SrcType, - SrcExpr.get()->getExprLoc())) { - SrcExpr = ExprError(); - return; - } - } } } if (Self.IsTraitExpr(SrcExpr.get()) && DestType->isPointerType()) { @@ -2940,7 +2932,8 @@ void CastOperation::CheckCStyleCast() { Kind = CK_BitCast; } return; - } else if (SrcCanType->isFatPtrRecordType() && DestType.isFatQualified()) { + } else if (SrcCanType.isFatPtrRecordType() || + DestCanType.isFatPtrRecordType()) { return; } } diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 3dd0f99dbb13..71e69ae94420 100755 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -4106,6 +4106,7 @@ bool Sema::MergeFunctionDecl(FunctionDecl *New, NamedDecl *&OldD, Scope *S, // If any declaration of a BSC function or function // template has a constexpr specifier then all its declarations shall // contain the constexpr specifier. + bool AreCompatibleFunctionTypeWithFatPtr = false; if (getLangOpts().BSC) { if (New->getConstexprKind() != Old->getConstexprKind()) { Diag(New->getLocation(), diag::err_constexpr_redecl_mismatch) @@ -4114,12 +4115,25 @@ bool Sema::MergeFunctionDecl(FunctionDecl *New, NamedDecl *&OldD, Scope *S, Diag(Old->getLocation(), diag::note_previous_declaration); return true; } + if (New->getSafeZoneSpecifier() != Old->getSafeZoneSpecifier()) { + Diag(New->getLocation(), diag::err_conflicting_types) << New; + Diag(Old->getLocation(), PrevDiag) << Old << Old->getType(); + return true; + } if (HasDiffBorrowOrOwnedParamsTypeAtBothFunction(Old->getType(), New->getType())) { Diag(New->getLocation(), diag::err_conflicting_types) << New; Diag(Old->getLocation(), PrevDiag) << Old << Old->getType(); return true; } + if (Old->getType().hasFat() || New->getType().hasFat()) { + if (HasDiffFatTypeAtBothFunction(Old->getType(), New->getType())) { + Diag(New->getLocation(), diag::err_conflicting_types) << New; + Diag(Old->getLocation(), PrevDiag) << Old << Old->getType(); + return true; + } + AreCompatibleFunctionTypeWithFatPtr = true; + } if (New->getOverloadedOperator() != Old->getOverloadedOperator()) { Diag(New->getLocation(), diag::err_conflicting_types) << New; Diag(Old->getLocation(), PrevDiag) << Old << Old->getType(); @@ -4197,7 +4211,11 @@ bool Sema::MergeFunctionDecl(FunctionDecl *New, NamedDecl *&OldD, Scope *S, } } - if (Context.typesAreCompatible(OldQType, NewQType)) { + if (Context.typesAreCompatible(OldQType, NewQType) +#if ENABLE_BSC + || AreCompatibleFunctionTypeWithFatPtr +#endif + ) { const FunctionType *OldFuncType = OldQType->getAs(); const FunctionType *NewFuncType = NewQType->getAs(); const FunctionProtoType *OldProto = nullptr; @@ -6878,6 +6896,11 @@ Sema::ActOnTypedefNameDecl(Scope *S, DeclContext *DC, TypedefNameDecl *NewTD, Context.setucontext_tDecl(NewTD); } +#if ENABLE_BSC + if (getLangOpts().BSC && getLangOpts().EnableFatPtr) + DesugarTypedefNameDeclWithFatPtr(NewTD); +#endif + return NewTD; } diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 30381de78101..92ed21bdec69 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -5159,7 +5159,8 @@ ExprResult Sema::ActOnArraySubscriptExpr(Scope *S, Expr *base, << base->getType() << base->getSourceRange(); return ExprError(); } - if (getLangOpts().BSC && base->getType()->isRecordType()) { + if (getLangOpts().BSC && base->getType()->isRecordType() && + !base->getType().isFatPtrRecordType()) { return CreateOverloadedArraySubscriptExpr(lbLoc, rbLoc, base, ArgExprs); } #endif @@ -5897,10 +5898,10 @@ Sema::CreateBuiltinArraySubscriptExpr(Expr *Base, SourceLocation LLoc, ResultType = PTy->getPointeeType(); #if ENABLE_BSC } else if (getLangOpts().BSC && getLangOpts().EnableFatPtr && - LHSTy->isFatPtrRecordType()) { + LHSTy.isFatPtrRecordType()) { BaseExpr = LHSExp; IndexExpr = RHSExp; - ResultType = LHSTy->getFatPtrPointeeType(); + ResultType = LHSTy.getFatPtrPointeeType(); #endif } else if (const ObjCObjectPointerType *PTy = LHSTy->getAs()) { @@ -8662,6 +8663,11 @@ static bool checkCondition(Sema &S, Expr *Cond, SourceLocation QuestionLoc) { // C99 6.5.15p2 if (CondTy->isScalarType()) return false; +#if ENABLE_BSC + if (S.getLangOpts().BSC && S.getLangOpts().EnableFatPtr && + CondTy.isFatPtrRecordType()) + return false; +#endif S.Diag(QuestionLoc, diag::err_typecheck_cond_expect_scalar) << CondTy << Cond->getSourceRange(); return true; @@ -9241,7 +9247,17 @@ QualType Sema::CheckConditionalOperands(ExprResult &Cond, ExprResult &LHS, // the type of the other operand." if (!checkConditionalNullPointer(*this, RHS, LHSTy)) return LHSTy; if (!checkConditionalNullPointer(*this, LHS, RHSTy)) return RHSTy; - +#if ENABLE_BSC + if (getLangOpts().BSC && getLangOpts().EnableFatPtr) { + if (RHS.get()->isNullExpr(Context) && LHSTy.isFatPtrType()) + return LHSTy; + if (LHS.get()->isNullExpr(Context) && RHSTy.isFatPtrType()) + return RHSTy; + if (LHSTy.isFatPtrType() && RHSTy.isFatPtrType() && + Context.hasSameFatPtrType(LHSTy, RHSTy)) + return LHSTy; + } +#endif // All objective-c pointer type analysis is done here. QualType compositeType = FindCompositeObjCPointerType(LHS, RHS, QuestionLoc); @@ -9704,6 +9720,16 @@ ExprResult Sema::ActOnConditionalOp(SourceLocation QuestionLoc, RHS.isInvalid()) return ExprError(); +#if ENABLE_BSC + if (getLangOpts().BSC && getLangOpts().EnableFatPtr) { + bool CondResult = CheckSelfIncOrDecOfFatPtr(CondExpr); + bool LHSResult = CheckSelfIncOrDecOfFatPtr(LHSExpr); + bool RHSResult = CheckSelfIncOrDecOfFatPtr(RHSExpr); + if (!CondResult || !LHSResult || !RHSResult) + return ExprError(); + } +#endif + DiagnoseConditionalPrecedence(*this, QuestionLoc, Cond.get(), LHS.get(), RHS.get()); @@ -10449,16 +10475,13 @@ static bool IsTraitEqualExpr(Sema &S, QualType DstType, QualType SrcType, } static bool IsFatPtrEqualExpr(Sema &S, QualType DstType, QualType SrcType) { - if (auto *Typedef = dyn_cast(DstType)) { - DstType = Typedef->getDecl()->getUnderlyingType(); - } - if (auto *Typedef = dyn_cast(SrcType)) { - SrcType = Typedef->getDecl()->getUnderlyingType(); - } if (DstType.isFatPtrType() && (SrcType.isFatPtrType() || SrcType->isNullPtrType())) { return true; } + if (DstType->isVoidPointerType() && SrcType.isFatPtrType()) { + return true; + } return false; } #endif @@ -10504,10 +10527,6 @@ Sema::CheckSingleAssignmentConstraints(QualType LHSType, ExprResult &CallerRHS, if (!CheckBorrowQualTypeAssignment(LHSType, RHS.get())) return IncompatibleBorrowPointer; } - if (RHSCanType.isFatPtrType() || LHSCanType.isFatPtrType()) { - if (!CheckFatQualTypeAssignment(LHSType, RHS.get())) - return IncompatibleFatPointer; - } if (const auto *LHSPtrType = LHSType->getAs()) { if (const auto *RHSPtrType = RHS.get()->getType()->getAs()) { if (LHSPtrType->hasOwnedFields() || RHSPtrType->hasOwnedFields()) { @@ -10520,13 +10539,15 @@ Sema::CheckSingleAssignmentConstraints(QualType LHSType, ExprResult &CallerRHS, return IncompatibleBorrowPointer; } } - if (LHSPtrType->hasFatFields() || RHSPtrType->hasFatFields()) { - if (!CheckFatQualTypeAssignment(LHSType, RHS.get())) { - return IncompatibleFatPointer; - } - } } } + if (LHSType.hasFat() || RHS.get()->getType().hasFat()) { + if (!CheckFatQualTypeAssignment(LHSType, RHS.get())) + return IncompatibleFatPointer; + if (LHSType->isFunctionPointerType() && + !CheckFatFunctionPointerType(LHSType, RHS.get())) + return IncompatibleFatPointer; + } if (LHSType->isFunctionPointerType() && (RHS.get()->getType()->isFunctionPointerType() || RHS.get()->getType()->isFunctionType())) { @@ -10534,8 +10555,6 @@ Sema::CheckSingleAssignmentConstraints(QualType LHSType, ExprResult &CallerRHS, return IncompatibleOwnedPointer; if (!CheckBorrowFunctionPointerType(LHSType, RHS.get())) return IncompatibleBorrowPointer; - if (!CheckFatFunctionPointerType(LHSType, RHS.get())) - return IncompatibleFatPointer; } } #endif @@ -13466,7 +13485,8 @@ QualType Sema::CheckCompareOperands(ExprResult &LHS, ExprResult &RHS, } #if ENABLE_BSC - if (getLangOpts().BSC && LHSType->isPointerType() && RHSType->isNullPtrType()) + if (getLangOpts().BSC && RHSType->isNullPtrType() && + (LHSType->isPointerType() || LHSType.isFatPtrType())) return computeResultTy(); #endif @@ -15246,7 +15266,6 @@ QualType Sema::CheckAddressOfOperand(ExprResult &OrigOp, SourceLocation OpLoc) { return Context.getObjCObjectPointerType(op->getType()); CheckAddressOfPackedMember(op); - return Context.getPointerType(op->getType()); } @@ -15294,8 +15313,9 @@ static QualType CheckIndirectionOperand(Sema &S, Expr *Op, ExprValueKind &VK, OpTy->getAs()) Result = OPT->getPointeeType(); #if ENABLE_BSC - else if (S.getLangOpts().BSC && S.getLangOpts().EnableFatPtr && OpTy->isFatPtrRecordType()) - Result = OpTy->getFatPtrPointeeType(); + else if (S.getLangOpts().BSC && S.getLangOpts().EnableFatPtr && + OpTy.isFatPtrRecordType()) + Result = OpTy.getFatPtrPointeeType(); #endif else { ExprResult PR = S.CheckPlaceholderExpr(Op); @@ -15684,157 +15704,166 @@ ExprResult Sema::CreateBuiltinBinOp(SourceLocation OpLoc, checkTypeSupport(LHSExpr->getType(), OpLoc, /*ValueDecl*/ nullptr); checkTypeSupport(RHSExpr->getType(), OpLoc, /*ValueDecl*/ nullptr); - - switch (Opc) { - case BO_Assign: - ResultTy = CheckAssignmentOperands(LHS.get(), RHS, OpLoc, QualType(), Opc); - if (getLangOpts().CPlusPlus && - LHS.get()->getObjectKind() != OK_ObjCProperty) { - VK = LHS.get()->getValueKind(); - OK = LHS.get()->getObjectKind(); - } - if (!ResultTy.isNull()) { - DiagnoseSelfAssignment(*this, LHS.get(), RHS.get(), OpLoc, true); - DiagnoseSelfMove(LHS.get(), RHS.get(), OpLoc); - - // Avoid copying a block to the heap if the block is assigned to a local - // auto variable that is declared in the same scope as the block. This - // optimization is unsafe if the local variable is declared in an outer - // scope. For example: - // - // BlockTy b; - // { - // b = ^{...}; - // } - // // It is unsafe to invoke the block here if it wasn't copied to the - // // heap. - // b(); - - if (auto *BE = dyn_cast(RHS.get()->IgnoreParens())) - if (auto *DRE = dyn_cast(LHS.get()->IgnoreParens())) - if (auto *VD = dyn_cast(DRE->getDecl())) - if (VD->hasLocalStorage() && getCurScope()->isDeclScope(VD)) - BE->getBlockDecl()->setCanAvoidCopyToHeap(); - - if (LHS.get()->getType().hasNonTrivialToPrimitiveCopyCUnion()) - checkNonTrivialCUnion(LHS.get()->getType(), LHS.get()->getExprLoc(), - NTCUC_Assignment, NTCUK_Copy); - } - RecordModifiableNonNullParam(*this, LHS.get()); - break; - case BO_PtrMemD: - case BO_PtrMemI: - ResultTy = CheckPointerToMemberOperands(LHS, RHS, VK, OpLoc, - Opc == BO_PtrMemI); - break; - case BO_Mul: - case BO_Div: - ConvertHalfVec = true; - ResultTy = CheckMultiplyDivideOperands(LHS, RHS, OpLoc, false, - Opc == BO_Div); - break; - case BO_Rem: - ResultTy = CheckRemainderOperands(LHS, RHS, OpLoc); - break; - case BO_Add: - ConvertHalfVec = true; - ResultTy = CheckAdditionOperands(LHS, RHS, OpLoc, Opc); - break; - case BO_Sub: - ConvertHalfVec = true; - ResultTy = CheckSubtractionOperands(LHS, RHS, OpLoc); - break; - case BO_Shl: - case BO_Shr: - ResultTy = CheckShiftOperands(LHS, RHS, OpLoc, Opc); - break; - case BO_LE: - case BO_LT: - case BO_GE: - case BO_GT: - ConvertHalfVec = true; - ResultTy = CheckCompareOperands(LHS, RHS, OpLoc, Opc); - break; - case BO_EQ: - case BO_NE: - ConvertHalfVec = true; - ResultTy = CheckCompareOperands(LHS, RHS, OpLoc, Opc); - break; - case BO_Cmp: - ConvertHalfVec = true; - ResultTy = CheckCompareOperands(LHS, RHS, OpLoc, Opc); - assert(ResultTy.isNull() || ResultTy->getAsCXXRecordDecl()); - break; - case BO_And: - checkObjCPointerIntrospection(*this, LHS, RHS, OpLoc); - LLVM_FALLTHROUGH; - case BO_Xor: - case BO_Or: - ResultTy = CheckBitwiseOperands(LHS, RHS, OpLoc, Opc); - break; - case BO_LAnd: - case BO_LOr: - ConvertHalfVec = true; - ResultTy = CheckLogicalOperands(LHS, RHS, OpLoc, Opc); - break; - case BO_MulAssign: - case BO_DivAssign: - ConvertHalfVec = true; - CompResultTy = CheckMultiplyDivideOperands(LHS, RHS, OpLoc, true, - Opc == BO_DivAssign); - CompLHSTy = CompResultTy; - if (!CompResultTy.isNull() && !LHS.isInvalid() && !RHS.isInvalid()) - ResultTy = - CheckAssignmentOperands(LHS.get(), RHS, OpLoc, CompResultTy, Opc); - break; - case BO_RemAssign: - CompResultTy = CheckRemainderOperands(LHS, RHS, OpLoc, true); - CompLHSTy = CompResultTy; - if (!CompResultTy.isNull() && !LHS.isInvalid() && !RHS.isInvalid()) - ResultTy = - CheckAssignmentOperands(LHS.get(), RHS, OpLoc, CompResultTy, Opc); - break; - case BO_AddAssign: - ConvertHalfVec = true; - CompResultTy = CheckAdditionOperands(LHS, RHS, OpLoc, Opc, &CompLHSTy); - if (!CompResultTy.isNull() && !LHS.isInvalid() && !RHS.isInvalid()) - ResultTy = - CheckAssignmentOperands(LHS.get(), RHS, OpLoc, CompResultTy, Opc); - break; - case BO_SubAssign: - ConvertHalfVec = true; - CompResultTy = CheckSubtractionOperands(LHS, RHS, OpLoc, &CompLHSTy); - if (!CompResultTy.isNull() && !LHS.isInvalid() && !RHS.isInvalid()) +#if ENABLE_BSC + if (getLangOpts().BSC && getLangOpts().EnableFatPtr && + LHSExpr->getType().isFatPtrRecordType() && + (Opc == BO_Add || Opc == BO_Sub)) { + ResultTy = + Context.getPointerType(LHSExpr->getType().getFatPtrPointeeType()); + ResultTy.addFat(); + } else +#endif + switch (Opc) { + case BO_Assign: ResultTy = - CheckAssignmentOperands(LHS.get(), RHS, OpLoc, CompResultTy, Opc); - break; - case BO_ShlAssign: - case BO_ShrAssign: - CompResultTy = CheckShiftOperands(LHS, RHS, OpLoc, Opc, true); - CompLHSTy = CompResultTy; - if (!CompResultTy.isNull() && !LHS.isInvalid() && !RHS.isInvalid()) + CheckAssignmentOperands(LHS.get(), RHS, OpLoc, QualType(), Opc); + if (getLangOpts().CPlusPlus && + LHS.get()->getObjectKind() != OK_ObjCProperty) { + VK = LHS.get()->getValueKind(); + OK = LHS.get()->getObjectKind(); + } + if (!ResultTy.isNull()) { + DiagnoseSelfAssignment(*this, LHS.get(), RHS.get(), OpLoc, true); + DiagnoseSelfMove(LHS.get(), RHS.get(), OpLoc); + + // Avoid copying a block to the heap if the block is assigned to a local + // auto variable that is declared in the same scope as the block. This + // optimization is unsafe if the local variable is declared in an outer + // scope. For example: + // + // BlockTy b; + // { + // b = ^{...}; + // } + // // It is unsafe to invoke the block here if it wasn't copied to the + // // heap. + // b(); + + if (auto *BE = dyn_cast(RHS.get()->IgnoreParens())) + if (auto *DRE = dyn_cast(LHS.get()->IgnoreParens())) + if (auto *VD = dyn_cast(DRE->getDecl())) + if (VD->hasLocalStorage() && getCurScope()->isDeclScope(VD)) + BE->getBlockDecl()->setCanAvoidCopyToHeap(); + + if (LHS.get()->getType().hasNonTrivialToPrimitiveCopyCUnion()) + checkNonTrivialCUnion(LHS.get()->getType(), LHS.get()->getExprLoc(), + NTCUC_Assignment, NTCUK_Copy); + } + RecordModifiableNonNullParam(*this, LHS.get()); + break; + case BO_PtrMemD: + case BO_PtrMemI: ResultTy = - CheckAssignmentOperands(LHS.get(), RHS, OpLoc, CompResultTy, Opc); - break; - case BO_AndAssign: - case BO_OrAssign: // fallthrough - DiagnoseSelfAssignment(*this, LHS.get(), RHS.get(), OpLoc, true); - LLVM_FALLTHROUGH; - case BO_XorAssign: - CompResultTy = CheckBitwiseOperands(LHS, RHS, OpLoc, Opc); - CompLHSTy = CompResultTy; - if (!CompResultTy.isNull() && !LHS.isInvalid() && !RHS.isInvalid()) + CheckPointerToMemberOperands(LHS, RHS, VK, OpLoc, Opc == BO_PtrMemI); + break; + case BO_Mul: + case BO_Div: + ConvertHalfVec = true; ResultTy = - CheckAssignmentOperands(LHS.get(), RHS, OpLoc, CompResultTy, Opc); - break; - case BO_Comma: - ResultTy = CheckCommaOperands(*this, LHS, RHS, OpLoc); - if (getLangOpts().CPlusPlus && !RHS.isInvalid()) { - VK = RHS.get()->getValueKind(); - OK = RHS.get()->getObjectKind(); + CheckMultiplyDivideOperands(LHS, RHS, OpLoc, false, Opc == BO_Div); + break; + case BO_Rem: + ResultTy = CheckRemainderOperands(LHS, RHS, OpLoc); + break; + case BO_Add: + ConvertHalfVec = true; + ResultTy = CheckAdditionOperands(LHS, RHS, OpLoc, Opc); + break; + case BO_Sub: + ConvertHalfVec = true; + ResultTy = CheckSubtractionOperands(LHS, RHS, OpLoc); + break; + case BO_Shl: + case BO_Shr: + ResultTy = CheckShiftOperands(LHS, RHS, OpLoc, Opc); + break; + case BO_LE: + case BO_LT: + case BO_GE: + case BO_GT: + ConvertHalfVec = true; + ResultTy = CheckCompareOperands(LHS, RHS, OpLoc, Opc); + break; + case BO_EQ: + case BO_NE: + ConvertHalfVec = true; + ResultTy = CheckCompareOperands(LHS, RHS, OpLoc, Opc); + break; + case BO_Cmp: + ConvertHalfVec = true; + ResultTy = CheckCompareOperands(LHS, RHS, OpLoc, Opc); + assert(ResultTy.isNull() || ResultTy->getAsCXXRecordDecl()); + break; + case BO_And: + checkObjCPointerIntrospection(*this, LHS, RHS, OpLoc); + LLVM_FALLTHROUGH; + case BO_Xor: + case BO_Or: + ResultTy = CheckBitwiseOperands(LHS, RHS, OpLoc, Opc); + break; + case BO_LAnd: + case BO_LOr: + ConvertHalfVec = true; + ResultTy = CheckLogicalOperands(LHS, RHS, OpLoc, Opc); + break; + case BO_MulAssign: + case BO_DivAssign: + ConvertHalfVec = true; + CompResultTy = CheckMultiplyDivideOperands(LHS, RHS, OpLoc, true, + Opc == BO_DivAssign); + CompLHSTy = CompResultTy; + if (!CompResultTy.isNull() && !LHS.isInvalid() && !RHS.isInvalid()) + ResultTy = + CheckAssignmentOperands(LHS.get(), RHS, OpLoc, CompResultTy, Opc); + break; + case BO_RemAssign: + CompResultTy = CheckRemainderOperands(LHS, RHS, OpLoc, true); + CompLHSTy = CompResultTy; + if (!CompResultTy.isNull() && !LHS.isInvalid() && !RHS.isInvalid()) + ResultTy = + CheckAssignmentOperands(LHS.get(), RHS, OpLoc, CompResultTy, Opc); + break; + case BO_AddAssign: + ConvertHalfVec = true; + CompResultTy = CheckAdditionOperands(LHS, RHS, OpLoc, Opc, &CompLHSTy); + if (!CompResultTy.isNull() && !LHS.isInvalid() && !RHS.isInvalid()) + ResultTy = + CheckAssignmentOperands(LHS.get(), RHS, OpLoc, CompResultTy, Opc); + break; + case BO_SubAssign: + ConvertHalfVec = true; + CompResultTy = CheckSubtractionOperands(LHS, RHS, OpLoc, &CompLHSTy); + if (!CompResultTy.isNull() && !LHS.isInvalid() && !RHS.isInvalid()) + ResultTy = + CheckAssignmentOperands(LHS.get(), RHS, OpLoc, CompResultTy, Opc); + break; + case BO_ShlAssign: + case BO_ShrAssign: + CompResultTy = CheckShiftOperands(LHS, RHS, OpLoc, Opc, true); + CompLHSTy = CompResultTy; + if (!CompResultTy.isNull() && !LHS.isInvalid() && !RHS.isInvalid()) + ResultTy = + CheckAssignmentOperands(LHS.get(), RHS, OpLoc, CompResultTy, Opc); + break; + case BO_AndAssign: + case BO_OrAssign: // fallthrough + DiagnoseSelfAssignment(*this, LHS.get(), RHS.get(), OpLoc, true); + LLVM_FALLTHROUGH; + case BO_XorAssign: + CompResultTy = CheckBitwiseOperands(LHS, RHS, OpLoc, Opc); + CompLHSTy = CompResultTy; + if (!CompResultTy.isNull() && !LHS.isInvalid() && !RHS.isInvalid()) + ResultTy = + CheckAssignmentOperands(LHS.get(), RHS, OpLoc, CompResultTy, Opc); + break; + case BO_Comma: + ResultTy = CheckCommaOperands(*this, LHS, RHS, OpLoc); + if (getLangOpts().CPlusPlus && !RHS.isInvalid()) { + VK = RHS.get()->getValueKind(); + OK = RHS.get()->getObjectKind(); + } + break; } - break; - } if (ResultTy.isNull() || LHS.isInvalid() || RHS.isInvalid()) return ExprError(); @@ -16329,7 +16358,10 @@ ExprResult Sema::BuildBinOp(Scope *S, SourceLocation OpLoc, // overloadable type. if (LHSExpr->getType()->isOverloadableType() || RHSExpr->getType()->isOverloadableType()) - return BuildOverloadedBinOp(*this, S, OpLoc, Opc, LHSExpr, RHSExpr); +#if ENABLE_BSC + if (!(getLangOpts().BSC && LHSExpr->getType().isFatPtrRecordType())) +#endif + return BuildOverloadedBinOp(*this, S, OpLoc, Opc, LHSExpr, RHSExpr); } if (getLangOpts().RecoveryAST && @@ -16537,6 +16569,11 @@ ExprResult Sema::CreateBuiltinUnaryOp(SourceLocation OpLoc, if (resultType->isDependentType()) break; +#if ENABLE_BSC + if (getLangOpts().BSC && getLangOpts().EnableFatPtr && + resultType.isFatPtrRecordType()) + resultType = Context.getPointerType(resultType.getFatPtrPointeeType()); +#endif if (resultType->isScalarType() && !isScopedEnumerationType(resultType)) { // C99 6.5.3.3p1: ok, fallthrough; if (Context.getLangOpts().CPlusPlus) { @@ -17825,8 +17862,7 @@ bool Sema::DiagnoseAssignmentResult(AssignConvertType ConvTy, case IncompatiblePointer: #if ENABLE_BSC // int a = 1; trait F* f = &a; - if (getLangOpts().BSC && (IsTraitEqualExpr(*this, DstType, SrcType, Loc) || - IsFatPtrEqualExpr(*this, DstType, SrcType))) + if (getLangOpts().BSC && IsTraitEqualExpr(*this, DstType, SrcType, Loc)) return false; #endif if (Action == AA_Passing_CFAudited) { @@ -20940,6 +20976,11 @@ ExprResult Sema::CheckBooleanCondition(SourceLocation Loc, Expr *E, E = ERes.get(); QualType T = E->getType(); +#if ENABLE_BSC + if (getLangOpts().BSC && getLangOpts().EnableFatPtr && + T.isFatPtrRecordType()) + return E; +#endif if (!T->isScalarType()) { // C99 6.8.4.1p1 Diag(Loc, diag::err_typecheck_statement_requires_scalar) << T << E->getSourceRange(); @@ -20973,6 +21014,14 @@ Sema::ConditionResult Sema::ActOnCondition(Scope *S, SourceLocation Loc, Cond = CheckSwitchCondition(Loc, SubExpr); break; } + +#if ENABLE_BSC + if (getLangOpts().BSC && getLangOpts().EnableFatPtr) { + if (!CheckSelfIncOrDecOfFatPtr(SubExpr)) + Cond = ExprError(); + } +#endif + if (Cond.isInvalid()) { Cond = CreateRecoveryExpr(SubExpr->getBeginLoc(), SubExpr->getEndLoc(), {SubExpr}, PreferredConditionType(CK)); diff --git a/clang/lib/Sema/SemaExprMember.cpp b/clang/lib/Sema/SemaExprMember.cpp index 4d6becec6f43..1615ed62b836 100644 --- a/clang/lib/Sema/SemaExprMember.cpp +++ b/clang/lib/Sema/SemaExprMember.cpp @@ -1077,8 +1077,8 @@ Sema::BuildMemberReferenceExpr(Expr *BaseExpr, QualType BaseExprType, if (IsArrow) { #if ENABLE_BSC if (getLangOpts().BSC && getLangOpts().EnableFatPtr && - BaseType->isFatPtrRecordType()) - BaseType = BaseType->getFatPtrPointeeType(); + BaseType.isFatPtrRecordType()) + BaseType = BaseType.getFatPtrPointeeType(); else { #endif assert(BaseType->isPointerType()); @@ -1507,8 +1507,8 @@ static ExprResult LookupMemberExpr(Sema &S, LookupResult &R, BaseType = Ptr->getPointeeType(); # if ENABLE_BSC else if (S.getLangOpts().BSC && S.getLangOpts().EnableFatPtr && - BaseType->isFatPtrRecordType()) - BaseType = BaseType->getFatPtrPointeeType(); + BaseType.isFatPtrRecordType()) + BaseType = BaseType.getFatPtrPointeeType(); # endif else if (BaseType->isRecordType()) { // Recover from arrow accesses to records, e.g.: @@ -2124,8 +2124,8 @@ Sema::BuildFieldReferenceExpr(Expr *BaseExpr, bool IsArrow, # if ENABLE_BSC { if (getLangOpts().BSC && getLangOpts().EnableFatPtr && - BaseType->isFatPtrRecordType()) - BaseType = BaseType->getFatPtrPointeeType(); + BaseType.isFatPtrRecordType()) + BaseType = BaseType.getFatPtrPointeeType(); else # endif BaseType = BaseType->castAs()->getPointeeType(); diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index 5599d29754fe..2cfb27547633 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -2017,6 +2017,13 @@ static bool IsStandardConversion(Sema &S, Expr* From, QualType ToType, if (TD == S.TryDesugarTrait(FromType)) return true; } + if (S.getLangOpts().EnableFatPtr) { + if (FromType.isFatPtrType() && ToType.isFatPtrType() && + S.Context.hasSameType(FromType.getFatPtrPointeeType(), + ToType.getFatPtrPointeeType())) { + return true; + } + } } #endif diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp index 64a83ad41060..13f9d49d1c17 100644 --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -1752,6 +1752,13 @@ Sema::ActOnDoStmt(SourceLocation DoLoc, Stmt *Body, ExprResult CondResult = CheckBooleanCondition(DoLoc, Cond); if (CondResult.isInvalid()) return StmtError(); + +#if ENABLE_BSC + if (getLangOpts().BSC && getLangOpts().EnableFatPtr) { + if (!CheckSelfIncOrDecOfFatPtr(Cond)) + return StmtError(); + } +#endif Cond = CondResult.get(); CondResult = ActOnFinishFullExpr(Cond, DoLoc, /*DiscardedValue*/ false); diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index 5882e115de86..e4c4c48200dd 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -1999,13 +1999,22 @@ QualType Sema::BuildQualifiedType(QualType T, SourceLocation Loc, Qs.removeVolatile(); } #if ENABLE_BSC - if (getLangOpts().BSC && !T->isPointerType()) { - if (Qs.hasBorrow()) + if (getLangOpts().BSC) { + if (Qs.hasBorrow() && !T->isPointerType()) { Diag(DS ? DS->getBorrowSpecLoc() : Loc, - diag::err_typecheck_invalid_borrow_not_pointer) << T; - if (Qs.hasFat()) - Diag(DS ? DS->getFatSpecLoc() : Loc, - diag::err_typecheck_invalid_fat_not_pointer); + diag::err_typecheck_invalid_borrow_not_pointer) + << T; + } + if (Qs.hasFat()) { + if (!T->isPointerType()) { + Diag(DS ? DS->getFatSpecLoc() : Loc, + diag::err_typecheck_invalid_fat_not_pointer); + } + if (T->isFunctionPointerType()) { + Diag(DS ? DS->getFatSpecLoc() : Loc, + diag::err_typecheck_invalid_fat_not_function_pointer); + } + } } #endif diff --git a/clang/test/BSC/Negative/FatPtr/addr_fat.cbs b/clang/test/BSC/Negative/FatPtr/addr_fat.cbs index 09c1b718876a..c9c3f31b38d8 100644 --- a/clang/test/BSC/Negative/FatPtr/addr_fat.cbs +++ b/clang/test/BSC/Negative/FatPtr/addr_fat.cbs @@ -6,23 +6,23 @@ void test1() { struct S *p2; int *fat p3 = &fat p1->a; int *fat p4 = &fat p2->a;// expected-error {{'&fat' on member of no-fat pointer is not allowed}} - float *fat p5 = &fat p1->a;// expected-error {{incompatible fat types, cannot cast 'int *fat' to 'float *fat'}} + float *fat p5 = &fat p1->a;// expected-error {{incompatible fat types, cannot implicitly cast 'int *fat' to 'float *fat'}} float *fat p6 = (float *fat)&fat p1->a; int *fat p7; int *p8; int *fat p9 = &fat p7[1]; int *fat p10 = &fat p8[1]; // expected-error {{'&fat' on member of no-fat pointer is not allowed}} - float *fat p11 = &fat p7[1];// expected-error {{incompatible fat types, cannot cast 'int *fat' to 'float *fat'}} + float *fat p11 = &fat p7[1];// expected-error {{incompatible fat types, cannot implicitly cast 'int *fat' to 'float *fat'}} float *fat p12 = (float *fat)&fat p7[1]; struct S s; int arr[10]; int *fat p13 = &fat s.a; - float *fat p14 = &fat s.a; // expected-error {{incompatible fat types, cannot cast 'int *fat' to 'float *fat'}} + float *fat p14 = &fat s.a; // expected-error {{incompatible fat types, cannot implicitly cast 'int *fat' to 'float *fat'}} float *fat p15 = (float *fat)&fat s.a; int *fat p16 = &fat arr[5]; - float *fat p17 = &fat arr[5];// expected-error {{incompatible fat types, cannot cast 'int *fat' to 'float *fat'}} + float *fat p17 = &fat arr[5];// expected-error {{incompatible fat types, cannot implicitly cast 'int *fat' to 'float *fat'}} float *fat p18 = (float *fat)&fat arr[5]; } diff --git a/clang/test/BSC/Negative/FatPtr/fat_cast.cbs b/clang/test/BSC/Negative/FatPtr/fat_explicit_cast.cbs similarity index 40% rename from clang/test/BSC/Negative/FatPtr/fat_cast.cbs rename to clang/test/BSC/Negative/FatPtr/fat_explicit_cast.cbs index 50008ac52448..970a18d4398c 100644 --- a/clang/test/BSC/Negative/FatPtr/fat_cast.cbs +++ b/clang/test/BSC/Negative/FatPtr/fat_explicit_cast.cbs @@ -4,29 +4,13 @@ void foo2(int *p); int *fat foo3(); int * foo4(); -typedef void (*PF1)(int *fat p); -typedef void (*PF2)(int *p); -typedef int *fat (*PF3)(); -typedef int * (*PF4)(); - -void test1() { //cast between function pointer and function - PF1 p1 = foo1; - PF1 p2 = foo2;// expected-error {{incompatible fat function pointer types, cannot cast 'PF1' (aka 'void (*)(int *fat)') to 'void (int *)'}} - PF2 p3 = foo1;// expected-error {{incompatible fat function pointer types, cannot cast 'PF2' (aka 'void (*)(int *)') to 'void (int *fat)'}} - PF2 p4 = foo2; - PF3 p5 = foo3; - PF3 p6 = foo4;// expected-error {{incompatible fat function pointer types, cannot cast 'PF3' (aka 'int *fat (*)(void)') to 'int *(void)'}} - PF4 p7 = foo3;// expected-error {{incompatible fat function pointer types, cannot cast 'PF4' (aka 'int *(*)(void)') to 'int *fat (void)'}} - PF4 p8 = foo4; -} - -void test2() { //cast between zero and fat pointer - int * fat b1 = 0; // expected-error {{incompatible fat types, cannot cast 'int' to 'int *fat'}} - int * fat b2 = {0};// expected-error {{incompatible fat types, cannot cast 'int' to 'int *fat'}} - foo1(0); // expected-error {{incompatible fat types, cannot cast 'int' to 'int *fat'}} +void test1() { //cast between zero and fat pointer + int * fat b1 = (int *fat)0; // expected-error {{incompatible fat types, cannot explicitly cast 'int' to 'int *fat'}} + int * fat b2 = {(int *fat)0};// expected-error {{incompatible fat types, cannot explicitly cast 'int' to 'int *fat'}} + foo1((int *fat)0); // expected-error {{incompatible fat types, cannot explicitly cast 'int' to 'int *fat'}} } -void test3() { //cast between raw pointer and fat ptr +void test2() { //cast between raw pointer and fat ptr int a = 1; // explicit cast between raw pointer and fat ptr is allowed. int *fat p1 = nullptr; @@ -45,17 +29,6 @@ void test3() { //cast between raw pointer and fat ptr p4 = (const int *)p2; p3 = (int *)foo3(); foo2((int *)p1); - // implicit cast between raw pointer and fat ptr is not allowed. - // T * --> T *fat - p1 = p3; // expected-error {{incompatible fat types, cannot cast 'int *' to 'int *fat'}} - p2 = p4; // expected-error {{incompatible fat types, cannot cast 'const int *' to 'const int *fat'}} - p1 = foo4();// expected-error {{incompatible fat types, cannot cast 'int *' to 'int *fat'}} - foo1(p3);// expected-error {{incompatible fat types, cannot cast 'int *' to 'int *fat'}} - // T *fat --> T * - p3 = p1; // expected-error {{incompatible fat types, cannot cast 'int *fat' to 'int *'}} - p4 = p2; // expected-error {{incompatible fat types, cannot cast 'const int *fat' to 'const int *'}} - p3 = foo3();// expected-error {{incompatible fat types, cannot cast 'int *fat' to 'int *'}} - foo2(p1);// expected-error {{incompatible fat types, cannot cast 'int *fat' to 'int *'}} safe { int *fat p1 = nullptr; @@ -69,4 +42,28 @@ void test3() { //cast between raw pointer and fat ptr p3 = (int *)p1; // expected-error {{conversion from type 'int *fat' to 'int *' is forbidden in the safe zone}} p4 = (const int *)p2; // expected-error {{conversion from type 'const int *fat' to 'const int *' is forbidden in the safe zone}} } +} + +void test3() { + int *fat p1 = nullptr; + float *fat p2 = nullptr; + int * p3 = nullptr; + float * p4 = nullptr; + p1 = (int *fat)p2; + p1 = (int *fat)p4; + p3 = (int *)p2; + p3 = (int *)p4; +} + +void foo5(int ** p); +void foo6(int *fat * p); +void test4() { + int *fat p = nullptr; + foo5((int **)&p); // expected-error {{incompatible fat types, cannot explicitly cast 'int *fat *' to 'int **'}} + int *fat *p1 = nullptr; + int **p2 = nullptr; + p1 = (int *fat *)p2;// expected-error {{incompatible fat types, cannot explicitly cast 'int **' to 'int *fat *'}} + p2 = (int **)p1;// expected-error {{incompatible fat types, cannot explicitly cast 'int *fat *' to 'int **'}} + foo5((int **)p1);// expected-error {{incompatible fat types, cannot explicitly cast 'int *fat *' to 'int **'}} + foo6((int *fat *)p2);// expected-error {{incompatible fat types, cannot explicitly cast 'int **' to 'int *fat *'}} } \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/fat_func_decl_several_times.cbs b/clang/test/BSC/Negative/FatPtr/fat_func_decl_several_times.cbs new file mode 100644 index 000000000000..1c705bf1af81 --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/fat_func_decl_several_times.cbs @@ -0,0 +1,29 @@ +// RUN: %clang_cc1 -verify %s + +void foo1(int *fat p);// expected-note {{previous declaration is here}} +void foo1(int *p); // expected-error {{conflicting types for 'foo1'}} + +void foo2(T *fat a);// expected-note {{previous declaration is here}} +void foo2(T *p); // expected-error {{conflicting types for 'foo2'}} + +int *fat foo3();// expected-note {{previous declaration is here}} +int *foo3(); // expected-error {{conflicting types for 'foo3'}} + +T *fat foo4();// expected-note {{previous declaration is here}} +T *foo4(); // expected-error {{conflicting types for 'foo4'}} + +void foo5(int *fat p);// expected-note {{previous declaration is here}} +void foo5(float *fat p);// expected-error {{conflicting types for 'foo5'}} + +void foo6(int *fat p);// expected-note {{previous declaration is here}} +void foo6(const int *fat p);// expected-error {{conflicting types for 'foo6'}} + +typedef struct S { + int *fat p; +} SS; +void foo7(struct S *fat p); +void foo7(SS *fat p); + +typedef int *fat Int; +void foo8(int *fat p); +void foo8(Int p); \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/fat_implicit_cast.cbs b/clang/test/BSC/Negative/FatPtr/fat_implicit_cast.cbs new file mode 100644 index 000000000000..56d1eec8a217 --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/fat_implicit_cast.cbs @@ -0,0 +1,73 @@ +// RUN: %clang_cc1 -verify %s +void foo1(int *fat p); +void foo2(int *p); +int *fat foo3(); +int * foo4(); + +int *fat test1() { //cast between zero and fat pointer + int *fat b1 = 0; // expected-error {{incompatible fat types, cannot implicitly cast 'int' to 'int *fat'}} + int *fat b2 = {0};// expected-error {{incompatible fat types, cannot implicitly cast 'int' to 'int *fat'}} + foo1(0); // expected-error {{incompatible fat types, cannot implicitly cast 'int' to 'int *fat'}} + return 0; // expected-error {{incompatible fat types, cannot implicitly cast 'int' to 'int *fat'}} +} + +void test2() { //cast between raw pointer and fat ptr + int a = 1; + int *fat p1 = nullptr; + const int *fat p2 = nullptr; + int *p3 = &a; + const int *p4 = &a; + // implicit cast between raw pointer and fat ptr is not allowed. + // T * --> T *fat ERROR + p1 = p3; // expected-error {{incompatible fat types, cannot implicitly cast 'int *' to 'int *fat'}} + p2 = p4; // expected-error {{incompatible fat types, cannot implicitly cast 'const int *' to 'const int *fat'}} + p1 = foo4();// expected-error {{incompatible fat types, cannot implicitly cast 'int *' to 'int *fat'}} + foo1(p3);// expected-error {{incompatible fat types, cannot implicitly cast 'int *' to 'int *fat'}} + // T *fat --> T * ERROR + p3 = p1; // expected-error {{incompatible fat types, cannot implicitly cast 'int *fat' to 'int *'}} + p4 = p2; // expected-error {{incompatible fat types, cannot implicitly cast 'const int *fat' to 'const int *'}} + p3 = foo3();// expected-error {{incompatible fat types, cannot implicitly cast 'int *fat' to 'int *'}} + foo2(p1);// expected-error {{incompatible fat types, cannot implicitly cast 'int *fat' to 'int *'}} +} + +void test3() { //cast between fat pointer and fat ptr + int a = 1; + int *fat p1 = nullptr; + const int *fat p2 = nullptr; + float *fat p3 = nullptr; + const float *fat p4 = nullptr; + // implicit cast between fat pointer with different pointee type is not allowed. + // T1 *fat --> T2 *fat ERROR + p1 = p3; // expected-error {{incompatible fat types, cannot implicitly cast 'float *fat' to 'int *fat'}} + p2 = p4; // expected-error {{incompatible fat types, cannot implicitly cast 'const float *fat' to 'const int *fat'}} + foo1(p3);// expected-error {{incompatible fat types, cannot implicitly cast 'float *fat' to 'int *fat'}} + p3 = foo3();// expected-error {{incompatible fat types, cannot implicitly cast 'int *fat' to 'float *fat'}} +} + +void test4() { + int a = 1; + int *fat p1 = nullptr; + const int *fat p2 = nullptr; + // T *fat --> const T *fat OK + p2 = p1; + p2 = foo3(); + // const T *fat --> T *fat ERROR + p1 = p2; // expected-error {{incompatible fat types, cannot implicitly cast 'const int *fat' to 'int *fat'}} + foo1(p2); // expected-error {{incompatible fat types, cannot implicitly cast 'const int *fat' to 'int *fat'}} +} + +void foo5(int ** p); +void foo6(int *fat* p); +void test5() { + int *fat *p1 = nullptr; + int **p2 = nullptr; + p1 = p2;// expected-error {{incompatible fat types, cannot implicitly cast 'int **' to 'int *fat *'}} + p2 = p1;// expected-error {{incompatible fat types, cannot implicitly cast 'int *fat *' to 'int **'}} + foo5(p1); // expected-error {{incompatible fat types, cannot implicitly cast 'int *fat *' to 'int **'}} + foo6(p2); // expected-error {{incompatible fat types, cannot implicitly cast 'int **' to 'int *fat *'}} + + int *fat p3; + int *p4; + foo5(&p3); // expected-error {{incompatible fat types, cannot implicitly cast 'int *fat *' to 'int **'}} + foo6(&p4);// expected-error {{incompatible fat types, cannot implicitly cast 'int **' to 'int *fat *'}} +} \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/fat_qulifier_only_for_pointer.cbs b/clang/test/BSC/Negative/FatPtr/fat_qulifier_only_for_pointer.cbs index 3006aa21235d..2ef4bd8a7e41 100644 --- a/clang/test/BSC/Negative/FatPtr/fat_qulifier_only_for_pointer.cbs +++ b/clang/test/BSC/Negative/FatPtr/fat_qulifier_only_for_pointer.cbs @@ -15,7 +15,6 @@ void f3(T fat a) {} // expected-error {{only pointer type can be qualified by void f4(int *fat a) {} void f5(T *fat a) {} - void test() { int fat b1; // expected-error {{only pointer type can be qualified by fat}} const int fat b2; // expected-error {{only pointer type can be qualified by fat}} diff --git a/clang/test/BSC/Negative/FatPtr/func_ptr_has_fat.cbs b/clang/test/BSC/Negative/FatPtr/func_ptr_has_fat.cbs new file mode 100644 index 000000000000..2e2e1f07225e --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/func_ptr_has_fat.cbs @@ -0,0 +1,23 @@ +// RUN: %clang_cc1 -verify %s +void foo1(int *fat p); +void foo2(int *p); +int *fat foo3(); +int * foo4(); + +typedef void (*fat PF)();// expected-error {{function pointer type cannot be qualified by fat}} + +typedef void (*PF1)(int *fat p); +typedef void (*PF2)(int *p); +typedef int *fat (*PF3)(); +typedef int * (*PF4)(); + +void test() { //cast between function pointer and function + PF1 p1 = foo1; + PF1 p2 = foo2;// expected-error {{incompatible fat function pointer types, cannot cast 'PF1' (aka 'void (*)(int *fat)') to 'void (int *)'}} + PF2 p3 = foo1;// expected-error {{incompatible fat function pointer types, cannot cast 'PF2' (aka 'void (*)(int *)') to 'void (int *fat)'}} + PF2 p4 = foo2; + PF3 p5 = foo3; + PF3 p6 = foo4;// expected-error {{incompatible fat function pointer types, cannot cast 'PF3' (aka 'int *fat (*)(void)') to 'int *(void)'}} + PF4 p7 = foo3;// expected-error {{incompatible fat function pointer types, cannot cast 'PF4' (aka 'int *(*)(void)') to 'int *fat (void)'}} + PF4 p8 = foo4; +} \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/self_inc_or_dec_of_fat_ptr.cbs b/clang/test/BSC/Negative/FatPtr/self_inc_or_dec_of_fat_ptr.cbs new file mode 100644 index 000000000000..ff136be63880 --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/self_inc_or_dec_of_fat_ptr.cbs @@ -0,0 +1,22 @@ +// RUN: %clang_cc1 -enable-fat-ptr -verify %s + +void test() { + int arr[3] = { 0, 1, 2 }; + int *fat p = &fat arr[0]; + int a = 10; + if (*p++ > 0) { // expected-error {{increment or decrement operation of fat pointer is not allowed here}} + a = *p++ == 1 ? *p++ : *++p; /* expected-error {{increment or decrement operation of fat pointer is not allowed here}} + expected-error {{increment or decrement operation of fat pointer is not allowed here}} + expected-error {{increment or decrement operation of fat pointer is not allowed here}} + */ + } else { + a = *++p == 1 ? *p-- : *--p; /* expected-error {{increment or decrement operation of fat pointer is not allowed here}} + expected-error {{increment or decrement operation of fat pointer is not allowed here}} + expected-error {{increment or decrement operation of fat pointer is not allowed here}} + */ + } + switch (*p++) {} // expected-error {{increment or decrement operation of fat pointer is not allowed here}} + while (*p++ > 0) {}// expected-error {{increment or decrement operation of fat pointer is not allowed here}} + do {} while (*p++ > 0);// expected-error {{increment or decrement operation of fat pointer is not allowed here}} + for (; *p++ > 0; p++) {}// expected-error {{increment or decrement operation of fat pointer is not allowed here}} +} \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/global_array/array_out_of_bounds1.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/global_array/array_out_of_bounds1.cbs index 5594d6b6d211..bcd766bdb6e4 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/global_array/array_out_of_bounds1.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/global_array/array_out_of_bounds1.cbs @@ -3,10 +3,11 @@ #include -__attribute__((fat_ptr_checked)) int arr[3]; +CHECKED int arr[3]; int main() { int *fat p = &fat arr[3]; *p = 10; return 0; } -// CHECK: pointer offset exceed the allocation size! \ No newline at end of file +// CHECK: Error: Pointer offset exceeds the allocation size! +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/global_array/array_out_of_bounds1.cbs:[[@LINE-4]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/global_array/array_out_of_bounds2.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/global_array/array_out_of_bounds2.cbs index 2d8b7aae4bb6..5b260692ffc0 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/global_array/array_out_of_bounds2.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/global_array/array_out_of_bounds2.cbs @@ -11,4 +11,5 @@ int main() { *p1 = 10; return 0; } -// CHECK: pointer offset exceed the allocation size! \ No newline at end of file +// CHECK: Error: Pointer offset exceeds the allocation size! +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/global_array/array_out_of_bounds2.cbs:[[@LINE-4]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds1.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds1.cbs index 43d004ddc475..975c7550754f 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds1.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds1.cbs @@ -4,8 +4,9 @@ #include int main() { - int *fat p = checked_malloc(3 * sizeof(int)); + int *fat p = checked_malloc(3 * sizeof(int)); int a = p[3]; return 0; } -// CHECK: pointer offset exceed the allocation size! \ No newline at end of file +// CHECK: Error: Pointer offset exceeds the allocation size! +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds1.cbs:[[@LINE-4]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds10.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds10.cbs new file mode 100644 index 000000000000..61354828836e --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds10.cbs @@ -0,0 +1,16 @@ +// RUN: %clang -enable-fat-ptr %s -o %t.output +// RUN: not %t.output 2>&1 | FileCheck %s + +#include + +int main() { + int *fat p = nullptr; + if (!p) { + p = checked_malloc(sizeof(int)); + p = p + 1; + *p = 10; + } + return 0; +} +// CHECK: Error: Pointer offset exceeds the allocation size! +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds10.cbs:[[@LINE-5]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds11.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds11.cbs new file mode 100644 index 000000000000..2d354b2f52cf --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds11.cbs @@ -0,0 +1,17 @@ +// RUN: %clang -enable-fat-ptr %s -o %t.output +// RUN: not %t.output 2>&1 | FileCheck %s + +#include + +int main() { + int *fat p1 = checked_malloc(sizeof(int)); + int *fat p2 = p1; + if (p1 == p2) { + p2 = checked_malloc(sizeof(int)); + p2++; + *p2 = 10; + } + return 0; +} +// CHECK: Error: Pointer offset exceeds the allocation size! +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds11.cbs:[[@LINE-5]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds12.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds12.cbs new file mode 100644 index 000000000000..f905d5644648 --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds12.cbs @@ -0,0 +1,15 @@ +// RUN: %clang -enable-fat-ptr %s -o %t.output +// RUN: not %t.output 2>&1 | FileCheck %s + +#include + +int main() { + int *fat p1 = nullptr; + int *fat p2 = p1 ? nullptr : checked_malloc(sizeof(int) * 2); + *p2 = 0; + int *fat p3 = *p2 ? p1 : p2; + p3[2] = 10; + return 0; +} +// CHECK: Error: Pointer offset exceeds the allocation size! +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds12.cbs:[[@LINE-4]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds13.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds13.cbs new file mode 100644 index 000000000000..a6cdfadb2e08 --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds13.cbs @@ -0,0 +1,16 @@ +// RUN: %clang -enable-fat-ptr %s -o %t.output +// RUN: not %t.output 2>&1 | FileCheck %s + +#include + +int main() { + int *fat p = checked_malloc(sizeof(int) * 10); + for (int i = 0; i < 10; i++) { + p[i] = i - 5; + } + for ( ; *p < 5; p++) { + *p = 10; + } +} +// CHECK: Error: Pointer offset exceeds the allocation size! +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds13.cbs:[[@LINE-5]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds14.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds14.cbs new file mode 100644 index 000000000000..0a550df0117d --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds14.cbs @@ -0,0 +1,13 @@ +// RUN: %clang -enable-fat-ptr %s -o %t.output +// RUN: not %t.output 2>&1 | FileCheck %s + +#include + +int main() { + int *fat p = checked_malloc(sizeof(int) * 10); + p += 10; + *p = 10; + return 0; +} +// CHECK: Error: Pointer offset exceeds the allocation size! +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds14.cbs:[[@LINE-4]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds15.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds15.cbs new file mode 100644 index 000000000000..281f9d9c6dd0 --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds15.cbs @@ -0,0 +1,15 @@ +// RUN: %clang -enable-fat-ptr %s -o %t.output +// RUN: not %t.output 2>&1 | FileCheck %s + +#include + +int main() { + int *fat *fat p = checked_malloc(sizeof(int *fat) * 10); + p[9] = checked_malloc(sizeof(int) * 10); + p += 9; + *p += 10; + **p = 10; + return 0; +} +// CHECK: Error: Pointer offset exceeds the allocation size! +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds15.cbs:[[@LINE-4]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds16.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds16.cbs new file mode 100644 index 000000000000..4a4060a9a2b8 --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds16.cbs @@ -0,0 +1,14 @@ +// RUN: %clang -enable-fat-ptr %s -o %t.output +// RUN: not %t.output 2>&1 | FileCheck %s + +#include + +int main() { + int *fat p = checked_malloc(sizeof(int) * 10); + p += 9; + *p++ = 9; + *p++ = 10; + return 0; +} +// CHECK: Error: Pointer offset exceeds the allocation size! +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds16.cbs:[[@LINE-4]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds17.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds17.cbs new file mode 100644 index 000000000000..7259b6793597 --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds17.cbs @@ -0,0 +1,18 @@ +// RUN: %clang -enable-fat-ptr %s -o %t.output +// RUN: not %t.output 2>&1 | FileCheck %s + +#include + +typedef void (*Foo) (int *fat); +void bar(int *fat p) { + *p = 10; +} +Foo foo = bar; + +int main() { + int *fat p = checked_malloc(sizeof(int) * 10); + int *fat p1 = p + 10; + foo(p1); +} +// CHECK: Error: Pointer offset exceeds the allocation size! +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds17.cbs:[[@LINE-10]] in bar \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds18.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds18.cbs new file mode 100644 index 000000000000..9e5dcc4c2519 --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds18.cbs @@ -0,0 +1,18 @@ +// RUN: %clang -enable-fat-ptr %s -o %t.output +// RUN: not %t.output 2>&1 | FileCheck %s + +#include + +int main() { + int *fat p = checked_malloc(sizeof(int) * 3); + p[0] = 0; + p[1] = 1; + p[2] = 2; + int *fat q = checked_malloc(sizeof(int) * 3); + for (; *p < 3; p++) { + *q = *p; + q++; + } +} +// CHECK: Error: Pointer offset exceeds the allocation size! +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds18.cbs:[[@LINE-6]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds19.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds19.cbs new file mode 100644 index 000000000000..64f0f702afa6 --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds19.cbs @@ -0,0 +1,19 @@ +// RUN: %clang -enable-fat-ptr %s -o %t.output +// RUN: not %t.output 2>&1 | FileCheck %s + +#include + +int main() { + int *fat p = checked_malloc(sizeof(int) * 3); + p[0] = 0; + p[1] = 1; + p[2] = 2; + int *fat q = checked_malloc(sizeof(int) * 3); + while (*p < 3) { + *q = *p; + p++; + q++; + } +} +// CHECK: Error: Pointer offset exceeds the allocation size! +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds19.cbs:[[@LINE-7]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds2.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds2.cbs index c3b97993ee12..51835b6ee0e2 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds2.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds2.cbs @@ -4,8 +4,9 @@ #include struct S { int *fat p; }; int main() { - struct S s = { checked_malloc(3 * sizeof(int)) }; + struct S s = { checked_malloc(3 * sizeof(int)) }; int a = s.p[3]; return 0; } -// CHECK: pointer offset exceed the allocation size! \ No newline at end of file +// CHECK: Error: Pointer offset exceeds the allocation size! +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds2.cbs:[[@LINE-4]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds3.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds3.cbs index 910359d28473..26e2f6ce7375 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds3.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds3.cbs @@ -4,9 +4,10 @@ #include int main() { - int *fat p = checked_malloc(3 * sizeof(int)); + int *fat p = checked_malloc(3 * sizeof(int)); int *fat p1 = p + 3; int a = *p1; return 0; } -// CHECK: pointer offset exceed the allocation size! \ No newline at end of file +// CHECK: Error: Pointer offset exceeds the allocation size! +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds3.cbs:[[@LINE-4]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds4.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds4.cbs index 9a6cbe7ba543..7b7305318d45 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds4.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds4.cbs @@ -5,9 +5,10 @@ struct S { int a; }; int main() { - struct S *fat p = checked_malloc(sizeof(struct S)); + struct S *fat p = checked_malloc(sizeof(struct S)); struct S *fat p1 = p + 1; p1->a = 10; return 0; } -// CHECK: pointer offset exceed the allocation size! \ No newline at end of file +// CHECK: Error: Pointer offset exceeds the allocation size! +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds4.cbs:[[@LINE-4]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds5.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds5.cbs index 8267ee6635cd..b80177c9e6ae 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds5.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds5.cbs @@ -5,9 +5,10 @@ struct S { int a; int arr[3]; }; int main() { - struct S *fat p1 = checked_malloc(sizeof(struct S) * 2); + struct S *fat p1 = checked_malloc(sizeof(struct S) * 2); struct S *fat p2 = p1 + 1; int a = p2->arr[3]; return 0; } -// CHECK: pointer offset exceed the allocation size! \ No newline at end of file +// CHECK: Error: Pointer offset exceeds the allocation size! +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds5.cbs:[[@LINE-4]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds6.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds6.cbs index dedb1e9f8d70..cd4c9e508722 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds6.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds6.cbs @@ -5,7 +5,8 @@ struct S { int arr[3]; }; int main() { - struct S *fat p = checked_malloc(sizeof(struct S)); + struct S *fat p = checked_malloc(sizeof(struct S)); int a = p->arr[3]; } -// CHECK: pointer offset exceed the allocation size! \ No newline at end of file +// CHECK: Error: Pointer offset exceeds the allocation size! +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds6.cbs:[[@LINE-3]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds7.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds7.cbs new file mode 100644 index 000000000000..42b814927dcc --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds7.cbs @@ -0,0 +1,17 @@ +// RUN: %clang -enable-fat-ptr %s -o %t.output +// RUN: not %t.output 2>&1 | FileCheck %s + +#include + +int main() { + int *fat p = checked_malloc(sizeof(int) * 10); + for (int i = 0; i < 10; i++) { + p[i] = i - 5; + } + while (*p < 5) { + p = p + 1; + } + return 0; +} +// CHECK: Error: Pointer offset exceeds the allocation size! +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds7.cbs:[[@LINE-6]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds8.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds8.cbs new file mode 100644 index 000000000000..f29da60932a4 --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds8.cbs @@ -0,0 +1,17 @@ +// RUN: %clang -enable-fat-ptr %s -o %t.output +// RUN: not %t.output 2>&1 | FileCheck %s + +#include + +int main() { + int *fat p = checked_malloc(sizeof(int) * 10); + for (int i = 0; i < 10; i++) { + p[i] = i - 5; + } + do { + p = p + 1; + } while (*p < 5); + return 0; +} +// CHECK: Error: Pointer offset exceeds the allocation size! +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds8.cbs:[[@LINE-4]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds9.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds9.cbs new file mode 100644 index 000000000000..904126abeadb --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds9.cbs @@ -0,0 +1,15 @@ +// RUN: %clang -enable-fat-ptr %s -o %t.output +// RUN: not %t.output 2>&1 | FileCheck %s + +#include + +int main() { + int *fat p = checked_malloc(sizeof(int)); + if (p) { + p = p + 1; + *p = 10; + } + return 0; +} +// CHECK: Error: Pointer offset exceeds the allocation size! +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds9.cbs:[[@LINE-5]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/buffer_overflow.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/buffer_overflow.cbs index 474e6c77a4ab..a40d1bc46c3b 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/buffer_overflow.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/buffer_overflow.cbs @@ -14,8 +14,9 @@ char *fat fat_strcpy(char *fat dest, const char *src) { } int main(int argc, char **argv) { - char *fat buf = checked_malloc(sizeof(char)*BUFSIZE); + char *fat buf = checked_malloc(sizeof(char)*BUFSIZE); fat_strcpy(buf, argv[1]); return 0; } -// CHECK: pointer offset exceed the allocation size! \ No newline at end of file +// CHECK: Error: Pointer offset exceeds the allocation size! +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/buffer_overflow.cbs:[[@LINE-13]] in fat_strcpy \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free1.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free1.cbs index b7cf5d4ce5e7..a9c67262df80 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free1.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free1.cbs @@ -4,10 +4,10 @@ #include int main() { - int *fat p = checked_malloc(sizeof(int)); - checked_free(p); - checked_free(p); + int *fat p = checked_malloc(sizeof(int)); + checked_free(p); + checked_free(p); return 0; } -// CHECK: version number error when free! \ No newline at end of file +// CHECK: Error: Cannot free this pointer because the allocation may have been freed! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free2.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free2.cbs index adb31c07c81e..64a65afc5220 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free2.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free2.cbs @@ -4,11 +4,11 @@ #include int main() { - int *fat p1 = checked_malloc(sizeof(int)); + int *fat p1 = checked_malloc(sizeof(int)); int *fat p2 = p1; - checked_free(p1); - checked_free(p2); + checked_free(p1); + checked_free(p2); return 0; } -// CHECK: version number error when free! \ No newline at end of file +// CHECK: Error: Cannot free this pointer because the allocation may have been freed! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free3.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free3.cbs index a5a4b818647f..26db032c3e72 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free3.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free3.cbs @@ -5,12 +5,12 @@ struct G { int *fat a; }; int main() { - struct G g = { .a = checked_malloc(sizeof(int)) }; - struct G *fat p = checked_malloc(sizeof(struct G)); + struct G g = { .a = checked_malloc(sizeof(int)) }; + struct G *fat p = checked_malloc(sizeof(struct G)); p->a = g.a; - checked_free(p->a); - checked_free(g.a); + checked_free(p->a); + checked_free(g.a); return 0; } -// CHECK: version number error when free! \ No newline at end of file +// CHECK: Error: Cannot free this pointer because the allocation may have been freed! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free4.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free4.cbs index f9ddbb207a9c..8d294688d9f8 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free4.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free4.cbs @@ -4,15 +4,15 @@ #include int *fat test() { - int *fat p = checked_malloc(sizeof(int)); - checked_free(p); + int *fat p = checked_malloc(sizeof(int)); + checked_free(p); return p; } int main() { int *fat p = test(); - checked_free(p); + checked_free(p); return 0; } -// CHECK: version number error when free! \ No newline at end of file +// CHECK: Error: Cannot free this pointer because the allocation may have been freed! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free1.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free1.cbs index 42fc9676d354..fbf926b906af 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free1.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free1.cbs @@ -4,11 +4,12 @@ #include int main() { - int *fat p = checked_malloc(sizeof(int)); + int *fat p = checked_malloc(sizeof(int)); *p = 10; - checked_free(p); + checked_free(p); *p = 10; return 0; } -// CHECK: version number error! \ No newline at end of file +// CHECK: Error: Cannot use this pointer because the allocation may have been freed or reseored! +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free1.cbs:[[@LINE-5]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free2.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free2.cbs index 31ff1217ea98..9faa09e3f4c3 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free2.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free2.cbs @@ -5,11 +5,12 @@ struct S { int a; }; int main() { - struct S *fat p = checked_malloc(sizeof(struct S)); + struct S *fat p = checked_malloc(sizeof(struct S)); p->a = 10; - checked_free(p); + checked_free(p); p->a = 10; return 0; } -// CHECK: version number error! \ No newline at end of file +// CHECK: Error: Cannot use this pointer because the allocation may have been freed or reseored! +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free2.cbs:[[@LINE-5]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free3.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free3.cbs index 3c9ef85f86d0..fe79ee91d3f8 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free3.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free3.cbs @@ -3,9 +3,9 @@ #include -int *fat test() __attribute__((fat_ptr_checked)) { - int *fat p = checked_malloc(sizeof(int)); - checked_free(p); +int *fat test() { + int *fat p = checked_malloc(sizeof(int)); + checked_free(p); return p; } @@ -14,4 +14,5 @@ int main() { *p = 10; return 0; } -// CHECK: version number error! \ No newline at end of file +// CHECK: Error: Cannot use this pointer because the allocation may have been freed or reseored! +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free3.cbs:[[@LINE-4]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free4.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free4.cbs index e0227f63c4db..08ce74e36d4d 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free4.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free4.cbs @@ -5,11 +5,12 @@ struct G { int *fat a; }; int main() { - struct G g = { .a = checked_malloc(sizeof(int)) }; - struct G *fat p = checked_malloc(sizeof(struct G)); + struct G g = { .a = checked_malloc(sizeof(int)) }; + struct G *fat p = checked_malloc(sizeof(struct G)); p->a = g.a; - checked_free(p->a); + checked_free(p->a); *g.a = 10; return 0; } -// CHECK: version number error! \ No newline at end of file +// CHECK: Error: Cannot use this pointer because the allocation may have been freed or reseored! +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free4.cbs:[[@LINE-4]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free5.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free5.cbs index 0c420fce218d..1c37a3ec2e69 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free5.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free5.cbs @@ -16,14 +16,15 @@ char *fat fat_strncpy(char *fat dest, const char *src, size_t n) { } int main() { - char *fat buf1 = checked_malloc(BUFSIZE); - checked_free(buf1); + char *fat buf1 = checked_malloc(BUFSIZE); + checked_free(buf1); - char *fat buf2 = checked_malloc(BUFSIZE); + char *fat buf2 = checked_malloc(BUFSIZE); memset((char *)buf2, 0, BUFSIZE); fat_strncpy(buf1, "hack", 5); - checked_free(buf2); + checked_free(buf2); return 0; } -// CHECK: version number error! \ No newline at end of file +// CHECK: Error: Cannot use this pointer because the allocation may have been freed or reseored! +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free5.cbs:[[@LINE-20]] in fat_strncpy \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free6.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free6.cbs index 4b00fec29811..1159a594974c 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free6.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free6.cbs @@ -6,9 +6,10 @@ void pass_fat(int *p) {} int main() { - int *fat p = checked_malloc(sizeof(int)); - checked_free(p); + int *fat p = checked_malloc(sizeof(int)); + checked_free(p); pass_fat((int *)p); return 0; } -// CHECK: version number error! \ No newline at end of file +// CHECK: Error: Cannot use this pointer because the allocation may have been freed or reseored! +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free6.cbs:[[@LINE-4]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free7.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free7.cbs new file mode 100644 index 000000000000..0e96d5e77f6f --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free7.cbs @@ -0,0 +1,15 @@ +// RUN: %clang -enable-fat-ptr %s -o %t.output +// RUN: not %t.output 2>&1 | FileCheck %s + +#include + +int main() { + int *fat p = checked_malloc(sizeof(int)); + checked_free(p); + if (*p == 5) { + return 5; + } + return 0; +} +// CHECK: Error: Cannot use this pointer because the allocation may have been freed or reseored! +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free7.cbs:[[@LINE-6]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free8.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free8.cbs new file mode 100644 index 000000000000..04b70ea5aa83 --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free8.cbs @@ -0,0 +1,17 @@ +// RUN: %clang -enable-fat-ptr %s -o %t.output +// RUN: not %t.output 2>&1 | FileCheck %s + +#include + +int *fat p = nullptr; + +int main() { + p = checked_malloc(sizeof(int)); + checked_free(p); + if (*p == 5) { + return 5; + } + return 0; +} +// CHECK: Error: Cannot use this pointer because the allocation may have been freed or reseored! +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free8.cbs:[[@LINE-6]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds1.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds1.cbs index 07ea7ca2204c..e99f7725e1b5 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds1.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds1.cbs @@ -9,4 +9,5 @@ int main() { *p = 10; return 0; } -// CHECK: pointer offset exceed the allocation size! \ No newline at end of file +// CHECK: Error: Pointer offset exceeds the allocation size! +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds1.cbs:[[@LINE-4]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds2.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds2.cbs index 80280bf71a33..2b1425733499 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds2.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds2.cbs @@ -10,4 +10,5 @@ int main() { *p1 = 10; return 0; } -// CHECK: pointer offset exceed the allocation size! \ No newline at end of file +// CHECK: Error: Pointer offset exceeds the allocation size! +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds2.cbs:[[@LINE-4]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds3.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds3.cbs new file mode 100644 index 000000000000..52a72be91eeb --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds3.cbs @@ -0,0 +1,14 @@ +// RUN: %clang -enable-fat-ptr %s -o %t.output +// RUN: not %t.output 2>&1 | FileCheck %s + +#include + +int main() { + int arr1[3] = {1, 2, 3}, arr2[2] = { 1, 2 }; + int *fat p = &fat arr1[2]; + int *fat p1 = p + 1; + *p1 = 10; + return 0; +} +// CHECK: Error: Pointer offset exceeds the allocation size! +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds3.cbs:[[@LINE-4]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds4.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds4.cbs new file mode 100644 index 000000000000..2c6abd37814b --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds4.cbs @@ -0,0 +1,14 @@ +// RUN: %clang -enable-fat-ptr %s -o %t.output +// RUN: not %t.output 2>&1 | FileCheck %s + +#include + +int main() { + char *fat arr[2] = { (char *fat)"Hello", (char *fat)"world" }; + char *fat *fat p = &fat arr[0]; + printf("%s\n", (char *)p[2]); + return 0; +} +// CHECK: Error: Pointer offset exceeds the allocation size! +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds4.cbs:[[@LINE-4]] in main + diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/buffer_overflow.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/buffer_overflow.cbs index d4230082860d..610d45c170c3 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/buffer_overflow.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/buffer_overflow.cbs @@ -19,4 +19,5 @@ int main(int argc, char **argv) { fat_strcpy(p, argv[1]); return 0; } -// CHECK: pointer offset exceed the allocation size! \ No newline at end of file +// CHECK: Error: Pointer offset exceeds the allocation size! +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/buffer_overflow.cbs:[[@LINE-14]] in fat_strcpy \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/return_local_address.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/return_local_address.cbs index c39bf785d13b..c2d89d83e759 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/return_local_address.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/return_local_address.cbs @@ -14,4 +14,5 @@ int main() { *p = 10; return 0; } -// CHECK: version number error! \ No newline at end of file +// CHECK: Error: Cannot use this pointer because the allocation may have been freed or reseored! +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/return_local_address.cbs:[[@LINE-4]] in main \ No newline at end of file diff --git a/clang/test/BSC/Positive/FatPtr/heap.cbs b/clang/test/BSC/Positive/FatPtr/heap.cbs index 5c8a83374c7e..f30b12beda57 100644 --- a/clang/test/BSC/Positive/FatPtr/heap.cbs +++ b/clang/test/BSC/Positive/FatPtr/heap.cbs @@ -5,62 +5,62 @@ #include struct S { int a; }; void test1() { - int *fat p1 = checked_malloc(sizeof(int)); + int *fat p1 = checked_malloc(sizeof(int)); *p1 = 10; - checked_free(p1); + checked_free(p1); - struct S *fat p2 = checked_malloc(sizeof(struct S)); + struct S *fat p2 = checked_malloc(sizeof(struct S)); p2->a = 10; - checked_free(p2); + checked_free(p2); } struct G { int *fat a; }; void test2() { - struct G g = { .a = checked_malloc(sizeof(int)) }; - struct G *fat p = checked_malloc(sizeof(struct G)); + struct G g = { .a = checked_malloc(sizeof(int)) }; + struct G *fat p = checked_malloc(sizeof(struct G)); *p = g; *p->a = 10; - checked_free(p->a); - checked_free(p); + checked_free(p->a); + checked_free(p); } void test3() { - int *fat p1 = checked_malloc(sizeof(int)); - int *fat p2 = checked_malloc(sizeof(int)); + int *fat p1 = checked_malloc(sizeof(int)); + int *fat p2 = checked_malloc(sizeof(int)); *p1 = *p2; - checked_free(p1); - checked_free(p2); + checked_free(p1); + checked_free(p2); } void test4() { - int *fat p1 = checked_malloc(sizeof(int)); - int *fat p2 = checked_malloc(sizeof(int)); + int *fat p1 = checked_malloc(sizeof(int)); + int *fat p2 = checked_malloc(sizeof(int)); p1 = p2; - checked_free(p1); //only free once + checked_free(p1); //only free once } void test5() { - int *fat p = checked_malloc(sizeof(int) * 3); + int *fat p = checked_malloc(sizeof(int) * 3); int a = p[2]; - checked_free(p); + checked_free(p); } void test6() { - int *fat p1 = checked_malloc(sizeof(int)); + int *fat p1 = checked_malloc(sizeof(int)); int *fat p2 = p1; int *p3 = (int *)p1; float *fat p4 = (float *fat)p1; - checked_free(p1); + checked_free(p1); } void test7() { - int *fat p = checked_malloc(3 * sizeof(int)); + int *fat p = checked_malloc(3 * sizeof(int)); int *fat p1 = p + 2; int a = *p1; } void test8(int *fat p1, int *fat p2) { - p1 = checked_malloc(3 * sizeof(int)); + p1 = checked_malloc(3 * sizeof(int)); p2 = p1 + 2; int a = *p1; int b = *p2; @@ -68,7 +68,7 @@ void test8(int *fat p1, int *fat p2) { struct K { int arr[3]; }; void test9() { - struct K *fat p1 = checked_malloc(sizeof(struct K) * 2); + struct K *fat p1 = checked_malloc(sizeof(struct K) * 2); int a = p1->arr[3]; int *fat p2 = &fat p1->arr[3]; *p2 = 10; @@ -79,6 +79,25 @@ void test9() { *p4 = 10; } +void test10() { + int *fat *fat p = checked_malloc(sizeof(int *fat) * 10); + p[9] = checked_malloc(sizeof(int) * 10); + p += 9; + *p += 9; + **p = 10; +} + +typedef void (*Foo) (int *fat); +void bar(int *fat p) { + *p = 10; +} +Foo foo = bar; +void test11() { + int *fat p = checked_malloc(sizeof(int) * 10); + int *fat p1 = p + 9; + foo(p1); +} + int main() { test1(); test2(); @@ -90,5 +109,8 @@ int main() { int *fat p1; int *fat p2; test8(p1, p2); + test9(); + test10(); + test11(); return 0; } \ No newline at end of file diff --git a/clang/test/BSC/Positive/FatPtr/multi_level_pointer.cbs b/clang/test/BSC/Positive/FatPtr/multi_level_pointer.cbs new file mode 100644 index 000000000000..2989912aba2b --- /dev/null +++ b/clang/test/BSC/Positive/FatPtr/multi_level_pointer.cbs @@ -0,0 +1,29 @@ +// RUN: %clang %s -enable-fat-ptr -o %t.output +// RUN: %t.output | FileCheck %s +// expected-no-diagnostics + +#include +void test1() { + char *fat arr[2] = { (char *fat)"Hello", (char *fat)"world" }; + char *fat *fat p = &fat arr[0]; + printf("%s %s!\n", (char *)*p, (char *)p[1]); +} + +void test2() { + char *fat p = checked_malloc(sizeof(char) * 6); + p[0] = 'H'; + p[1] = 'e'; + p[2] = 'l'; + p[3] = 'l'; + p[4] = 'o'; + char *fat *p1 = &p; + printf("%s!\n", (char *)*p1); +} + +int main() { + test1(); + test2(); + return 0; +} +// CHECK: Hello world! +// CHECK-NEXT: Hello! \ No newline at end of file diff --git a/clang/test/BSC/Positive/FatPtr/perform_anaysis/deref_loop/deref_loop.cbs b/clang/test/BSC/Positive/FatPtr/perform_anaysis/deref_loop/deref_loop.cbs index ac7ec4d457e9..43ba61776199 100644 --- a/clang/test/BSC/Positive/FatPtr/perform_anaysis/deref_loop/deref_loop.cbs +++ b/clang/test/BSC/Positive/FatPtr/perform_anaysis/deref_loop/deref_loop.cbs @@ -20,7 +20,7 @@ void test_raw(long n) { } void test_fat(long n) { - long *fat p = checked_malloc(sizeof(long)); + long *fat p = checked_malloc(sizeof(long)); while (n > 0) { *p = n; n--; diff --git a/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/bisort_bsc/bitonic.cbs b/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/bisort_bsc/bitonic.cbs index 4535551ee5b1..8065f9620351 100644 --- a/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/bisort_bsc/bitonic.cbs +++ b/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/bisort_bsc/bitonic.cbs @@ -21,8 +21,6 @@ typedef struct future_cell_int{ HANDLE *fat value; } future_cell_int; -#define NIL ((HANDLE *) 0) - int NumNodes, NDim; int random_c(int); @@ -32,11 +30,11 @@ int flag=0,foo=0; //FIXME #define LocalNewNode(h,v) \ { \ - h = checked_malloc(sizeof(HANDLE)); \ - h->value = v; \ - h->left = (HANDLE *fat)NIL; \ - h->right = (HANDLE *fat)NIL; \ - }; + h = checked_malloc(sizeof(HANDLE)); \ + h->value = v; \ + h->left = nullptr; \ + h->right = nullptr; \ +}; #define NewNode(h,v,procid) LocalNewNode(h,v) @@ -121,7 +119,7 @@ HANDLE *fat RandTree(int n, int seed, int node, int level) { h->left = f_left.value; h->right = f_right.value; } else { - h = checked_malloc(sizeof(HANDLE)); //FIXME + h = checked_malloc(sizeof(HANDLE)); //FIXME } return h; } diff --git a/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd_bsc/node.cbs b/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd_bsc/node.cbs index f859e368b7ae..ad2fac0b6695 100644 --- a/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd_bsc/node.cbs +++ b/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd_bsc/node.cbs @@ -70,7 +70,7 @@ tree_t *fat TreeAlloc (int level, int lo, int proc) { tree_t *fat new; tree_t *fat right; tree_t *fat left; - new = checked_malloc(sizeof(tree_t)); + new = checked_malloc(sizeof(tree_t)); left = TreeAlloc(level -1, lo+proc/2, proc/2); right = TreeAlloc(level-1,lo,proc/2); new->val = 1; diff --git a/clang/test/BSC/Positive/FatPtr/string_literal.cbs b/clang/test/BSC/Positive/FatPtr/string_literal.cbs new file mode 100644 index 000000000000..7460656d2b6d --- /dev/null +++ b/clang/test/BSC/Positive/FatPtr/string_literal.cbs @@ -0,0 +1,27 @@ +// RUN: %clang %s -enable-fat-ptr -o %t.output +// RUN: %t.output | FileCheck %s +// expected-no-diagnostics + +#include + +void foo(const char *fat p) { + printf("%s", p); +} + +char *fat bar(char *fat p) { + return p; +} + +int main() { + char *fat p = (char *fat)"1: Hello world!\n"; + printf("%s", (char *)p); + foo((char *fat)"2: Hello world!\n"); + char *fat ss[] = { (char *fat)"Hello", (char *fat)"world"}; + printf("3: %s %s!\n", (char *)ss[0], (char *)ss[1]); + printf("4: %s %s!\n", (char *)bar(ss[0]), (char *)bar(ss[1])); + return 0; +} +// CHECK: 1: Hello world! +// CHECK-NEXT: 2: Hello world! +// CHECK-NEXT: 3: Hello world! +// CHECK-NEXT: 4: Hello world! \ No newline at end of file -- Gitee From 7e99fe47a7f7551cd31ec085a2a1b2d2fb9cdce7 Mon Sep 17 00:00:00 2001 From: xiaoyu Date: Tue, 18 Feb 2025 19:13:01 +0800 Subject: [PATCH 04/29] [FatPtr] Performance optimization for fatptr check, and also fix some bugs 1.check function call is inlined; 2.combine check version and offset if necessary; 3.BB based optm of reduce redundant checks, and and mayfree function call judge; 4.fix some bugs, including type cast, fatptr in compound expr adjsut name of some var and add mayfree logic --- clang/include/clang/AST/Expr.h | 7 + .../clang/Analysis/Analyses/BSCFatPtrCheck.h | 30 + clang/lib/AST/ExprConstant.cpp | 8 + clang/lib/Analysis/BSCFatPtrCheck.cpp | 598 ++++++++++++++++++ clang/lib/Analysis/CMakeLists.txt | 1 + clang/lib/Headers/bsc_include/bsc_fat_ptr.hbs | 21 +- clang/lib/Sema/BSC/SemaBSCFatPtr.cpp | 77 ++- clang/lib/Sema/BSC/SemaDeclBSC.cpp | 4 + clang/lib/Sema/SemaExpr.cpp | 13 +- clang/lib/Sema/SemaExprCXX.cpp | 7 + 10 files changed, 760 insertions(+), 6 deletions(-) create mode 100644 clang/include/clang/Analysis/Analyses/BSCFatPtrCheck.h create mode 100644 clang/lib/Analysis/BSCFatPtrCheck.cpp diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h index 867dede1da1b..98615e310b26 100644 --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -151,6 +151,13 @@ public: bool IsDesugaredCastExpr = false; // Add BSC Member func desugar flag. bool IsDesugaredBSCMethodCall = false; + // Stores the check status of a fat pointer used in the expression. + // Possible values: + // Unchecked: 0 - No checks have been performed. + // KeyCheckedOnly: 1 - Only the key was checked. + // OffsetCheckedOnly: 2 - Only the offset was checked. + // BothChecked: 3 - Both the key and the offset were checked. + uint8_t FatPtrCheckedStatus = 0; // Default: Unchecked SourceLocation getExtendedTypeBeginLoc() { return EBLoc; } void setExtendedTypeBeginLoc(SourceLocation L) { EBLoc = L; } diff --git a/clang/include/clang/Analysis/Analyses/BSCFatPtrCheck.h b/clang/include/clang/Analysis/Analyses/BSCFatPtrCheck.h new file mode 100644 index 000000000000..e9df8841a989 --- /dev/null +++ b/clang/include/clang/Analysis/Analyses/BSCFatPtrCheck.h @@ -0,0 +1,30 @@ +//===- BSCNullabilityCheck.h - Nullability Check for Source CFGs -*- BSC ---*// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements BSC Pointer Nullability Check for source-level CFGs. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_ANALYSIS_ANALYSES_BSCFATPTRCHECK_H +#define LLVM_CLANG_ANALYSIS_ANALYSES_BSCFATPTRCHECK_H + +#if ENABLE_BSC + +#include "clang/Analysis/AnalysisDeclContext.h" +#include "clang/Analysis/CallGraph.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Sema/Sema.h" + +namespace clang { +void runFatPtrReduntantCheck(const FunctionDecl &fd, const CFG &cfg, + AnalysisDeclContext &ac, ASTContext &ctx); +} + +#endif // ENABLE_BSC + +#endif // LLVM_CLANG_ANALYSIS_ANALYSES_BSCFATPTRCHECK_H \ No newline at end of file diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 0c9154cfe4ea..c46223656371 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -8823,7 +8823,15 @@ public: static bool EvaluatePointer(const Expr* E, LValue& Result, EvalInfo &Info, bool InvalidBaseOK) { assert(!E->isValueDependent()); +#if ENABLE_BSC + if(E->getType().isFatPtrRecordType()) { + assert(E->isPRValue()); + } else { +#endif assert(E->isPRValue() && E->getType()->hasPointerRepresentation()); +#if ENABLE_BSC + } +#endif return PointerExprEvaluator(Info, Result, InvalidBaseOK).Visit(E); } diff --git a/clang/lib/Analysis/BSCFatPtrCheck.cpp b/clang/lib/Analysis/BSCFatPtrCheck.cpp new file mode 100644 index 000000000000..86900a4c8a04 --- /dev/null +++ b/clang/lib/Analysis/BSCFatPtrCheck.cpp @@ -0,0 +1,598 @@ +#if ENABLE_BSC + +#include "clang/Analysis/Analyses/BSCFatPtrCheck.h" +#include "clang/AST/ParentMap.h" +#include "clang/AST/StmtVisitor.h" +#include "clang/Analysis/AnalysisDeclContext.h" +#include "clang/Analysis/CFG.h" +#include "clang/Analysis/FlowSensitive/DataflowWorklist.h" +#include "llvm/ADT/DenseMap.h" + +using namespace clang; + +/// Represents the check status of a fat pointer's associated VarDecl after the last operation that may modify it, +/// such as free(p) or p++. +enum class FatPtrCheckStatus : uint8_t { + Unchecked = 0, // No checks have been performed + + KeyCheckedOnly = 1, // Only the key was checked + + OffsetCheckedOnly = 2, // Only the offset was checked + + BothChecked = 3, // Both the key and the offset were checked +}; + +using FatPtrVar = std::pair; +using FatPtrVarCheckStatus = std::map; + +class FatPtrCheckImpl { +public: + // record each BB's Last and Out pointer var check status + llvm::DenseMap BBLastStatus; + llvm::DenseMap BBOutStatus; + + // For branch statement with condition, such as IfStmt, WhileStmt, + // true branch and else branch may have different status. + // we merge the check status of all the paths to the current BB + // For example: + // @code + // int *fat p = checked_malloc(sizeof(int)); + // *p = 0; + // if (p != nullptr) { + // use(p); // p is perhaps freed + // } else { + // *p = 10; // p is checked before, so the check of p can be deleted here + // } + // *p = 5; // p may be freed after the ifstmt, so the check of p is kept here + // @endcode + // CFG is: + // B4(has condition as terminitor) + // true / \ false + // B3 B2 + // \ / + // B1 + // BBOutStatus records the check status of each BB: + // Key is current BB, value is the status after all the statements in current + // for this example, BBOutStatus will be: + // { B4 : p BothChecked }, { B3 : p Unchecked }, { B2 : p BothChecked }, { B1 : p BothChecked } + // Before caculating BBOutStatus of each BB, we merge the check status of all the pred BBs, + // And then, use the merged status as the input of the current BB. + // for B1, the input check status of p is: BBOutStatus[B3][p] & BBOutStatus[B2][p] + + // BBLastStatus records the check status of each BB at last analysis: + // After analysis of current BB, we compare the BBOutStatus and BBLastStatus, + // if they are different, we add the subsequent BB of the current BB to the analysis worklist. + + FatPtrVarCheckStatus runOnBlock(const CFGBlock *block, + FatPtrVarCheckStatus &status, ASTContext &ctx, + const FunctionDecl &fd, ParentMap &PM); + + void initStatus(const CFG &cfg, ASTContext &ctx); + FatPtrVarCheckStatus mergePredStatus(FatPtrVarCheckStatus currStatus, + FatPtrVarCheckStatus predStatus); + + FatPtrCheckImpl() : BBLastStatus(0), BBOutStatus(0) {} +}; + +namespace { +class TransferFunctions : public StmtVisitor { + FatPtrCheckImpl &FCI; + const CFGBlock *Block; + FatPtrVarCheckStatus &CurrStatus; + ASTContext &Ctx; + const FunctionDecl &Fd; + ParentMap &PM; + CallGraph CG; + +public: + TransferFunctions(FatPtrCheckImpl &fci, const CFGBlock *block, + FatPtrVarCheckStatus &status, ASTContext &ctx, + const FunctionDecl &fd, ParentMap &pm) + : FCI(fci), Block(block), CurrStatus(status), Ctx(ctx), Fd(fd), PM(pm){} + + bool mayFreeCallExpr(CallExpr *CE); + void VisitBinaryOperator(BinaryOperator *BO); + void VisitUnaryOperator(UnaryOperator *UO); + void VisitMemberExpr(MemberExpr *ME); + void VisitCallExpr(CallExpr *CE); + void VisitCStyleCastExpr(CStyleCastExpr *CSCE); + void VisitArraySubscriptExpr(ArraySubscriptExpr *ASE); + +private: + // The whitelist of functions that will not free heap memory. + // FIXME: Add more common libc functions that do not free. + std::set MayFreeFnWL = { + "malloc", "checked_malloc", "_check_version", "_check_offset", "_check_version_and_offset", "_new_version_number", + // libc C functions. Need add more. + "printf", "sprintf", "abort", "exit", "srand", + "atoi", "atol", + "strlen", "strcmp", "strncmp", "strcpy", "strncpy", "strcat", "strchar", + "strtod", "strrchr", "strcasecmp", "strdup", "strstr", "strchr", "strpbrk", + "strspn", "atoll", + "memcpy", "memmove", "memcmp", "memset", + "fread", "fputs", "fopen", "syslog", "opendir", + "getpwnam", "getnameinfo", + // Syscalls + "stat", "readlink", "execve", "read", "write", + }; + +}; +} // namespace + +static void VisitMEForFieldPath(Expr *E, FatPtrVar &FP) { + if (auto ME = dyn_cast(E)) { + if (auto FD = dyn_cast(ME->getMemberDecl())) { + FP.second = "." + FD->getNameAsString() + FP.second; + VisitMEForFieldPath(ME->getBase(), FP); + } + } else if (auto DRE = dyn_cast(E)) { + if (VarDecl *VD = dyn_cast(DRE->getDecl())) + FP.first = VD; + } else if (auto ICE = dyn_cast(E)) { + VisitMEForFieldPath(ICE->getSubExpr(), FP); + } else if (auto PE = dyn_cast(E)) { + VisitMEForFieldPath(PE->getSubExpr(), FP); + } +} + +static DeclRefExpr *getDREFromExpr(Expr *E) { + if (auto DRE = dyn_cast(E)) { + return DRE; + } else if (auto ICE = dyn_cast(E)) { + return getDREFromExpr(ICE->getSubExpr()); + } else if (auto PE = dyn_cast(E)) { + return getDREFromExpr(PE->getSubExpr()); + } + return nullptr; +} + +static MemberExpr *getMemberExprFromExpr(Expr *E) { + if (auto ME = dyn_cast(E)) { + return ME; + } else if (auto ICE = dyn_cast(E)) { + return getMemberExprFromExpr(ICE->getSubExpr()); + } else if (auto PE = dyn_cast(E)) { + return getMemberExprFromExpr(PE->getSubExpr()); + } + return nullptr; +} + +static bool IsFatPtrSelfOffSetExpr(BinaryOperator *BO) { + if (!BO->isAssignmentOp()) + return false; + + Expr *LHS = BO->getLHS(); + Expr *RHS = BO->getRHS(); + if (!LHS->getType().isFatPtrType()) + return false; + + // handle `p += 1`, `p -= 1`, if p is fat ptr. + BinaryOperator::Opcode Op = BO->getOpcode(); + if ((Op == BO_AddAssign || Op == BO_SubAssign) && + RHS->getType()->isIntegerType()) + return true; + + // handle `p = p + 1`, if p is fat ptr. + // eg. `p = p + 1`: true + // eg. `p = p2 +1`: false + if (auto *RHSBO = dyn_cast(RHS)) { + if (!RHSBO->isAdditiveOp()) + return false; + + Expr *RBLHS = RHSBO->getLHS(); + Expr *RBRHS = RHSBO->getRHS(); + if (!RBLHS->getType().isFatPtrType() || !RBRHS->getType()->isIntegerType()) + return false; + + if (auto *LHSDRF = getDREFromExpr(LHS)) { + if (auto *RHSLeftDRE = getDREFromExpr(RBLHS)) { + return LHSDRF->getDecl() == RHSLeftDRE->getDecl(); + } + } + // handle `p.a = p.a + 1`, if p.a is fat ptr. + if (auto *LHSME = getMemberExprFromExpr(LHS)) { + if (auto *RHSLeftME = getMemberExprFromExpr(RBLHS)) { + return LHSME->getMemberDecl() == RHSLeftME->getMemberDecl(); + } + } + } + return false; +} + +void TransferFunctions::VisitBinaryOperator(BinaryOperator *BO) { + Expr *LHS = BO->getLHS(); + if (!BO->isAssignmentOp() || !LHS->getType().isFatPtrType()) { + return; + } + + if (DeclRefExpr *DRE = getDREFromExpr(LHS)) { + if (VarDecl *VD = dyn_cast(DRE->getDecl())) { + FatPtrVar FP = {VD, ""}; + if (!VD->getType().isFatPtrType() || !CurrStatus.count(FP)) { + return; + } + if (IsFatPtrSelfOffSetExpr(BO)) { + // handle `p = p + 1`, `p += 1`, if p is fat ptr. + // reset the offset check status when p is self-offset + CurrStatus[FP] = static_cast( + static_cast(CurrStatus[FP]) & + static_cast(FatPtrCheckStatus::KeyCheckedOnly)); + } else { + // reset the both check status when p is reassign + // eg. `p = p2`, `p = p2 + 1`, `p = func()` + CurrStatus[FP] = FatPtrCheckStatus::Unchecked; + } + } + } else if (MemberExpr *ME = getMemberExprFromExpr(LHS)) { + if (auto FD = dyn_cast(ME->getMemberDecl())) { + if (!FD->getType().isFatPtrType()) + return; + FatPtrVar FP; + VisitMEForFieldPath(ME, FP); + if (!CurrStatus.count(FP)) + return; + if (IsFatPtrSelfOffSetExpr(BO)) { + // handle `p.a = p.a + 1`, `p.a += 1`, if p.a is fat ptr. + // reset the offset check status when p is self-offset + CurrStatus[FP] = static_cast( + static_cast(CurrStatus[FP]) & + static_cast(FatPtrCheckStatus::KeyCheckedOnly)); + } else { + // reset the both check status when p.a is reassign + // eg. `p.a = p2.a`, `p.a = p2.a + 1`, `p.a = func()` + CurrStatus[FP] = FatPtrCheckStatus::Unchecked; + } + } + } +} + +// *p will change the check status to BothChecked. +void TransferFunctions::VisitUnaryOperator(UnaryOperator *UO) { + UnaryOperator::Opcode Op = UO->getOpcode(); + FatPtrVar FP; + if (Op == UO_Deref || Op == UO_AddrMutDeref || Op == UO_AddrConstDeref) { + // handle *p if p is fat ptr. + if (DeclRefExpr *DRE = getDREFromExpr(UO->getSubExpr())) { + if (VarDecl *VD = dyn_cast(DRE->getDecl())) { + FP = {VD, ""}; + if (VD->getType().isFatPtrType() && CurrStatus.count(FP)) { + // Label the check kind in the sema phase, for unary expression: *p; + DRE->FatPtrCheckedStatus = static_cast(CurrStatus[FP]); + // update the redundant check status + CurrStatus[FP] = FatPtrCheckStatus::BothChecked; + } + } + } else if (MemberExpr *ME = getMemberExprFromExpr(UO->getSubExpr())) { + // handle *(p->a), if p->a is fat ptr. + if (auto FD = dyn_cast(ME->getMemberDecl())) { + if (FD->getType().isFatPtrType()) { + VisitMEForFieldPath(ME, FP); + if (CurrStatus.count(FP)) { + ME->FatPtrCheckedStatus = static_cast(CurrStatus[FP]); + CurrStatus[FP] = FatPtrCheckStatus::BothChecked; + } + } + } + } + } else if (UO->isIncrementDecrementOp()) { + // handle p++/p--/++p/--p if p is fat ptr. + if (DeclRefExpr *DRE = getDREFromExpr(UO->getSubExpr())) { + if (VarDecl *VD = dyn_cast(DRE->getDecl())) { + FP = {VD, ""}; + if (VD->getType().isFatPtrType() && CurrStatus.count(FP)) { + // reset the offset check status when p is self-offset + CurrStatus[FP] = static_cast( + static_cast(CurrStatus[FP]) & + static_cast(FatPtrCheckStatus::KeyCheckedOnly)); + } + } + } else if (MemberExpr *ME = getMemberExprFromExpr(UO->getSubExpr())) { + // handle (p->a)++, if p->a is fat ptr. + if (auto FD = dyn_cast(ME->getMemberDecl())) { + if (FD->getType().isFatPtrType()) { + // reset the offset check status when p is self-offset + VisitMEForFieldPath(ME, FP); + if (CurrStatus.count(FP)) { + CurrStatus[FP] = static_cast( + static_cast(CurrStatus[FP]) & + static_cast(FatPtrCheckStatus::KeyCheckedOnly)); + } + } + } + } + } +} + +void TransferFunctions::VisitMemberExpr(MemberExpr *ME) { + if (ME->isArrow()) { + FatPtrVar FP; + if (DeclRefExpr *DRE = getDREFromExpr(ME->getBase())) { + if (VarDecl *VD = dyn_cast(DRE->getDecl())) { + FP = {VD, ""}; + if (VD->getType().isFatPtrType() && CurrStatus.count(FP)) { + // Label the check kind in the sema phase, for member expression: p -> a; + DRE->FatPtrCheckedStatus = static_cast(CurrStatus[FP]); + // update the redundant check status + CurrStatus[FP] = FatPtrCheckStatus::BothChecked; + } + } + } else if (MemberExpr *SubME = getMemberExprFromExpr(ME->getBase())) { + // handle p->a->b, if p->a is fat ptr. + if (auto FD = dyn_cast(SubME->getMemberDecl())) { + if (FD->getType().isFatPtrType()) { + VisitMEForFieldPath(SubME, FP); + if (CurrStatus.count(FP)) { + SubME->FatPtrCheckedStatus = static_cast(CurrStatus[FP]); + CurrStatus[FP] = FatPtrCheckStatus::BothChecked; + } + } + } + } + } +} + +// This function judge a user-defined functions in the +// current module may directly or indirectly frees heap memory. +// It conservertively assuems that a function call may free heap objects if it +// +// 1. is an indirect call that is not resolved by compiler or +// 2. calls to potentially unsafe functions +// +// For the second condition, we have a whitelist that contains all the functions +// that we are sure will not free memory, such as malloc. + +bool TransferFunctions::mayFreeCallExpr(CallExpr *CE) { + // Check if it is an direct call, for indirect call, we assuems it may free + if (FunctionDecl *CalleeDecl = CE->getDirectCallee()) { + // Add the CalleeDecl to the call graph + CG.addToCallGraph(CalleeDecl); + CallGraphNode *Node = CG.getNode(CalleeDecl); + if (!Node || Node->empty()) { + // Callee Function definition is in the current module, and has no other function call + if (CalleeDecl->hasBody()) { + return false; + } + // Check if the function is in the whitelist + return MayFreeFnWL.find(CalleeDecl->getNameAsString()) == MayFreeFnWL.end(); + } else { + // Check if the CalleeDecl calls any function that may free heap objects + bool mayFree = false; + for (CallGraphNode::iterator It = Node->begin(); It != Node->end(); + ++It) { + CallGraphNode *CalleeNode = It->Callee; + if (!CalleeNode) + continue; + const FunctionDecl *CalleeFD = + dyn_cast_or_null(CalleeNode->getDecl()); + if (!CalleeFD || MayFreeFnWL.find(CalleeFD->getNameAsString()) == MayFreeFnWL.end()) { + if (CalleeDecl->getNameAsString() != CalleeFD->getNameAsString()){ + mayFree = true; + break; + } + } + } + return mayFree; + } + } + return true; +} + +// For mayfree function call, reset the check status to Unchecked for all the fat pointers +// More optimizations can be done here to reduce resets, including: +// 1. more accurate mayfree analysis +// 2. reset only some pointers through alias analysis. +void TransferFunctions::VisitCallExpr(CallExpr *CE) { + // If the function call may free pointers, reset all fat pointers check status + if (mayFreeCallExpr(CE)) { + for (auto &Entry : CurrStatus) { + Entry.second = FatPtrCheckStatus::Unchecked; + } + return; + } + + // If the function call does not free pointers, check if the arguments are fat pointers + for (unsigned i = 0; i < CE->getNumArgs(); i++) { + Expr *Arg = CE->getArg(i)->IgnoreImpCasts(); + FatPtrVar FP; + if (auto *DRE = dyn_cast(Arg)) { + // If the argument is a fat pointer variable, change its check status to key only + if (VarDecl *VD = dyn_cast(DRE->getDecl())) { + FP = {VD, ""}; + if (VD->getType().isFatPtrType() && CurrStatus.count(FP)) { + CurrStatus[FP] = static_cast( + static_cast(CurrStatus[FP]) & + static_cast(FatPtrCheckStatus::KeyCheckedOnly)); + } + } + } else if(auto *ME = dyn_cast(Arg)) { + // handle func(p->a), if p->a is fat ptr. + if (auto FD = dyn_cast(ME->getMemberDecl())) { + if (FD->getType().isFatPtrType()) { + VisitMEForFieldPath(ME, FP); + if (CurrStatus.count(FP)) { + CurrStatus[FP] = static_cast( + static_cast(CurrStatus[FP]) & + static_cast(FatPtrCheckStatus::KeyCheckedOnly)); + } + } + } + } + } +} + +// Reset the redundant check status to BothChecked for the fat pointers +// which are used after CStyleCast. +void TransferFunctions::VisitCStyleCastExpr(CStyleCastExpr *CSCE) { + FatPtrVar FP; + if (DeclRefExpr *DRE = getDREFromExpr(CSCE->getSubExpr())) { + if (VarDecl *VD = dyn_cast(DRE->getDecl())) { + FP = {VD, ""}; + if (VD->getType().isFatPtrType() && CurrStatus.count(FP)) { + // Label the check kind in the sema phase, for CStyleCast expression: (int *)p; + DRE->FatPtrCheckedStatus = static_cast(CurrStatus[FP]); + // update the redundant check status + CurrStatus[FP] = FatPtrCheckStatus::BothChecked; + } + } + } else if (auto *ME = getMemberExprFromExpr(CSCE->getSubExpr())) { + // handle (int *)(p->a), if p->a is fat ptr. + if (auto FD = dyn_cast(ME->getMemberDecl())) { + if (FD->getType().isFatPtrType()) { + VisitMEForFieldPath(ME, FP); + if (CurrStatus.count(FP)) { + ME->FatPtrCheckedStatus = static_cast(CurrStatus[FP]); + CurrStatus[FP] = FatPtrCheckStatus::BothChecked; + } + } + } + } +} + +void TransferFunctions::VisitArraySubscriptExpr(ArraySubscriptExpr *ASE) { + FatPtrVar FP; + if (DeclRefExpr *DRE = getDREFromExpr(ASE->getLHS())) { + if (VarDecl *VD = dyn_cast(DRE->getDecl())) { + FP = {VD, ""}; + if (VD->getType().isFatPtrType() && CurrStatus.count(FP)) { + // the offset check is not redundant, must be remained for array subscript expression: p[i]; + DRE->FatPtrCheckedStatus = + static_cast(CurrStatus[FP]) & + static_cast(FatPtrCheckStatus::KeyCheckedOnly); + // update the redundant check status to KeyCheckedOnly + CurrStatus[FP] = FatPtrCheckStatus::BothChecked; + } + } + } else if (auto *ME = getMemberExprFromExpr(ASE->getLHS())) { + // handle p->a[i], if p->a is fat ptr. + if (auto FD = dyn_cast(ME->getMemberDecl())) { + if (FD->getType().isFatPtrType()) { + VisitMEForFieldPath(ME, FP); + if (CurrStatus.count(FP)) { + ME->FatPtrCheckedStatus = + static_cast(CurrStatus[FP]) & + static_cast(FatPtrCheckStatus::KeyCheckedOnly); + CurrStatus[FP] = FatPtrCheckStatus::BothChecked; + } + } + } + } +} + +// Traverse all blocks of cfg to collect all fat pointers used, +// including local and global variable and parameters. +// Init check status of these pointers. +void FatPtrCheckImpl::initStatus(const CFG &cfg, ASTContext &ctx) { + const CFGBlock *entry = &cfg.getEntry(); + for (const CFGBlock *B : cfg.const_nodes()) { + if (B != entry && B != &cfg.getExit() && !B->succ_empty() && + !B->pred_empty()) { + for (CFGBlock::const_iterator it = B->begin(), ei = B->end(); it != ei; + ++it) { + const CFGElement &elem = *it; + if (elem.getAs()) { + Stmt *S = const_cast(elem.castAs().getStmt()); + FatPtrVar FP; + if (auto DRE = dyn_cast(S)) { + if (VarDecl *VD = dyn_cast(DRE->getDecl())) + if (VD->getType().isFatPtrType()) { + FP = {VD, ""}; + BBOutStatus[entry][FP] = FatPtrCheckStatus::Unchecked; + } + } else if (auto ME = dyn_cast(S)) { + if (FieldDecl *FD = dyn_cast(ME->getMemberDecl())) { + if (FD->getType().isFatPtrType()) { + VisitMEForFieldPath(ME, FP); + BBOutStatus[entry][FP] = FatPtrCheckStatus::Unchecked; + } + } + } + } + } + } + } +} + +FatPtrVarCheckStatus +FatPtrCheckImpl::mergePredStatus(FatPtrVarCheckStatus currStatus, + FatPtrVarCheckStatus predStatus) { + if (currStatus.empty()) + return predStatus; + for (auto predStatusOfFP : predStatus) { + FatPtrVar FV = predStatusOfFP.first; + FatPtrCheckStatus predKind = predStatusOfFP.second; + if (currStatus.count(FV)) { + FatPtrCheckStatus currKind = currStatus[FV]; + currStatus[FV] = static_cast( + static_cast(currKind) & static_cast(predKind)); + } else { + currStatus[FV] = predKind; + } + } + return currStatus; +} + +FatPtrVarCheckStatus FatPtrCheckImpl::runOnBlock(const CFGBlock *block, + FatPtrVarCheckStatus &status, + ASTContext &ctx, + const FunctionDecl &fd, + ParentMap &PM) { + TransferFunctions TF(*this, block, status, ctx, fd, PM); + + for (CFGBlock::const_iterator it = block->begin(), ei = block->end(); + it != ei; ++it) { + const CFGElement &elem = *it; + if (elem.getAs()) { + const Stmt *S = elem.castAs().getStmt(); + TF.Visit(const_cast(S)); + } + } + + return status; +} + +void clang::runFatPtrReduntantCheck(const FunctionDecl &fd, const CFG &cfg, + AnalysisDeclContext &ac, ASTContext &ctx) { + // The analysis currently has scalability issues for very large CFGs. + // Bail out if it looks too large. + if (cfg.getNumBlockIDs() > 300000) + return; + + FatPtrCheckImpl FCI; + FCI.initStatus(cfg, ctx); + + // Proceed with the worklist. + ForwardDataflowWorklist worklist(cfg, ac); + const CFGBlock *entry = &cfg.getEntry(); + for (const CFGBlock *B : cfg.const_reverse_nodes()) + if (B != entry && !B->pred_empty()) + worklist.enqueueBlock(B); + + while (const CFGBlock *block = worklist.dequeue()) { + // record the last check status of the current block + FatPtrVarCheckStatus lastCurrStatus = FCI.BBLastStatus[block]; + // get the check status of the pred block + FatPtrVarCheckStatus currValStatus, predValStatus; + for (CFGBlock::const_pred_iterator it = block->pred_begin(), + ei = block->pred_end(); + it != ei; ++it) { + if (const CFGBlock *pred = *it) { + predValStatus = FCI.BBOutStatus[pred]; + currValStatus = FCI.mergePredStatus(currValStatus, predValStatus); + } + } + + FatPtrVarCheckStatus BBOutVal = + FCI.runOnBlock(block, currValStatus, ctx, fd, ac.getParentMap()); + FCI.BBOutStatus[block] = BBOutVal; + + // If the value has changed, add the successors in the worklist. + // For While-Stmt, the stmst in the block may change the redundant check status of some fat pointers. + // So, it will be added into the worklist more than once until the analysis result remains unchanged. + if (lastCurrStatus != BBOutVal) { + FCI.BBLastStatus[block] = BBOutVal; + worklist.enqueueSuccessors(block); + } + } +} + +#endif diff --git a/clang/lib/Analysis/CMakeLists.txt b/clang/lib/Analysis/CMakeLists.txt index 2695d5700731..97bcac8898d0 100644 --- a/clang/lib/Analysis/CMakeLists.txt +++ b/clang/lib/Analysis/CMakeLists.txt @@ -6,6 +6,7 @@ set(LLVM_LINK_COMPONENTS add_clang_library(clangAnalysis AnalysisDeclContext.cpp BodyFarm.cpp + BSCFatPtrCheck.cpp BSCNullabilityCheck.cpp BSCOwnership.cpp BSCBorrowCheck.cpp diff --git a/clang/lib/Headers/bsc_include/bsc_fat_ptr.hbs b/clang/lib/Headers/bsc_include/bsc_fat_ptr.hbs index e973722e54b0..5b1c32a1cb87 100644 --- a/clang/lib/Headers/bsc_include/bsc_fat_ptr.hbs +++ b/clang/lib/Headers/bsc_include/bsc_fat_ptr.hbs @@ -111,7 +111,7 @@ void checked_free(_FatPtr ptr) { free(phead); } -static void _check_version( +static __always_inline void _check_version( void *raw_ptr, uint32_t key_version, int32_t offset, char *file_name, int line_no, char *func_name) { // if key_version == 0, means this fat ptr is from a raw ptr, @@ -126,7 +126,7 @@ static void _check_version( } } -static void _check_offset( +static __always_inline void _check_offset( int32_t bytes, void *raw_ptr, uint32_t key_version, int32_t offset, char *file_name, int line_no, char *func_name) { // if key_version == 0, means this fat ptr is from a raw ptr, @@ -141,4 +141,21 @@ static void _check_offset( } } +static __always_inline void _check_version_and_offset( + int32_t bytes,void *raw_ptr, uint32_t key_version, int32_t offset, + char *file_name, int line_no, char *func_name) { + // if key_version == 0, means this fat ptr is from a raw ptr, skip both check + // if key_version == 1, means this fat ptr points to a global array, skip offset check + // we should skip check version. + if (key_version != 0) { + const uint8_t *head = (const uint8_t *)raw_ptr - offset - 8; + const struct _AllocationUnit *phead = (const struct _AllocationUnit *)head; + if (key_version != 1 && key_version != phead->lock_version) + _report_error(UseAfterFree, file_name, line_no, func_name); + int32_t new_offset = offset + bytes; + if (phead->size != 0 &&new_offset > phead->size) + _report_error(OutOfBoundsAccess, file_name, line_no, func_name); + } +} + #endif /* BSC_FAT_PTR_HBS */ \ No newline at end of file diff --git a/clang/lib/Sema/BSC/SemaBSCFatPtr.cpp b/clang/lib/Sema/BSC/SemaBSCFatPtr.cpp index 8c3494be693d..563545220405 100644 --- a/clang/lib/Sema/BSC/SemaBSCFatPtr.cpp +++ b/clang/lib/Sema/BSC/SemaBSCFatPtr.cpp @@ -550,6 +550,7 @@ private: Expr *BuildFatPtrCall(SourceLocation SLoc, std::string FDName, llvm::SmallVector &Args); MemberExpr *BuildMemberRawPtrAndInsertCheckCallForFatPtr(Expr *FatPtrBaseE); + Expr *BuildFatPtrCStyleCastExpr(SourceLocation SLoc, QualType ToType, Expr *SubExpr); Expr *BuildFatPtrCompoundLiteralExprForRawPtr(Expr *RawPtrE, QualType QT); Expr *BuildFatPtrCompoundLiteralExpr(Expr *FirstInitE, Expr *SecondInitE, Expr *ThirdInitE, QualType QT); @@ -1248,7 +1249,7 @@ ExprResult TransformFatPtr::TransformCStyleCastExpr(CStyleCastExpr *CSCE) { SourceLocation SLoc = CSCE->getBeginLoc(); QualType QT = CSCE->getType(); Expr *SubE = CSCE->getSubExpr(); - QualType SubQT = SubE->getType(); + QualType SubQT = SubE->getType(); if (QT->isPointerType() && !QT.isFatPtrType() && SubQT.isFatPtrType()) { // desuagr `(int*)fat_p` to `(int*)fat_p.raw_ptr` CSCE->setCastKind(CK_NoOp); @@ -1268,6 +1269,15 @@ ExprResult TransformFatPtr::TransformCStyleCastExpr(CStyleCastExpr *CSCE) { return ImplicitCastExpr::Create(SemaRef.Context, QT, CK_NoOp, FatPtrE, nullptr, VK_PRValue, FPOptionsOverride()); } else if (QT.isFatPtrType() && SubQT.isFatPtrType()) { + if (DesugarToMemberPtr) { + QualType ToPointerTy = + SemaRef.Context.getPointerType(QT.getFatPtrPointeeType()); + Expr *Res = BaseTransform::TransformExpr(SubE).get(); + Res = ImplicitCastExpr::Create(SemaRef.Context, ToPointerTy, + CK_NoOp, Res, nullptr, VK_PRValue, + FPOptionsOverride()); + return Res; + } return BaseTransform::TransformExpr(SubE).get(); } CSCE->setSubExpr(BaseTransform::TransformExpr(SubE).get()); @@ -1384,6 +1394,18 @@ ExprResult TransformFatPtr::TransformBinaryOperator(BinaryOperator *BO) { QualType LHSQT = LHS->getType(); QualType RHSQT = RHS->getType(); BinaryOperator::Opcode Op = BO->getOpcode(); + if (Op == BO_LAnd || Op == BO_LOr) { + if (LHSQT.isFatPtrType() && isa(LHS)) { + // `p1 && Expr2` should be desugared to `p1.raw_ptr && ` + DesugarToMemberPtr = true; + BO->setLHS(BaseTransform::TransformExpr(LHS).get()); + } + if (RHSQT.isFatPtrType() && isa(RHS)) { + // `Expr1 && p2` should be desugared to `p2.raw_ptr && ` + DesugarToMemberPtr = true; + BO->setRHS(BaseTransform::TransformExpr(RHS).get()); + } + } if ((Op == BO_Sub || BO->isComparisonOp()) && LHSQT.isFatPtrType() && RHSQT.isFatPtrType()) { // Substraction between two fat ptr @@ -1450,7 +1472,7 @@ ExprResult TransformFatPtr::HandleBOFatPtrAddOrSubInteger(BinaryOperator *BO) { Expr *RHS = BaseTransform::TransformExpr(BO->getRHS()).get(); Expr *FirstInitE = SemaRef.CreateBuiltinBinOp(SLoc, Op, LHS, RHS).get(); - if (auto MemberFatPtr = dyn_cast(LHS->IgnoreImpCasts())) { + if (auto MemberFatPtr = dyn_cast(LHS->IgnoreParenImpCasts())) { Expr *FatPtrBase = MemberFatPtr->getBase(); Expr *SecondInitE = BuildMemberForFatPtr(FatPtrBase, "key_version"); Expr *ThirdInitLHS = BuildMemberForFatPtr(FatPtrBase, "offset"); @@ -1860,6 +1882,40 @@ TransformFatPtr::DesugarFatPtrType(SourceLocation SLoc, QualType T) { return T; } +Expr *TransformFatPtr::BuildFatPtrCStyleCastExpr(SourceLocation SLoc, QualType ToType, Expr *SubExpr) { + QualType FromType = SubExpr->getType(); + + if (FromType.isFatPtrType() && ToType.isFatPtrType()) { + // 1. Extract raw_ptr and perform raw pointer CStyleCastExpr + if (auto ICE = dyn_cast(SubExpr)) { + SubExpr = ICE->getSubExpr(); + } + SubExpr = BaseTransform::TransformExpr(SubExpr).get(); + Expr *RawPtr = BuildMemberForFatPtr(SubExpr, "raw_ptr"); + QualType ToPointerTy = + SemaRef.Context.getPointerType(ToType.getFatPtrPointeeType()); + Expr *CastedRawPtr = + SemaRef + .BuildCStyleCastExpr( + SLoc, SemaRef.Context.getTrivialTypeSourceInfo(ToPointerTy), + SLoc, RawPtr) + .get(); + + // 2. Pass key_version and offset directly + Expr *KeyVersion = BuildMemberForFatPtr(SubExpr, "key_version"); + Expr *Offset = BuildMemberForFatPtr(SubExpr, "offset"); + + // 3. Assemble the target fat pointer, as _FatPtr + QualType QT = DesugarFatPtrType(SLoc, ToType); + Expr *CLE = BuildFatPtrCompoundLiteralExpr(CastedRawPtr, KeyVersion, Offset, QT); + CLE = SemaRef.DefaultFunctionArrayLvalueConversion(CLE).get(); + return CLE; + } + + // Otherwise, keep the original transformation + return BaseTransform::TransformExpr(SubExpr).get(); +} + // Build CallExpr for _check_version, _check_offset or _new_version_number. Expr *TransformFatPtr::BuildFatPtrCall(SourceLocation SLoc, std::string FDName, llvm::SmallVector &Args) { @@ -1941,6 +1997,23 @@ MemberExpr *TransformFatPtr::BuildMemberForFatPtr(Expr *FatPtrBaseE, MemberExpr *TransformFatPtr::BuildMemberRawPtrAndInsertCheckCallForFatPtr( Expr *FatPtrBaseE) { SourceLocation SLoc = FatPtrBaseE->getBeginLoc(); + if ((InsertCheckVersionCall || InsertCheckOffsetCall.first) && FatPtrBaseE->FatPtrCheckedStatus) { + if (FatPtrBaseE->FatPtrCheckedStatus == 1 || FatPtrBaseE->FatPtrCheckedStatus == 3) { + InsertCheckVersionCall = false; + } + if (FatPtrBaseE->FatPtrCheckedStatus == 2 || FatPtrBaseE->FatPtrCheckedStatus == 3) { + InsertCheckOffsetCall.first = false; + InsertCheckOffsetCall.second = nullptr; + } + } + if (InsertCheckOffsetCall.first && InsertCheckVersionCall) { + llvm::SmallVector Args{InsertCheckOffsetCall.second, FatPtrBaseE}; + Expr *CE = BuildFatPtrCall(SLoc, "_check_version_and_offset", Args); + CheckCallExpr.push(std::make_pair(FatPtrBaseE, CE)); + InsertCheckOffsetCall.first = false; + InsertCheckOffsetCall.second = nullptr; + InsertCheckVersionCall = false; + } if (InsertCheckOffsetCall.first) { // Build CallExpr to check offset. llvm::SmallVector Args{InsertCheckOffsetCall.second, diff --git a/clang/lib/Sema/BSC/SemaDeclBSC.cpp b/clang/lib/Sema/BSC/SemaDeclBSC.cpp index 979b2db10f44..e54957c42bb2 100644 --- a/clang/lib/Sema/BSC/SemaDeclBSC.cpp +++ b/clang/lib/Sema/BSC/SemaDeclBSC.cpp @@ -16,6 +16,7 @@ #include "clang/AST/BSC/WalkerBSC.h" #include "clang/Analysis/Analyses/BSCBorrowCheck.h" #include "clang/Analysis/Analyses/BSCNullabilityCheck.h" +#include "clang/Analysis/Analyses/BSCFatPtrCheck.h" #include "clang/Analysis/Analyses/BSCOwnership.h" #include "clang/Analysis/AnalysisDeclContext.h" #include "clang/Sema/Sema.h" @@ -220,6 +221,9 @@ void Sema::BSCDataflowAnalysis(const Decl *D, bool EnableOwnershipCheck, runBorrowCheck(*FD, *AC.getCFG(), BorrowCheckReporter, Context); } } + if (getLangOpts().EnableFatPtr) { + runFatPtrReduntantCheck(*FD, *cfg, AC, Context); + } } } #endif \ No newline at end of file diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 92ed21bdec69..9c7a7f12d004 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -13485,7 +13485,7 @@ QualType Sema::CheckCompareOperands(ExprResult &LHS, ExprResult &RHS, } #if ENABLE_BSC - if (getLangOpts().BSC && RHSType->isNullPtrType() && + if (getLangOpts().BSC && (RHSType.isFatPtrType() || RHSType->isNullPtrType()) && (LHSType->isPointerType() || LHSType.isFatPtrType())) return computeResultTy(); #endif @@ -14034,7 +14034,16 @@ inline QualType Sema::CheckLogicalOperands(ExprResult &LHS, ExprResult &RHS, RHS = UsualUnaryConversions(RHS.get()); if (RHS.isInvalid()) return QualType(); - +#if ENABLE_BSC + if (getLangOpts().BSC && getLangOpts().EnableFatPtr) { + QualType LT = LHS.get()->getType(); + QualType RT = RHS.get()->getType(); + if ((LT->isScalarType() || LT.isFatPtrType()) && + (RT->isScalarType() || RT.isFatPtrType())) { + return Context.IntTy; + } + } +#endif if (!LHS.get()->getType()->isScalarType() || !RHS.get()->getType()->isScalarType()) return InvalidOperands(Loc, LHS, RHS); diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index 4a98035b77d9..a97fd9eaefde 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -7559,6 +7559,13 @@ ExprResult Sema::ActOnStartCXXMemberReference(Scope *S, Expr *Base, CTypes.insert(Context.getCanonicalType(BaseType)); while (BaseType->isRecordType()) { +#if ENABLE_BSC + if (getLangOpts().BSC && getLangOpts().EnableFatPtr && + BaseType.isFatPtrRecordType()) { + BaseType = BaseType.getFatPtrPointeeType(); + break; + } +#endif if (OperatorArrows.size() >= getLangOpts().ArrowDepth) { Diag(OpLoc, diag::err_operator_arrow_depth_exceeded) << StartingType << getLangOpts().ArrowDepth << Base->getSourceRange(); -- Gitee From 6980f46f51645c069d27c17e26793c219c48403f Mon Sep 17 00:00:00 2001 From: liuxinyi Date: Wed, 26 Feb 2025 14:57:49 +0800 Subject: [PATCH 05/29] modify bsc_fat_ptr.hbs and some bugfix 1. modify checked_malloc and checked_free from generic funciton to normal function 2. add cast_two_level_fat_to_raw api 3. add desugar for CStyleCastExpr from a fat ptr type to another fat ptr type 4. desugar p++ where p is fat ptr --- clang/include/clang/Sema/Sema.h | 1 - clang/lib/AST/DeclPrinter.cpp | 7 +- clang/lib/AST/Expr.cpp | 7 + clang/lib/AST/StmtPrinter.cpp | 10 +- clang/lib/Headers/bsc_include/bsc_fat_ptr.hbs | 27 +- clang/lib/Sema/BSC/SemaBSCFatPtr.cpp | 453 ++++++++++-------- clang/lib/Sema/BSC/SemaDeclBSC.cpp | 13 +- clang/lib/Sema/SemaDecl.cpp | 4 +- clang/lib/Sema/SemaExpr.cpp | 23 +- clang/lib/Sema/SemaStmt.cpp | 6 - .../FatPtr/self_inc_or_dec_of_fat_ptr.cbs | 22 - .../heap/array_out_of_bounds1.cbs | 2 +- .../heap/array_out_of_bounds10.cbs | 2 +- .../heap/array_out_of_bounds11.cbs | 4 +- .../heap/array_out_of_bounds12.cbs | 2 +- .../heap/array_out_of_bounds13.cbs | 2 +- .../heap/array_out_of_bounds14.cbs | 2 +- .../heap/array_out_of_bounds15.cbs | 4 +- .../heap/array_out_of_bounds16.cbs | 2 +- .../heap/array_out_of_bounds17.cbs | 2 +- .../heap/array_out_of_bounds18.cbs | 6 +- .../heap/array_out_of_bounds19.cbs | 4 +- .../heap/array_out_of_bounds2.cbs | 2 +- .../heap/array_out_of_bounds20.cbs | 14 + .../heap/array_out_of_bounds21.cbs | 12 + .../heap/array_out_of_bounds22.cbs | 16 + .../heap/array_out_of_bounds23.cbs | 16 + .../heap/array_out_of_bounds24.cbs | 16 + .../heap/array_out_of_bounds3.cbs | 2 +- .../heap/array_out_of_bounds4.cbs | 2 +- .../heap/array_out_of_bounds5.cbs | 2 +- .../heap/array_out_of_bounds6.cbs | 2 +- .../heap/array_out_of_bounds7.cbs | 2 +- .../heap/array_out_of_bounds8.cbs | 2 +- .../heap/array_out_of_bounds9.cbs | 2 +- .../heap/buffer_overflow.cbs | 2 +- .../heap/double_free1.cbs | 6 +- .../heap/double_free2.cbs | 6 +- .../heap/double_free3.cbs | 8 +- .../heap/double_free4.cbs | 6 +- .../heap/use_after_free1.cbs | 4 +- .../heap/use_after_free2.cbs | 4 +- .../heap/use_after_free3.cbs | 4 +- .../heap/use_after_free4.cbs | 6 +- .../heap/use_after_free5.cbs | 8 +- .../heap/use_after_free6.cbs | 4 +- .../heap/use_after_free7.cbs | 4 +- .../heap/use_after_free8.cbs | 4 +- clang/test/BSC/Positive/FatPtr/heap.cbs | 77 ++- .../Positive/FatPtr/multi_level_pointer.cbs | 14 +- .../perform_anaysis/deref_loop/deref_loop.cbs | 39 -- .../perform_anaysis/olden/bisort/bitonic.c | 303 ------------ .../olden/bisort_bsc/bitonic.cbs | 302 ------------ .../perform_anaysis/olden/treeadd/node.c | 233 --------- .../perform_anaysis/olden/treeadd/tree.h | 25 - .../olden/treeadd_bsc/node.cbs | 193 -------- .../olden/treeadd_bsc/tree.hbs | 29 -- clang/test/BSC/Positive/FatPtr/stack.cbs | 10 + 58 files changed, 527 insertions(+), 1459 deletions(-) delete mode 100644 clang/test/BSC/Negative/FatPtr/self_inc_or_dec_of_fat_ptr.cbs create mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds20.cbs create mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds21.cbs create mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds22.cbs create mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds23.cbs create mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds24.cbs delete mode 100644 clang/test/BSC/Positive/FatPtr/perform_anaysis/deref_loop/deref_loop.cbs delete mode 100644 clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/bisort/bitonic.c delete mode 100644 clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/bisort_bsc/bitonic.cbs delete mode 100644 clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd/node.c delete mode 100644 clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd/tree.h delete mode 100644 clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd_bsc/node.cbs delete mode 100644 clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd_bsc/tree.hbs diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 4af40cb08a0c..877b67177dba 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -3227,7 +3227,6 @@ public: bool CheckFatQualTypeAssignment(QualType LHSType, QualType RHSType); bool CheckFatFunctionPointerType(QualType LHSType, Expr *RHSExpr); bool HasDiffFatTypeAtBothFunction(QualType LHSType, QualType RHSType); - bool CheckSelfIncOrDecOfFatPtr(Expr *E); void DesugarFunctionDeclWithFatPtr(FunctionDecl *FD); void DesugarRecordDeclWithFatPtr(RecordDecl *RD); void DesugarGlobalVarDeclWithFatPtr(VarDecl *VD); diff --git a/clang/lib/AST/DeclPrinter.cpp b/clang/lib/AST/DeclPrinter.cpp index c7cd98eebf87..22075632e80d 100644 --- a/clang/lib/AST/DeclPrinter.cpp +++ b/clang/lib/AST/DeclPrinter.cpp @@ -1057,7 +1057,12 @@ void DeclPrinter::VisitVarDecl(VarDecl *D) { QualType T = D->getTypeSourceInfo() ? D->getTypeSourceInfo()->getType() : D->getASTContext().getUnqualifiedObjCPointerType(D->getType()); - +#if ENABLE_BSC + if (Policy.RewriteBSC && Context.getLangOpts().EnableFatPtr && + T.isFatPtrType()) { + T = D->getType(); + } +#endif if (!Policy.SuppressSpecifiers) { StorageClass SC = D->getStorageClass(); if (SC != SC_None) diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index 025a70a754fa..1cd4e8ceb469 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -2002,9 +2002,16 @@ ImplicitCastExpr *ImplicitCastExpr::Create(const ASTContext &C, QualType T, PathSize, FPO.requiresTrailingStorage())); // Per C++ [conv.lval]p3, lvalue-to-rvalue conversions on class and // std::nullptr_t have special semantics not captured by CK_LValueToRValue. +#if ENABLE_BSC + assert((Kind != CK_LValueToRValue || + (!(T->isNullPtrType() || T->getAsCXXRecordDecl()) || + T.isFatPtrType())) && + "invalid type for lvalue-to-rvalue conversion"); +#else assert((Kind != CK_LValueToRValue || !(T->isNullPtrType() || T->getAsCXXRecordDecl())) && "invalid type for lvalue-to-rvalue conversion"); +#endif ImplicitCastExpr *E = new (Buffer) ImplicitCastExpr(T, Kind, Operand, PathSize, FPO, VK); if (PathSize) diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp index 444b2e161204..570d7bee40f4 100644 --- a/clang/lib/AST/StmtPrinter.cpp +++ b/clang/lib/AST/StmtPrinter.cpp @@ -1853,8 +1853,14 @@ void StmtPrinter::VisitConvertVectorExpr(ConvertVectorExpr *Node) { void StmtPrinter::VisitInitListExpr(InitListExpr* Node) { if (Node->getSyntacticForm()) { - Visit(Node->getSyntacticForm()); - return; +#if ENABLE_BSC + if (!(Policy.RewriteBSC && Context->getLangOpts().EnableFatPtr)) { +#endif + Visit(Node->getSyntacticForm()); + return; +#if ENABLE_BSC + } +#endif } OS << "{"; diff --git a/clang/lib/Headers/bsc_include/bsc_fat_ptr.hbs b/clang/lib/Headers/bsc_include/bsc_fat_ptr.hbs index 5b1c32a1cb87..e70f88cbcf20 100644 --- a/clang/lib/Headers/bsc_include/bsc_fat_ptr.hbs +++ b/clang/lib/Headers/bsc_include/bsc_fat_ptr.hbs @@ -36,7 +36,8 @@ enum _FatPtrErrorKind { FreeNotHeadPointer, DoubleFree, UseAfterFree, - OutOfBoundsAccess + OutOfBoundsAccess, + CannotCast }; static void _report_error(enum _FatPtrErrorKind kind, char *file_name, @@ -58,6 +59,9 @@ static void _report_error(enum _FatPtrErrorKind kind, char *file_name, case OutOfBoundsAccess: msg = "Pointer offset exceeds the allocation size"; break; + case CannotCast: + msg = "Current two-level fat pointer cannot be cast to two-level raw pointer"; + break; } fprintf(stderr, "Error: %s!\n", msg); if (kind == UseAfterFree || kind == OutOfBoundsAccess) @@ -80,7 +84,7 @@ static uint32_t _new_version_number() { return atomic_load(&uuid); } -_FatPtr checked_malloc(size_t payload_bytes) { +static _FatPtr checked_malloc(size_t payload_bytes) { if (payload_bytes == 0) _report_error(AllocateZero, 0, 0, 0); size_t all_size = payload_bytes + 8; @@ -89,11 +93,11 @@ _FatPtr checked_malloc(size_t payload_bytes) { uint32_t ver = _new_version_number(); *p = ver; *(p + 1) = payload_bytes; - _FatPtr ptr = { .raw_ptr = (T*)(p + 2), .key_version = ver, .offset = 0 }; + _FatPtr ptr = { .raw_ptr = (void*)(p + 2), .key_version = ver, .offset = 0 }; return ptr; } -void checked_free(_FatPtr ptr) { +static void checked_free(_FatPtr ptr) { if (ptr.raw_ptr == nullptr) return; if (ptr.key_version == 0) { @@ -158,4 +162,19 @@ static __always_inline void _check_version_and_offset( } } +T** cast_two_level_fat_to_raw (T *fat *fat ptr) { + _FatPtr<_FatPtr> ptr_copy = ptr; + if (ptr_copy.key_version == 0) + _report_error(CannotCast, 0, 0, 0); + const uint8_t *head = (const uint8_t *)ptr_copy.raw_ptr - ptr_copy.offset - 8; + const struct _AllocationUnit *phead = (const struct _AllocationUnit *)head; + size_t fat_ptr_size = sizeof(void *) + 8; + if (phead->size < fat_ptr_size) + _report_error(CannotCast, 0, 0, 0); + int element_num = phead->size / fat_ptr_size; + T **raw = (T **)malloc(element_num * sizeof(T *)); + for (int i = 0; i < element_num; i++) + raw[i] = (T *)ptr_copy.raw_ptr[i]; + return raw; +} #endif /* BSC_FAT_PTR_HBS */ \ No newline at end of file diff --git a/clang/lib/Sema/BSC/SemaBSCFatPtr.cpp b/clang/lib/Sema/BSC/SemaBSCFatPtr.cpp index 563545220405..21c3ed3c4eb0 100644 --- a/clang/lib/Sema/BSC/SemaBSCFatPtr.cpp +++ b/clang/lib/Sema/BSC/SemaBSCFatPtr.cpp @@ -184,30 +184,29 @@ public: // 1) while (*p > 0) foo(); -> while (*p > 0) { foo(); } // 2) if (*p > 0 ) foo(); else foo(); // ->if (*p > 0) { foo(); } else { foo(); } -// 4. for SelfIncExpr or SelfDecExpr of FatPtr, we split it -// into two expr. For example: -// 1) int a = *p++; -> int a = *p; p = p + 1; -// 2) int a = *++p; -> p = p + 1; int a = *p; +// 4. for SelfIncExpr or SelfDecExpr of FatPtr, we build a +// CommaExpr to replace it, for example: +// 1) int a = *p++; -> int a = *(p = p + 1, p - 1); +// 2) int a = *++p; -> int a = *(p = p + 1, p); +// 3) int a = *p--; -> int a = *(p = p - 1, p + 1); +// 4) int a = *--p; -> int a = *(p = p - 1, p); +// 5. for CompoundAssignOperator of FatPtr, build an assignment +// BinaryOperator to replace it, for example +// `p += 1` should be preprocessed to `p = p + 1` // It should be noted that in this preprocess stage, we don't // desugar fat ptr pointer to _FatPtr struct, but only do some // stmt replacement. class FatPtrPreprocessor : public TreeTransform { typedef TreeTransform BaseTransform; - - // whether need to create a temporary variable to replace - // the current CallExpr. - bool NeedToReplace = false; - - // The first element of pair records if current SelfIncExpr or - // SelfDecExpr is postfix or not: - // 1) true means `p++` or `p--`, then the second element of pair - // will be inserted behind this SelfIncExpr or SelfDecExpr; - // 2) false means `++p` or `--p`, then the second element of pair - // will be inserted before this SelfIncExpr or SelfDecExpr. - std::pair IncOrDecFatPtrExpr = std::make_pair(false, nullptr); + FunctionDecl *FD; + unsigned TempVarCounter = 0; + llvm::SmallVector CurrStmts; public: - FatPtrPreprocessor(Sema &SemaRef) : BaseTransform(SemaRef) {} + llvm::SmallSet PreprocessedSelfIncOrDecExpr; + + FatPtrPreprocessor(Sema &SemaRef, FunctionDecl *Fd) + : BaseTransform(SemaRef), FD(Fd) {} StmtResult TransformCompoundStmt(CompoundStmt *CS); StmtResult TransformCompoundStmt(CompoundStmt *CS, bool IsStmtExpr); StmtResult TransformIfStmt(IfStmt *IS); @@ -219,13 +218,15 @@ public: StmtResult TransformDefaultStmt(DefaultStmt *DS); StmtResult TransformDeclStmt(DeclStmt *DS); ExprResult TransformInitListExpr(InitListExpr *ILE); - ExprResult TransformConditionalOperator(ConditionalOperator *CO); ExprResult TransformUnaryOperator(UnaryOperator *UO); ExprResult TransformBinaryOperator(BinaryOperator *BO); + ExprResult TransformCStyleCastExpr(CStyleCastExpr *CSCE); + ExprResult TransformCompoundAssignOperator(CompoundAssignOperator *CAO); ExprResult TransformImplicitCastExpr(ImplicitCastExpr *ICE); private: Stmt *BuildCompoundStmtForIfOrLoopStmtBody(Stmt *Body); + ImplicitCastExpr *ReplaceWithTemporaryVarDecl(Expr *E); }; StmtResult FatPtrPreprocessor::TransformCompoundStmt(CompoundStmt *CS) { @@ -236,7 +237,8 @@ StmtResult FatPtrPreprocessor::TransformCompoundStmt(CompoundStmt *CS, bool IsStmtExpr) { if (CS == nullptr) return CS; - llvm::SmallVector Stmts; + llvm::SmallVector PrevStmts = CurrStmts; + CurrStmts = llvm::SmallVector(); for (Stmt *S : CS->body()) { if (auto DS = dyn_cast(S)) { if (!DS->isSingleDecl()) { @@ -245,41 +247,18 @@ StmtResult FatPtrPreprocessor::TransformCompoundStmt(CompoundStmt *CS, for (auto D : DS->decls()) { DeclStmt *NewDS = new (SemaRef.Context) DeclStmt(DeclGroupRef(D), D->getLocation(), D->getLocation()); - Stmt *NewS = BaseTransform::TransformStmt(NewDS).get(); - if (IncOrDecFatPtrExpr.second) { - if (IncOrDecFatPtrExpr.first) { - Stmts.push_back(NewS); - Stmts.push_back(IncOrDecFatPtrExpr.second); - } else { - Stmts.push_back(IncOrDecFatPtrExpr.second); - Stmts.push_back(NewS); - } - IncOrDecFatPtrExpr.second = nullptr; - } else { - Stmts.push_back(NewS); - } + CurrStmts.push_back(BaseTransform::TransformStmt(NewDS).get()); } continue; } } - Stmt *NewS = BaseTransform::TransformStmt(S).get(); - if (IncOrDecFatPtrExpr.second) { - if (IncOrDecFatPtrExpr.first) { - Stmts.push_back(NewS); - Stmts.push_back(IncOrDecFatPtrExpr.second); - } else { - Stmts.push_back(IncOrDecFatPtrExpr.second); - Stmts.push_back(NewS); - } - IncOrDecFatPtrExpr.second = nullptr; - } else { - Stmts.push_back(NewS); - } + CurrStmts.push_back(BaseTransform::TransformStmt(S).get()); } CompoundStmt *NewCS = CompoundStmt::Create( - SemaRef.Context, Stmts, FPOptionsOverride(), CS->getLBracLoc(), + SemaRef.Context, CurrStmts, FPOptionsOverride(), CS->getLBracLoc(), CS->getRBracLoc(), CS->getSafeSpecifier(), CS->getSafeSpecifierLoc(), CS->getCompSafeZoneSpecifier()); + CurrStmts = PrevStmts; return NewCS; } @@ -342,8 +321,7 @@ StmtResult FatPtrPreprocessor::TransformDefaultStmt(DefaultStmt *DS) { StmtResult FatPtrPreprocessor::TransformDeclStmt(DeclStmt *DS) { assert(DS->isSingleDecl() && "DeclStmt can only contain a Decl here"); - Decl *D = DS->getSingleDecl(); - if (VarDecl *VD = dyn_cast(D)) { + if (VarDecl *VD = dyn_cast(DS->getSingleDecl())) { if (VD->getInit()) VD->setInit(BaseTransform::TransformExpr(VD->getInit()).get()); } @@ -356,48 +334,95 @@ ExprResult FatPtrPreprocessor::TransformInitListExpr(InitListExpr *ILE) { return ILE; } -ExprResult -FatPtrPreprocessor::TransformConditionalOperator(ConditionalOperator *CO) { - Expr *CondExpr = BaseTransform::TransformExpr(CO->getCond()).get(); - Expr *LHS = BaseTransform::TransformExpr(CO->getLHS()).get(); - Expr *RHS = BaseTransform::TransformExpr(CO->getRHS()).get(); - return BaseTransform::RebuildConditionalOperator( - CondExpr, CO->getQuestionLoc(), LHS, CO->getColonLoc(), RHS); -} - ExprResult FatPtrPreprocessor::TransformUnaryOperator(UnaryOperator *UO) { - // 1) int a = *p++; -> int a = *p; p = p + 1; - // 2) int a = *++p; -> p = p + 1; int a = *p; SourceLocation SLoc = UO->getBeginLoc(); Expr *SubE = UO->getSubExpr(); - if ((UO->isPrefix() || UO->isPostfix()) && SubE->getType().isFatPtrType()) { - // Build p = p + 1 for p++/++p, build p = p - 1 for p--/--p - Expr *InitRHS = IntegerLiteral::Create(SemaRef.Context, llvm::APInt(32, 1), - SemaRef.Context.IntTy, SLoc); - Expr *InitE = - SemaRef - .CreateBuiltinBinOp(SLoc, UO->isIncrementOp() ? BO_Add : BO_Sub, - SubE, InitRHS) + QualType SubQT = SubE->getType(); + if ((UO->isPrefix() || UO->isPostfix()) && SubQT.isFatPtrType()) { + SubE = SubE->IgnoreParens(); + bool IsPostfix = UO->isPostfix(); + bool IsInc = UO->isIncrementOp(); + // Build the first expr of CommaExpr: + // 1) p = p + 1 for p++/++p; 2) p = p - 1 for p--/--p; + Expr *One = IntegerLiteral::Create(SemaRef.Context, llvm::APInt(32, 1), + SemaRef.Context.IntTy, SLoc); + Expr *FirstRHS = + SemaRef.CreateBuiltinBinOp(SLoc, IsInc ? BO_Add : BO_Sub, SubE, One) .get(); - IncOrDecFatPtrExpr.first = UO->isPostfix(); - IncOrDecFatPtrExpr.second = - SemaRef.CreateBuiltinBinOp(SLoc, BO_Assign, SubE, InitE).get(); - return ImplicitCastExpr::Create(SemaRef.Context, SubE->getType(), - CK_LValueToRValue, SubE, nullptr, - VK_PRValue, FPOptionsOverride()); - } - UO->setSubExpr(BaseTransform::TransformExpr(SubE).get()); + Expr *FirstE = + SemaRef.CreateBuiltinBinOp(SLoc, BO_Assign, SubE, FirstRHS).get(); + // Build the second expr of CommaExpr: + // 1) p for ++p/--p; 2) p - 1 for p++; 3) p + 1 for p-- + Expr *SecondE = ImplicitCastExpr::Create(SemaRef.Context, SubQT, + CK_LValueToRValue, SubE, nullptr, + VK_PRValue, FPOptionsOverride()); + if (IsPostfix) + SecondE = + SemaRef + .CreateBuiltinBinOp(SLoc, IsInc ? BO_Sub : BO_Add, SecondE, One) + .get(); + // Build CommaExpr and wrap it in a ParenExpr. + Expr *CommaE = + SemaRef.CreateBuiltinBinOp(SLoc, BO_Comma, FirstE, SecondE).get(); + Expr *ParenE = + new (SemaRef.Context) ParenExpr(SLoc, UO->getEndLoc(), CommaE); + PreprocessedSelfIncOrDecExpr.insert(ParenE); + return ParenE; + } + UO->setSubExpr(isa(SubE->IgnoreParens()) && SubQT.isFatPtrType() + ? ReplaceWithTemporaryVarDecl(SubE->IgnoreParens()) + : BaseTransform::TransformExpr(SubE).get()); return UO; } ExprResult FatPtrPreprocessor::TransformBinaryOperator(BinaryOperator *BO) { Expr *LHS = BO->getLHS(); Expr *RHS = BO->getRHS(); - BO->setLHS(BaseTransform::TransformExpr(LHS).get()); - BO->setRHS(BaseTransform::TransformExpr(RHS).get()); + if (BO->isAdditiveOp()) { + BO->setLHS(isa(LHS->IgnoreParens()) && + LHS->getType().isFatPtrType() + ? ReplaceWithTemporaryVarDecl(LHS->IgnoreParens()) + : BaseTransform::TransformExpr(LHS).get()); + BO->setRHS(isa(RHS->IgnoreParens()) && + RHS->getType().isFatPtrType() + ? ReplaceWithTemporaryVarDecl(RHS->IgnoreParens()) + : BaseTransform::TransformExpr(RHS).get()); + } else { + BO->setLHS(BaseTransform::TransformExpr(LHS).get()); + BO->setRHS(BaseTransform::TransformExpr(RHS).get()); + } return BO; } +ExprResult FatPtrPreprocessor::TransformCompoundAssignOperator( + CompoundAssignOperator *CAO) { + BinaryOperator::Opcode Op = CAO->getOpcode(); + if (CAO->getType().isFatPtrType() && + (Op == BO_AddAssign || Op == BO_SubAssign)) { + Expr *LHS = CAO->getLHS(); + Expr *RHS = CAO->getRHS(); + SourceLocation SLoc = CAO->getBeginLoc(); + Expr *NewLHS = LHS; + Expr *NewRHS = SemaRef + .CreateBuiltinBinOp( + SLoc, Op == BO_AddAssign ? BO_Add : BO_Sub, LHS, RHS) + .get(); + Expr *New = + SemaRef.CreateBuiltinBinOp(SLoc, BO_Assign, NewLHS, NewRHS).get(); + return BaseTransform::TransformExpr(New); + } + return CAO; +} + +ExprResult FatPtrPreprocessor::TransformCStyleCastExpr(CStyleCastExpr *CSCE) { + Expr *SubE = CSCE->getSubExpr(); + CSCE->setSubExpr(isa(SubE->IgnoreParens()) && + SubE->getType().isFatPtrType() + ? ReplaceWithTemporaryVarDecl(SubE->IgnoreParens()) + : BaseTransform::TransformExpr(SubE).get()); + return CSCE; +} + ExprResult FatPtrPreprocessor::TransformImplicitCastExpr(ImplicitCastExpr *ICE) { ICE->setSubExpr(BaseTransform::TransformExpr(ICE->getSubExpr()).get()); @@ -407,28 +432,41 @@ FatPtrPreprocessor::TransformImplicitCastExpr(ImplicitCastExpr *ICE) { // If body of IfStmt/WhileStmt/DoStmt/ForStmt is not CompoundStmt, // we build CompoundStmt and add original body into it. Stmt *FatPtrPreprocessor::BuildCompoundStmtForIfOrLoopStmtBody(Stmt *Body) { - if (isa(Body) && !IncOrDecFatPtrExpr.second) + if (isa(Body)) return Body; llvm::SmallVector Statements; - if (auto CS = dyn_cast(Body)) { - for (Stmt *S : CS->body()) - Statements.push_back(S); - } else if (!isa(Body)) { + if (!isa(Body)) { Statements.push_back(Body); } - - // IncExpr of ForStmt maybe SelfIncExpr or SelfDecExpr of fat ptr, - // we add it in the end of ForStmt body, for example: - // for (;;p++) {} -> for (;;p) { p = p + 1; } - if (IncOrDecFatPtrExpr.second) { - Statements.push_back(IncOrDecFatPtrExpr.second); - IncOrDecFatPtrExpr.second = nullptr; - } - return CompoundStmt::Create(SemaRef.Context, Statements, FPOptionsOverride(), Body->getBeginLoc(), Body->getEndLoc()); } +// If return type of a CallExpr is fat ptr type, build a temporary +// VarDecl and return the corresponding DeclRefExpr. +ImplicitCastExpr *FatPtrPreprocessor::ReplaceWithTemporaryVarDecl(Expr *E) { + assert(isa(E) && E->getType().isFatPtrType() && + "Only replace call expr whose return type is fat ptr"); + auto CE = dyn_cast(E); + for (unsigned i = 0; i < CE->getNumArgs(); i++) + CE->setArg(i, BaseTransform::TransformExpr(CE->getArg(i)).get()); + SourceLocation SLoc = CE->getBeginLoc(); + QualType QT = CE->getType(); + std::string Name = "_FatPtr_temp_" + std::to_string(TempVarCounter++); + VarDecl *VD = + VarDecl::Create(SemaRef.Context, FD, SLoc, SLoc, + &SemaRef.Context.Idents.get(Name), QT, nullptr, SC_None); + VD->setInit(CE); + VD->markUsed(SemaRef.Context); + DeclStmt *DS = new (SemaRef.Context) DeclStmt(DeclGroupRef(VD), SLoc, SLoc); + CurrStmts.push_back(DS); + DeclRefExpr *DRE = DeclRefExpr::Create( + SemaRef.Context, NestedNameSpecifierLoc(), SourceLocation(), VD, false, + SLoc, CE->getType(), VK_LValue); + return ImplicitCastExpr::Create(SemaRef.Context, QT, CK_LValueToRValue, DRE, + nullptr, VK_PRValue, FPOptionsOverride()); +} + // Desugar fat ptr for FunctionDecl, VarDecl, RecordDecl and TypedefNameDecl class TransformFatPtr : public TreeTransform { typedef TreeTransform BaseTransform; @@ -478,6 +516,7 @@ class TransformFatPtr : public TreeTransform { // such as `p->arr[3]` or `p->a.b.arr[3]` // we should check offset to avoid out of bounds. ArraySubscriptExpr *CurrentASEToAccessMemberThroughFatPtr = nullptr; + llvm::SmallSet PreprocessedSelfIncOrDecExpr; public: TransformFatPtr(Sema &SemaRef) : BaseTransform(SemaRef) {} @@ -512,7 +551,6 @@ public: StmtResult TransformReturnStmt(ReturnStmt *RS); ExprResult TransformDeclRefExpr(DeclRefExpr *DRE); ExprResult TransformImplicitCastExpr(ImplicitCastExpr *ICE); - ExprResult TransformParenExpr(ParenExpr *PE); ExprResult TransformCompoundLiteralExpr(CompoundLiteralExpr *CLE); ExprResult TransformInitListExpr(InitListExpr *ILE); ExprResult TransformCallExpr(CallExpr *CE); @@ -521,7 +559,9 @@ public: ExprResult TransformUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *UETT); ExprResult TransformMemberExpr(MemberExpr *ME); ExprResult TransformConditionalOperator(ConditionalOperator *CO); - ExprResult TransformCompoundAssignOperator(CompoundAssignOperator *CAO); + + ExprResult TransformParenExpr(ParenExpr *PE); + ExprResult HandlePEFromPreprocessedSelfIncOrDecExpr(ParenExpr *PE); ExprResult TransformBinaryOperator(BinaryOperator *BO); ExprResult HandleBOFatPtrAddOrSubInteger(BinaryOperator *BO); @@ -548,7 +588,8 @@ private: QualType DesugarFatPtrType(SourceLocation SLoc, QualType T); MemberExpr *BuildMemberForFatPtr(Expr *FatPtrBaseE, std::string FieldName); Expr *BuildFatPtrCall(SourceLocation SLoc, std::string FDName, - llvm::SmallVector &Args); + llvm::SmallVector &Args, + bool ArgsAreReady = false); MemberExpr *BuildMemberRawPtrAndInsertCheckCallForFatPtr(Expr *FatPtrBaseE); Expr *BuildFatPtrCStyleCastExpr(SourceLocation SLoc, QualType ToType, Expr *SubExpr); Expr *BuildFatPtrCompoundLiteralExprForRawPtr(Expr *RawPtrE, QualType QT); @@ -581,9 +622,9 @@ void TransformFatPtr::TransformFunctionDecl() { return; // First traversal: do preprocess - FatPtrPreprocessor Preprocessor(SemaRef); + FatPtrPreprocessor Preprocessor(SemaRef, FD); FD->setBody(Preprocessor.TransformStmt(FD->getBody()).get()); - + PreprocessedSelfIncOrDecExpr = Preprocessor.PreprocessedSelfIncOrDecExpr; // Second traversal: // find local vars whose address is taken by `&fat` expr. Finder.VisitStmt(FD->getBody()); @@ -1070,6 +1111,9 @@ ExprResult TransformFatPtr::TransformImplicitCastExpr(ImplicitCastExpr *ICE) { ExprResult TransformFatPtr::TransformParenExpr(ParenExpr *PE) { SourceLocation SLoc = PE->getBeginLoc(); + if (DesugarToMemberPtr && PreprocessedSelfIncOrDecExpr.count(PE)) { + return HandlePEFromPreprocessedSelfIncOrDecExpr(PE); + } QualType QT = PE->getType(); if (DesugarToMemberPtr && QT.isFatPtrType()) { QT = DesugarFatPtrType(SLoc, QT.getFatPtrPointeeType()); @@ -1082,6 +1126,61 @@ ExprResult TransformFatPtr::TransformParenExpr(ParenExpr *PE) { return PE; } +ExprResult +TransformFatPtr::HandlePEFromPreprocessedSelfIncOrDecExpr(ParenExpr *PE) { + SourceLocation SLoc = PE->getBeginLoc(); + PE->setType(DesugarFatPtrType(SLoc, PE->getType())); + + assert(isa(PE->getSubExpr())); + auto BO = dyn_cast(PE->getSubExpr()); + assert(BO->getOpcode() == BO_Comma); + DesugarToMemberPtr = false; + if (!InsertCheckVersionCall && !InsertCheckOffsetCall.first) { + BO->setLHS(BaseTransform::TransformExpr(BO->getLHS()).get()); + BO->setRHS(BaseTransform::TransformExpr(BO->getRHS()).get()); + PE->setSubExpr(BO); + } else { + llvm::SmallVector Args{InsertCheckOffsetCall.second}; + InsertCheckOffsetCall.first = false; + InsertCheckVersionCall = false; + Expr *FirstE = BaseTransform::TransformExpr(BO->getLHS()).get(); + Expr *ThirdE = BaseTransform::TransformExpr(BO->getRHS()).get(); + Expr *SecondE; + if (auto CLE = dyn_cast(ThirdE)) { + ThirdE = SemaRef.DefaultFunctionArrayLvalueConversion(CLE).get(); + // *p++ has been preprocessed to *(p = p + 1, p - 1), + // here we will desugar it to + // @code + // *(p = (_FatPtr) {p.ptr + 1, p.key_version, p.offset + 4}, + // _check_version_and_offset(4, p.ptr - 1, + // p.key_version, p.offset - 4), + // (_FatPtr) {p.ptr - 1, p.key_version, p.offset - 4}).ptr + // @endcode + assert(isa(CLE->getInitializer())); + auto ILE = dyn_cast(CLE->getInitializer()); + for (unsigned i = 0; i < ILE->getNumInits(); i++) + Args.push_back(ILE->getInit(i)); + SecondE = BuildFatPtrCall(SLoc, "_check_version_and_offset", Args, true); + } else if (auto ICE = dyn_cast(ThirdE)) { + // `*++p` has been preprocessed to `*(p = p + 1, p)`, + // here we desugar it to + // @code + // *(p = (_FatPtr) {p.ptr + 1, p.key_version, p.offset + 4}, + // _check_version_and_offset(4, p.ptr, p.key_version, p.offset), + // p).ptr + // @endcode + Args.push_back(ICE->getSubExpr()); + SecondE = BuildFatPtrCall(SLoc, "_check_version_and_offset", Args); + } + Expr *FirstSecondE = + SemaRef.CreateBuiltinBinOp(SLoc, BO_Comma, FirstE, SecondE).get(); + Expr *FirstSecondThirdE = + SemaRef.CreateBuiltinBinOp(SLoc, BO_Comma, FirstSecondE, ThirdE).get(); + PE->setSubExpr(FirstSecondThirdE); + } + return BuildMemberForFatPtr(PE, "raw_ptr"); +} + StmtResult TransformFatPtr::TransformDeclStmt(DeclStmt *DS) { assert(DS->isSingleDecl() && "DeclStmt can only contain a Decl here"); Decl *D = DS->getSingleDecl(); @@ -1134,16 +1233,19 @@ ExprResult TransformFatPtr::TransformCallExpr(CallExpr *CE) { MemberRawPtr = BuildMemberRawPtrAndInsertCheckCallForFatPtr(CE); Decl *CalleeD = CE->getCalleeDecl(); - if (CalleeD == FD) { - // For self-call Expr, desugar the callee function type - if (auto CalleeICE = dyn_cast(CE->getCallee())) { - CalleeICE->setType(SemaRef.Context.getPointerType(FD->getType())); - if (auto CalleeDRE = dyn_cast(CalleeICE->getSubExpr())) { - CalleeDRE->setType(FD->getType()); + if (auto CalleeFD = dyn_cast(CalleeD)) { + // Desugar callee function type + QualType CalleeFDType = CalleeFD->getType(); + if (CalleeFDType.hasFat()) { + if (auto CalleeICE = dyn_cast(CE->getCallee())) { + CalleeFDType = DesugarFatPtrType(SLoc, CalleeFD->getType()); + CalleeICE->setType(SemaRef.Context.getPointerType(CalleeFDType)); + if (auto CalleeDRE = dyn_cast(CalleeICE->getSubExpr())) { + CalleeDRE->setType(CalleeFDType); + } } } - } - if (auto CalleeFD = dyn_cast(CalleeD)) { + // Desugar argument expr for (unsigned i = 0; i < CE->getNumArgs(); i++) { Expr *ArgE = CE->getArg(i); if (i < CalleeFD->getNumParams() && CalleeFD->getParamDecl(i) && @@ -1247,10 +1349,11 @@ TransformFatPtr::TransformArraySubscriptExpr(ArraySubscriptExpr *ASE) { ExprResult TransformFatPtr::TransformCStyleCastExpr(CStyleCastExpr *CSCE) { SourceLocation SLoc = CSCE->getBeginLoc(); - QualType QT = CSCE->getType(); + QualType QT = DesugarFatPtrType(SLoc, CSCE->getType()); + CSCE->setType(QT); Expr *SubE = CSCE->getSubExpr(); - QualType SubQT = SubE->getType(); - if (QT->isPointerType() && !QT.isFatPtrType() && SubQT.isFatPtrType()) { + QualType SubQT = SubE->getType(); + if (QT->isPointerType() && SubQT.isFatPtrType()) { // desuagr `(int*)fat_p` to `(int*)fat_p.raw_ptr` CSCE->setCastKind(CK_NoOp); DesugarToMemberPtr = true; @@ -1261,14 +1364,19 @@ ExprResult TransformFatPtr::TransformCStyleCastExpr(CStyleCastExpr *CSCE) { IntegerLiteral::Create(SemaRef.Context, llvm::APInt(32, 1), SemaRef.Context.getIntTypeForBitwidth(32, true), SLoc); InsertCheckOffsetCall.second = OffsetExpr; - } else if (QT.isFatPtrType() && SubQT->isPointerType() && !SubQT.isFatPtrType()) { - QT = DesugarFatPtrType(SLoc, QT); + } else if (QT.isFatPtrType() && SubQT->isPointerType() && + !SubQT.isFatPtrType()) { // desugar `(int *fat)raw_p` to `(_FatPtr) { raw_p, 0, 0 }` Expr *SubRawPtrE = BaseTransform::TransformExpr(SubE).get(); Expr *FatPtrE = BuildFatPtrCompoundLiteralExprForRawPtr(SubRawPtrE, QT); return ImplicitCastExpr::Create(SemaRef.Context, QT, CK_NoOp, FatPtrE, nullptr, VK_PRValue, FPOptionsOverride()); } else if (QT.isFatPtrType() && SubQT.isFatPtrType()) { + // desuagr `(float *fat)fat_int` to + // @code + // (_FatPtr) { fat_int.raw_ptr, fat_int.key_version, + // fat_int.offset } + //@endcode if (DesugarToMemberPtr) { QualType ToPointerTy = SemaRef.Context.getPointerType(QT.getFatPtrPointeeType()); @@ -1277,8 +1385,8 @@ ExprResult TransformFatPtr::TransformCStyleCastExpr(CStyleCastExpr *CSCE) { CK_NoOp, Res, nullptr, VK_PRValue, FPOptionsOverride()); return Res; - } - return BaseTransform::TransformExpr(SubE).get(); + } + return BuildFatPtrCStyleCastExpr(SLoc, QT, SubE); } CSCE->setSubExpr(BaseTransform::TransformExpr(SubE).get()); return CSCE; @@ -1333,48 +1441,6 @@ TransformFatPtr::TransformConditionalOperator(ConditionalOperator *CO) { CondExpr, CO->getQuestionLoc(), LHS, CO->getColonLoc(), RHS); } -ExprResult -TransformFatPtr::TransformCompoundAssignOperator(CompoundAssignOperator *CAO) { - SourceLocation SLoc = CAO->getBeginLoc(); - QualType QT = DesugarFatPtrType(SLoc, CAO->getType()); - CAO->setType(QT); - BinaryOperator::Opcode Op = CAO->getOpcode(); - CAO->setLHS(BaseTransform::TransformExpr(CAO->getLHS()).get()); - CAO->setRHS(BaseTransform::TransformExpr(CAO->getRHS()).get()); - if (QT.isFatPtrType() && (Op == BO_AddAssign || Op == BO_SubAssign)) { - // `p += 1` should be desugared to - // `p = (_FatPtr) { p.raw_ptr + 1, p.key_version, p.offset + 1 * - // sizeof(int) }` - Expr *LHS = CAO->getLHS(); - Expr *RHS = CAO->getRHS(); - bool OpIsAddAssign = Op == BO_AddAssign ? true : false; - Expr *FirstInitE = - SemaRef - .CreateBuiltinBinOp(SLoc, OpIsAddAssign ? BO_Add : BO_Sub, - BuildMemberForFatPtr(LHS, "raw_ptr"), RHS) - .get(); - Expr *SecondInitE = BuildMemberForFatPtr(LHS, "key_version"); - Expr *ThirdInitLHS = BuildMemberForFatPtr(LHS, "offset"); - QualType PointeeQT = LHS->getType().getFatPtrPointeeType(); - QualType Int32Ty = SemaRef.Context.getIntTypeForBitwidth(32, true); - Expr *ThirdInitRHSRHS = IntegerLiteral::Create( - SemaRef.Context, - llvm::APInt(32, SemaRef.Context.getTypeSize(PointeeQT) / 8), Int32Ty, - SLoc); - Expr *ThirdInitRHS = - SemaRef.CreateBuiltinBinOp(SLoc, BO_Mul, RHS, ThirdInitRHSRHS).get(); - Expr *ThirdInitE = - SemaRef - .CreateBuiltinBinOp(SLoc, OpIsAddAssign ? BO_Add : BO_Sub, - ThirdInitLHS, ThirdInitRHS) - .get(); - RHS = - BuildFatPtrCompoundLiteralExpr(FirstInitE, SecondInitE, ThirdInitE, QT); - return SemaRef.CreateBuiltinBinOp(SLoc, BO_Assign, LHS, RHS); - } - return CAO; -} - // BinaryOperator includes: // reassign fat ptr : p1 = p2, p1 = nullptr // comparison between fat ptr : p1 != p2, p == nullptr, p1 < p2 @@ -1618,6 +1684,8 @@ ExprResult TransformFatPtr::HandleUOAddrFatOnExprWithFatPtrBase( SemaRef .CreateBuiltinBinOp(SLoc, BO_Sub, ThirdInitLHSLHS, ThirdInitLHSRHS) .get(); + ThirdInitLHS = + new (SemaRef.Context) ParenExpr(SLoc, UO->getEndLoc(), ThirdInitLHS); QualType Int32Ty = SemaRef.Context.getIntTypeForBitwidth(32, true); ThirdInitLHS = SemaRef @@ -1811,6 +1879,21 @@ ExprResult TransformFatPtr::TransformMemberExpr(MemberExpr *ME) { SemaRef.CreateBuiltinBinOp(SLoc, BO_Add, OffsetExprLHS, OffsetExprRHS).get(); } ME->setBase(BaseTransform::TransformExpr(ME->getBase()).get()); + // The type of base expr maybe desugared to another record type, + // so we should bind MemberExpr to the according FieldDecl of + // desugared record type + if (auto RT = + dyn_cast(ME->getBase()->getType().getCanonicalType())) { + if (auto RD = dyn_cast(RT->getDecl())) { + if (const FieldDecl *FD = dyn_cast(ME->getMemberDecl())) { + RecordDecl::field_iterator it = RD->field_begin(); + for (unsigned i = 0; i < FD->getFieldIndex(); i++) + it++; + FieldDecl *NewFD = *it; + ME->setMemberDecl(NewFD); + } + } + } return MemberRawPtr ? MemberRawPtr : ME; } @@ -1821,9 +1904,12 @@ ExprResult TransformFatPtr::TransformMemberExpr(MemberExpr *ME) { // 4. `int *fat *fat` will be desugared to `_FatPtr<_FatPtr>`; QualType TransformFatPtr::DesugarFatPtrType(SourceLocation SLoc, QualType T) { - if (T->isFunctionPointerType()) { - const FunctionProtoType *FuncType = - T->getAs()->getPointeeType()->getAs(); + if (T->isFunctionPointerType() || T->isFunctionProtoType()) { + const FunctionProtoType *FuncType = T->isFunctionPointerType() + ? T->getAs() + ->getPointeeType() + ->getAs() + : T->getAs(); if (!FuncType->hasFatRetOrParams()) return T; // Desugar fat qualified return type. @@ -1833,8 +1919,11 @@ TransformFatPtr::DesugarFatPtrType(SourceLocation SLoc, QualType T) { for (auto ParamTy : FuncType->getParamTypes()) { ParamTys.push_back(DesugarFatPtrType(SLoc, ParamTy)); } - return SemaRef.Context.getPointerType(SemaRef.Context.getFunctionType( - ReturnTy, ParamTys, FuncType->getExtProtoInfo())); + QualType DesugaredFuncTy = SemaRef.Context.getFunctionType( + ReturnTy, ParamTys, FuncType->getExtProtoInfo()); + return T->isFunctionPointerType() + ? SemaRef.Context.getPointerType(DesugaredFuncTy) + : DesugaredFuncTy; } if (auto AT = dyn_cast(T)) { QualType ElementT = DesugarFatPtrType(SLoc, AT->getElementType()); @@ -1918,7 +2007,8 @@ Expr *TransformFatPtr::BuildFatPtrCStyleCastExpr(SourceLocation SLoc, QualType T // Build CallExpr for _check_version, _check_offset or _new_version_number. Expr *TransformFatPtr::BuildFatPtrCall(SourceLocation SLoc, std::string FDName, - llvm::SmallVector &Args) { + llvm::SmallVector &Args, + bool ArgsAreReady) { if (!FatPtrFD[FDName]) { DeclContext::lookup_result Decls = SemaRef.Context.getTranslationUnitDecl()->lookup( @@ -1931,10 +2021,12 @@ Expr *TransformFatPtr::BuildFatPtrCall(SourceLocation SLoc, std::string FDName, // 1. split the first _FatPtr argument into three fields argument // 2. add file name, line number and function name to report error if (FDName != "_new_version_number") { - Expr *FatPtrE = Args.pop_back_val(); - Args.push_back(BuildMemberForFatPtr(FatPtrE, "raw_ptr")); - Args.push_back(BuildMemberForFatPtr(FatPtrE, "key_version")); - Args.push_back(BuildMemberForFatPtr(FatPtrE, "offset")); + if (!ArgsAreReady) { + Expr *FatPtrE = Args.pop_back_val(); + Args.push_back(BuildMemberForFatPtr(FatPtrE, "raw_ptr")); + Args.push_back(BuildMemberForFatPtr(FatPtrE, "key_version")); + Args.push_back(BuildMemberForFatPtr(FatPtrE, "offset")); + } // Build argE for file name. StringRef FileName = SemaRef.Context.getSourceManager().getBufferName(SLoc); @@ -2259,7 +2351,11 @@ bool Sema::CheckFatQualTypeAssignment(QualType LHSType, QualType RHSType) { // T *fat -> const void *fat is OK if (LHSPointeeType->isVoidType()) return true; - // T1 *fat -> const T2 *fat is ERROR + return false; + } + + if (LHSPointeeType.hasFat() || RHSPointeeType.hasFat()) { + return CheckFatQualTypeAssignment(LHSPointeeType, RHSPointeeType); } return false; } @@ -2277,7 +2373,9 @@ bool Sema::CheckFatQualTypeAssignment(QualType LHSType, QualType RHSType) { LHSCanType->getAs()->getPointeeType(); QualType RHSPointeeType = RHSCanType->getAs()->getPointeeType(); - return CheckFatQualTypeAssignment(LHSPointeeType, RHSPointeeType); + if (LHSPointeeType.hasFat() || RHSPointeeType.hasFat()) { + return CheckFatQualTypeAssignment(LHSPointeeType, RHSPointeeType); + } } return true; } @@ -2313,31 +2411,6 @@ bool Sema::CheckFatFunctionPointerType(QualType LHSType, Expr *RHSExpr) { return true; } -// In some context, it is hard to desugar SelfIncExpr or -// SelfDecExpr of fat ptr, so we report error when: -// 1) the CondExpr of IfStmt/WhileStmt/DoStmt/ForStmt/ -// SwitchStmt/ConditionalOperator has SelfIncExpr or -// SelfDecExpr of fat ptr, such as `while (*p++ > 0)`, -// `*p++ > 0 ? : *p : *q;` -// 2) the LHS or RHS of ConditionalOperator has SelfIncExpr -// or SelfDecExpr of fat ptr, such as `*p ? : p++ : q++;` -bool Sema::CheckSelfIncOrDecOfFatPtr(Expr *E) { - if (auto UO = dyn_cast(E)) { - if ((UO->isPrefix() || UO->isPostfix()) && - UO->getSubExpr()->getType().isFatPtrType()) { - Diag(UO->getBeginLoc(), diag::err_no_inc_or_dec_of_fat_ptr); - return false; - } - } - for (auto C : E->children()) { - if (auto ChildE = dyn_cast_or_null(C)) { - if (!CheckSelfIncOrDecOfFatPtr(ChildE)) - return false; - } - } - return true; -} - // Build allocation unit RecordDecl for every global array var // which has attribute fat_ptr_checked, for example: // @code diff --git a/clang/lib/Sema/BSC/SemaDeclBSC.cpp b/clang/lib/Sema/BSC/SemaDeclBSC.cpp index e54957c42bb2..34ad26b1b50b 100644 --- a/clang/lib/Sema/BSC/SemaDeclBSC.cpp +++ b/clang/lib/Sema/BSC/SemaDeclBSC.cpp @@ -165,7 +165,8 @@ bool Sema::HasSafeZoneInFunction(const FunctionDecl* FnDecl) { /// --> | BorrowCheck | --> FuncDecl --> CodeGen /// |__________________| /// ==================================================================== -void Sema::BSCDataflowAnalysis(const Decl *D, bool EnableOwnershipCheck, +void Sema::BSCDataflowAnalysis(const FunctionDecl *FD, + bool EnableOwnershipCheck, bool EnableNullabilityCheck) { AnalysisDeclContext AC(/* AnalysisDeclContextManager */ nullptr, FD); @@ -179,8 +180,6 @@ void Sema::BSCDataflowAnalysis(const Decl *D, bool EnableOwnershipCheck, AC.getCFGBuildOptions().BSCMode = true; AC.getCFGBuildOptions().setAllAlwaysAdd(); - const FunctionDecl *FD = dyn_cast_or_null(D); - // If D does not use memory safety features like "owned, borrow, &mut, &const", // we should not do borrow checking. bool RequireBorrowCheck = LangOpts.BSC ? FindSafeFeatures(FD) : false; @@ -221,9 +220,11 @@ void Sema::BSCDataflowAnalysis(const Decl *D, bool EnableOwnershipCheck, runBorrowCheck(*FD, *AC.getCFG(), BorrowCheckReporter, Context); } } - if (getLangOpts().EnableFatPtr) { - runFatPtrReduntantCheck(*FD, *cfg, AC, Context); - } + } + // TODO: add RequireFatPtrReduntantCheck according to whether + // current function contains fat ptr feature + if (getLangOpts().EnableFatPtr && FD && AC.getCFG()) { + runFatPtrReduntantCheck(*FD, *AC.getCFG(), AC, Context); } } #endif \ No newline at end of file diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 71e69ae94420..ca9200c92de0 100755 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -16021,10 +16021,10 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body, getDiagnostics().getNumOwnershipErrors() + getDiagnostics().getNumBorrowCheckErrors() + getDiagnostics().getNumNullabilityCheckErrors()) { - auto md = dyn_cast_or_null(D); + auto md = dyn_cast_or_null(FD); // Don't check ownership rules of destructor parameters if (!md || !md->isDestructor()) - BSCDataflowAnalysis(D, EnableOwnershipCheck, + BSCDataflowAnalysis(FD, EnableOwnershipCheck, EnableNullabilityCheck); } } diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 9c7a7f12d004..1a65e55cd7b8 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -9720,16 +9720,6 @@ ExprResult Sema::ActOnConditionalOp(SourceLocation QuestionLoc, RHS.isInvalid()) return ExprError(); -#if ENABLE_BSC - if (getLangOpts().BSC && getLangOpts().EnableFatPtr) { - bool CondResult = CheckSelfIncOrDecOfFatPtr(CondExpr); - bool LHSResult = CheckSelfIncOrDecOfFatPtr(LHSExpr); - bool RHSResult = CheckSelfIncOrDecOfFatPtr(RHSExpr); - if (!CondResult || !LHSResult || !RHSResult) - return ExprError(); - } -#endif - DiagnoseConditionalPrecedence(*this, QuestionLoc, Cond.get(), LHS.get(), RHS.get()); @@ -10043,6 +10033,12 @@ Sema::CheckAssignmentConstraints(QualType LHSType, ExprResult &RHS, QualType RHSType = RHS.get()->getType(); QualType OrigLHSType = LHSType; + if (getLangOpts().BSC && getLangOpts().EnableFatPtr && + Context.hasSameFatPtrType(LHSType, RHSType)) { + Kind = CK_NoOp; + return Compatible; + } + // Get canonical types. We're not formatting these types, just comparing // them. LHSType = Context.getCanonicalType(LHSType).getUnqualifiedType(); @@ -21024,13 +21020,6 @@ Sema::ConditionResult Sema::ActOnCondition(Scope *S, SourceLocation Loc, break; } -#if ENABLE_BSC - if (getLangOpts().BSC && getLangOpts().EnableFatPtr) { - if (!CheckSelfIncOrDecOfFatPtr(SubExpr)) - Cond = ExprError(); - } -#endif - if (Cond.isInvalid()) { Cond = CreateRecoveryExpr(SubExpr->getBeginLoc(), SubExpr->getEndLoc(), {SubExpr}, PreferredConditionType(CK)); diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp index 13f9d49d1c17..1ff622209128 100644 --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -1753,12 +1753,6 @@ Sema::ActOnDoStmt(SourceLocation DoLoc, Stmt *Body, if (CondResult.isInvalid()) return StmtError(); -#if ENABLE_BSC - if (getLangOpts().BSC && getLangOpts().EnableFatPtr) { - if (!CheckSelfIncOrDecOfFatPtr(Cond)) - return StmtError(); - } -#endif Cond = CondResult.get(); CondResult = ActOnFinishFullExpr(Cond, DoLoc, /*DiscardedValue*/ false); diff --git a/clang/test/BSC/Negative/FatPtr/self_inc_or_dec_of_fat_ptr.cbs b/clang/test/BSC/Negative/FatPtr/self_inc_or_dec_of_fat_ptr.cbs deleted file mode 100644 index ff136be63880..000000000000 --- a/clang/test/BSC/Negative/FatPtr/self_inc_or_dec_of_fat_ptr.cbs +++ /dev/null @@ -1,22 +0,0 @@ -// RUN: %clang_cc1 -enable-fat-ptr -verify %s - -void test() { - int arr[3] = { 0, 1, 2 }; - int *fat p = &fat arr[0]; - int a = 10; - if (*p++ > 0) { // expected-error {{increment or decrement operation of fat pointer is not allowed here}} - a = *p++ == 1 ? *p++ : *++p; /* expected-error {{increment or decrement operation of fat pointer is not allowed here}} - expected-error {{increment or decrement operation of fat pointer is not allowed here}} - expected-error {{increment or decrement operation of fat pointer is not allowed here}} - */ - } else { - a = *++p == 1 ? *p-- : *--p; /* expected-error {{increment or decrement operation of fat pointer is not allowed here}} - expected-error {{increment or decrement operation of fat pointer is not allowed here}} - expected-error {{increment or decrement operation of fat pointer is not allowed here}} - */ - } - switch (*p++) {} // expected-error {{increment or decrement operation of fat pointer is not allowed here}} - while (*p++ > 0) {}// expected-error {{increment or decrement operation of fat pointer is not allowed here}} - do {} while (*p++ > 0);// expected-error {{increment or decrement operation of fat pointer is not allowed here}} - for (; *p++ > 0; p++) {}// expected-error {{increment or decrement operation of fat pointer is not allowed here}} -} \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds1.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds1.cbs index 975c7550754f..0fbfd3e63612 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds1.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds1.cbs @@ -4,7 +4,7 @@ #include int main() { - int *fat p = checked_malloc(3 * sizeof(int)); + int *fat p = (int *fat)checked_malloc(3 * sizeof(int)); int a = p[3]; return 0; } diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds10.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds10.cbs index 61354828836e..77f601353803 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds10.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds10.cbs @@ -6,7 +6,7 @@ int main() { int *fat p = nullptr; if (!p) { - p = checked_malloc(sizeof(int)); + p = (int *fat)checked_malloc(sizeof(int)); p = p + 1; *p = 10; } diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds11.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds11.cbs index 2d354b2f52cf..be9b109ef756 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds11.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds11.cbs @@ -4,10 +4,10 @@ #include int main() { - int *fat p1 = checked_malloc(sizeof(int)); + int *fat p1 = (int *fat)checked_malloc(sizeof(int)); int *fat p2 = p1; if (p1 == p2) { - p2 = checked_malloc(sizeof(int)); + p2 = (int *fat)checked_malloc(sizeof(int)); p2++; *p2 = 10; } diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds12.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds12.cbs index f905d5644648..d56eea2a6605 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds12.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds12.cbs @@ -5,7 +5,7 @@ int main() { int *fat p1 = nullptr; - int *fat p2 = p1 ? nullptr : checked_malloc(sizeof(int) * 2); + int *fat p2 = p1 ? nullptr : (int *fat)checked_malloc(sizeof(int) * 2); *p2 = 0; int *fat p3 = *p2 ? p1 : p2; p3[2] = 10; diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds13.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds13.cbs index a6cdfadb2e08..9bf14e9103f5 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds13.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds13.cbs @@ -4,7 +4,7 @@ #include int main() { - int *fat p = checked_malloc(sizeof(int) * 10); + int *fat p = (int *fat)checked_malloc(sizeof(int) * 10); for (int i = 0; i < 10; i++) { p[i] = i - 5; } diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds14.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds14.cbs index 0a550df0117d..3bcc0dd4bad0 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds14.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds14.cbs @@ -4,7 +4,7 @@ #include int main() { - int *fat p = checked_malloc(sizeof(int) * 10); + int *fat p = (int *fat)checked_malloc(sizeof(int) * 10); p += 10; *p = 10; return 0; diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds15.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds15.cbs index 281f9d9c6dd0..73a2921356e5 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds15.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds15.cbs @@ -4,8 +4,8 @@ #include int main() { - int *fat *fat p = checked_malloc(sizeof(int *fat) * 10); - p[9] = checked_malloc(sizeof(int) * 10); + int *fat *fat p = (int *fat *fat)checked_malloc(sizeof(int *fat) * 10); + p[9] = (int *fat)checked_malloc(sizeof(int) * 10); p += 9; *p += 10; **p = 10; diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds16.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds16.cbs index 4a4060a9a2b8..a2dced01ec47 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds16.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds16.cbs @@ -4,7 +4,7 @@ #include int main() { - int *fat p = checked_malloc(sizeof(int) * 10); + int *fat p = (int *fat)checked_malloc(sizeof(int) * 10); p += 9; *p++ = 9; *p++ = 10; diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds17.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds17.cbs index 7259b6793597..ec5abdf668aa 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds17.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds17.cbs @@ -10,7 +10,7 @@ void bar(int *fat p) { Foo foo = bar; int main() { - int *fat p = checked_malloc(sizeof(int) * 10); + int *fat p = (int *fat)checked_malloc(sizeof(int) * 10); int *fat p1 = p + 10; foo(p1); } diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds18.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds18.cbs index 9e5dcc4c2519..3d46121aa394 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds18.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds18.cbs @@ -4,15 +4,15 @@ #include int main() { - int *fat p = checked_malloc(sizeof(int) * 3); + int *fat p = (int *fat)checked_malloc(sizeof(int) * 3); p[0] = 0; p[1] = 1; p[2] = 2; - int *fat q = checked_malloc(sizeof(int) * 3); + int *fat q = (int *fat)checked_malloc(sizeof(int) * 3); for (; *p < 3; p++) { *q = *p; q++; } } // CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds18.cbs:[[@LINE-6]] in main \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds18.cbs:[[@LINE-5]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds19.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds19.cbs index 64f0f702afa6..7a1b384742c3 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds19.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds19.cbs @@ -4,11 +4,11 @@ #include int main() { - int *fat p = checked_malloc(sizeof(int) * 3); + int *fat p = (int *fat)checked_malloc(sizeof(int) * 3); p[0] = 0; p[1] = 1; p[2] = 2; - int *fat q = checked_malloc(sizeof(int) * 3); + int *fat q = (int *fat)checked_malloc(sizeof(int) * 3); while (*p < 3) { *q = *p; p++; diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds2.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds2.cbs index 51835b6ee0e2..3f9fe90d1241 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds2.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds2.cbs @@ -4,7 +4,7 @@ #include struct S { int *fat p; }; int main() { - struct S s = { checked_malloc(3 * sizeof(int)) }; + struct S s = { (int *fat)checked_malloc(3 * sizeof(int)) }; int a = s.p[3]; return 0; } diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds20.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds20.cbs new file mode 100644 index 000000000000..ca43167ee674 --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds20.cbs @@ -0,0 +1,14 @@ +// RUN: %clang -enable-fat-ptr %s -o %t.output +// RUN: not %t.output 2>&1 | FileCheck %s + +#include +int *fat return_fat() { + return (int *fat)checked_malloc(sizeof(int) * 10); +} + +int main() { + int *fat p = return_fat() + 10; + *p = 10; +} +// CHECK: Error: Pointer offset exceeds the allocation size! +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds20.cbs:[[@LINE-3]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds21.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds21.cbs new file mode 100644 index 000000000000..1f153854bb56 --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds21.cbs @@ -0,0 +1,12 @@ +// RUN: %clang -enable-fat-ptr %s -o %t.output +// RUN: not %t.output 2>&1 | FileCheck %s + +#include + +int main() { + int *fat p = (int *fat)checked_malloc(sizeof(int)); + long long *fat p1 = (long long *fat)p; + *p1 = 10; +} +// CHECK: Error: Pointer offset exceeds the allocation size! +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds21.cbs:[[@LINE-3]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds22.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds22.cbs new file mode 100644 index 000000000000..d67859854fb7 --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds22.cbs @@ -0,0 +1,16 @@ +// RUN: %clang -enable-fat-ptr %s -o %t.output +// RUN: not %t.output 2>&1 | FileCheck %s + +#include + +int main() { + int arr[2] = { 0, 1 }; + int *fat p = &fat arr[0]; + int a = 10; + if (*p++ == 0) { + a = *p++ == 1 ? *p++ : 0; + } + return 0; +} +// CHECK: Error: Pointer offset exceeds the allocation size! +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds22.cbs:[[@LINE-5]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds23.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds23.cbs new file mode 100644 index 000000000000..a9e49d3a7c23 --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds23.cbs @@ -0,0 +1,16 @@ +// RUN: %clang -enable-fat-ptr %s -o %t.output +// RUN: not %t.output 2>&1 | FileCheck %s + +#include + +int main() { + int arr[2] = { 0, 1 }; + int *fat p = &fat arr[0]; + int a = 10; + while (*p++ <= 1) { + a = *p; + } + return 0; +} +// CHECK: Error: Pointer offset exceeds the allocation size! +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds23.cbs:[[@LINE-5]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds24.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds24.cbs new file mode 100644 index 000000000000..1849f3f1dd55 --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds24.cbs @@ -0,0 +1,16 @@ +// RUN: %clang -enable-fat-ptr %s -o %t.output +// RUN: not %t.output 2>&1 | FileCheck %s + +#include + +int main() { + int arr[2] = { 0, 1 }; + int *fat p = &fat arr[0]; + int a = 10; + do { + + } while (*p++ <= 1); + return 0; +} +// CHECK: Error: Pointer offset exceeds the allocation size! +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds24.cbs:[[@LINE-4]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds3.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds3.cbs index 26e2f6ce7375..ffd3256569c8 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds3.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds3.cbs @@ -4,7 +4,7 @@ #include int main() { - int *fat p = checked_malloc(3 * sizeof(int)); + int *fat p = (int *fat)checked_malloc(3 * sizeof(int)); int *fat p1 = p + 3; int a = *p1; return 0; diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds4.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds4.cbs index 7b7305318d45..50a853dd1568 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds4.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds4.cbs @@ -5,7 +5,7 @@ struct S { int a; }; int main() { - struct S *fat p = checked_malloc(sizeof(struct S)); + struct S *fat p = (struct S *fat)checked_malloc(sizeof(struct S)); struct S *fat p1 = p + 1; p1->a = 10; return 0; diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds5.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds5.cbs index b80177c9e6ae..5065a33b6391 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds5.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds5.cbs @@ -5,7 +5,7 @@ struct S { int a; int arr[3]; }; int main() { - struct S *fat p1 = checked_malloc(sizeof(struct S) * 2); + struct S *fat p1 = (struct S *fat)checked_malloc(sizeof(struct S) * 2); struct S *fat p2 = p1 + 1; int a = p2->arr[3]; return 0; diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds6.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds6.cbs index cd4c9e508722..6b2be24bedba 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds6.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds6.cbs @@ -5,7 +5,7 @@ struct S { int arr[3]; }; int main() { - struct S *fat p = checked_malloc(sizeof(struct S)); + struct S *fat p = (struct S *fat)checked_malloc(sizeof(struct S)); int a = p->arr[3]; } // CHECK: Error: Pointer offset exceeds the allocation size! diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds7.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds7.cbs index 42b814927dcc..6c41e1f26999 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds7.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds7.cbs @@ -4,7 +4,7 @@ #include int main() { - int *fat p = checked_malloc(sizeof(int) * 10); + int *fat p = (int *fat)checked_malloc(sizeof(int) * 10); for (int i = 0; i < 10; i++) { p[i] = i - 5; } diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds8.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds8.cbs index f29da60932a4..7f02135b5b61 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds8.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds8.cbs @@ -4,7 +4,7 @@ #include int main() { - int *fat p = checked_malloc(sizeof(int) * 10); + int *fat p = (int *fat)checked_malloc(sizeof(int) * 10); for (int i = 0; i < 10; i++) { p[i] = i - 5; } diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds9.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds9.cbs index 904126abeadb..e39e5e37957c 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds9.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds9.cbs @@ -4,7 +4,7 @@ #include int main() { - int *fat p = checked_malloc(sizeof(int)); + int *fat p = (int *fat)checked_malloc(sizeof(int)); if (p) { p = p + 1; *p = 10; diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/buffer_overflow.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/buffer_overflow.cbs index a40d1bc46c3b..8a11f4223d8a 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/buffer_overflow.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/buffer_overflow.cbs @@ -14,7 +14,7 @@ char *fat fat_strcpy(char *fat dest, const char *src) { } int main(int argc, char **argv) { - char *fat buf = checked_malloc(sizeof(char)*BUFSIZE); + char *fat buf = (char *fat)checked_malloc(sizeof(char)*BUFSIZE); fat_strcpy(buf, argv[1]); return 0; } diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free1.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free1.cbs index a9c67262df80..76ce095d0468 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free1.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free1.cbs @@ -4,9 +4,9 @@ #include int main() { - int *fat p = checked_malloc(sizeof(int)); - checked_free(p); - checked_free(p); + int *fat p = (int *fat)checked_malloc(sizeof(int)); + checked_free((void *fat)p); + checked_free((void *fat)p); return 0; } diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free2.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free2.cbs index 64a65afc5220..0011f1b6ca6c 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free2.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free2.cbs @@ -4,10 +4,10 @@ #include int main() { - int *fat p1 = checked_malloc(sizeof(int)); + int *fat p1 = (int *fat)checked_malloc(sizeof(int)); int *fat p2 = p1; - checked_free(p1); - checked_free(p2); + checked_free((void *fat)p1); + checked_free((void *fat)p2); return 0; } diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free3.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free3.cbs index 26db032c3e72..621c6515254b 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free3.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free3.cbs @@ -5,11 +5,11 @@ struct G { int *fat a; }; int main() { - struct G g = { .a = checked_malloc(sizeof(int)) }; - struct G *fat p = checked_malloc(sizeof(struct G)); + struct G g = { .a = (int *fat)checked_malloc(sizeof(int)) }; + struct G *fat p = (struct G *fat)checked_malloc(sizeof(struct G)); p->a = g.a; - checked_free(p->a); - checked_free(g.a); + checked_free((void *fat)p->a); + checked_free((void *fat)g.a); return 0; } diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free4.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free4.cbs index 8d294688d9f8..3872ebea1bc9 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free4.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free4.cbs @@ -4,14 +4,14 @@ #include int *fat test() { - int *fat p = checked_malloc(sizeof(int)); - checked_free(p); + int *fat p = (int *fat)checked_malloc(sizeof(int)); + checked_free((void *fat)p); return p; } int main() { int *fat p = test(); - checked_free(p); + checked_free((void *fat)p); return 0; } diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free1.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free1.cbs index fbf926b906af..da6ab753e4e1 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free1.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free1.cbs @@ -4,9 +4,9 @@ #include int main() { - int *fat p = checked_malloc(sizeof(int)); + int *fat p = (int *fat)checked_malloc(sizeof(int)); *p = 10; - checked_free(p); + checked_free((void *fat)p); *p = 10; return 0; } diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free2.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free2.cbs index 9faa09e3f4c3..02d21d1a8651 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free2.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free2.cbs @@ -5,9 +5,9 @@ struct S { int a; }; int main() { - struct S *fat p = checked_malloc(sizeof(struct S)); + struct S *fat p = (struct S *fat)checked_malloc(sizeof(struct S)); p->a = 10; - checked_free(p); + checked_free((void *fat)p); p->a = 10; return 0; } diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free3.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free3.cbs index fe79ee91d3f8..fdc25eaec623 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free3.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free3.cbs @@ -4,8 +4,8 @@ #include int *fat test() { - int *fat p = checked_malloc(sizeof(int)); - checked_free(p); + int *fat p = (int *fat)checked_malloc(sizeof(int)); + checked_free((void *fat)p); return p; } diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free4.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free4.cbs index 08ce74e36d4d..4a0f2ee93bf7 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free4.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free4.cbs @@ -5,10 +5,10 @@ struct G { int *fat a; }; int main() { - struct G g = { .a = checked_malloc(sizeof(int)) }; - struct G *fat p = checked_malloc(sizeof(struct G)); + struct G g = { .a = (int *fat)checked_malloc(sizeof(int)) }; + struct G *fat p = (struct G *fat)checked_malloc(sizeof(struct G)); p->a = g.a; - checked_free(p->a); + checked_free((void *fat)p->a); *g.a = 10; return 0; } diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free5.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free5.cbs index 1c37a3ec2e69..3568698813ac 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free5.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free5.cbs @@ -16,14 +16,14 @@ char *fat fat_strncpy(char *fat dest, const char *src, size_t n) { } int main() { - char *fat buf1 = checked_malloc(BUFSIZE); - checked_free(buf1); + char *fat buf1 = (char *fat)checked_malloc(BUFSIZE); + checked_free((void *fat)buf1); - char *fat buf2 = checked_malloc(BUFSIZE); + char *fat buf2 = (char *fat)checked_malloc(BUFSIZE); memset((char *)buf2, 0, BUFSIZE); fat_strncpy(buf1, "hack", 5); - checked_free(buf2); + checked_free((void *fat)buf2); return 0; } // CHECK: Error: Cannot use this pointer because the allocation may have been freed or reseored! diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free6.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free6.cbs index 1159a594974c..4292d301d94b 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free6.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free6.cbs @@ -6,8 +6,8 @@ void pass_fat(int *p) {} int main() { - int *fat p = checked_malloc(sizeof(int)); - checked_free(p); + int *fat p = (int *fat)checked_malloc(sizeof(int)); + checked_free((void *fat)p); pass_fat((int *)p); return 0; } diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free7.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free7.cbs index 0e96d5e77f6f..9b6f5b78119c 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free7.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free7.cbs @@ -4,8 +4,8 @@ #include int main() { - int *fat p = checked_malloc(sizeof(int)); - checked_free(p); + int *fat p = (int *fat)checked_malloc(sizeof(int)); + checked_free((void *fat)p); if (*p == 5) { return 5; } diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free8.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free8.cbs index 04b70ea5aa83..28b7648db77d 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free8.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free8.cbs @@ -6,8 +6,8 @@ int *fat p = nullptr; int main() { - p = checked_malloc(sizeof(int)); - checked_free(p); + p = (int *fat)checked_malloc(sizeof(int)); + checked_free((void *fat)p); if (*p == 5) { return 5; } diff --git a/clang/test/BSC/Positive/FatPtr/heap.cbs b/clang/test/BSC/Positive/FatPtr/heap.cbs index f30b12beda57..4d904d01d50a 100644 --- a/clang/test/BSC/Positive/FatPtr/heap.cbs +++ b/clang/test/BSC/Positive/FatPtr/heap.cbs @@ -3,64 +3,65 @@ // expected-no-diagnostics #include +#include struct S { int a; }; void test1() { - int *fat p1 = checked_malloc(sizeof(int)); + int *fat p1 = (int *fat)checked_malloc(sizeof(int)); *p1 = 10; - checked_free(p1); + checked_free((void *fat)p1); - struct S *fat p2 = checked_malloc(sizeof(struct S)); + struct S *fat p2 = (struct S *fat)checked_malloc(sizeof(struct S)); p2->a = 10; - checked_free(p2); + checked_free((void *fat)p2); } struct G { int *fat a; }; void test2() { - struct G g = { .a = checked_malloc(sizeof(int)) }; - struct G *fat p = checked_malloc(sizeof(struct G)); + struct G g = { .a = (int *fat)checked_malloc(sizeof(int)) }; + struct G *fat p = (struct G *fat)checked_malloc(sizeof(struct G)); *p = g; *p->a = 10; - checked_free(p->a); - checked_free(p); + checked_free((void *fat)p->a); + checked_free((void *fat)p); } void test3() { - int *fat p1 = checked_malloc(sizeof(int)); - int *fat p2 = checked_malloc(sizeof(int)); + int *fat p1 = (int *fat)checked_malloc(sizeof(int)); + int *fat p2 = (int *fat)checked_malloc(sizeof(int)); *p1 = *p2; - checked_free(p1); - checked_free(p2); + checked_free((void *fat)p1); + checked_free((void *fat)p2); } void test4() { - int *fat p1 = checked_malloc(sizeof(int)); - int *fat p2 = checked_malloc(sizeof(int)); + int *fat p1 = (int *fat)checked_malloc(sizeof(int)); + int *fat p2 = (int *fat)checked_malloc(sizeof(int)); p1 = p2; - checked_free(p1); //only free once + checked_free((void *fat)p1); //only free once } void test5() { - int *fat p = checked_malloc(sizeof(int) * 3); + int *fat p = (int *fat)checked_malloc(sizeof(int) * 3); int a = p[2]; - checked_free(p); + checked_free((void *fat)p); } void test6() { - int *fat p1 = checked_malloc(sizeof(int)); + int *fat p1 = (int *fat)checked_malloc(sizeof(int)); int *fat p2 = p1; int *p3 = (int *)p1; float *fat p4 = (float *fat)p1; - checked_free(p1); + checked_free((void *fat)p1); } void test7() { - int *fat p = checked_malloc(3 * sizeof(int)); + int *fat p = (int *fat)checked_malloc(3 * sizeof(int)); int *fat p1 = p + 2; int a = *p1; } void test8(int *fat p1, int *fat p2) { - p1 = checked_malloc(3 * sizeof(int)); + p1 = (int *fat)checked_malloc(3 * sizeof(int)); p2 = p1 + 2; int a = *p1; int b = *p2; @@ -68,7 +69,7 @@ void test8(int *fat p1, int *fat p2) { struct K { int arr[3]; }; void test9() { - struct K *fat p1 = checked_malloc(sizeof(struct K) * 2); + struct K *fat p1 = (struct K *fat)checked_malloc(sizeof(struct K) * 2); int a = p1->arr[3]; int *fat p2 = &fat p1->arr[3]; *p2 = 10; @@ -80,8 +81,8 @@ void test9() { } void test10() { - int *fat *fat p = checked_malloc(sizeof(int *fat) * 10); - p[9] = checked_malloc(sizeof(int) * 10); + int *fat *fat p = (int *fat *fat)checked_malloc(sizeof(int *fat) * 10); + p[9] = (int *fat)checked_malloc(sizeof(int) * 10); p += 9; *p += 9; **p = 10; @@ -93,11 +94,34 @@ void bar(int *fat p) { } Foo foo = bar; void test11() { - int *fat p = checked_malloc(sizeof(int) * 10); + int *fat p = (int *fat)checked_malloc(sizeof(int) * 10); int *fat p1 = p + 9; foo(p1); } +int *fat return_fat() { + return (int *fat)checked_malloc(sizeof(int) * 10); +} + +void test12() { + int *fat p = return_fat() + 9; + *p = 10; +} + +void test13() { + int *p = (int *)return_fat(); + *p = 10; + *return_fat() = 10; +} + +void test14() { + int *fat * p; + int *fat *p1 = p + 1; + int *fat *p2 = (int *fat *)p + 1; + assert((long long)p2 - (long long)p == 16); + assert((long long)p2 - (long long)p == 16); +} + int main() { test1(); test2(); @@ -112,5 +136,8 @@ int main() { test9(); test10(); test11(); + test12(); + test13(); + test14(); return 0; } \ No newline at end of file diff --git a/clang/test/BSC/Positive/FatPtr/multi_level_pointer.cbs b/clang/test/BSC/Positive/FatPtr/multi_level_pointer.cbs index 2989912aba2b..b0cfd7a418fd 100644 --- a/clang/test/BSC/Positive/FatPtr/multi_level_pointer.cbs +++ b/clang/test/BSC/Positive/FatPtr/multi_level_pointer.cbs @@ -10,7 +10,7 @@ void test1() { } void test2() { - char *fat p = checked_malloc(sizeof(char) * 6); + char *fat p = (char *fat)checked_malloc(sizeof(char) * 6); p[0] = 'H'; p[1] = 'e'; p[2] = 'l'; @@ -20,10 +20,20 @@ void test2() { printf("%s!\n", (char *)*p1); } +void test3() { + char *fat *fat p = (char *fat *fat)checked_malloc(sizeof(char *fat) * 2); + p[0] = (char *fat)"Hello"; + p[1] = (char *fat)"world"; + printf("%s %s!\n", (char *)p[0], (char *)p[1]); + checked_free((void *fat)p); +} + int main() { test1(); test2(); + test3(); return 0; } // CHECK: Hello world! -// CHECK-NEXT: Hello! \ No newline at end of file +// CHECK-NEXT: Hello! +// CHECK-NEXT: Hello world! \ No newline at end of file diff --git a/clang/test/BSC/Positive/FatPtr/perform_anaysis/deref_loop/deref_loop.cbs b/clang/test/BSC/Positive/FatPtr/perform_anaysis/deref_loop/deref_loop.cbs deleted file mode 100644 index 43ba61776199..000000000000 --- a/clang/test/BSC/Positive/FatPtr/perform_anaysis/deref_loop/deref_loop.cbs +++ /dev/null @@ -1,39 +0,0 @@ -// RUN: %clang %s -enable-fat-ptr -o %test.output -// RUN: %test.output 20 - -#include - -long dealwithargs(int argc, char *argv[]) -{ - int num = 0; - if (argc > 2) - num = atoi(argv[2]); - return 1L << num; -} - -void test_raw(long n) { - long *p = malloc(sizeof(long)); - while (n > 0) { - *p = n; - n--; - } -} - -void test_fat(long n) { - long *fat p = checked_malloc(sizeof(long)); - while (n > 0) { - *p = n; - n--; - } -} - -int main(int argc, char *argv[]) { - long n = dealwithargs(argc, argv); - int flag = atoi(argv[1]); - if (flag == 0) { - test_raw(n); - } else { - test_fat(n); - } - return 0; -} diff --git a/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/bisort/bitonic.c b/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/bisort/bitonic.c deleted file mode 100644 index cc3f2cb278aa..000000000000 --- a/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/bisort/bitonic.c +++ /dev/null @@ -1,303 +0,0 @@ -// RUN: %clang %s -o %test.output -// RUN: %test.output 300 - -#include -#include - -#define CONST_m1 10000 -#define CONST_b 31415821 -#define RANGE 100 - -typedef struct node HANDLE; - -struct node { - int value; - HANDLE* left; - HANDLE *right; -}; - -typedef struct future_cell_int{ - HANDLE *value; -} future_cell_int; - -extern void *malloc(unsigned long); - -#define NIL ((HANDLE *) 0) - -int NumNodes, NDim; - -int random_c(int); - -int flag=0,foo=0; - -#define LocalNewNode(h,v) \ -{ \ - h = (HANDLE *) malloc(sizeof(struct node)); \ - h->value = v; \ - h->left = NIL; \ - h->right = NIL; \ - }; - -#define NewNode(h,v,procid) LocalNewNode(h,v) - -int mylog(int num) { - int j=0,k=1; - - while(k 3) - flag = atoi(argv[3]); - else - flag = 1; - - if (argc > 2) - NumNodes = atoi(argv[2]); - else - NumNodes = 4; - - if (argc > 1) - size = atoi(argv[1]); - else - size = 1 << 15; - - NDim = mylog(NumNodes); - return size; -} - -void InOrder(HANDLE *h) { - HANDLE *l, *r; - if ((h != NIL)) { - l = h->left; - r = h->right; - InOrder(l); - static unsigned char counter = 0; - if (counter++ == 0) /* reduce IO */ - printf("%d @ 0x%x\n",h->value, 0); - InOrder(r); - } -} - -int mult(int p, int q) { - int p1, p0, q1, q0; - - p1 = p/CONST_m1; p0 = p%CONST_m1; - q1 = q/CONST_m1; q0 = q%CONST_m1; - return ((p0*q1+p1*q0) % CONST_m1)*CONST_m1+p0*q0; -} - -/* Generate the nth random_c # */ -int skiprand(int seed, int n) { - for (; n; n--) seed=random_c(seed); - return seed; -} - -int random_c(int seed) { - return mult(seed,CONST_b)+1; -} - -HANDLE* RandTree(int n, int seed, int node, int level) { - int next_val,my_name; - future_cell_int f_left, f_right; - HANDLE *h; - my_name=foo++; - if (n > 1) { - int newnode; - if (levelleft = f_left.value; - h->right = f_right.value; - } else { - h = 0; - } - return h; -} - -void SwapValue(HANDLE *l, HANDLE *r) { - int temp,temp2; - - temp = l->value; - temp2 = r->value; - r->value = temp; - l->value = temp2; -} - -void -/***********/ -SwapValLeft(HANDLE *l, HANDLE *r, HANDLE *ll, HANDLE *rl, int lval, int rval) -/***********/ -{ - r->value = lval; - r->left = ll; - l->left = rl; - l->value = rval; -} - - -void -/************/ -SwapValRight(HANDLE *l, HANDLE *r, HANDLE *lr, HANDLE *rr, int lval, int rval) -/************/ -{ - r->value = lval; - r->right = lr; - l->right = rr; - l->value = rval; - /*printf("Swap Val Right l 0x%x,r 0x%x val: %d %d\n",l,r,lval,rval);*/ -} - -int -/********************/ -Bimerge(HANDLE *root, int spr_val, int dir) -/********************/ -{ int rightexchange; - int elementexchange; - HANDLE *pl,*pll,*plr; - HANDLE *pr,*prl,*prr; - HANDLE *rl; - HANDLE *rr; - int rv,lv; - - - /*printf("enter bimerge %x\n", root);*/ - rv = root->value; - - pl = root->left; - pr = root->right; - rightexchange = ((rv > spr_val) ^ dir); - if (rightexchange) - { - root->value = spr_val; - spr_val = rv; - } - - while ((pl != NIL)) - { - /*printf("pl = 0x%x,pr = 0x%x\n",pl,pr);*/ - lv = pl->value; /* <------- 8.2% load penalty */ - pll = pl->left; - plr = pl->right; /* <------- 1.35% load penalty */ - rv = pr->value; /* <------ 57% load penalty */ - prl = pr->left; /* <------ 7.6% load penalty */ - prr = pr->right; /* <------ 7.7% load penalty */ - elementexchange = ((lv > rv) ^ dir); - if (rightexchange) - if (elementexchange) - { - SwapValRight(pl,pr,plr,prr,lv,rv); - pl = pll; - pr = prl; - } - else - { pl = plr; - pr = prr; - } - else - if (elementexchange) - { - SwapValLeft(pl,pr,pll,prl,lv,rv); - pl = plr; - pr = prr; - } - else - { pl = pll; - pr = prl; - } - } - if ((root->left != NIL)) - { - int value; - rl = root->left; - rr = root->right; - value = root->value; - - root->value=Bimerge(rl,value,dir); - spr_val=Bimerge(rr,spr_val,dir); - } - /*printf("exit bimerge %x\n", root);*/ - return spr_val; -} - -int -/*******************/ -Bisort(HANDLE *root, int spr_val, int dir) -/*******************/ -{ HANDLE *l; - HANDLE *r; - int val; - /*printf("bisort %x\n", root);*/ - if (root->left == NIL) /* <---- 8.7% load penalty */ - { - if (((root->value > spr_val) ^ dir)) - { - val = spr_val; - spr_val = root->value; - root->value =val; - } - } - else - { - int ndir; - l = root->left; - r = root->right; - val = root->value; - /*printf("root 0x%x, l 0x%x, r 0x%x\n", root,l,r);*/ - root->value=Bisort(l,val,dir); - ndir = !dir; - spr_val=Bisort(r,spr_val,ndir); - spr_val=Bimerge(root,spr_val,dir); - } - /*printf("exit bisort %x\n", root);*/ - return spr_val; -} - -int main(int argc, char **argv) { - HANDLE *h; - int sval; - int n; - - n = dealwithargs(argc,argv); - - printf("Bisort with %d size of dim %d\n", n, NDim); - - h = RandTree(n,12345768,0,0); - sval = random_c(245867) % RANGE; - if (flag) { - InOrder(h); - printf("%d\n",sval); - } - printf("**************************************\n"); - printf("BEGINNING BITONIC SORT ALGORITHM HERE\n"); - printf("**************************************\n"); - - sval=Bisort(h,sval,0); - - if (flag) { - printf("Sorted Tree:\n"); - InOrder(h); - printf("%d\n",sval); - } - - sval=Bisort(h,sval,1); - - if (flag) { - printf("Sorted Tree:\n"); - InOrder(h); - printf("%d\n",sval); - } - - return 0; -} diff --git a/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/bisort_bsc/bitonic.cbs b/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/bisort_bsc/bitonic.cbs deleted file mode 100644 index 8065f9620351..000000000000 --- a/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/bisort_bsc/bitonic.cbs +++ /dev/null @@ -1,302 +0,0 @@ -// RUN: %clang %s -enable-fat-ptr -o %test.output -// RUN: %test.output 3000 - -#include -#include -#include - -#define CONST_m1 10000 -#define CONST_b 31415821 -#define RANGE 100 - -typedef struct node HANDLE; - -struct node { - int value; - HANDLE *fat left; - HANDLE *fat right; -}; - -typedef struct future_cell_int{ - HANDLE *fat value; -} future_cell_int; - -int NumNodes, NDim; - -int random_c(int); - -int flag=0,foo=0; - -//FIXME -#define LocalNewNode(h,v) \ -{ \ - h = checked_malloc(sizeof(HANDLE)); \ - h->value = v; \ - h->left = nullptr; \ - h->right = nullptr; \ -}; - -#define NewNode(h,v,procid) LocalNewNode(h,v) - -int mylog(int num) { - int j=0,k=1; - - while(k 3) - flag = atoi(argv[3]); - else - flag = 1; - - if (argc > 2) - NumNodes = atoi(argv[2]); - else - NumNodes = 4; - - if (argc > 1) - size = atoi(argv[1]); - else - size = 1 << 15; - - NDim = mylog(NumNodes); - return size; -} - -void InOrder(HANDLE *fat h) { - HANDLE *fat l, *fat r; - if ((h != nullptr)) { - l = h->left; - r = h->right; - InOrder(l); - static unsigned char counter = 0; - if (counter++ == 0) /* reduce IO */ - // printf("%d @ 0x%x\n",h->value, 0); - InOrder(r); - } -} - -int mult(int p, int q) { - int p1, p0, q1, q0; - - p1 = p/CONST_m1; p0 = p%CONST_m1; - q1 = q/CONST_m1; q0 = q%CONST_m1; - return ((p0*q1+p1*q0) % CONST_m1)*CONST_m1+p0*q0; -} - -/* Generate the nth random_c # */ -int skiprand(int seed, int n) { - for (; n; n--) seed=random_c(seed); - return seed; -} - -int random_c(int seed) { - return mult(seed, CONST_b) + 1; -} - -HANDLE *fat RandTree(int n, int seed, int node, int level) { - int next_val,my_name; - future_cell_int f_left, f_right; - HANDLE *fat h; - my_name=foo++; - if (n > 1) { - int newnode; - if (levelleft = f_left.value; - h->right = f_right.value; - } else { - h = checked_malloc(sizeof(HANDLE)); //FIXME - } - return h; -} - -void SwapValue(HANDLE *fat l, HANDLE *fat r) { - int temp,temp2; - - temp = l->value; - temp2 = r->value; - r->value = temp; - l->value = temp2; -} - -void -/***********/ -SwapValLeft(HANDLE *fat l, HANDLE *fat r, HANDLE *fat ll, HANDLE *fat rl, int lval, int rval) -/***********/ -{ - r->value = lval; - r->left = ll; - l->left = rl; - l->value = rval; -} - - -void -/************/ -SwapValRight(HANDLE *fat l, HANDLE *fat r, HANDLE *fat lr, HANDLE *fat rr, int lval, int rval) -/************/ -{ - r->value = lval; - r->right = lr; - l->right = rr; - l->value = rval; - /*printf("Swap Val Right l 0x%x,r 0x%x val: %d %d\n",l,r,lval,rval);*/ -} - -int -/********************/ -Bimerge(HANDLE *fat root, int spr_val, int dir) -/********************/ -{ int rightexchange; - int elementexchange; - HANDLE *fat pl,*fat pll,*fat plr; - HANDLE *fat pr,*fat prl,*fat prr; - HANDLE *fat rl; - HANDLE *fat rr; - int rv,lv; - - - /*printf("enter bimerge %x\n", root);*/ - rv = root->value; - - pl = root->left; - pr = root->right; - rightexchange = ((rv > spr_val) ^ dir); - if (rightexchange) - { - root->value = spr_val; - spr_val = rv; - } - - while ((pl != nullptr)) - { - /*printf("pl = 0x%x,pr = 0x%x\n",pl,pr);*/ - lv = pl->value; /* <------- 8.2% load penalty */ - pll = pl->left; - plr = pl->right; /* <------- 1.35% load penalty */ - rv = pr->value; /* <------ 57% load penalty */ - prl = pr->left; /* <------ 7.6% load penalty */ - prr = pr->right; /* <------ 7.7% load penalty */ - elementexchange = ((lv > rv) ^ dir); - if (rightexchange) - if (elementexchange) - { - SwapValRight(pl,pr,plr,prr,lv,rv); - pl = pll; - pr = prl; - } - else - { pl = plr; - pr = prr; - } - else - if (elementexchange) - { - SwapValLeft(pl,pr,pll,prl,lv,rv); - pl = plr; - pr = prr; - } - else - { pl = pll; - pr = prl; - } - } - HANDLE *fat root_left = root->left; - if (root_left != nullptr) - { - int value; - rl = root->left; - rr = root->right; - value = root->value; - - root->value=Bimerge(rl,value,dir); - spr_val=Bimerge(rr,spr_val,dir); - } - /*printf("exit bimerge %x\n", root);*/ - return spr_val; -} - -int -/*******************/ -Bisort(HANDLE *fat root, int spr_val, int dir) -/*******************/ -{ HANDLE *fat l; - HANDLE *fat r; - int val; - /*printf("bisort %x\n", root);*/ - HANDLE *fat root_left = root->left; - if (root_left == nullptr) /* <---- 8.7% load penalty */ - { - if (((root->value > spr_val) ^ dir)){ - val = spr_val; - spr_val = root->value; - root->value =val; - } - } - else - { - int ndir; - l = root->left; - r = root->right; - val = root->value; - /*printf("root 0x%x, l 0x%x, r 0x%x\n", root,l,r);*/ - root->value=Bisort(l,val,dir); - ndir = !dir; - spr_val=Bisort(r,spr_val,ndir); - spr_val=Bimerge(root,spr_val,dir); - } - /*printf("exit bisort %x\n", root);*/ - return spr_val; -} - -int main(int argc, char **argv) { - HANDLE *fat h; - int sval; - int n; - - n = dealwithargs(argc,argv); - - printf("Bisort with %d size of dim %d\n", n, NDim); - - h = RandTree(n,12345768,0,0); - sval = random_c(245867) % RANGE; - if (flag) { - InOrder(h); - printf("%d\n",sval); - } - printf("**************************************\n"); - printf("BEGINNING BITONIC SORT ALGORITHM HERE\n"); - printf("**************************************\n"); - - sval=Bisort(h,sval,0); - - if (flag) { - printf("Sorted Tree:\n"); - InOrder(h); - printf("%d\n",sval); - } - - sval=Bisort(h,sval,1); - - if (flag) { - printf("Sorted Tree:\n"); - InOrder(h); - printf("%d\n",sval); - } - - return 0; -} diff --git a/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd/node.c b/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd/node.c deleted file mode 100644 index c8af4371bf4b..000000000000 --- a/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd/node.c +++ /dev/null @@ -1,233 +0,0 @@ -// RUN: %clang %s -DTORONTO -o %test.output -// RUN: %test.output 16 - -/* For copyright information, see olden_v1.0/COPYRIGHT */ - -/* node.c - */ -#ifndef TORONTO -#include -#include -#include "mem-ref.h" -#endif - -#ifdef FUTURES -#include "future-cell.h" -#endif - -#include -#include "tree.h" - -#ifdef TORONTO -int NumNodes; -#else -int __NumNodes; -#endif - -typedef struct { - long level; -} startmsg_t; - -#ifndef TORONTO -void filestuff() -{ - CMMD_fset_io_mode(stdout, CMMD_independent); - fcntl(fileno(stdout), F_SETFL, O_APPEND); - if (CMMD_self_address()) exit(0); - __InitRegs(0); -} -#endif - -int dealwithargs(int argc, char *argv[]) -{ - int level; - -#ifdef TORONTO - if (argc > 2) - NumNodes = atoi(argv[2]); - else - NumNodes = 4; -#else - if (argc > 2) - __NumNodes = atoi(argv[2]); - else - __NumNodes = 4; -#endif - - if (argc > 1) - level = atoi(argv[1]); - else - level = 16; - - return level; -} - -tree_t *TreeAlloc (int level, int lo, int proc) { - if (level == 0) - return NULL; - else { - struct tree *new, *right, *left; - new = (struct tree *) malloc(sizeof(tree_t)); - left = TreeAlloc(level -1, lo+proc/2, proc/2); - right=TreeAlloc(level-1,lo,proc/2); - new->val = 1; - new->left = (struct tree *) left; - new->right = (struct tree *) right; - return new; - } -} - -/* TreeAdd: - */ -int TreeAdd (tree_t *t) -{ - if (t == NULL) { - return 0; - } - else { -#ifdef FUTURES - future_cell_int leftval; - int rightval; - tree_t *tleft, *tright; - int value; - - tleft = t->left; - RPC(tleft, tleft,TreeAdd,&(leftval)); - NOTEST(); - tright = t->right; - rightval = TreeAdd(tright); - RTOUCH(&leftval); - /*chatting("after touch @ 0x%x\n",t);*/ - value = t->val; - /*chatting("returning from treeadd %d\n",*/ - /*leftval.value + rightval.value + value);*/ - return leftval.value + rightval + value; - RETEST(); -#else - int leftval; - int rightval; - tree_t *tleft, *tright; - int value; - - tleft = t->left; /* <---- 57% load penalty */ - leftval = TreeAdd(tleft); - tright = t->right; /* <---- 11.4% load penalty */ - rightval = TreeAdd(tright); - /*chatting("after touch\n");*/ - value = t->val; - /*chatting("returning from treeadd %d\n",*/ - /*leftval.value + rightval.value + value);*/ - return leftval + rightval + value; -#endif - } -} /* end of TreeAdd */ - -int main (int argc, char *argv[]) -{ - tree_t *root; - int level,result; - -#ifdef FUTURES - level = SPMDInit(argc,argv); -#else -#ifndef TORONTO - filestuff(); -#endif - level = dealwithargs(argc, argv); -#endif -#ifndef TORONTO - CMMD_node_timer_clear(0); - CMMD_node_timer_clear(1); -#endif -#ifdef TORONTO - chatting("Treeadd with %d levels on %d processors \n", - level, NumNodes); -#else - chatting("Treeadd with %d levels on %d processors \n", - level, __NumNodes); -#endif - /* only processor 0 will continue here. */ - chatting("About to enter TreeAlloc\n"); -#ifndef TORONTO - CMMD_node_timer_start(0); -#endif - -#ifdef TORONTO - root = TreeAlloc (level, 0, NumNodes); -#else - root = TreeAlloc (level, 0, __NumNodes); -#endif - -#ifndef TORONTO - CMMD_node_timer_stop(0); -#endif - chatting("About to enter TreeAdd\n"); - -#ifndef PLAIN - ClearAllStats(); -#endif -#ifndef TORONTO - CMMD_node_timer_start(1); -#endif -{ int i; for (i = 0; i < 100; ++i) - result = TreeAdd (root); -} -#ifndef TORONTO - CMMD_node_timer_stop(1); -#endif - chatting("Received result of %d\n",result); - -#ifndef TORONTO - chatting("Alloc Time = %f seconds\n", CMMD_node_timer_elapsed(0)); - chatting(/* TreeAdd: - */ -int TreeAdd (tree_t *t) -{ - if (t == NULL) { - return 0; - } - else { -#ifdef FUTURES - future_cell_int leftval; - int rightval; - tree_t *tleft, *tright; - int value; - - tleft = t->left; - RPC(tleft, tleft,TreeAdd,&(leftval)); - NOTEST(); - tright = t->right; - rightval = TreeAdd(tright); - RTOUCH(&leftval); - /*chatting("after touch @ 0x%x\n",t);*/ - value = t->val; - /*chatting("returning from treeadd %d\n",*/ - /*leftval.value + rightval.value + value);*/ - return leftval.value + rightval + value; - RETEST(); -#else - int leftval; - int rightval; - tree_t *tleft, *tright; - int value; - - tleft = t->left; /* <---- 57% load penalty */ - leftval = TreeAdd(tleft); - tright = t->right; /* <---- 11.4% load penalty */ - rightval = TreeAdd(tright); - /*chatting("after touch\n");*/ - value = t->val; - /*chatting("returning from treeadd %d\n",*/ - /*leftval.value + rightval.value + value);*/ - return leftval + rightval + value; -#endif - } -} /* end of TreeAdd */ -"Add Time = %f seconds\n", CMMD_node_timer_elapsed(1)); -#endif - -#ifdef FUTURES - __ShutDown(); -#endif - exit(0); -} diff --git a/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd/tree.h b/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd/tree.h deleted file mode 100644 index b8d075e41cb0..000000000000 --- a/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd/tree.h +++ /dev/null @@ -1,25 +0,0 @@ -/* For copyright information, see olden_v1.0/COPYRIGHT */ - -/* tree.h - */ - -#ifdef TORONTO -#include -#define chatting printf -#define PLAIN -#endif - -typedef struct tree { - int val; - struct tree *left, *right; -} tree_t; - -tree_t *TreeAlloc (int level, int lo, int hi); -int TreeAdd (tree_t *t); - - - - - - - diff --git a/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd_bsc/node.cbs b/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd_bsc/node.cbs deleted file mode 100644 index ad2fac0b6695..000000000000 --- a/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd_bsc/node.cbs +++ /dev/null @@ -1,193 +0,0 @@ -// RUN: %clang %s -DTORONTO -enable-fat-ptr -o %test.output -// RUN: %test.output 16 - -#ifndef TORONTO -#include -#include -#endif - -#ifdef FUTURES -#include "future-cell.h" -#endif - -#ifndef TORONTO -#include "mem-ref.h" -#endif - -#include -#include "tree.hbs" - -#ifdef TORONTO -int NumNodes; -#else -int __NumNodes; -#endif - -extern int atoi(const char *); - -#ifndef TORONTO -void filestuff() -{ - CMMD_fset_io_mode(stdout, CMMD_independent); - fcntl(fileno(stdout), F_SETFL, O_APPEND); - if (CMMD_self_address()) exit(0); - __InitRegs(0); -} -#endif - -int dealwithargs(int argc, char *argv[]) -{ - int level; - -#ifdef TORONTO - if (argc > 2) - NumNodes = atoi(argv[2]); - else - NumNodes = 4; -#else - if (argc > 2) - __NumNodes = atoi(argv[2]); - else - __NumNodes = 4; -#endif - - if (argc > 1) - level = atoi(argv[1]); - else - level = 16; - - return level; -} - -typedef struct { - long level; -} startmsg_t; - -tree_t *fat TreeAlloc (int level, int lo, int proc) { - if (level == 0) - return nullptr; - else { - tree_t *fat new; - tree_t *fat right; - tree_t *fat left; - new = checked_malloc(sizeof(tree_t)); - left = TreeAlloc(level -1, lo+proc/2, proc/2); - right = TreeAlloc(level-1,lo,proc/2); - new->val = 1; - new->left = left; - new->right = right; - return new; - } -} - -/* TreeAdd: - */ -int TreeAdd (tree_t *fat t) -{ - if (t == nullptr) { - return 0; - } - else { -#ifdef FUTURES - future_cell_int leftval; - int rightval; - tree_t *fat tleft, *fat tright; - int value; - - tleft = t->left; - RPC(tleft, tleft,TreeAdd,&(leftval)); - NOTEST(); - tright = t->right; - rightval = TreeAdd(tright); - RTOUCH(&leftval); - /*chatting("after touch @ 0x%x\n",t);*/ - value = t->val; - /*chatting("returning from treeadd %d\n",*/ - /*leftval.value + rightval.value + value);*/ - return leftval.value + rightval + value; - RETEST(); -#else - int leftval; - int rightval; - tree_t *fat tleft; - tree_t *fat tright; - int value; - - tleft = t->left; /* <---- 57% load penalty */ - leftval = TreeAdd(tleft); - tright = t->right; /* <---- 11.4% load penalty */ - rightval = TreeAdd(tright); - /*chatting("after touch\n");*/ - value = t->val; - /*chatting("returning from treeadd %d\n",*/ - /*leftval.value + rightval.value + value);*/ - return leftval + rightval + value; -#endif - } -} /* end of TreeAdd */ - -int main (int argc, char *argv[]) -{ - tree_t *fat root; - int level, result; - -#ifdef FUTURES - level = SPMDInit(argc,argv); -#else -#ifndef TORONTO - filestuff(); -#endif - level = dealwithargs(argc, argv); -#endif -#ifndef TORONTO - CMMD_node_timer_clear(0); - CMMD_node_timer_clear(1); -#endif -#ifdef TORONTO - chatting("Treeadd with %d levels on %d processors \n", - level, NumNodes); -#else - chatting("Treeadd with %d levels on %d processors \n", - level, __NumNodes); -#endif - /* only processor 0 will continue here. */ - chatting("About to enter TreeAlloc\n"); -#ifndef TORONTO - CMMD_node_timer_start(0); -#endif - int a0 = 0; -#ifdef TORONTO - root = TreeAlloc (level, a0, NumNodes); -#else - root = TreeAlloc (level, a0, __NumNodes); -#endif - -#ifndef TORONTO - CMMD_node_timer_stop(0); -#endif - chatting("About to enter TreeAdd\n"); - -#ifndef PLAIN - ClearAllStats(); -#endif -#ifndef TORONTO - CMMD_node_timer_start(1); -#endif -{ int i; for (i = 0; i < 100; ++i) - result = TreeAdd(root); -} -#ifndef TORONTO - CMMD_node_timer_stop(1); -#endif - chatting("Received result of %d\n",result); - -#ifndef TORONTO - chatting("Alloc Time = %f seconds\n", CMMD_node_timer_elapsed(0)); - chatting("Add Time = %f seconds\n", CMMD_node_timer_elapsed(1)); -#endif - -#ifdef FUTURES - __ShutDown(); -#endif - exit(0); -} diff --git a/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd_bsc/tree.hbs b/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd_bsc/tree.hbs deleted file mode 100644 index 0b8014a09f3d..000000000000 --- a/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd_bsc/tree.hbs +++ /dev/null @@ -1,29 +0,0 @@ -/* For copyright information, see olden_v1.0/COPYRIGHT */ - -/* tree.h - */ - -#ifdef TORONTO -#include -#define chatting printf -#define PLAIN -#endif - -#include -typedef struct tree tree_t; - -struct tree { - int val; - tree_t *fat left; - tree_t *fat right; -}; - -tree_t *fat TreeAlloc (int level, int lo, int hi); -int TreeAdd (tree_t *fat t); - - - - - - - diff --git a/clang/test/BSC/Positive/FatPtr/stack.cbs b/clang/test/BSC/Positive/FatPtr/stack.cbs index c44dab32adca..22b307601f5b 100644 --- a/clang/test/BSC/Positive/FatPtr/stack.cbs +++ b/clang/test/BSC/Positive/FatPtr/stack.cbs @@ -17,8 +17,18 @@ void test2() { *p1 = 10; } +void test3() { + int arr[2] = { 0, 1 }; + int *fat p = &fat arr[0]; + int a = 10; + if (*p++ == 0) { + a = *p++ == 0 ? *p++ : 0; + } +} + int main() { test1(); test2(); + test3(); return 0; } \ No newline at end of file -- Gitee From 4f659828d67d50f0e8ccde7e32e6b78f43a9397d Mon Sep 17 00:00:00 2001 From: xiaoyu Date: Wed, 26 Feb 2025 17:51:35 +0800 Subject: [PATCH 06/29] [FatPtr] Adjust the redundant check analysis results to be stored in the ASTContext, which optimizes the memory usage of Expr and add some test suits for fatptr redundant check analysis. --- clang/include/clang/AST/ASTContext.h | 1 + clang/include/clang/AST/Expr.h | 7 --- .../clang/Analysis/Analyses/BSCFatPtrCheck.h | 4 +- clang/lib/AST/BSC/TypeBSC.cpp | 2 +- clang/lib/Analysis/BSCFatPtrCheck.cpp | 33 ++++++------ clang/lib/Analysis/ThreadSafetyCommon.cpp | 6 +-- clang/lib/CodeGen/CGCall.cpp | 2 + clang/lib/Sema/BSC/SemaBSCFatPtr.cpp | 21 +++++--- clang/lib/Sema/SemaExpr.cpp | 9 ++-- clang/lib/Sema/SemaStmt.cpp | 1 - .../FatPtr/redundant_check/func_call.cbs | 52 +++++++++++++++++++ .../FatPtr/redundant_check/if_stmt.cbs | 46 ++++++++++++++++ .../FatPtr/redundant_check/member_visit.cbs | 52 +++++++++++++++++++ 13 files changed, 195 insertions(+), 41 deletions(-) create mode 100644 clang/test/BSC/Positive/FatPtr/redundant_check/func_call.cbs create mode 100644 clang/test/BSC/Positive/FatPtr/redundant_check/if_stmt.cbs create mode 100644 clang/test/BSC/Positive/FatPtr/redundant_check/member_visit.cbs diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index 64d6e5ec52a9..855103b0e9a4 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -497,6 +497,7 @@ public: mutable llvm::DenseMap> BSCDesugaredMap; llvm::SmallVector InstantiationVec; + mutable llvm::DenseMap FatPtrCheckStatusMap; #endif diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h index 98615e310b26..867dede1da1b 100644 --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -151,13 +151,6 @@ public: bool IsDesugaredCastExpr = false; // Add BSC Member func desugar flag. bool IsDesugaredBSCMethodCall = false; - // Stores the check status of a fat pointer used in the expression. - // Possible values: - // Unchecked: 0 - No checks have been performed. - // KeyCheckedOnly: 1 - Only the key was checked. - // OffsetCheckedOnly: 2 - Only the offset was checked. - // BothChecked: 3 - Both the key and the offset were checked. - uint8_t FatPtrCheckedStatus = 0; // Default: Unchecked SourceLocation getExtendedTypeBeginLoc() { return EBLoc; } void setExtendedTypeBeginLoc(SourceLocation L) { EBLoc = L; } diff --git a/clang/include/clang/Analysis/Analyses/BSCFatPtrCheck.h b/clang/include/clang/Analysis/Analyses/BSCFatPtrCheck.h index e9df8841a989..f353e43e2e7c 100644 --- a/clang/include/clang/Analysis/Analyses/BSCFatPtrCheck.h +++ b/clang/include/clang/Analysis/Analyses/BSCFatPtrCheck.h @@ -1,4 +1,4 @@ -//===- BSCNullabilityCheck.h - Nullability Check for Source CFGs -*- BSC ---*// +//===- BSCFatPtrCheck.h - FatPtr Check Reduntant Analysis for Source CFGs -*- BSC ---*// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,7 +6,7 @@ // //===----------------------------------------------------------------------===// // -// This file implements BSC Pointer Nullability Check for source-level CFGs. +// This file implements BSC Fat Pointer Check Reduntant Analysis for source-level CFGs. // //===----------------------------------------------------------------------===// diff --git a/clang/lib/AST/BSC/TypeBSC.cpp b/clang/lib/AST/BSC/TypeBSC.cpp index 5f51c1388ca9..29e91229b0ed 100644 --- a/clang/lib/AST/BSC/TypeBSC.cpp +++ b/clang/lib/AST/BSC/TypeBSC.cpp @@ -68,7 +68,7 @@ bool Type::hasBorrowFields() const { // T *fat, T *fat *, T *fat[n], struct S which has fat fields... bool QualType::hasFat() const { - auto Ty = getTypePtr(); + const Type * Ty = getTypePtr(); // getCanonicalType from FunctionProtoType will lose fat qualifier // of return type or parameter types, from ArrayType will lose fat // qualifier of element type, so we handle these two kinds type first. diff --git a/clang/lib/Analysis/BSCFatPtrCheck.cpp b/clang/lib/Analysis/BSCFatPtrCheck.cpp index 86900a4c8a04..e93ba320e750 100644 --- a/clang/lib/Analysis/BSCFatPtrCheck.cpp +++ b/clang/lib/Analysis/BSCFatPtrCheck.cpp @@ -257,7 +257,7 @@ void TransferFunctions::VisitUnaryOperator(UnaryOperator *UO) { FP = {VD, ""}; if (VD->getType().isFatPtrType() && CurrStatus.count(FP)) { // Label the check kind in the sema phase, for unary expression: *p; - DRE->FatPtrCheckedStatus = static_cast(CurrStatus[FP]); + Ctx.FatPtrCheckStatusMap[DRE] = static_cast(CurrStatus[FP]); // update the redundant check status CurrStatus[FP] = FatPtrCheckStatus::BothChecked; } @@ -268,7 +268,7 @@ void TransferFunctions::VisitUnaryOperator(UnaryOperator *UO) { if (FD->getType().isFatPtrType()) { VisitMEForFieldPath(ME, FP); if (CurrStatus.count(FP)) { - ME->FatPtrCheckedStatus = static_cast(CurrStatus[FP]); + Ctx.FatPtrCheckStatusMap[ME] = static_cast(CurrStatus[FP]); CurrStatus[FP] = FatPtrCheckStatus::BothChecked; } } @@ -311,7 +311,7 @@ void TransferFunctions::VisitMemberExpr(MemberExpr *ME) { FP = {VD, ""}; if (VD->getType().isFatPtrType() && CurrStatus.count(FP)) { // Label the check kind in the sema phase, for member expression: p -> a; - DRE->FatPtrCheckedStatus = static_cast(CurrStatus[FP]); + Ctx.FatPtrCheckStatusMap[DRE] = static_cast(CurrStatus[FP]); // update the redundant check status CurrStatus[FP] = FatPtrCheckStatus::BothChecked; } @@ -322,7 +322,7 @@ void TransferFunctions::VisitMemberExpr(MemberExpr *ME) { if (FD->getType().isFatPtrType()) { VisitMEForFieldPath(SubME, FP); if (CurrStatus.count(FP)) { - SubME->FatPtrCheckedStatus = static_cast(CurrStatus[FP]); + Ctx.FatPtrCheckStatusMap[SubME] = static_cast(CurrStatus[FP]); CurrStatus[FP] = FatPtrCheckStatus::BothChecked; } } @@ -344,21 +344,22 @@ void TransferFunctions::VisitMemberExpr(MemberExpr *ME) { bool TransferFunctions::mayFreeCallExpr(CallExpr *CE) { // Check if it is an direct call, for indirect call, we assuems it may free if (FunctionDecl *CalleeDecl = CE->getDirectCallee()) { + // Check if the function is in the whitelist + if (MayFreeFnWL.find(CalleeDecl->getNameAsString()) != MayFreeFnWL.end()) { + return false; + } // Add the CalleeDecl to the call graph CG.addToCallGraph(CalleeDecl); CallGraphNode *Node = CG.getNode(CalleeDecl); + // Current callee has no other function call in call graph if (!Node || Node->empty()) { - // Callee Function definition is in the current module, and has no other function call - if (CalleeDecl->hasBody()) { - return false; - } - // Check if the function is in the whitelist - return MayFreeFnWL.find(CalleeDecl->getNameAsString()) == MayFreeFnWL.end(); + // If there is no function definition, it is considered mayfree + return !CalleeDecl->hasBody(); } else { // Check if the CalleeDecl calls any function that may free heap objects bool mayFree = false; - for (CallGraphNode::iterator It = Node->begin(); It != Node->end(); - ++It) { + for (CallGraphNode::iterator It = Node->begin(), end = Node->end(); + It != end; ++It) { CallGraphNode *CalleeNode = It->Callee; if (!CalleeNode) continue; @@ -429,7 +430,7 @@ void TransferFunctions::VisitCStyleCastExpr(CStyleCastExpr *CSCE) { FP = {VD, ""}; if (VD->getType().isFatPtrType() && CurrStatus.count(FP)) { // Label the check kind in the sema phase, for CStyleCast expression: (int *)p; - DRE->FatPtrCheckedStatus = static_cast(CurrStatus[FP]); + Ctx.FatPtrCheckStatusMap[DRE] = static_cast(CurrStatus[FP]); // update the redundant check status CurrStatus[FP] = FatPtrCheckStatus::BothChecked; } @@ -440,7 +441,7 @@ void TransferFunctions::VisitCStyleCastExpr(CStyleCastExpr *CSCE) { if (FD->getType().isFatPtrType()) { VisitMEForFieldPath(ME, FP); if (CurrStatus.count(FP)) { - ME->FatPtrCheckedStatus = static_cast(CurrStatus[FP]); + Ctx.FatPtrCheckStatusMap[ME] = static_cast(CurrStatus[FP]); CurrStatus[FP] = FatPtrCheckStatus::BothChecked; } } @@ -455,7 +456,7 @@ void TransferFunctions::VisitArraySubscriptExpr(ArraySubscriptExpr *ASE) { FP = {VD, ""}; if (VD->getType().isFatPtrType() && CurrStatus.count(FP)) { // the offset check is not redundant, must be remained for array subscript expression: p[i]; - DRE->FatPtrCheckedStatus = + Ctx.FatPtrCheckStatusMap[DRE] = static_cast(CurrStatus[FP]) & static_cast(FatPtrCheckStatus::KeyCheckedOnly); // update the redundant check status to KeyCheckedOnly @@ -468,7 +469,7 @@ void TransferFunctions::VisitArraySubscriptExpr(ArraySubscriptExpr *ASE) { if (FD->getType().isFatPtrType()) { VisitMEForFieldPath(ME, FP); if (CurrStatus.count(FP)) { - ME->FatPtrCheckedStatus = + Ctx.FatPtrCheckStatusMap[ME] = static_cast(CurrStatus[FP]) & static_cast(FatPtrCheckStatus::KeyCheckedOnly); CurrStatus[FP] = FatPtrCheckStatus::BothChecked; diff --git a/clang/lib/Analysis/ThreadSafetyCommon.cpp b/clang/lib/Analysis/ThreadSafetyCommon.cpp index 89b71e7a37e4..732901769dde 100644 --- a/clang/lib/Analysis/ThreadSafetyCommon.cpp +++ b/clang/lib/Analysis/ThreadSafetyCommon.cpp @@ -462,7 +462,7 @@ til::SExpr *SExprBuilder::translateUnaryOperator(const UnaryOperator *UO, return new (Arena) til::Undefined(UO); case UO_AddrOf: - #if ENABLE_BSC +#if ENABLE_BSC case UO_AddrMut: case UO_AddrConst: case UO_AddrFat: @@ -484,10 +484,10 @@ til::SExpr *SExprBuilder::translateUnaryOperator(const UnaryOperator *UO, // We treat these as no-ops case UO_Deref: case UO_Plus: - #if ENABLE_BSC +#if ENABLE_BSC case UO_AddrMutDeref: case UO_AddrConstDeref: - #endif +#endif return translate(UO->getSubExpr(), Ctx); case UO_Minus: diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp index 4ff156690c82..366de5b41d51 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -4185,6 +4185,7 @@ void CodeGenFunction::EmitCallArgs( CallExpr::const_arg_iterator Arg = ArgRange.begin(); for (QualType Ty : ArgTypes) { assert(Arg != ArgRange.end() && "Running over edge of argument list!"); +#if ENABLE_BSC if (Ty.getCanonicalType()->isRecordType()) if (RecordDecl *RD = Ty.getCanonicalType()->getAs()->getDecl()) @@ -4192,6 +4193,7 @@ void CodeGenFunction::EmitCallArgs( ++Arg; continue; } +#endif assert( (isGenericMethod || Ty->isVariablyModifiedType() || Ty.getNonReferenceType()->isObjCRetainableType() || diff --git a/clang/lib/Sema/BSC/SemaBSCFatPtr.cpp b/clang/lib/Sema/BSC/SemaBSCFatPtr.cpp index 21c3ed3c4eb0..c04f2ec8753d 100644 --- a/clang/lib/Sema/BSC/SemaBSCFatPtr.cpp +++ b/clang/lib/Sema/BSC/SemaBSCFatPtr.cpp @@ -2089,16 +2089,21 @@ MemberExpr *TransformFatPtr::BuildMemberForFatPtr(Expr *FatPtrBaseE, MemberExpr *TransformFatPtr::BuildMemberRawPtrAndInsertCheckCallForFatPtr( Expr *FatPtrBaseE) { SourceLocation SLoc = FatPtrBaseE->getBeginLoc(); - if ((InsertCheckVersionCall || InsertCheckOffsetCall.first) && FatPtrBaseE->FatPtrCheckedStatus) { - if (FatPtrBaseE->FatPtrCheckedStatus == 1 || FatPtrBaseE->FatPtrCheckedStatus == 3) { - InsertCheckVersionCall = false; - } - if (FatPtrBaseE->FatPtrCheckedStatus == 2 || FatPtrBaseE->FatPtrCheckedStatus == 3) { - InsertCheckOffsetCall.first = false; - InsertCheckOffsetCall.second = nullptr; - } + // Determine whether to remove redundant checks based on static analysis results. + // If the the fat ptr has been checked before, it can be safely removed. + if ((InsertCheckVersionCall || InsertCheckOffsetCall.first) && + SemaRef.Context.FatPtrCheckStatusMap.count(FatPtrBaseE)) { + uint8_t FatPtrCheckedStatus = SemaRef.Context.FatPtrCheckStatusMap[FatPtrBaseE]; + if (FatPtrCheckedStatus == 1 || FatPtrCheckedStatus == 3) { + InsertCheckVersionCall = false; } + if (FatPtrCheckedStatus == 2 || FatPtrCheckedStatus == 3) { + InsertCheckOffsetCall.first = false; + InsertCheckOffsetCall.second = nullptr; + } + } if (InsertCheckOffsetCall.first && InsertCheckVersionCall) { + // Build CallExpr to check version and offset. llvm::SmallVector Args{InsertCheckOffsetCall.second, FatPtrBaseE}; Expr *CE = BuildFatPtrCall(SLoc, "_check_version_and_offset", Args); CheckCallExpr.push(std::make_pair(FatPtrBaseE, CE)); diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 1a65e55cd7b8..88048413a43f 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -10033,12 +10033,13 @@ Sema::CheckAssignmentConstraints(QualType LHSType, ExprResult &RHS, QualType RHSType = RHS.get()->getType(); QualType OrigLHSType = LHSType; +#if ENABLE_BSC if (getLangOpts().BSC && getLangOpts().EnableFatPtr && Context.hasSameFatPtrType(LHSType, RHSType)) { Kind = CK_NoOp; return Compatible; } - +#endif // Get canonical types. We're not formatting these types, just comparing // them. LHSType = Context.getCanonicalType(LHSType).getUnqualifiedType(); @@ -18009,8 +18010,10 @@ bool Sema::DiagnoseAssignmentResult(AssignConvertType ConvTy, isInvalid = true; break; case Incompatible: +#if ENABLE_BSC if (getLangOpts().BSC && IsFatPtrEqualExpr(*this, DstType, SrcType)) return false; +#endif if (maybeDiagnoseAssignmentToFunction(*this, DstType, SrcExpr)) { if (Complained) *Complained = true; @@ -18023,7 +18026,7 @@ bool Sema::DiagnoseAssignmentResult(AssignConvertType ConvTy, isInvalid = true; MayHaveFunctionDiff = true; break; - #if ENABLE_BSC +#if ENABLE_BSC case IncompatibleOwnedPointer: case IncompatibleBorrowPointer: case IncompatibleFatPointer: @@ -18058,7 +18061,7 @@ bool Sema::DiagnoseAssignmentResult(AssignConvertType ConvTy, AssignmentAction ActionForDiag = Action; if (Action == AA_Passing_CFAudited) ActionForDiag = AA_Passing; - #if ENABLE_BSC +#if ENABLE_BSC SecondType = CompleteTraitType(SecondType); #endif diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp index 1ff622209128..64a83ad41060 100644 --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -1752,7 +1752,6 @@ Sema::ActOnDoStmt(SourceLocation DoLoc, Stmt *Body, ExprResult CondResult = CheckBooleanCondition(DoLoc, Cond); if (CondResult.isInvalid()) return StmtError(); - Cond = CondResult.get(); CondResult = ActOnFinishFullExpr(Cond, DoLoc, /*DiscardedValue*/ false); diff --git a/clang/test/BSC/Positive/FatPtr/redundant_check/func_call.cbs b/clang/test/BSC/Positive/FatPtr/redundant_check/func_call.cbs new file mode 100644 index 000000000000..929d2a344982 --- /dev/null +++ b/clang/test/BSC/Positive/FatPtr/redundant_check/func_call.cbs @@ -0,0 +1,52 @@ +// RUN: %clang %s -enable-fat-ptr -c -Xclang -ast-dump \ +// RUN: | FileCheck --strict-whitespace %s +// RUN: %clang %s -enable-fat-ptr -o %test.output + +#include + +void use_mayfree(int *fat p){ + checked_free((void *fat)p); +} + +void use_notfree(int *fat p){ + *p = 0; +} + +void test_call_mayfree() { + int *fat p = (int *fat)checked_malloc(sizeof(int)); + *p = 1; + use_mayfree(p); + *p = 2; +} + +void test_call_notfree() { + int *fat p = (int *fat)checked_malloc(sizeof(int)); + *p = 1; + use_notfree(p); + *p = 2; +} + +int main() { + test_call_mayfree(); + test_call_notfree(); +} + +// CHECK: FunctionDecl 0x{{[^ ]*}} <{{.*}}> line:{{.*}} used test_call_mayfree 'void (void)' + +// CHECK: DeclRefExpr 0x{{[^ ]*}} <{{.*}}> 'void ({{.*}})' lvalue Function 0x{{[^ ]*}} '_check_version_and_offset' 'void ({{.*}})' +// CHECK: BinaryOperator 0x{{[^ ]*}} <{{.*}}> 'int' '=' +// CHECK-NEXT: UnaryOperator 0x{{[^ ]*}} <{{.*}}> 'int' lvalue prefix '*' cannot overflow + +// CHECK: DeclRefExpr 0x{{[^ ]*}} <{{.*}}> 'void ({{.*}})' lvalue Function 0x{{[^ ]*}} '_check_version_and_offset' 'void ({{.*}})' +// CHECK: BinaryOperator 0x{{[^ ]*}} <{{.*}}> 'int' '=' +// CHECK-NEXT: UnaryOperator 0x{{[^ ]*}} <{{.*}}> 'int' lvalue prefix '*' cannot overflow + +// CHECK: FunctionDecl 0x{{[^ ]*}} <{{.*}}> line:{{.*}} used test_call_notfree 'void (void)' + +// CHECK: DeclRefExpr 0x{{[^ ]*}} <{{.*}}> 'void ({{.*}})' lvalue Function 0x{{[^ ]*}} '_check_version_and_offset' 'void ({{.*}})' +// CHECK: BinaryOperator 0x{{[^ ]*}} <{{.*}}> 'int' '=' +// CHECK-NEXT: UnaryOperator 0x{{[^ ]*}} <{{.*}}> 'int' lvalue prefix '*' cannot overflow + +// CHECK: DeclRefExpr 0x{{[^ ]*}} <{{.*}}> 'void ({{.*}})' lvalue Function 0x{{[^ ]*}} '_check_offset' 'void ({{.*}})' +// CHECK: BinaryOperator 0x{{[^ ]*}} <{{.*}}> 'int' '=' +// CHECK-NEXT: UnaryOperator 0x{{[^ ]*}} <{{.*}}> 'int' lvalue prefix '*' cannot overflow \ No newline at end of file diff --git a/clang/test/BSC/Positive/FatPtr/redundant_check/if_stmt.cbs b/clang/test/BSC/Positive/FatPtr/redundant_check/if_stmt.cbs new file mode 100644 index 000000000000..4d9a6383aa67 --- /dev/null +++ b/clang/test/BSC/Positive/FatPtr/redundant_check/if_stmt.cbs @@ -0,0 +1,46 @@ +// RUN: %clang %s -enable-fat-ptr -c -Xclang -ast-dump \ +// RUN: | FileCheck --strict-whitespace %s +// RUN: %clang %s -enable-fat-ptr -o %test.output + +#include + +int *fat unsure_func() { + int *fat p = (int *fat)checked_malloc(sizeof(int)); + // do some thing unsure + return p; +} +int *fat get_ptr(){ + return unsure_func(); +} + +void test_if_stmt() { + int *fat p = (int *fat)checked_malloc(sizeof(int)); + *p = 1; + if (p) { + p = get_ptr(); + } else { + *p = 2; + } + *p = 3; +} + +int main() { + test_if_stmt(); +} + +// CHECK: FunctionDecl 0x{{[^ ]*}} <{{.*}}> line:{{.*}} used test_if_stmt 'void (void)' + +// CHECK: DeclRefExpr 0x{{[^ ]*}} <{{.*}}> 'void ({{.*}})' lvalue Function 0x{{[^ ]*}} '_check_version_and_offset' 'void ({{.*}})' +// CHECK: BinaryOperator 0x{{[^ ]*}} <{{.*}}> 'int' '=' +// CHECK-NEXT: UnaryOperator 0x{{[^ ]*}} <{{.*}}> 'int' lvalue prefix '*' cannot overflow + +// CHECK: IfStmt 0x{{[^ ]*}} <{{.*}}> has_else +// CHECK: CompoundStmt 0x{{[^ ]*}} <{{.*}}> +// CHECK: CompoundStmt 0x{{[^ ]*}} <{{.*}}> +// CHECK-NOT: DeclRefExpr 0x{{[^ ]*}} <{{.*}}> 'void ({{.*}})' lvalue Function 0x{{[^ ]*}} '_check_version_and_offset' 'void ({{.*}})' +// CHECK: BinaryOperator 0x{{[^ ]*}} <{{.*}}> 'int' '=' +// CHECK-NEXT: UnaryOperator 0x{{[^ ]*}} <{{.*}}> 'int' lvalue prefix '*' cannot overflow + +// CHECK: DeclRefExpr 0x{{[^ ]*}} <{{.*}}> 'void ({{.*}})' lvalue Function 0x{{[^ ]*}} '_check_version_and_offset' 'void ({{.*}})' +// CHECK: BinaryOperator 0x{{[^ ]*}} <{{.*}}> 'int' '=' +// CHECK-NEXT: UnaryOperator 0x{{[^ ]*}} <{{.*}}> 'int' lvalue prefix '*' cannot overflow diff --git a/clang/test/BSC/Positive/FatPtr/redundant_check/member_visit.cbs b/clang/test/BSC/Positive/FatPtr/redundant_check/member_visit.cbs new file mode 100644 index 000000000000..0ac958d9d6ac --- /dev/null +++ b/clang/test/BSC/Positive/FatPtr/redundant_check/member_visit.cbs @@ -0,0 +1,52 @@ +// RUN: %clang %s -enable-fat-ptr -c -Xclang -ast-dump \ +// RUN: | FileCheck --strict-whitespace %s +// RUN: %clang %s -enable-fat-ptr -o %test.output + + +#include + +typedef struct node node_fat; + +struct node { + int val; + double *fat weight; + node_fat *fat next; +}; + + + +void new_node(int n) { + node_fat *fat p = (node_fat *fat)checked_malloc(sizeof(node_fat)); + p->val = n; + p->next = nullptr; + p->weight = (double *fat)checked_malloc(sizeof(double)); + double a = *p->weight; +} + +int main() { + new_node(1); + return 0; +} + +// CHECK: FunctionDecl 0x{{[^ ]*}} <{{.*}}> line:{{.*}} used new_node 'void (int)' + +// CHECK: VarDecl 0x{{[^ ]*}} <{{.*}}> col:{{.*}} used p '_FatPtr':'struct _FatPtr' cinit + +// CHECK: DeclRefExpr 0x{{[^ ]*}} <{{.*}}> 'void ({{.*}})' lvalue Function 0x{{[^ ]*}} '_check_version_and_offset' 'void ({{.*}})' +// CHECK: BinaryOperator 0x{{[^ ]*}} <{{.*}}> 'int' '=' +// CHECK-NEXT: MemberExpr 0x{{[^ ]*}} <{{.*}}> 'int' lvalue ->val 0x{{[^ ]*}} + +// CHECK-NOT: DeclRefExpr 0x{{[^ ]*}} <{{.*}}> 'void ({{.*}})' lvalue Function 0x{{[^ ]*}} '_check_version_and_offset' 'void ({{.*}})' +// CHECK: BinaryOperator 0x{{[^ ]*}} <{{.*}}> '_FatPtr':'struct _FatPtr' '=' +// CHECK-NEXT: MemberExpr 0x{{[^ ]*}} <{{.*}}> '_FatPtr':'struct _FatPtr' lvalue ->next 0x{{[^ ]*}} + +// CHECK-NOT: DeclRefExpr 0x{{[^ ]*}} <{{.*}}> 'void ({{.*}})' lvalue Function 0x{{[^ ]*}} '_check_version_and_offset' 'void ({{.*}})' +// CHECK: BinaryOperator 0x{{[^ ]*}} <{{.*}}> '_FatPtr':'struct _FatPtr' '=' +// CHECK-NEXT: MemberExpr 0x{{[^ ]*}} <{{.*}}> '_FatPtr':'struct _FatPtr' lvalue ->weight 0x{{[^ ]*}} + +// CHECK: DeclRefExpr 0x{{[^ ]*}} <{{.*}}> 'void ({{.*}})' lvalue Function 0x{{[^ ]*}} '_check_version_and_offset' 'void ({{.*}})' +// CHECK: MemberExpr 0x{{[^ ]*}} <{{.*}}> '_FatPtr':'struct _FatPtr' lvalue ->weight 0x{{[^ ]*}} + +// CHECK: VarDecl 0x{{[^ ]*}} <{{.*}}> col:{{.*}} a 'double' cinit +// CHECK: UnaryOperator 0x{{[^ ]*}} <{{.*}}> 'double' lvalue prefix '*' cannot overflow +// CHECK: MemberExpr 0x{{[^ ]*}} <{{.*}}> '_FatPtr':'struct _FatPtr' lvalue ->weight 0x{{[^ ]*}} \ No newline at end of file -- Gitee From 330dfc2b6114a011b7b6217c92321fae92c11557 Mon Sep 17 00:00:00 2001 From: liuxinyi Date: Tue, 4 Mar 2025 16:49:47 +0800 Subject: [PATCH 07/29] [FatPtr]support rewrite to C for fat ptr --- clang/include/clang/AST/ASTContext.h | 2 +- clang/lib/AST/ASTContext.cpp | 2 +- clang/lib/AST/DeclPrinter.cpp | 6 + clang/lib/AST/StmtPrinter.cpp | 32 ++- clang/lib/AST/TypePrinter.cpp | 6 + clang/lib/Frontend/Rewrite/RewriteBSC.cpp | 47 ++++ clang/lib/Headers/bsc_include/bsc_fat_ptr.hbs | 15 -- clang/lib/Sema/BSC/SemaBSCFatPtr.cpp | 205 +++++++++--------- clang/lib/Sema/SemaExpr.cpp | 3 +- .../global_array/array_out_of_bounds1.cbs | 7 +- .../global_array/array_out_of_bounds2.cbs | 7 +- .../heap/array_out_of_bounds1.cbs | 8 +- .../heap/array_out_of_bounds10.cbs | 7 +- .../heap/array_out_of_bounds11.cbs | 7 +- .../heap/array_out_of_bounds12.cbs | 7 +- .../heap/array_out_of_bounds13.cbs | 7 +- .../heap/array_out_of_bounds14.cbs | 7 +- .../heap/array_out_of_bounds15.cbs | 7 +- .../heap/array_out_of_bounds16.cbs | 7 +- .../heap/array_out_of_bounds17.cbs | 7 +- .../heap/array_out_of_bounds18.cbs | 7 +- .../heap/array_out_of_bounds19.cbs | 7 +- .../heap/array_out_of_bounds2.cbs | 7 +- .../heap/array_out_of_bounds20.cbs | 7 +- .../heap/array_out_of_bounds21.cbs | 7 +- .../heap/array_out_of_bounds22.cbs | 7 +- .../heap/array_out_of_bounds23.cbs | 7 +- .../heap/array_out_of_bounds24.cbs | 7 +- .../heap/array_out_of_bounds3.cbs | 7 +- .../heap/array_out_of_bounds4.cbs | 7 +- .../heap/array_out_of_bounds5.cbs | 7 +- .../heap/array_out_of_bounds6.cbs | 7 +- .../heap/array_out_of_bounds7.cbs | 7 +- .../heap/array_out_of_bounds8.cbs | 7 +- .../heap/array_out_of_bounds9.cbs | 7 +- .../heap/buffer_overflow.cbs | 7 +- .../heap/double_free1.cbs | 7 +- .../heap/double_free2.cbs | 7 +- .../heap/double_free3.cbs | 7 +- .../heap/double_free4.cbs | 7 +- .../heap/use_after_free1.cbs | 7 +- .../heap/use_after_free2.cbs | 7 +- .../heap/use_after_free3.cbs | 7 +- .../heap/use_after_free4.cbs | 7 +- .../heap/use_after_free5.cbs | 7 +- .../heap/use_after_free6.cbs | 7 +- .../heap/use_after_free7.cbs | 7 +- .../heap/use_after_free8.cbs | 7 +- .../stack/array_out_of_bounds1.cbs | 7 +- .../stack/array_out_of_bounds2.cbs | 7 +- .../stack/array_out_of_bounds3.cbs | 7 +- .../stack/array_out_of_bounds4.cbs | 4 + .../stack/buffer_overflow.cbs | 7 +- .../stack/return_local_address.cbs | 7 +- .../test/BSC/Positive/FatPtr/global_array.cbs | 3 + clang/test/BSC/Positive/FatPtr/heap.cbs | 3 + .../Positive/FatPtr/multi_level_pointer.cbs | 3 + .../FatPtr/nullptr_cast/nullptr_cast.cbs | 3 + clang/test/BSC/Positive/FatPtr/stack.cbs | 3 + .../BSC/Positive/FatPtr/string_literal.cbs | 5 +- 60 files changed, 486 insertions(+), 165 deletions(-) diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index 855103b0e9a4..47306d4d1add 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -2566,7 +2566,7 @@ public: return getCanonicalType(T1) == getCanonicalType(T2); } #if ENABLE_BSC - bool hasSameFatPtrType(QualType LHSType, QualType RHSType); + bool hasSameFatPtrType(QualType LHSType, QualType RHSType) const; #endif /// Return this type as a completely-unqualified array type, /// capturing the qualifiers in \p Quals. diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index d730b1638a11..1395799d197f 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -3248,7 +3248,7 @@ bool ASTContext::hasSameFunctionTypeIgnoringPtrSizes(QualType T, QualType U) { } #if ENABLE_BSC -bool ASTContext::hasSameFatPtrType(QualType LHSType, QualType RHSType) { +bool ASTContext::hasSameFatPtrType(QualType LHSType, QualType RHSType) const { if (LHSType.hasFat() != RHSType.hasFat()) return false; if (LHSType->isFunctionProtoType() && RHSType->isFunctionProtoType()) { diff --git a/clang/lib/AST/DeclPrinter.cpp b/clang/lib/AST/DeclPrinter.cpp index 22075632e80d..ee62eb74de5f 100644 --- a/clang/lib/AST/DeclPrinter.cpp +++ b/clang/lib/AST/DeclPrinter.cpp @@ -1233,6 +1233,12 @@ void DeclPrinter::VisitCXXRecordDecl(CXXRecordDecl *D) { if (Policy.RewriteBSC) { std::string BSCRecordNameStr = S->getNameAsString(); BSCRecordNameStr += MangleBSCContext::getBSCTemplateArgsName(Args, Policy); + if (S->getNameAsString() == "_FatPtr" && + S->getTemplateArgs().size() == 1) { + size_t constStartPos = BSCRecordNameStr.find("_const"); + if (constStartPos != std::string::npos) + BSCRecordNameStr.erase(constStartPos, 6); + } Out << ' ' << BSCRecordNameStr; } else { #endif diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp index 570d7bee40f4..6fcdaaac38c1 100644 --- a/clang/lib/AST/StmtPrinter.cpp +++ b/clang/lib/AST/StmtPrinter.cpp @@ -1661,6 +1661,30 @@ void StmtPrinter::VisitOMPIteratorExpr(OMPIteratorExpr *Node) { } void StmtPrinter::PrintCallArgs(CallExpr *Call) { +#if ENABLE_BSC + if (Policy.RewriteBSC && Context->getLangOpts().EnableFatPtr) { + if (auto FD = dyn_cast(Call->getCalleeDecl())) { + if (FD->getNameAsString() == "_check_version") { + for (unsigned i = 0; i < 3; ++i) { + if (i) + OS << ", "; + PrintExpr(Call->getArg(i)); + } + OS << ", __FILE__, __LINE__, __func__"; + return; + } else if (FD->getNameAsString() == "_check_offset" || + FD->getNameAsString() == "_check_version_and_offset") { + for (unsigned i = 0; i < 4; ++i) { + if (i) + OS << ", "; + PrintExpr(Call->getArg(i)); + } + OS << ", __FILE__, __LINE__, __func__"; + return; + } + } + } +#endif for (unsigned i = 0, e = Call->getNumArgs(); i != e; ++i) { if (isa(Call->getArg(i))) { // Don't print any defaulted arguments @@ -1764,7 +1788,13 @@ void StmtPrinter::VisitExtVectorElementExpr(ExtVectorElementExpr *Node) { void StmtPrinter::VisitCStyleCastExpr(CStyleCastExpr *Node) { OS << '('; - Node->getTypeAsWritten().print(OS, Policy); +#if ENABLE_BSC + if (Policy.RewriteBSC && Context->getLangOpts().EnableFatPtr && + Node->getType().hasFat()) + Node->getType().print(OS, Policy); + else +#endif + Node->getTypeAsWritten().print(OS, Policy); OS << ')'; PrintExpr(Node->getSubExpr()); } diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp index 8279e434ad67..bbd0aa26dae6 100644 --- a/clang/lib/AST/TypePrinter.cpp +++ b/clang/lib/AST/TypePrinter.cpp @@ -1486,6 +1486,12 @@ void TypePrinter::printTag(TagDecl *D, raw_ostream &OS) { if (Policy.RewriteBSC) { Args = Spec->getTemplateArgs().asArray(); std::string BSCRecordNameStr = MangleBSCContext::getBSCTemplateArgsName(Args, Policy); + if (Spec->getNameAsString() == "_FatPtr" && + Spec->getTemplateArgs().size() == 1) { + size_t constStartPos = BSCRecordNameStr.find("_const"); + if (constStartPos != std::string::npos) + BSCRecordNameStr.erase(constStartPos, 6); + } OS << BSCRecordNameStr; } else { #endif diff --git a/clang/lib/Frontend/Rewrite/RewriteBSC.cpp b/clang/lib/Frontend/Rewrite/RewriteBSC.cpp index 464a26f320f2..9513f424dcab 100644 --- a/clang/lib/Frontend/Rewrite/RewriteBSC.cpp +++ b/clang/lib/Frontend/Rewrite/RewriteBSC.cpp @@ -213,6 +213,7 @@ private: std::set &RewritedFunctionDeclarationSet); void RewriteNonGenericFuncAndVar(std::vector &DeclList); void RewriteInstantFunctionDef(std::vector &DeclList); + void RewriteFatPtrRecord(ClassTemplateDecl *CTD); void InsertText(SourceLocation Loc, StringRef Str, bool InsertAfter = true) { // If insertion succeeded or warning disabled return with no warning. @@ -552,6 +553,10 @@ void RewriteBSC::RewriteDecls() { // For decls with bsc feature, we use pretty printer. // For decls without bsc feature, we use original string text. if (DeclsWithoutBSCFeature.find(D) == DeclsWithoutBSCFeature.end()) { + if (Context->getLangOpts().EnableFatPtr) + if (auto CTSD = dyn_cast(D)) + if (CTSD->getNameAsString() == "_FatPtr") + continue; D->print(Buf, Policy); Buf << ";\n\n"; } else { @@ -614,6 +619,11 @@ void RewriteBSC::RewriteRecordDeclaration(std::vector &DeclList) { break; } } + if (Context->getLangOpts().EnableFatPtr && + CTD->getNameAsString() == "_FatPtr") { + RewriteFatPtrRecord(CTD); + break; + } for (ClassTemplateSpecializationDecl *CTSD : CTD->specializations()) { // Because we only want to print the declaration of the generic struct, // we set it to incomplete before printing, and then restore it after @@ -924,4 +934,41 @@ void RewriteBSC::RewriteInstantFunctionDef(std::vector &DeclList) { } } +void RewriteBSC::RewriteFatPtrRecord(ClassTemplateDecl *CTD) { + llvm::DenseMap SimpleFatPtr; + llvm::DenseMap NestedFatPtr; + for (ClassTemplateSpecializationDecl *CTSD : CTD->specializations()) { + QualType ArgType = CTSD->getTemplateArgs()[0].getAsType(); + if (ArgType.isFatPtrRecordType()) + NestedFatPtr[ArgType.getFatPtrPointeeType()] = CTSD; + else + SimpleFatPtr[ArgType] = CTSD; + } + for (auto ArgTypeAndCTSD : SimpleFatPtr) { + QualType ArgType = ArgTypeAndCTSD.first; + QualType ArgTypeCopy = ArgType; + if (ArgType.isLocalConstQualified()) { + ArgType.removeLocalConst(); + if (SimpleFatPtr.count(ArgType)) + SimpleFatPtr.erase(ArgTypeCopy); + } + } + for (auto ArgTypeAndCTSD : NestedFatPtr) { + QualType ArgType = ArgTypeAndCTSD.first; + QualType ArgTypeCopy = ArgType; + if (ArgType.isLocalConstQualified()) { + ArgType.removeLocalConst(); + if (NestedFatPtr.count(ArgType)) + NestedFatPtr.erase(ArgTypeCopy); + } + } + for (auto ArgTypeAndCTSD : SimpleFatPtr) { + ArgTypeAndCTSD.second->print(Buf, Policy); + Buf << ";\n"; + } + for (auto ArgTypeAndCTSD : NestedFatPtr) { + ArgTypeAndCTSD.second->print(Buf, Policy); + Buf << ";\n"; + } +} #endif \ No newline at end of file diff --git a/clang/lib/Headers/bsc_include/bsc_fat_ptr.hbs b/clang/lib/Headers/bsc_include/bsc_fat_ptr.hbs index e70f88cbcf20..9cf3488305d6 100644 --- a/clang/lib/Headers/bsc_include/bsc_fat_ptr.hbs +++ b/clang/lib/Headers/bsc_include/bsc_fat_ptr.hbs @@ -162,19 +162,4 @@ static __always_inline void _check_version_and_offset( } } -T** cast_two_level_fat_to_raw (T *fat *fat ptr) { - _FatPtr<_FatPtr> ptr_copy = ptr; - if (ptr_copy.key_version == 0) - _report_error(CannotCast, 0, 0, 0); - const uint8_t *head = (const uint8_t *)ptr_copy.raw_ptr - ptr_copy.offset - 8; - const struct _AllocationUnit *phead = (const struct _AllocationUnit *)head; - size_t fat_ptr_size = sizeof(void *) + 8; - if (phead->size < fat_ptr_size) - _report_error(CannotCast, 0, 0, 0); - int element_num = phead->size / fat_ptr_size; - T **raw = (T **)malloc(element_num * sizeof(T *)); - for (int i = 0; i < element_num; i++) - raw[i] = (T *)ptr_copy.raw_ptr[i]; - return raw; -} #endif /* BSC_FAT_PTR_HBS */ \ No newline at end of file diff --git a/clang/lib/Sema/BSC/SemaBSCFatPtr.cpp b/clang/lib/Sema/BSC/SemaBSCFatPtr.cpp index c04f2ec8753d..7a752ee200ab 100644 --- a/clang/lib/Sema/BSC/SemaBSCFatPtr.cpp +++ b/clang/lib/Sema/BSC/SemaBSCFatPtr.cpp @@ -254,10 +254,9 @@ StmtResult FatPtrPreprocessor::TransformCompoundStmt(CompoundStmt *CS, } CurrStmts.push_back(BaseTransform::TransformStmt(S).get()); } - CompoundStmt *NewCS = CompoundStmt::Create( - SemaRef.Context, CurrStmts, FPOptionsOverride(), CS->getLBracLoc(), - CS->getRBracLoc(), CS->getSafeSpecifier(), CS->getSafeSpecifierLoc(), - CS->getCompSafeZoneSpecifier()); + Sema::CompoundScopeRAII CompoundScope(SemaRef); + StmtResult NewCS = BaseTransform::RebuildCompoundStmt( + CS->getLBracLoc(), CurrStmts, CS->getRBracLoc(), false); CurrStmts = PrevStmts; return NewCS; } @@ -339,7 +338,7 @@ ExprResult FatPtrPreprocessor::TransformUnaryOperator(UnaryOperator *UO) { Expr *SubE = UO->getSubExpr(); QualType SubQT = SubE->getType(); if ((UO->isPrefix() || UO->isPostfix()) && SubQT.isFatPtrType()) { - SubE = SubE->IgnoreParens(); + SubE = BaseTransform::TransformExpr(SubE).get(); bool IsPostfix = UO->isPostfix(); bool IsInc = UO->isIncrementOp(); // Build the first expr of CommaExpr: @@ -372,6 +371,9 @@ ExprResult FatPtrPreprocessor::TransformUnaryOperator(UnaryOperator *UO) { UO->setSubExpr(isa(SubE->IgnoreParens()) && SubQT.isFatPtrType() ? ReplaceWithTemporaryVarDecl(SubE->IgnoreParens()) : BaseTransform::TransformExpr(SubE).get()); + if ((UO->getOpcode() == UO_Deref && UO->getType().isFatPtrType())) { + return new (SemaRef.Context) ParenExpr(SLoc, UO->getEndLoc(), UO); + } return UO; } @@ -438,8 +440,10 @@ Stmt *FatPtrPreprocessor::BuildCompoundStmtForIfOrLoopStmtBody(Stmt *Body) { if (!isa(Body)) { Statements.push_back(Body); } - return CompoundStmt::Create(SemaRef.Context, Statements, FPOptionsOverride(), - Body->getBeginLoc(), Body->getEndLoc()); + Sema::CompoundScopeRAII CompoundScope(SemaRef); + return BaseTransform::RebuildCompoundStmt(Body->getBeginLoc(), Statements, + Body->getEndLoc(), false) + .get(); } // If return type of a CallExpr is fat ptr type, build a temporary @@ -460,9 +464,8 @@ ImplicitCastExpr *FatPtrPreprocessor::ReplaceWithTemporaryVarDecl(Expr *E) { VD->markUsed(SemaRef.Context); DeclStmt *DS = new (SemaRef.Context) DeclStmt(DeclGroupRef(VD), SLoc, SLoc); CurrStmts.push_back(DS); - DeclRefExpr *DRE = DeclRefExpr::Create( - SemaRef.Context, NestedNameSpecifierLoc(), SourceLocation(), VD, false, - SLoc, CE->getType(), VK_LValue); + DeclRefExpr *DRE = + SemaRef.BuildDeclRefExpr(VD, CE->getType(), VK_LValue, SLoc); return ImplicitCastExpr::Create(SemaRef.Context, QT, CK_LValueToRValue, DRE, nullptr, VK_PRValue, FPOptionsOverride()); } @@ -725,11 +728,10 @@ TransformFatPtr::HandleLocalAllocationUnit(CompoundStmt *CS, if (IsTopLevelCompoundStmt && Finder.LocalNoArrayVarAddressIsFatTaken) Statements.push_back( BuildLockVersionAssignmentForLocalNoArrayVars(CS->getEndLoc())); - auto NewCS = CompoundStmt::Create( - SemaRef.Context, Statements, FPOptionsOverride(), CS->getLBracLoc(), - CS->getRBracLoc(), CS->getSafeSpecifier(), CS->getSafeSpecifierLoc(), - CS->getCompSafeZoneSpecifier()); - return NewCS; + Sema::CompoundScopeRAII CompoundScope(SemaRef); + StmtResult NewCS = BaseTransform::RebuildCompoundStmt( + CS->getLBracLoc(), Statements, CS->getRBracLoc(), false); + return NewCS.getAs(); } // Build allocation unit header for all local vars whose @@ -775,7 +777,7 @@ TransformFatPtr::BuildAllocationUnitHeaderForLocalNoArrayVars( // int arr[3]; // }; // struct _arr_AllocationUnit _arr = -// { _new_version_number(), 12, {1, 2, 3} }; +// { _new_version_number(), sizeof(int[3]), {1, 2, 3} }; // @endcode std::pair TransformFatPtr::BuildAllocationUnitForLocalArrayVar(VarDecl *VD) { @@ -818,11 +820,13 @@ TransformFatPtr::BuildAllocationUnitForLocalArrayVar(VarDecl *VD) { // Build first init expr: _ner_version_number() llvm::SmallVector Args; Expr *LockVersionInit = BuildFatPtrCall(SLoc, "_new_version_number", Args); - // Build second init expr by array size - auto ArraySize = - SemaRef.Context.getTypeSize(DesugarFatPtrType(SLoc, VD->getType())) / 8; - Expr *SizeInit = IntegerLiteral::Create( - SemaRef.Context, llvm::APInt(32, ArraySize), UInt32Ty, SLoc); + // Build second init expr: sizeof(array type) + Expr *SizeInit = SemaRef + .CreateUnaryExprOrTypeTraitExpr( + SemaRef.Context.getTrivialTypeSourceInfo( + DesugarFatPtrType(SLoc, VD->getType()), SLoc), + SLoc, UETT_SizeOf, SourceRange(SLoc)) + .get(); // Build InitListExpr SmallVector Inits{LockVersionInit, SizeInit}; if (VD->getInit()) @@ -881,11 +885,9 @@ StmtResult TransformFatPtr::TransformCompoundStmt(CompoundStmt *CS, for (auto S : TempStatements) { DesugaredStatements[CS].push_back(S); } - auto NewCS = CompoundStmt::Create( - SemaRef.Context, DesugaredStatements[CS], FPOptionsOverride(), - CS->getLBracLoc(), CS->getRBracLoc(), CS->getSafeSpecifier(), - CS->getSafeSpecifierLoc(), CS->getCompSafeZoneSpecifier()); - return NewCS; + Sema::CompoundScopeRAII CompoundScope(SemaRef); + return BaseTransform::RebuildCompoundStmt( + CS->getLBracLoc(), DesugaredStatements[CS], CS->getRBracLoc(), false); } // If there is fat ptr deref operation in CondExpr of IfStmt or @@ -1081,10 +1083,10 @@ ExprResult TransformFatPtr::TransformDeclRefExpr(DeclRefExpr *DRE) { it++; FieldDecl *ArrayFD = *it; return SemaRef.BuildMemberExpr( - AllocationUnitVDRef, false, SLoc, NestedNameSpecifierLoc(), SLoc, - ArrayFD, DeclAccessPair::make(AllocationUnitRD, ArrayFD->getAccess()), - false, DeclarationNameInfo(), ArrayFD->getType(), VK_LValue, - OK_Ordinary); + AllocationUnitVDRef, false, SLoc, NestedNameSpecifierLoc(), + SourceLocation(), ArrayFD, + DeclAccessPair::make(AllocationUnitRD, ArrayFD->getAccess()), false, + DeclarationNameInfo(), ArrayFD->getType(), VK_LValue, OK_Ordinary); } } return DRE; @@ -1110,20 +1112,19 @@ ExprResult TransformFatPtr::TransformImplicitCastExpr(ImplicitCastExpr *ICE) { } ExprResult TransformFatPtr::TransformParenExpr(ParenExpr *PE) { - SourceLocation SLoc = PE->getBeginLoc(); if (DesugarToMemberPtr && PreprocessedSelfIncOrDecExpr.count(PE)) { return HandlePEFromPreprocessedSelfIncOrDecExpr(PE); } - QualType QT = PE->getType(); - if (DesugarToMemberPtr && QT.isFatPtrType()) { - QT = DesugarFatPtrType(SLoc, QT.getFatPtrPointeeType()); - PE->setType(SemaRef.Context.getPointerType(QT)); - } else { - QT = DesugarFatPtrType(SLoc, QT); - PE->setType(QT); - } + SourceLocation SLoc = PE->getBeginLoc(); + QualType QT = DesugarFatPtrType(SLoc, PE->getType()); + PE->setType(QT); + + Expr *MemberRawPtr = nullptr; + if (QT.isFatPtrRecordType() && DesugarToMemberPtr) + MemberRawPtr = BuildMemberRawPtrAndInsertCheckCallForFatPtr(PE); + PE->setSubExpr(BaseTransform::TransformExpr(PE->getSubExpr()).get()); - return PE; + return MemberRawPtr ? MemberRawPtr : PE; } ExprResult @@ -1322,15 +1323,20 @@ TransformFatPtr::TransformArraySubscriptExpr(ArraySubscriptExpr *ASE) { // Build offset arg expr, for example: // for p[i] where p[i] is int type, // offset should be `i * sizeof(int) + sizeof(int)` - QualType Int32Ty = SemaRef.Context.getIntTypeForBitwidth(32, true); Expr *OffsetExprLHS = - IntegerLiteral::Create(SemaRef.Context, - llvm::APInt(32, SemaRef.Context.getTypeSize(QT) / 8), Int32Ty, SLoc); + SemaRef + .CreateUnaryExprOrTypeTraitExpr( + SemaRef.Context.getTrivialTypeSourceInfo(QT, SLoc), SLoc, + UETT_SizeOf, SourceRange(SLoc)) + .get(); OffsetExprLHS = SemaRef.CreateBuiltinBinOp(SLoc, BO_Mul, ASE->getRHS(), OffsetExprLHS).get(); Expr *OffsetExprRHS = - IntegerLiteral::Create(SemaRef.Context, - llvm::APInt(32, SemaRef.Context.getTypeSize(QT) / 8), Int32Ty, SLoc); + SemaRef + .CreateUnaryExprOrTypeTraitExpr( + SemaRef.Context.getTrivialTypeSourceInfo(QT, SLoc), SLoc, + UETT_SizeOf, SourceRange(SLoc)) + .get(); Expr *OffsetExpr = SemaRef.CreateBuiltinBinOp(SLoc, BO_Add, OffsetExprLHS, OffsetExprRHS).get(); InsertCheckOffsetCall.second = OffsetExpr; @@ -1395,20 +1401,16 @@ ExprResult TransformFatPtr::TransformCStyleCastExpr(CStyleCastExpr *CSCE) { ExprResult TransformFatPtr::TransformUnaryExprOrTypeTraitExpr( UnaryExprOrTypeTraitExpr *UETT) { SourceLocation SLoc = UETT->getBeginLoc(); - QualType QT = DesugarFatPtrType(SLoc, UETT->getType()); - UETT->setType(QT); // Recalculate size of fat ptr which is larger than raw ptr: // `sizeof(int *)` equals to 8, but `sizeof(int *fat)` equals to 16 if (UETT->isArgumentType()) { - QualType OldQT = UETT->getArgumentType(); - if (OldQT.isFatPtrType()) { - QualType NewQT = DesugarFatPtrType(SLoc, OldQT); - TypeSourceInfo *NewTInfo = - SemaRef.Context.getTrivialTypeSourceInfo(NewQT, SLoc); - return BaseTransform::RebuildUnaryExprOrTypeTrait( - NewTInfo, UETT->getOperatorLoc(), UETT->getKind(), - UETT->getSourceRange()); - } + // handle sizeof(type) + UETT->setArgument(SemaRef.Context.getTrivialTypeSourceInfo( + DesugarFatPtrType(SLoc, UETT->getArgumentType()), SLoc)); + } else { + // handle sizeof(expr) + UETT->setArgument( + BaseTransform::TransformExpr(UETT->getArgumentExpr()).get()); } return UETT; } @@ -1533,29 +1535,26 @@ ExprResult TransformFatPtr::HandleBOFatPtrAddOrSubInteger(BinaryOperator *BO) { SourceLocation SLoc = BO->getBeginLoc(); QualType QT = BO->getType(); BinaryOperator::Opcode Op = BO->getOpcode(); - DesugarToMemberPtr = true; Expr *LHS = BaseTransform::TransformExpr(BO->getLHS()).get(); Expr *RHS = BaseTransform::TransformExpr(BO->getRHS()).get(); + Expr *FirstInitLHS = BuildMemberForFatPtr(LHS, "raw_ptr"); Expr *FirstInitE = - SemaRef.CreateBuiltinBinOp(SLoc, Op, LHS, RHS).get(); - if (auto MemberFatPtr = dyn_cast(LHS->IgnoreParenImpCasts())) { - Expr *FatPtrBase = MemberFatPtr->getBase(); - Expr *SecondInitE = BuildMemberForFatPtr(FatPtrBase, "key_version"); - Expr *ThirdInitLHS = BuildMemberForFatPtr(FatPtrBase, "offset"); - QualType PointeeQT = FatPtrBase->getType().getFatPtrPointeeType(); - QualType Int32Ty = SemaRef.Context.getIntTypeForBitwidth(32, true); - Expr *ThirdInitRHSRHS = IntegerLiteral::Create( - SemaRef.Context, - llvm::APInt(32, SemaRef.Context.getTypeSize(PointeeQT) / 8), Int32Ty, - SLoc); - Expr *ThirdInitRHS = - SemaRef.CreateBuiltinBinOp(SLoc, BO_Mul, RHS, ThirdInitRHSRHS).get(); - Expr *ThirdInitE = - SemaRef.CreateBuiltinBinOp(SLoc, Op, ThirdInitLHS, ThirdInitRHS).get(); - return BuildFatPtrCompoundLiteralExpr(FirstInitE, SecondInitE, ThirdInitE, - QT); - } - return BO; + SemaRef.CreateBuiltinBinOp(SLoc, Op, FirstInitLHS, RHS).get(); + Expr *SecondInitE = BuildMemberForFatPtr(LHS, "key_version"); + Expr *ThirdInitLHS = BuildMemberForFatPtr(LHS, "offset"); + QualType PointeeQT = LHS->getType().getFatPtrPointeeType(); + Expr *ThirdInitRHSRHS = + SemaRef + .CreateUnaryExprOrTypeTraitExpr( + SemaRef.Context.getTrivialTypeSourceInfo(PointeeQT, SLoc), SLoc, + UETT_SizeOf, SourceRange(SLoc)) + .get(); + Expr *ThirdInitRHS = + SemaRef.CreateBuiltinBinOp(SLoc, BO_Mul, RHS, ThirdInitRHSRHS).get(); + Expr *ThirdInitE = + SemaRef.CreateBuiltinBinOp(SLoc, Op, ThirdInitLHS, ThirdInitRHS).get(); + return BuildFatPtrCompoundLiteralExpr(FirstInitE, SecondInitE, ThirdInitE, + QT); } ExprResult TransformFatPtr::TransformUnaryOperator(UnaryOperator *UO) { @@ -1619,17 +1618,19 @@ ExprResult TransformFatPtr::TransformUnaryOperator(UnaryOperator *UO) { } ExprResult TransformFatPtr::HandleUODerefFatPtr(UnaryOperator *UO) { + SourceLocation SLoc = UO->getBeginLoc(); Expr *SubE = UO->getSubExpr(); DesugarToMemberPtr = true; InsertCheckVersionCall = true; InsertCheckOffsetCall.first = true; // Build offset arg expr, for example: // for *p, offset should be `sizeof(int)` - QualType Int32Ty = SemaRef.Context.getIntTypeForBitwidth(32, true); - Expr *OffsetExpr = IntegerLiteral::Create( - SemaRef.Context, - llvm::APInt(32, SemaRef.Context.getTypeSize(UO->getType()) / 8), Int32Ty, - UO->getBeginLoc()); + Expr *OffsetExpr = + SemaRef + .CreateUnaryExprOrTypeTraitExpr( + SemaRef.Context.getTrivialTypeSourceInfo(UO->getType(), SLoc), + SLoc, UETT_SizeOf, SourceRange(SLoc)) + .get(); InsertCheckOffsetCall.second = OffsetExpr; SubE = BaseTransform::TransformExpr(SubE).get(); if (auto ICE = dyn_cast(SubE)) { @@ -1665,7 +1666,7 @@ ExprResult TransformFatPtr::HandleUOAddrFatOnExprWithFatPtrBase( // Build second init expr: p.key_version Expr *SecondInitE = BuildMemberForFatPtr(FatPtrBaseE, "key_version"); // Build third init expr: - // `(int32_t)((uint8_t *)&p.ptr->a - (uint8_t *)p.ptr + p.offset` + // `(int32_t)((uint8_t *)&p.ptr->a - (uint8_t *)p.ptr) + p.offset` QualType UInt8PointerTy = SemaRef.Context.getPointerType( SemaRef.Context.getIntTypeForBitwidth(8, false)); Expr *ThirdInitLHSLHS = @@ -1742,6 +1743,8 @@ TransformFatPtr::HandleUOAddrFatOnLocalNoArrayVar(UnaryOperator *UO) { SemaRef .CreateBuiltinBinOp(SLoc, BO_Sub, ThirdInitLHSLHS,ThirdInitLHSRHS) .get(); + ThirdInitLHS = + new (SemaRef.Context) ParenExpr(SLoc, UO->getEndLoc(), ThirdInitLHS); QualType Int32Ty = SemaRef.Context.getIntTypeForBitwidth(32, true); ThirdInitLHS = SemaRef @@ -1798,10 +1801,12 @@ ExprResult TransformFatPtr::HandleUOAddrFatOnArrayVar( Expr *AllocationUnitVDRef = SemaRef.BuildDeclRefExpr( AllocationUnitVD, AllocationUnitVD->getType(), VK_LValue, SLoc); Expr *SecondInitE = SemaRef.BuildMemberExpr( - AllocationUnitVDRef, false, SLoc, NestedNameSpecifierLoc(), SLoc, LockVersionFD, + AllocationUnitVDRef, false, SLoc, NestedNameSpecifierLoc(), + SourceLocation(), LockVersionFD, DeclAccessPair::make(AllocationUnitRD, LockVersionFD->getAccess()), false, DeclarationNameInfo(), LockVersionFD->getType(), VK_LValue, OK_Ordinary); - // Build third init expr: 5 * size(int); + // Build third init expr: + // `(int32_t)((uint8_t *)&_arr.arr[5] - (uint8_t *)&_arr.lock_version) - 8` QualType UInt8PointerTy = SemaRef.Context.getPointerType( SemaRef.Context.getIntTypeForBitwidth(8, false)); Expr *ThirdInitLHSLHS = @@ -1820,6 +1825,8 @@ ExprResult TransformFatPtr::HandleUOAddrFatOnArrayVar( .get(); Expr *ThirdInitLHS = SemaRef.CreateBuiltinBinOp(SLoc, BO_Sub, ThirdInitLHSLHS, ThirdInitLHSRHS).get(); + ThirdInitLHS = + new (SemaRef.Context) ParenExpr(SLoc, UO->getEndLoc(), ThirdInitLHS); QualType Int32Ty = SemaRef.Context.getIntTypeForBitwidth(32, true); ThirdInitLHS = SemaRef @@ -1870,11 +1877,12 @@ ExprResult TransformFatPtr::TransformMemberExpr(MemberExpr *ME) { } Expr *OffsetExprLHS = SemaRef.BuildBuiltinOffsetOf(SLoc, PointeeTInfo, Comps, SLoc).get(); - QualType Int32Ty = SemaRef.Context.getIntTypeForBitwidth(32, true); - Expr *OffsetExprRHS = IntegerLiteral::Create( - SemaRef.Context, - llvm::APInt(32, SemaRef.Context.getTypeSize(ElementType) / 8), Int32Ty, - SLoc); + Expr *OffsetExprRHS = + SemaRef + .CreateUnaryExprOrTypeTraitExpr( + SemaRef.Context.getTrivialTypeSourceInfo(ElementType, SLoc), + SLoc, UETT_SizeOf, SourceRange(SLoc)) + .get(); InsertCheckOffsetCall.second = SemaRef.CreateBuiltinBinOp(SLoc, BO_Add, OffsetExprLHS, OffsetExprRHS).get(); } @@ -2186,9 +2194,7 @@ TransformFatPtr::IsAccessingArrayMemberThroughFatPtr(Expr *E) { void TransformFatPtr::VisitExprToBuildOffsetOfComponent( Expr *E, SourceLocation SLoc, SmallVector &Comps) { - if (E->getType().isFatPtrType()) { - return; - } else if (auto ASE = dyn_cast(E)) { + if (auto ASE = dyn_cast(E)) { VisitExprToBuildOffsetOfComponent(ASE->getBase(), SLoc, Comps); Comps.push_back(Sema::OffsetOfComponent()); Comps.back().isBrackets = true; @@ -2353,9 +2359,6 @@ bool Sema::CheckFatQualTypeAssignment(QualType LHSType, QualType RHSType) { // T *fat -> const T *fat is OK if (Context.hasSameFatPtrType(LHSPointeeType, RHSPointeeType)) return true; - // T *fat -> const void *fat is OK - if (LHSPointeeType->isVoidType()) - return true; return false; } @@ -2469,10 +2472,14 @@ Sema::BuildAllocationUnitForGlobalArrayVar(VarDecl *VD) { // Build first init expr : 1 Expr *LockVersionInit = IntegerLiteral::Create(Context, llvm::APInt(32, 1), UInt32Ty, SLoc); - // Build second init expr : array size - Expr *SizeInit = IntegerLiteral::Create( - Context, llvm::APInt(32, Context.getTypeSize(VD->getType()) / 8), - UInt32Ty, SLoc); + // Build second init expr: sizeof(array type) + Expr *SizeInit = CreateUnaryExprOrTypeTraitExpr( + Context.getTrivialTypeSourceInfo(VD->getType(), SLoc), + SLoc, UETT_SizeOf, SourceRange(SLoc)) + .get(); + SizeInit = + ImplicitCastExpr::Create(Context, UInt32Ty, CK_IntegralCast, SizeInit, + nullptr, VK_PRValue, FPOptionsOverride()); // Build InitListExpr SmallVector Inits{LockVersionInit, SizeInit}; if (VD->getInit()) diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 88048413a43f..fc83d60569f9 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -9253,8 +9253,7 @@ QualType Sema::CheckConditionalOperands(ExprResult &Cond, ExprResult &LHS, return LHSTy; if (LHS.get()->isNullExpr(Context) && RHSTy.isFatPtrType()) return RHSTy; - if (LHSTy.isFatPtrType() && RHSTy.isFatPtrType() && - Context.hasSameFatPtrType(LHSTy, RHSTy)) + if (LHSTy.isFatPtrType() && RHSTy.isFatPtrType()) return LHSTy; } #endif diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/global_array/array_out_of_bounds1.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/global_array/array_out_of_bounds1.cbs index bcd766bdb6e4..808528c68ccb 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/global_array/array_out_of_bounds1.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/global_array/array_out_of_bounds1.cbs @@ -1,5 +1,8 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s +// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c +// RUN: %clang %t-rw.c -o %t-rw.output +// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include @@ -10,4 +13,6 @@ int main() { return 0; } // CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/global_array/array_out_of_bounds1.cbs:[[@LINE-4]] in main \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/global_array/array_out_of_bounds1.cbs:[[@LINE-4]] in main + +// CHECK_REWRITE: Error: Pointer offset exceeds the allocation size! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/global_array/array_out_of_bounds2.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/global_array/array_out_of_bounds2.cbs index 5b260692ffc0..87932c20abc4 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/global_array/array_out_of_bounds2.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/global_array/array_out_of_bounds2.cbs @@ -1,5 +1,8 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s +// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c +// RUN: %clang %t-rw.c -o %t-rw.output +// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include @@ -12,4 +15,6 @@ int main() { return 0; } // CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/global_array/array_out_of_bounds2.cbs:[[@LINE-4]] in main \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/global_array/array_out_of_bounds2.cbs:[[@LINE-4]] in main + +// CHECK_REWRITE: Error: Pointer offset exceeds the allocation size! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds1.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds1.cbs index 0fbfd3e63612..5a33310bb120 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds1.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds1.cbs @@ -1,6 +1,8 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s - +// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c +// RUN: %clang %t-rw.c -o %t-rw.output +// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include int main() { @@ -9,4 +11,6 @@ int main() { return 0; } // CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds1.cbs:[[@LINE-4]] in main \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds1.cbs:[[@LINE-4]] in main + +// CHECK_REWRITE: Error: Pointer offset exceeds the allocation size! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds10.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds10.cbs index 77f601353803..d17365dc3916 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds10.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds10.cbs @@ -1,5 +1,8 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s +// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c +// RUN: %clang %t-rw.c -o %t-rw.output +// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include @@ -13,4 +16,6 @@ int main() { return 0; } // CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds10.cbs:[[@LINE-5]] in main \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds10.cbs:[[@LINE-5]] in main + +// CHECK_REWRITE: Error: Pointer offset exceeds the allocation size! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds11.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds11.cbs index be9b109ef756..6ccbfaf5f9dd 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds11.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds11.cbs @@ -1,5 +1,8 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s +// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c +// RUN: %clang %t-rw.c -o %t-rw.output +// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include @@ -14,4 +17,6 @@ int main() { return 0; } // CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds11.cbs:[[@LINE-5]] in main \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds11.cbs:[[@LINE-5]] in main + +// CHECK_REWRITE: Error: Pointer offset exceeds the allocation size! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds12.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds12.cbs index d56eea2a6605..adcf493632ca 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds12.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds12.cbs @@ -1,5 +1,8 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s +// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c +// RUN: %clang %t-rw.c -o %t-rw.output +// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include @@ -12,4 +15,6 @@ int main() { return 0; } // CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds12.cbs:[[@LINE-4]] in main \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds12.cbs:[[@LINE-4]] in main + +// CHECK_REWRITE: Error: Pointer offset exceeds the allocation size! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds13.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds13.cbs index 9bf14e9103f5..678efe61c9e4 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds13.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds13.cbs @@ -1,5 +1,8 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s +// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c +// RUN: %clang %t-rw.c -o %t-rw.output +// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include @@ -13,4 +16,6 @@ int main() { } } // CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds13.cbs:[[@LINE-5]] in main \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds13.cbs:[[@LINE-5]] in main + +// CHECK_REWRITE: Error: Pointer offset exceeds the allocation size! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds14.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds14.cbs index 3bcc0dd4bad0..0596724fb1a6 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds14.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds14.cbs @@ -1,5 +1,8 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s +// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c +// RUN: %clang %t-rw.c -o %t-rw.output +// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include @@ -10,4 +13,6 @@ int main() { return 0; } // CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds14.cbs:[[@LINE-4]] in main \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds14.cbs:[[@LINE-4]] in main + +// CHECK_REWRITE: Error: Pointer offset exceeds the allocation size! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds15.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds15.cbs index 73a2921356e5..ff9a588c37a0 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds15.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds15.cbs @@ -1,5 +1,8 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s +// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c +// RUN: %clang %t-rw.c -o %t-rw.output +// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include @@ -12,4 +15,6 @@ int main() { return 0; } // CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds15.cbs:[[@LINE-4]] in main \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds15.cbs:[[@LINE-4]] in main + +// CHECK_REWRITE: Error: Pointer offset exceeds the allocation size! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds16.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds16.cbs index a2dced01ec47..c73e2c7e3b94 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds16.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds16.cbs @@ -1,5 +1,8 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s +// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c +// RUN: %clang %t-rw.c -o %t-rw.output +// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include @@ -11,4 +14,6 @@ int main() { return 0; } // CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds16.cbs:[[@LINE-4]] in main \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds16.cbs:[[@LINE-4]] in main + +// CHECK_REWRITE: Error: Pointer offset exceeds the allocation size! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds17.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds17.cbs index ec5abdf668aa..ffff7ed5b15d 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds17.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds17.cbs @@ -1,5 +1,8 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s +// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c +// RUN: %clang %t-rw.c -o %t-rw.output +// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include @@ -15,4 +18,6 @@ int main() { foo(p1); } // CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds17.cbs:[[@LINE-10]] in bar \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds17.cbs:[[@LINE-10]] in bar + +// CHECK_REWRITE: Error: Pointer offset exceeds the allocation size! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds18.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds18.cbs index 3d46121aa394..0c5de151e3da 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds18.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds18.cbs @@ -1,5 +1,8 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s +// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c +// RUN: %clang %t-rw.c -o %t-rw.output +// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include @@ -15,4 +18,6 @@ int main() { } } // CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds18.cbs:[[@LINE-5]] in main \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds18.cbs:[[@LINE-5]] in main + +// CHECK_REWRITE: Error: Pointer offset exceeds the allocation size! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds19.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds19.cbs index 7a1b384742c3..3fe6c1756968 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds19.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds19.cbs @@ -1,5 +1,8 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s +// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c +// RUN: %clang %t-rw.c -o %t-rw.output +// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include @@ -16,4 +19,6 @@ int main() { } } // CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds19.cbs:[[@LINE-7]] in main \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds19.cbs:[[@LINE-7]] in main + +// CHECK_REWRITE: Error: Pointer offset exceeds the allocation size! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds2.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds2.cbs index 3f9fe90d1241..250a016ccd9a 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds2.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds2.cbs @@ -1,5 +1,8 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s +// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c +// RUN: %clang %t-rw.c -o %t-rw.output +// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include struct S { int *fat p; }; @@ -9,4 +12,6 @@ int main() { return 0; } // CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds2.cbs:[[@LINE-4]] in main \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds2.cbs:[[@LINE-4]] in main + +// CHECK_REWRITE: Error: Pointer offset exceeds the allocation size! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds20.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds20.cbs index ca43167ee674..b2b85fc016b0 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds20.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds20.cbs @@ -1,5 +1,8 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s +// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c +// RUN: %clang %t-rw.c -o %t-rw.output +// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include int *fat return_fat() { @@ -11,4 +14,6 @@ int main() { *p = 10; } // CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds20.cbs:[[@LINE-3]] in main \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds20.cbs:[[@LINE-3]] in main + +// CHECK_REWRITE: Error: Pointer offset exceeds the allocation size! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds21.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds21.cbs index 1f153854bb56..dab1a0677c53 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds21.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds21.cbs @@ -1,5 +1,8 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s +// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c +// RUN: %clang %t-rw.c -o %t-rw.output +// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include @@ -9,4 +12,6 @@ int main() { *p1 = 10; } // CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds21.cbs:[[@LINE-3]] in main \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds21.cbs:[[@LINE-3]] in main + +// CHECK_REWRITE: Error: Pointer offset exceeds the allocation size! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds22.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds22.cbs index d67859854fb7..06026faef3f1 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds22.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds22.cbs @@ -1,5 +1,8 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s +// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c +// RUN: %clang %t-rw.c -o %t-rw.output +// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include @@ -13,4 +16,6 @@ int main() { return 0; } // CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds22.cbs:[[@LINE-5]] in main \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds22.cbs:[[@LINE-5]] in main + +// CHECK_REWRITE: Error: Pointer offset exceeds the allocation size! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds23.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds23.cbs index a9e49d3a7c23..5121f47f5c43 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds23.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds23.cbs @@ -1,5 +1,8 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s +// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c +// RUN: %clang %t-rw.c -o %t-rw.output +// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include @@ -13,4 +16,6 @@ int main() { return 0; } // CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds23.cbs:[[@LINE-5]] in main \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds23.cbs:[[@LINE-5]] in main + +// CHECK_REWRITE: Error: Pointer offset exceeds the allocation size! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds24.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds24.cbs index 1849f3f1dd55..139155ec5d30 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds24.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds24.cbs @@ -1,5 +1,8 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s +// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c +// RUN: %clang %t-rw.c -o %t-rw.output +// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include @@ -13,4 +16,6 @@ int main() { return 0; } // CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds24.cbs:[[@LINE-4]] in main \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds24.cbs:[[@LINE-4]] in main + +// CHECK_REWRITE: Error: Pointer offset exceeds the allocation size! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds3.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds3.cbs index ffd3256569c8..7257cf7677e1 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds3.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds3.cbs @@ -1,5 +1,8 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s +// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c +// RUN: %clang %t-rw.c -o %t-rw.output +// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include @@ -10,4 +13,6 @@ int main() { return 0; } // CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds3.cbs:[[@LINE-4]] in main \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds3.cbs:[[@LINE-4]] in main + +// CHECK_REWRITE: Error: Pointer offset exceeds the allocation size! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds4.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds4.cbs index 50a853dd1568..9a04b5bb800f 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds4.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds4.cbs @@ -1,5 +1,8 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s +// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c +// RUN: %clang %t-rw.c -o %t-rw.output +// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include @@ -11,4 +14,6 @@ int main() { return 0; } // CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds4.cbs:[[@LINE-4]] in main \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds4.cbs:[[@LINE-4]] in main + +// CHECK_REWRITE: Error: Pointer offset exceeds the allocation size! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds5.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds5.cbs index 5065a33b6391..f5bae62d9392 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds5.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds5.cbs @@ -1,5 +1,8 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s +// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c +// RUN: %clang %t-rw.c -o %t-rw.output +// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include @@ -11,4 +14,6 @@ int main() { return 0; } // CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds5.cbs:[[@LINE-4]] in main \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds5.cbs:[[@LINE-4]] in main + +// CHECK_REWRITE: Error: Pointer offset exceeds the allocation size! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds6.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds6.cbs index 6b2be24bedba..c4108deaa7e4 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds6.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds6.cbs @@ -1,5 +1,8 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s +// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c +// RUN: %clang %t-rw.c -o %t-rw.output +// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include @@ -9,4 +12,6 @@ int main() { int a = p->arr[3]; } // CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds6.cbs:[[@LINE-3]] in main \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds6.cbs:[[@LINE-3]] in main + +// CHECK_REWRITE: Error: Pointer offset exceeds the allocation size! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds7.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds7.cbs index 6c41e1f26999..918ef42f5ef8 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds7.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds7.cbs @@ -1,5 +1,8 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s +// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c +// RUN: %clang %t-rw.c -o %t-rw.output +// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include @@ -14,4 +17,6 @@ int main() { return 0; } // CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds7.cbs:[[@LINE-6]] in main \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds7.cbs:[[@LINE-6]] in main + +// CHECK_REWRITE: Error: Pointer offset exceeds the allocation size! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds8.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds8.cbs index 7f02135b5b61..8bd93d146702 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds8.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds8.cbs @@ -1,5 +1,8 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s +// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c +// RUN: %clang %t-rw.c -o %t-rw.output +// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include @@ -14,4 +17,6 @@ int main() { return 0; } // CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds8.cbs:[[@LINE-4]] in main \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds8.cbs:[[@LINE-4]] in main + +// CHECK_REWRITE: Error: Pointer offset exceeds the allocation size! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds9.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds9.cbs index e39e5e37957c..29682bed1f40 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds9.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds9.cbs @@ -1,5 +1,8 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s +// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c +// RUN: %clang %t-rw.c -o %t-rw.output +// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include @@ -12,4 +15,6 @@ int main() { return 0; } // CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds9.cbs:[[@LINE-5]] in main \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds9.cbs:[[@LINE-5]] in main + +// CHECK_REWRITE: Error: Pointer offset exceeds the allocation size! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/buffer_overflow.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/buffer_overflow.cbs index 8a11f4223d8a..e84c92934dd0 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/buffer_overflow.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/buffer_overflow.cbs @@ -1,5 +1,8 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output longstring 2>&1 | FileCheck %s +// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c +// RUN: %clang %t-rw.c -o %t-rw.output +// RUN: not %t-rw.output longstring 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include #define BUFSIZE 8 @@ -19,4 +22,6 @@ int main(int argc, char **argv) { return 0; } // CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/buffer_overflow.cbs:[[@LINE-13]] in fat_strcpy \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/buffer_overflow.cbs:[[@LINE-13]] in fat_strcpy + +// CHECK_REWRITE: Error: Pointer offset exceeds the allocation size! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free1.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free1.cbs index 76ce095d0468..990b0694b5b5 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free1.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free1.cbs @@ -1,5 +1,8 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s +// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c +// RUN: %clang %t-rw.c -o %t-rw.output +// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include @@ -10,4 +13,6 @@ int main() { return 0; } -// CHECK: Error: Cannot free this pointer because the allocation may have been freed! \ No newline at end of file +// CHECK: Error: Cannot free this pointer because the allocation may have been freed! + +// CHECK_REWRITE: Error: Cannot free this pointer because the allocation may have been freed! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free2.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free2.cbs index 0011f1b6ca6c..10a74377018f 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free2.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free2.cbs @@ -1,5 +1,8 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s +// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c +// RUN: %clang %t-rw.c -o %t-rw.output +// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include @@ -11,4 +14,6 @@ int main() { return 0; } -// CHECK: Error: Cannot free this pointer because the allocation may have been freed! \ No newline at end of file +// CHECK: Error: Cannot free this pointer because the allocation may have been freed! + +// CHECK_REWRITE: Error: Cannot free this pointer because the allocation may have been freed! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free3.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free3.cbs index 621c6515254b..ec5603d9cab6 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free3.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free3.cbs @@ -1,5 +1,8 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s +// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c +// RUN: %clang %t-rw.c -o %t-rw.output +// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include @@ -13,4 +16,6 @@ int main() { return 0; } -// CHECK: Error: Cannot free this pointer because the allocation may have been freed! \ No newline at end of file +// CHECK: Error: Cannot free this pointer because the allocation may have been freed! + +// CHECK_REWRITE: Error: Cannot free this pointer because the allocation may have been freed! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free4.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free4.cbs index 3872ebea1bc9..4fead9d6cb42 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free4.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free4.cbs @@ -1,5 +1,8 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s +// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c +// RUN: %clang %t-rw.c -o %t-rw.output +// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include @@ -15,4 +18,6 @@ int main() { return 0; } -// CHECK: Error: Cannot free this pointer because the allocation may have been freed! \ No newline at end of file +// CHECK: Error: Cannot free this pointer because the allocation may have been freed! + +// CHECK_REWRITE: Error: Cannot free this pointer because the allocation may have been freed! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free1.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free1.cbs index da6ab753e4e1..fa819d279390 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free1.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free1.cbs @@ -1,5 +1,8 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s +// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c +// RUN: %clang %t-rw.c -o %t-rw.output +// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include @@ -12,4 +15,6 @@ int main() { } // CHECK: Error: Cannot use this pointer because the allocation may have been freed or reseored! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free1.cbs:[[@LINE-5]] in main \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free1.cbs:[[@LINE-5]] in main + +// CHECK_REWRITE: Error: Cannot use this pointer because the allocation may have been freed or reseored! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free2.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free2.cbs index 02d21d1a8651..81a61e4a3b99 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free2.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free2.cbs @@ -1,5 +1,8 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s +// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c +// RUN: %clang %t-rw.c -o %t-rw.output +// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include @@ -13,4 +16,6 @@ int main() { } // CHECK: Error: Cannot use this pointer because the allocation may have been freed or reseored! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free2.cbs:[[@LINE-5]] in main \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free2.cbs:[[@LINE-5]] in main + +// CHECK_REWRITE: Error: Cannot use this pointer because the allocation may have been freed or reseored! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free3.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free3.cbs index fdc25eaec623..9b0b9e4a1e1a 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free3.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free3.cbs @@ -1,5 +1,8 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s +// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c +// RUN: %clang %t-rw.c -o %t-rw.output +// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include @@ -15,4 +18,6 @@ int main() { return 0; } // CHECK: Error: Cannot use this pointer because the allocation may have been freed or reseored! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free3.cbs:[[@LINE-4]] in main \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free3.cbs:[[@LINE-4]] in main + +// CHECK_REWRITE: Error: Cannot use this pointer because the allocation may have been freed or reseored! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free4.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free4.cbs index 4a0f2ee93bf7..0f05fd96231e 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free4.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free4.cbs @@ -1,5 +1,8 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s +// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c +// RUN: %clang %t-rw.c -o %t-rw.output +// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include @@ -13,4 +16,6 @@ int main() { return 0; } // CHECK: Error: Cannot use this pointer because the allocation may have been freed or reseored! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free4.cbs:[[@LINE-4]] in main \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free4.cbs:[[@LINE-4]] in main + +// CHECK_REWRITE: Error: Cannot use this pointer because the allocation may have been freed or reseored! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free5.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free5.cbs index 3568698813ac..8de15ede94da 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free5.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free5.cbs @@ -1,5 +1,8 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s +// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c +// RUN: %clang %t-rw.c -o %t-rw.output +// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include #define BUFSIZE 32 @@ -27,4 +30,6 @@ int main() { return 0; } // CHECK: Error: Cannot use this pointer because the allocation may have been freed or reseored! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free5.cbs:[[@LINE-20]] in fat_strncpy \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free5.cbs:[[@LINE-20]] in fat_strncpy + +// CHECK_REWRITE: Error: Cannot use this pointer because the allocation may have been freed or reseored! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free6.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free6.cbs index 4292d301d94b..e5bfcd027159 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free6.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free6.cbs @@ -1,5 +1,8 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s +// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c +// RUN: %clang %t-rw.c -o %t-rw.output +// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include @@ -12,4 +15,6 @@ int main() { return 0; } // CHECK: Error: Cannot use this pointer because the allocation may have been freed or reseored! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free6.cbs:[[@LINE-4]] in main \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free6.cbs:[[@LINE-4]] in main + +// CHECK_REWRITE: Error: Cannot use this pointer because the allocation may have been freed or reseored! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free7.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free7.cbs index 9b6f5b78119c..6a4fe7029e6c 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free7.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free7.cbs @@ -1,5 +1,8 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s +// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c +// RUN: %clang %t-rw.c -o %t-rw.output +// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include @@ -12,4 +15,6 @@ int main() { return 0; } // CHECK: Error: Cannot use this pointer because the allocation may have been freed or reseored! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free7.cbs:[[@LINE-6]] in main \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free7.cbs:[[@LINE-6]] in main + +// CHECK_REWRITE: Error: Cannot use this pointer because the allocation may have been freed or reseored! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free8.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free8.cbs index 28b7648db77d..0ea01d4e2dfb 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free8.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free8.cbs @@ -1,5 +1,8 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s +// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c +// RUN: %clang %t-rw.c -o %t-rw.output +// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include @@ -14,4 +17,6 @@ int main() { return 0; } // CHECK: Error: Cannot use this pointer because the allocation may have been freed or reseored! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free8.cbs:[[@LINE-6]] in main \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free8.cbs:[[@LINE-6]] in main + +// CHECK_REWRITE: Error: Cannot use this pointer because the allocation may have been freed or reseored! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds1.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds1.cbs index e99f7725e1b5..95626b015062 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds1.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds1.cbs @@ -1,5 +1,8 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s +// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c +// RUN: %clang %t-rw.c -o %t-rw.output +// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include @@ -10,4 +13,6 @@ int main() { return 0; } // CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds1.cbs:[[@LINE-4]] in main \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds1.cbs:[[@LINE-4]] in main + +// CHECK_REWRITE: Error: Pointer offset exceeds the allocation size! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds2.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds2.cbs index 2b1425733499..402aa037db0f 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds2.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds2.cbs @@ -1,5 +1,8 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s +// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c +// RUN: %clang %t-rw.c -o %t-rw.output +// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include @@ -11,4 +14,6 @@ int main() { return 0; } // CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds2.cbs:[[@LINE-4]] in main \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds2.cbs:[[@LINE-4]] in main + +// CHECK_REWRITE: Error: Pointer offset exceeds the allocation size! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds3.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds3.cbs index 52a72be91eeb..dea1a8b0a909 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds3.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds3.cbs @@ -1,5 +1,8 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s +// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c +// RUN: %clang %t-rw.c -o %t-rw.output +// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include @@ -11,4 +14,6 @@ int main() { return 0; } // CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds3.cbs:[[@LINE-4]] in main \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds3.cbs:[[@LINE-4]] in main + +// CHECK_REWRITE: Error: Pointer offset exceeds the allocation size! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds4.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds4.cbs index 2c6abd37814b..8f3465267eb0 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds4.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds4.cbs @@ -1,5 +1,8 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s +// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c +// RUN: %clang %t-rw.c -o %t-rw.output +// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include @@ -12,3 +15,4 @@ int main() { // CHECK: Error: Pointer offset exceeds the allocation size! // CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds4.cbs:[[@LINE-4]] in main +// CHECK_REWRITE: Error: Pointer offset exceeds the allocation size! diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/buffer_overflow.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/buffer_overflow.cbs index 610d45c170c3..582df755ea21 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/buffer_overflow.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/buffer_overflow.cbs @@ -1,5 +1,8 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output longstring 2>&1 | FileCheck %s +// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c +// RUN: %clang %t-rw.c -o %t-rw.output +// RUN: not %t-rw.output longstring 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include #define BUFSIZE 8 @@ -20,4 +23,6 @@ int main(int argc, char **argv) { return 0; } // CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/buffer_overflow.cbs:[[@LINE-14]] in fat_strcpy \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/buffer_overflow.cbs:[[@LINE-14]] in fat_strcpy + +// CHECK_REWRITE: Error: Pointer offset exceeds the allocation size! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/return_local_address.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/return_local_address.cbs index c2d89d83e759..7f36b4c1932a 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/return_local_address.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/return_local_address.cbs @@ -1,5 +1,8 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s +// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c +// RUN: %clang %t-rw.c -o %t-rw.output +// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include @@ -15,4 +18,6 @@ int main() { return 0; } // CHECK: Error: Cannot use this pointer because the allocation may have been freed or reseored! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/return_local_address.cbs:[[@LINE-4]] in main \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/return_local_address.cbs:[[@LINE-4]] in main + +// CHECK_REWRITE: Error: Cannot use this pointer because the allocation may have been freed or reseored! \ No newline at end of file diff --git a/clang/test/BSC/Positive/FatPtr/global_array.cbs b/clang/test/BSC/Positive/FatPtr/global_array.cbs index 7f4893eab5f0..9c77c2999d8a 100644 --- a/clang/test/BSC/Positive/FatPtr/global_array.cbs +++ b/clang/test/BSC/Positive/FatPtr/global_array.cbs @@ -1,5 +1,8 @@ // RUN: %clang %s -enable-fat-ptr -o %t.output // RUN: %t.output +// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c +// RUN: %clang %t-rw.c -o %t-rw.output +// RUN: %t-rw.output // expected-no-diagnostics #include diff --git a/clang/test/BSC/Positive/FatPtr/heap.cbs b/clang/test/BSC/Positive/FatPtr/heap.cbs index 4d904d01d50a..3a02ff54c677 100644 --- a/clang/test/BSC/Positive/FatPtr/heap.cbs +++ b/clang/test/BSC/Positive/FatPtr/heap.cbs @@ -1,5 +1,8 @@ // RUN: %clang %s -enable-fat-ptr -o %t.output // RUN: %t.output +// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c +// RUN: %clang %t-rw.c -o %t-rw.output +// RUN: %t-rw.output // expected-no-diagnostics #include diff --git a/clang/test/BSC/Positive/FatPtr/multi_level_pointer.cbs b/clang/test/BSC/Positive/FatPtr/multi_level_pointer.cbs index b0cfd7a418fd..182dfc7a432c 100644 --- a/clang/test/BSC/Positive/FatPtr/multi_level_pointer.cbs +++ b/clang/test/BSC/Positive/FatPtr/multi_level_pointer.cbs @@ -1,5 +1,8 @@ // RUN: %clang %s -enable-fat-ptr -o %t.output // RUN: %t.output | FileCheck %s +// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c +// RUN: %clang %t-rw.c -o %t-rw.output +// RUN: %t-rw.output | FileCheck %s // expected-no-diagnostics #include diff --git a/clang/test/BSC/Positive/FatPtr/nullptr_cast/nullptr_cast.cbs b/clang/test/BSC/Positive/FatPtr/nullptr_cast/nullptr_cast.cbs index 9a47ca5dac2b..f835dc7a158a 100644 --- a/clang/test/BSC/Positive/FatPtr/nullptr_cast/nullptr_cast.cbs +++ b/clang/test/BSC/Positive/FatPtr/nullptr_cast/nullptr_cast.cbs @@ -1,5 +1,8 @@ // RUN: %clang %s -enable-fat-ptr -o %t.output // RUN: %t.output +// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c +// RUN: %clang %t-rw.c -o %t-rw.output +// RUN: %t-rw.output // expected-no-diagnostics #include diff --git a/clang/test/BSC/Positive/FatPtr/stack.cbs b/clang/test/BSC/Positive/FatPtr/stack.cbs index 22b307601f5b..b7a13ea08243 100644 --- a/clang/test/BSC/Positive/FatPtr/stack.cbs +++ b/clang/test/BSC/Positive/FatPtr/stack.cbs @@ -1,5 +1,8 @@ // RUN: %clang %s -enable-fat-ptr -o %t.output // RUN: %t.output +// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c +// RUN: %clang %t-rw.c -o %t-rw.output +// RUN: %t-rw.output // expected-no-diagnostics #include diff --git a/clang/test/BSC/Positive/FatPtr/string_literal.cbs b/clang/test/BSC/Positive/FatPtr/string_literal.cbs index 7460656d2b6d..237200bff930 100644 --- a/clang/test/BSC/Positive/FatPtr/string_literal.cbs +++ b/clang/test/BSC/Positive/FatPtr/string_literal.cbs @@ -1,5 +1,8 @@ // RUN: %clang %s -enable-fat-ptr -o %t.output // RUN: %t.output | FileCheck %s +// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c +// RUN: %clang %t-rw.c -o %t-rw.output +// RUN: %t-rw.output | FileCheck %s // expected-no-diagnostics #include @@ -15,7 +18,7 @@ char *fat bar(char *fat p) { int main() { char *fat p = (char *fat)"1: Hello world!\n"; printf("%s", (char *)p); - foo((char *fat)"2: Hello world!\n"); + foo((const char *fat)"2: Hello world!\n"); char *fat ss[] = { (char *fat)"Hello", (char *fat)"world"}; printf("3: %s %s!\n", (char *)ss[0], (char *)ss[1]); printf("4: %s %s!\n", (char *)bar(ss[0]), (char *)bar(ss[1])); -- Gitee From 0469b81e232ef0c85492dbc786caf1bbcbddce54 Mon Sep 17 00:00:00 2001 From: zhangziyao Date: Fri, 20 Dec 2024 18:09:17 +0800 Subject: [PATCH 08/29] [borrowck] A new implementation of BiShengC borrow checker based on NLL(Non Lexical Lifetimes). It calculates the NLL of region variables by generating a series of constraints, including liveness constraints, subtyping constraints and reborrow constraints and solving them. The algorithm has polynomial time complexity and performs well in complex control flow scenarios. Note: We provide a DEBUG_PRINT macro to display the execution process of the borrow checker. To enable it, change DEBUG_PRINT value to 1 in BSCBorrowChecker.h --- clang/include/clang/AST/Type.h | 7 +- .../Analysis/Analyses/BSCBorrowChecker.h | 940 +++++++++ clang/include/clang/Analysis/CFG.h | 5 +- .../clang/Basic/BSC/DiagnosticBSCSemaKinds.td | 23 + clang/include/clang/Sema/Sema.h | 1 + clang/lib/AST/BSC/TypeBSC.cpp | 29 + clang/lib/Analysis/BSCBorrowChecker.cpp | 1716 +++++++++++++++++ clang/lib/Analysis/CFG.cpp | 27 + clang/lib/Analysis/CMakeLists.txt | 1 + clang/lib/Sema/BSC/SemaDeclBSC.cpp | 698 ++++++- clang/lib/Sema/SemaDecl.cpp | 8 +- .../NullabilityCheck/while_stmt_struct.cbs | 9 +- .../assign_to_borrowed_with_many_loops.cbs | 29 + .../at_most_one_mut_borrow.cbs | 297 +-- .../at_most_one_mut_borrow_member_func.cbs | 28 + ...rrowat_most_one_mut_borrow_member_func.cbs | 29 - .../borrow_field_of_another_borrow.cbs | 86 +- .../borrow_field_of_struct.cbs | 103 +- .../borrow_live_longer/borrow_live_longer.cbs | 128 +- .../borrow_live_longer_loop.cbs | 111 +- .../borrow_same_var_in_params.cbs | 46 + .../borrow_struct_borrow_field.cbs | 151 +- .../borrow_var_assign/borrow_var_assign.cbs | 69 + .../borrow_var_expired/borrow_var_expired.cbs | 260 +-- .../borrow_with_complex_control_flow.cbs | 21 + .../field_borrow_field/field_borrow_field.cbs | 60 +- .../field_borrow_var/field_borrow_var.cbs | 43 +- .../kill_loans_by_write_to_path.cbs | 23 + .../member_function/member_function.cbs | 56 +- .../nested_call_and_member.cbs | 32 +- .../return_borrow/return_borrow.cbs | 144 +- .../return_borrow_loop/return_borrow_loop.cbs | 78 +- .../struct_has_own_struct_type_as_field.cbs | 128 +- .../struct_with_borrow_fields.cbs | 181 ++ .../target_is_raw_pointer.cbs | 36 +- .../use_borrow_in_condition.cbs | 220 ++- .../var_borrow_field/var_borrow_field.cbs | 70 +- .../var_borrow_struct/var_borrow_struct.cbs | 104 +- .../borrow_arr_freeze/borrow_arr_freeze.cbs | 47 +- .../borrow_ident_freeze.cbs | 348 ++-- .../borrow_star_freeze/borrow_star_freeze.cbs | 5 +- .../member_func_call_freeze.cbs | 21 +- .../Borrow/borrow_trait/borrow_trait.cbs | 5 +- .../rewrite_bsc_borrow/rewrite_bsc_borrow.cbs | 12 +- .../OperatorOverload/Subscript/Subscript.cbs | 8 +- .../Positive/OperatorOverload/deref/deref.cbs | 14 +- .../member_ref/member_ref.cbs | 45 +- .../at_most_one_mut_borrow.cbs | 15 +- .../borrow_with_assert/borrow_with_assert.cbs | 20 + .../read_when_mut_borrowed.cbs | 24 + .../return_borrow/return_borrow.cbs | 11 +- .../return_borrow/return_not_local_borrow.cbs | 6 +- .../return_borrow_from_raw_pointer.cbs | 86 +- .../struct_with_borrow_init_by_func_call.cbs | 52 +- .../struct_with_borrow_init_by_func_call2.cbs | 35 +- .../virtual_target_live_longer.cbs | 28 +- .../borrow_serialization.cbs | 134 +- 57 files changed, 5461 insertions(+), 1452 deletions(-) create mode 100644 clang/include/clang/Analysis/Analyses/BSCBorrowChecker.h create mode 100644 clang/lib/Analysis/BSCBorrowChecker.cpp create mode 100644 clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/assign_to_borrowed_with_many_loops/assign_to_borrowed_with_many_loops.cbs create mode 100644 clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/at_most_one_mut_borrow_member_func/at_most_one_mut_borrow_member_func.cbs delete mode 100644 clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/at_most_one_mut_borrow_member_func/at_most_one_mut_borrowat_most_one_mut_borrow_member_func.cbs create mode 100644 clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/borrow_same_var_in_params/borrow_same_var_in_params.cbs create mode 100644 clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/borrow_var_assign/borrow_var_assign.cbs create mode 100644 clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/borrow_with_complex_control_flow/borrow_with_complex_control_flow.cbs create mode 100644 clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/kill_loans_by_write_to_path/kill_loans_by_write_to_path.cbs create mode 100644 clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/struct_with_borrow_fields/struct_with_borrow_fields.cbs rename clang/test/BSC/{Positive/Ownership => Negative/Ownership/Borrow}/borrow_check_rules/target_is_raw_pointer/target_is_raw_pointer.cbs (33%) create mode 100644 clang/test/BSC/Positive/Ownership/borrow_check_rules/borrow_with_assert/borrow_with_assert.cbs create mode 100644 clang/test/BSC/Positive/Ownership/borrow_check_rules/read_when_mut_borrowed/read_when_mut_borrowed.cbs diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h index c189a56d6331..58edda5c4989 100644 --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -2211,7 +2211,8 @@ public: bool hasBorrowFields() const; -#endif + bool withBorrowFields() const; + #endif /// Test for a particular builtin type. bool isSpecificBuiltinType(unsigned K) const; @@ -4992,6 +4993,10 @@ public: bool hasBorrowFields() const; + /// Recursively check all fields in the record for borrow-ness. If any field + /// is declared borrow, return true. Otherwise, return false. + bool withBorrowFields() const; + void initBorrowStatus() const; bool hasFatFields() const; diff --git a/clang/include/clang/Analysis/Analyses/BSCBorrowChecker.h b/clang/include/clang/Analysis/Analyses/BSCBorrowChecker.h new file mode 100644 index 000000000000..67196a6b3108 --- /dev/null +++ b/clang/include/clang/Analysis/Analyses/BSCBorrowChecker.h @@ -0,0 +1,940 @@ +//===- BSCBorrowChecker.h - Borrow Check for Source CFGs -*- BSC --*----------// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements BSC borrow checker for source-level CFGs. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_ANALYSIS_ANALYSES_BSCBORROWCHECKER_H +#define LLVM_CLANG_ANALYSIS_ANALYSES_BSCBORROWCHECKER_H + +#if ENABLE_BSC + +#include "clang/AST/Decl.h" +#include "clang/Analysis/CFG.h" +#include "clang/Basic/DiagnosticSema.h" +#include "clang/Sema/Sema.h" +#include +#include + +#define DEBUG_PRINT 0 + +namespace clang { +namespace borrow { +class RegionCheck; + +/// Name of region. Each RegionName corresponds to an AST node. +/// +/// Each bound region is named `'region` plus a positive integer, +/// such as 'region_0, 'region_1, etc. Bound region is related to +/// variables or a borrow/reborrow expression in the function. +/// The free region is named 'region_r. Free region is related to +/// the return point in the function or points in the caller. +class RegionName { +private: + RegionName(std::string Name) : Name(Name) {} + +public: + std::string Name; + constexpr static const char *const NamePrefix = "'region_"; + static unsigned Cnt; + + bool operator<(const RegionName &other) const { return Name < other.Name; } + + static RegionName Create() { + return RegionName(NamePrefix + std::to_string(Cnt++)); + } + + static RegionName CreateFree() { + return RegionName(std::string(NamePrefix) + "r"); + } + + RegionName() { Name = "invalid"; } + + bool isInvalid() const { return Name == "invalid"; } + + std::string print() const { return "RegionName { " + Name + " }"; } +}; + +/// An index representation of RegionName, in order to facilitate calculations. +/// +/// Each RegionName corresponds to a RegionVariable. +/// The index of RegionVariable increases from 0. +struct RegionVariable { + unsigned index; + + RegionVariable(unsigned index = 0) : index(index) {} + + std::string print() const { + return "RegionVariable { index: " + std::to_string(index) + " }"; + } +}; + +/// Representation of variables and fields of structs. +/// +/// Examples: +/// +/// - the Path of `a` is: +/// Path(Var, "a"). +/// - the Path of `p.a` is: +/// Path(Extension, +/// Path(Var, "p"), +/// "a"). +/// - the Path of `p->a` is: +/// Path(Extension, +/// Path(Extension, +/// Path(Var, "p"), +/// "*"), +/// "a"). +/// - the Path of `*p.a` is: +/// Path(Extension, +/// Path(Extension, +/// Path(Var, "p"), +/// "a"), +/// "*"). +/// - the Path of `*(p->a)` is: +/// Path(Extension, +/// Path(Extension, +/// Path(Extension, +/// Path(Var, "p"), +/// "*"), +/// "a"), +/// "*"). +/// - the Path of `p->a->b` is: +/// Path(Extension, +/// Path(Extension, +/// Path(Extension, +/// Path(Extension, +/// Path(Var, "p"), +/// "*"), +/// "a"), +/// "*"), +/// "b"). +struct Path { + enum class PathType : unsigned char { Var, Extension }; + + PathType type; + + // Non-null when type is Extension. + std::unique_ptr base = nullptr; + + std::string fieldName; + + // Type of the current path. + QualType ty; + + // Need for reborrow constraints generation. + Decl *D = nullptr; + + SourceLocation Location; + + /// Construct a Var type Path. + explicit Path(const std::string &name, QualType ty, SourceLocation Location) + : type(PathType::Var), fieldName(name), ty(ty), Location(Location) {} + + // Construct an Extension type Path. + Path(std::unique_ptr base, const std::string &name, QualType ty, + SourceLocation Location) + : type(PathType::Extension), base(std::move(base)), fieldName(name), + ty(ty), Location(Location) {} + + Path(const Path &other) + : type(other.type), fieldName(other.fieldName), ty(other.ty), D(other.D), + Location(other.Location) { + if (other.base) { + base = std::make_unique(*other.base); + } + } + + ~Path() = default; + + void setDecl(Decl *D) { this->D = D; } + + bool isDeref() const { + if (type == PathType::Var) + return false; + return fieldName == "*"; + } + + /// The prefixes of a path are all the lvalues you get by stripping away + /// fields and derefs. + /// + /// Examples: + /// + /// - the prefixes of `*a.b` where `a` is a struct are + /// `*a.b`, `a.b` and `a`. + /// - the prefixes of `a.b.c` where both `a` and `b` are structs are + /// `a.b.c`, `a.b` and `a`. + llvm::SmallVector prefixes() const { + llvm::SmallVector result; + const Path *cur = this; + while (true) { + result.push_back(cur); + if (cur->type == PathType::Var) + return result; + cur = cur->base.get(); + } + } + + /// The supporting prefixes of a path are all the prefixes of a path that + /// must remain valid for the path itself to remain valid. For the most part, + /// this means all prefixes, except that recursion stops when dereferencing a + /// shared reference. + /// + /// Examples: + /// + /// - the supporting prefixes of `s.f` where `s` is a struct are + /// `s.f` and `s`. + /// - the supporting prefixes of `(*r).f` where `r` is a shared reference are + /// `(*r).f` and `*r`, but not `r`. + /// - Intuition: one could always copy `*r` into a temporary `t` and reach + /// the data through `*t`, so it is not important to preserve `r` itself. + /// - the supporting prefixes of `(*m).f` where `m` is a mutable reference are + /// `(*m).f`, `*m`, and `m`. + llvm::SmallVector supportingPrefixes() const { + llvm::SmallVector result; + const Path *cur = this; + while (true) { + result.push_back(cur); + if (cur->type == PathType::Var) + return result; + if (cur->fieldName == "*" && cur->base->ty.isConstBorrow()) { + return result; + } + cur = cur->base.get(); + } + } + + std::string to_string() const { + if (type == PathType::Var) + return fieldName; + if (fieldName == "*") + return "*" + base->to_string(); + if (base->isDeref()) + return '(' + base->to_string() + ")." + fieldName; + return base->to_string() + '.' + fieldName; + } + + std::string print() const { return to_string(); } +}; + +enum class BorrowKind { Mut, Shared }; + +/// An abstraction of CFG nodes. +/// +/// Every CFG node is converted to one or more actions. +struct Action { + enum ActionKind { Noop, Init, Borrow, Assign, Use, StorageDead }; + + ActionKind Kind; + + Action(ActionKind Kind) : Kind(Kind) {} + + virtual ~Action() = default; + + ActionKind getKind() const { return Kind; } + + virtual llvm::Optional OverWrites() const { return llvm::None; } + + virtual std::string print() const = 0; + +private: + virtual void anchor(); +}; + +struct ActionNoop : public Action { + ActionNoop() : Action(Noop) {} + + ~ActionNoop() override = default; + + std::string print() const override { return "ActionNoop"; } + + static bool classof(const Action *A) { return A->getKind() == Noop; } + +private: + void anchor() override; +}; + +struct ActionInit : public Action { + std::unique_ptr Dest; + std::vector> Sources; + std::vector> DerefSources; + + ActionInit(std::unique_ptr Dest, + std::vector> Sources, + std::vector> DerefSources = {}) + : Action(Init), Dest(std::move(Dest)), Sources(std::move(Sources)), + DerefSources(std::move(DerefSources)) {} + + ~ActionInit() override = default; + + virtual llvm::Optional OverWrites() const override { + return llvm::Optional(Dest.get()); + } + + std::string print() const override { + std::string Ret; + Ret += "ActionInit: "; + Ret += Dest->print(); + Ret += " = use("; + for (auto &a : Sources) { + Ret += a->print(); + Ret += ", "; + } + Ret += ")"; + return Ret; + } + + static bool classof(const Action *A) { return A->getKind() == Init; } + +private: + void anchor() override; +}; + +struct ActionBorrow : public Action { + std::unique_ptr Dest; + RegionName RNL; + RegionName RNR; + BorrowKind BK; + std::unique_ptr Source; + + ActionBorrow(std::unique_ptr Dest, RegionName RNL, RegionName RNR, + BorrowKind BK, std::unique_ptr Source) + : Action(Borrow), Dest(std::move(Dest)), RNL(RNL), RNR(RNR), BK(BK), + Source(std::move(Source)) {} + + ~ActionBorrow() override = default; + + virtual llvm::Optional OverWrites() const override { + return llvm::Optional(Dest.get()); + } + + std::string print() const override { + std::string Ret; + Ret += "ActionBorrow: "; + Ret += Dest->print(); + Ret += " = "; + Ret += RNR.Name; + if (BK == BorrowKind::Mut) + Ret += " &mut "; + else + Ret += " & "; + Ret += Source->print(); + return Ret; + } + + static bool classof(const Action *A) { return A->getKind() == Borrow; } + +private: + void anchor() override; +}; + +struct ActionAssign : public Action { + std::unique_ptr Dest; + RegionName RNL; + RegionName RNR; + std::unique_ptr Source; + RegionName DerefRN; + std::vector> DerefSources; + + ActionAssign(std::unique_ptr Dest, RegionName RNL, RegionName RNR, + std::unique_ptr Source, RegionName DerefRN, + std::vector> DerefSources) + : Action(Assign), Dest(std::move(Dest)), RNL(RNL), RNR(RNR), + Source(std::move(Source)), DerefRN(DerefRN), + DerefSources(std::move(DerefSources)) {} + + ~ActionAssign() override = default; + + virtual llvm::Optional OverWrites() const override { + return llvm::Optional(Dest.get()); + } + + std::string print() const override { + std::string Ret; + Ret += "ActionAssign: "; + Ret += Dest->print(); + Ret += " " + RNL.print(); + Ret += " = "; + Ret += Source->print(); + Ret += " " + RNR.print(); + return Ret; + } + + static bool classof(const Action *A) { return A->getKind() == Assign; } + +private: + void anchor() override; +}; + +struct ActionUse : public Action { + std::vector> Uses; + std::vector> DerefSources; + + ActionUse(std::vector> Uses, + std::vector> DerefSources) + : Action(Use), Uses(std::move(Uses)), + DerefSources(std::move(DerefSources)) {} + + ~ActionUse() override = default; + + std::string print() const override { + std::string Ret; + Ret += "ActionUse: "; + Ret += " use("; + for (auto &a : Uses) { + Ret += a->print(); + Ret += ", "; + } + Ret += ")"; + return Ret; + } + + static bool classof(const Action *A) { return A->getKind() == Use; } + +private: + void anchor() override; +}; + +struct ActionStorageDead : public Action { + std::unique_ptr Var; + + ActionStorageDead(std::unique_ptr Var) + : Action(StorageDead), Var(std::move(Var)) {} + + ~ActionStorageDead() override = default; + + std::string print() const override { + std::string Ret; + Ret += "ActionStorageDead: "; + Ret += Var->print(); + return Ret; + } + + static bool classof(const Action *A) { return A->getKind() == StorageDead; } + +private: + void anchor() override; +}; + +/// Represents the position of nodes in the cfg. +/// +/// `blockID` represents the index of the basic block, and `index` represents +/// the index of node in the current basic block. +struct Point { + unsigned blockID; + unsigned index; + + /// Indicates the `End('region_r)` of free region `'region_r`. + static const unsigned EndBlockID = -1u; + static const unsigned EndIndex = -1u; + + Point(unsigned blockID, unsigned index) : blockID(blockID), index(index) {} + + bool operator<(const Point &other) const { + if (this->blockID != other.blockID) + return this->blockID < other.blockID; + return this->index < other.index; + } + + std::string print() const { + if (blockID == EndBlockID && index == EndIndex) + return "End('region_r)"; + return "BB" + std::to_string(blockID) + '/' + std::to_string(index); + } +}; + +/// Represents the region scope of a region variable, consisting a series +/// points in the CFG. +struct Region { + std::set points; + + bool AddPoint(Point P) { + auto Ret = points.insert(P); + return Ret.second; + } + + bool MayContain(Point P) const { return points.find(P) != points.end(); } + + std::string print() const { + std::string Ret = "{ "; + unsigned index = 0; + for (const Point &point : points) { + Ret += point.print(); + if (index != points.size() - 1) + Ret += ", "; + ++index; + } + Ret += " }"; + return Ret; + } +}; + +/// A complete definition of a region variable, consisting of the corresponding +/// region name, region scope and a capped flag. +/// +/// Note that each region variable corresponds to a VarDecl or an explicit or +/// implicit borrow expression. +struct VarDefinition { + RegionName name; + + /// The current value of this region name. This is adjusted during region + /// check by calls to `AddLivePoint`, and then finally adjusted further by + /// the call to `Solve`. + Region value; + + /// Capped region names should no longer have to grow as a result of + /// inference. If they do wind up growing, we will report an error. + bool capped; + + VarDefinition(RegionName name, Region value, bool capped) + : name(name), value(value), capped(capped) {} +}; + +/// The constraint indicates `sub` outlives `sup` at `point`. +struct Constraint { + RegionVariable sub; + RegionVariable sup; + Point point; + + Constraint(RegionVariable sub, RegionVariable sup, Point point) + : sub(sub), sup(sup), point(point) {} + + std::string print() const { + return "Constraint { " + sub.print() + " : " + sup.print() + " @ " + + point.print() + " }" + '\n'; + } +}; + +class Environment { +public: + const FunctionDecl &fd; + const CFG &cfg; + const ASTContext &Ctx; + +public: + Environment(const FunctionDecl &fd, const CFG &cfg, const ASTContext &Ctx) + : fd(fd), cfg(cfg), Ctx(Ctx) {} + + llvm::SmallVector SuccessorPoints(Point point) const; +}; + +class InferenceContext { +private: + llvm::SmallVector definitions; + llvm::SmallVector constraints; + + Region emptyRegion; + +public: + InferenceContext() {} + + RegionVariable AddVar(RegionName Name) { + size_t index = definitions.size(); +#if DEBUG_PRINT + llvm::outs() << Name.print() << " => " << RegionVariable(index).print() + << '\n'; +#endif + definitions.push_back(VarDefinition(Name, Region(), false)); + return RegionVariable(index); + } + + void CapVar(RegionVariable RV) { definitions[RV.index].capped = true; } + + void AddLivePoint(RegionVariable RV, Point P); + + void AddOutLives(RegionVariable Sup, RegionVariable Sub, Point P) { +#if DEBUG_PRINT + llvm::outs() << "AddOutLives: " << Sub.print() << " : " << Sup.print() + << " @ " << P.print() << '\n'; +#endif + constraints.push_back(Constraint(Sub, Sup, P)); + } + + const Region &getRegion(RegionVariable RV) const { + return definitions[RV.index].value; + } + + const Region &getEmptyRegion() const { return emptyRegion; } + + void Solve(const Environment &env); +}; + +class DFS { +private: + llvm::SmallVector stack; + std::set visited; + const Environment &env; + +public: + DFS(const Environment &env) : env(env) {} + + bool Copy(const Region &From, Region &To, Point StartPoint); +}; + +/// Compute the set of live variables at each point. +class Liveness { +private: + using LivenessFact = llvm::DenseSet; + + const Environment &env; + RegionCheck &rc; + llvm::DenseMap liveness; + + void Kill(LivenessFact &fact, VarDecl *D) { fact.erase(D); } + + void Gen(LivenessFact &fact, VarDecl *D) { fact.insert(D); } + + bool SetFrom(LivenessFact &Dest, const LivenessFact &Src) { + if (Src.empty()) + return false; + + unsigned old = Dest.size(); + Dest.insert(Src.begin(), Src.end()); + return old != Dest.size(); + } + + template + void SimulateBlock(LivenessFact &fact, const CFGBlock *Block, CB callback); + +public: + Liveness(const Environment &env, RegionCheck &rc) : env(env), rc(rc) { + Compute(); + } + + void Compute(); + + template void Walk(CB callback); + + std::set LiveRegions(const LivenessFact &liveFact); + + void print() const { + llvm::outs() << "Liveness Result: \n"; + for (auto elem : liveness) { + llvm::outs() << "CFG Block ID: " << elem.first->getBlockID() << '\n'; + llvm::outs() << "Live Variables at block entry: \n"; + for (VarDecl *var : elem.second) { + llvm::outs() << var->getName() << '\t'; + } + llvm::outs() << '\n'; + } + } +}; + +struct Loan { + Point point; + const std::unique_ptr &path; + BorrowKind kind; + const Region ®ion; + + Loan(Point point, const std::unique_ptr &path, BorrowKind kind, + const Region ®ion) + : point(point), path(path), kind(kind), region(region) {} + + std::string print() const { + std::string Ret; + Ret += " Loan {\n"; + Ret += " point: " + point.print() + "\n"; + Ret += " path: " + path->print() + "\n"; + Ret += " kind: "; + if (kind == BorrowKind::Mut) + Ret += "Mut\n"; + else + Ret += "Shared\n"; + Ret += " region: " + region.print() + "\n"; + Ret += " }"; + return Ret; + } +}; + +class LoansInScope { +private: + using LoansFact = llvm::DenseSet; + + const Environment &env; + const RegionCheck &rc; + llvm::SmallVector loans; + llvm::DenseMap loansInScopeAfterBlock; + std::map> loansByPoint; + + void Kill(LoansFact &fact, unsigned index) { fact.erase(index); } + + void Gen(LoansFact &fact, std::vector indexes) { + fact.insert(indexes.begin(), indexes.end()); + } + + bool SetFrom(LoansFact &Dest, const LoansFact &Src) { + if (Src.empty()) + return false; + + unsigned old = Dest.size(); + Dest.insert(Src.begin(), Src.end()); + return old != Dest.size(); + } + + llvm::SmallVector LoansNotInScopeAt(Point point) const { + llvm::SmallVector ret; + for (const Loan *it = loans.begin(), *ei = loans.end(); it != ei; ++it) { + if (!it->region.MayContain(point)) + ret.push_back(std::distance(loans.begin(), it)); + } + return ret; + } + + llvm::SmallVector LoansKilledByWriteTo(const Path *path) const; + + template + void SimulateBlock(LoansFact &fact, const CFGBlock *Block, CB callback); + +public: + LoansInScope(const Environment &env, const RegionCheck &rc); + + void Compute(); + + template void Walk(CB callback); +}; + +enum class Depth { Shallow, Deep }; + +enum class Mode { Read, Write }; + +enum class BorrowDiagKind { + ForImmutWhenMut, + ForMove, + ForMultiMut, + ForMutWhenImmut, + ForRead, + ForWrite, + ForStorageDead, +}; + +struct BorrowDiagInfo { + BorrowDiagKind Kind; + SourceLocation Location; + std::string path; + SourceLocation LoanLoc; + std::string loanPath; + + BorrowDiagInfo(BorrowDiagKind Kind, SourceLocation Location, std::string path, + SourceLocation LoanLoc, std::string loanPath = "") + : Kind(Kind), Location(Location), path(path), LoanLoc(LoanLoc), + loanPath(loanPath) {} +}; + +class BorrowDiagReporter { +private: + Sema &S; + llvm::SmallVector Infos; + + void flushDiagnostics() { + for (BorrowDiagInfo Info : Infos) { + switch (Info.Kind) { + case BorrowDiagKind::ForImmutWhenMut: + S.Diag(Info.Location, diag::err_borrow_immut_borrow_when_mut_borrowed) + << Info.path; + S.Diag(Info.LoanLoc, diag::note_mutable_borrow_occurs_here); + break; + case BorrowDiagKind::ForMove: + S.Diag(Info.Location, diag::err_borrow_move_when_borrowed) << Info.path; + S.Diag(Info.LoanLoc, diag::note_borrowed_here) << Info.loanPath; + break; + case BorrowDiagKind::ForMultiMut: + S.Diag(Info.Location, diag::err_borrow_mut_borrow_more_than_once) + << Info.path; + S.Diag(Info.LoanLoc, diag::note_first_mut_borrow_occurs_here); + break; + case BorrowDiagKind::ForMutWhenImmut: + S.Diag(Info.Location, diag::err_borrow_mut_borrow_when_immut_borrowed) + << Info.path; + S.Diag(Info.LoanLoc, diag::note_immutable_borrow_occurs_here); + break; + case BorrowDiagKind::ForRead: + S.Diag(Info.Location, diag::err_borrow_use_when_mut_borrowed) + << Info.path; + S.Diag(Info.LoanLoc, diag::note_borrowed_here) << Info.loanPath; + break; + case BorrowDiagKind::ForStorageDead: + S.Diag(Info.Location, diag::err_borrow_not_live_long) << Info.path; + S.Diag(Info.LoanLoc, diag::note_dropped_while_borrowed) << Info.path; + break; + case BorrowDiagKind::ForWrite: + S.Diag(Info.Location, diag::err_borrow_assign_when_borrowed) + << Info.path; + S.Diag(Info.LoanLoc, diag::note_borrowed_here) << Info.path; + break; + default: + break; + } + S.getDiagnostics().increaseBorrowCheckErrors(); + } + } + +public: + BorrowDiagReporter(Sema &S) : S(S) {} + + ~BorrowDiagReporter() { flushDiagnostics(); } + + void ForImmutWhenMut(SourceLocation Location, const Path *path, + SourceLocation LoanLoc) { + Infos.push_back(BorrowDiagInfo(BorrowDiagKind::ForImmutWhenMut, Location, + path->to_string(), LoanLoc)); + } + + void ForMove(SourceLocation Location, const Path *path, + SourceLocation LoanLoc, const Path *loanPath) { + Infos.push_back(BorrowDiagInfo(BorrowDiagKind::ForMove, Location, + path->to_string(), LoanLoc, + loanPath->to_string())); + } + + void ForMultiMut(SourceLocation Location, const Path *path, + SourceLocation LoanLoc) { + Infos.push_back(BorrowDiagInfo(BorrowDiagKind::ForMultiMut, Location, + path->to_string(), LoanLoc)); + } + + void ForMutWhenImmut(SourceLocation Location, const Path *path, + SourceLocation LoanLoc) { + Infos.push_back(BorrowDiagInfo(BorrowDiagKind::ForMutWhenImmut, Location, + path->to_string(), LoanLoc)); + } + + void ForRead(SourceLocation Location, const Path *path, + SourceLocation LoanLoc, const Path *loanPath) { + Infos.push_back(BorrowDiagInfo(BorrowDiagKind::ForRead, Location, + path->to_string(), LoanLoc, + loanPath->to_string())); + } + + void ForStorageDead(SourceLocation Location, const Path *path, + SourceLocation LoanLoc) { + Infos.push_back(BorrowDiagInfo(BorrowDiagKind::ForStorageDead, LoanLoc, + path->to_string(), Location)); + } + + void ForWrite(SourceLocation Location, const Path *path, + SourceLocation LoanLoc) { + Infos.push_back(BorrowDiagInfo(BorrowDiagKind::ForWrite, Location, + path->to_string(), LoanLoc)); + } + + void emitDiag(BorrowDiagKind Kind, SourceLocation Location, const Path *path, + SourceLocation LoanLoc, const Path *loanPath = nullptr) { + switch (Kind) { + case BorrowDiagKind::ForImmutWhenMut: + ForImmutWhenMut(Location, path, LoanLoc); + break; + case BorrowDiagKind::ForMove: + ForMove(Location, path, LoanLoc, loanPath); + break; + case BorrowDiagKind::ForMultiMut: + ForMultiMut(Location, path, LoanLoc); + break; + case BorrowDiagKind::ForMutWhenImmut: + ForMutWhenImmut(Location, path, LoanLoc); + break; + case BorrowDiagKind::ForRead: + ForRead(Location, path, LoanLoc, loanPath); + break; + case BorrowDiagKind::ForStorageDead: + ForStorageDead(Location, path, LoanLoc); + break; + case BorrowDiagKind::ForWrite: + ForWrite(Location, path, LoanLoc); + break; + default: + break; + } + } +}; + +class BorrowCheck { +private: + BorrowDiagReporter &reporter; + const Environment &env; + Point point; + const llvm::SmallVector &loans; + bool IsBorrow = false; + + void CheckBorrows(Depth depth, Mode accessMode, + const std::unique_ptr &path); + void CheckMove(const std::unique_ptr &path); + void CheckMutBorrow(const std::unique_ptr &path); + void CheckRead(const std::unique_ptr &path); + void CheckShallowWrite(const std::unique_ptr &path); + void CheckStorageDead(const std::unique_ptr &path); + + llvm::SmallVector + FindLoansThatFreeze(const std::unique_ptr &path); + llvm::SmallVector + FindLoansThatIntersect(const std::unique_ptr &path); + llvm::SmallVector + FrozenByBorrowOf(const std::unique_ptr &path); + +public: + BorrowCheck(BorrowDiagReporter &reporter, const Environment &env, Point point, + const llvm::SmallVector &loans) + : reporter(reporter), env(env), point(point), loans(loans) {} + + void CheckAction(const std::unique_ptr &action); +}; + +class RegionCheck { +private: + BorrowDiagReporter &reporter; + Environment env; + InferenceContext infer; + std::unordered_map declToRegionNameMap; + std::unordered_map stmtToRegionNameMap; + std::map regionMap; + std::map>> actionMap; + + void PopulateInference(Liveness &liveness); + RegionVariable getRegionVariable(RegionName RV); + void EnsureBorrowSource(Point SuccPoint, RegionName BorrowRegionName, + const std::unique_ptr &SourcePath); + void RelateRegions(Point SuccPoint, RegionName Sub, RegionName Sup); + void PreprocessForParamAndReturn(); + +public: + RegionCheck(const FunctionDecl &fd, const CFG &cfg, ASTContext &Ctx, + BorrowDiagReporter &Reporter) + : reporter(Reporter), env(fd, cfg, Ctx) {} + + void Check(); + + /// Get the corresponding RegionName for a Decl in the regionMap. + /// If not existing, create a RegionName and return it. + RegionName getRegionName(Decl *D); + + /// Get the corresponding RegionName for a Stmt in the regionMap. + /// If not existing, create a RegionName and return it. + RegionName getRegionName(Stmt *S); + + const Region &getRegion(RegionName RN) const; + + const Region &getEmptyRegion() const { return infer.getEmptyRegion(); } + + BorrowDiagReporter &getReporter() { return reporter; } + + const std::map>> & + getActionMap() const { + return actionMap; + } +}; + +void BorrowCk(const Environment &env, RegionCheck &rc, LoansInScope &LIS); +void runBorrowChecker(const FunctionDecl &fd, const CFG &cfg, ASTContext &Ctx, + BorrowDiagReporter &Reporter); + +} // end namespace borrow +} // end namespace clang + +#endif // ENABLE_BSC + +#endif // LLVM_CLANG_ANALYSIS_ANALYSES_BSCBORROWCHECKER_H \ No newline at end of file diff --git a/clang/include/clang/Analysis/CFG.h b/clang/include/clang/Analysis/CFG.h index 2a27a377c54c..7bc04a64688c 100644 --- a/clang/include/clang/Analysis/CFG.h +++ b/clang/include/clang/Analysis/CFG.h @@ -1245,9 +1245,10 @@ public: bool AddLoopExit = false; bool AddTemporaryDtors = false; bool AddScopes = false; - #if ENABLE_BSC +#if ENABLE_BSC bool BSCMode = false; - #endif + bool BSCBorrowCk = false; +#endif bool AddStaticInitBranches = false; bool AddCXXNewAllocator = false; bool AddCXXDefaultInitExprInCtors = false; diff --git a/clang/include/clang/Basic/BSC/DiagnosticBSCSemaKinds.td b/clang/include/clang/Basic/BSC/DiagnosticBSCSemaKinds.td index fad65b1fe493..0ea50fbc5b61 100644 --- a/clang/include/clang/Basic/BSC/DiagnosticBSCSemaKinds.td +++ b/clang/include/clang/Basic/BSC/DiagnosticBSCSemaKinds.td @@ -198,6 +198,29 @@ def note_previous_borrow : Note<"previous borrow is here">; def err_use_expired_borrow_var : Error< "Can not use '%0' because expired">; +// New BSC Borrow Checker errors. +def err_borrow_assign_when_borrowed : Error< + "cannot assign to `%0` because it is borrowed">; +def err_borrow_move_when_borrowed : Error< + "cannot move out of `%0` because it is borrowed">; +def err_borrow_use_when_mut_borrowed : Error< + "cannot use `%0` because it was mutably borrowed">; +def note_borrowed_here : Note<"`%0` is borrowed here">; +def err_borrow_mut_borrow_more_than_once : Error< + "cannot borrow `%0` as mutable more than once at a time">; +def note_first_mut_borrow_occurs_here : Note<"first mut borrow occurs here">; +def err_borrow_return_local_borrow : Error< + "cannot return reference to local variable `%0`">; +def err_borrow_not_live_long : Error< + "`%0` does not live long enough">; +def note_dropped_while_borrowed: Note<"`%0` dropped here while still borrowed">; +def err_borrow_immut_borrow_when_mut_borrowed : Error< + "cannot borrow `%0` as immutable because it is also borrowed as mutable">; +def note_mutable_borrow_occurs_here : Note<"mutable borrow occurs here">; +def err_borrow_mut_borrow_when_immut_borrowed : Error< + "cannot borrow `%0` as mutable because it is also borrowed as immutable">; +def note_immutable_borrow_occurs_here : Note<"immutable borrow occurs here">; + // BSC operator overload errors. def err_unsupport_overload_fun : Error<"function unsupport overload">; def err_operator_overload_needs_point : Error< diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 877b67177dba..441767b1a5e3 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -12424,6 +12424,7 @@ public: bool CheckTemporaryVarMemoryLeak(Expr* E); void BSCDataflowAnalysis(const FunctionDecl *FD, bool EnableOwnershipCheck = true, bool EnableNullabilityCheck = true); + void BSCBorrowChecker(FunctionDecl *FD); bool IsInSafeZone(); bool IsSafeBuiltinTypeConversion(BuiltinType::Kind SourceType, BuiltinType::Kind DestType); diff --git a/clang/lib/AST/BSC/TypeBSC.cpp b/clang/lib/AST/BSC/TypeBSC.cpp index 29e91229b0ed..8b2d257d503c 100644 --- a/clang/lib/AST/BSC/TypeBSC.cpp +++ b/clang/lib/AST/BSC/TypeBSC.cpp @@ -154,6 +154,13 @@ bool RecordType::hasFatFields() const { return false; } +bool Type::withBorrowFields() const { + if (const auto *RT = dyn_cast(CanonicalType)) { + return RT->withBorrowFields(); + } + return false; +} + bool FunctionProtoType::hasOwnedRetOrParams() const { if (getReturnType().isOwnedQualified()) { return true; @@ -355,6 +362,28 @@ bool RecordType::hasBorrowFields() const { return false; } +bool RecordType::withBorrowFields() const { + std::vector RecordTypeList; + RecordTypeList.push_back(this); + unsigned NextToCheckIndex = 0; + + while (RecordTypeList.size() > NextToCheckIndex) { + for (FieldDecl *FD : + RecordTypeList[NextToCheckIndex]->getDecl()->fields()) { + QualType FieldTy = FD->getType(); + if (FieldTy.isBorrowQualified()) + return true; + FieldTy = FieldTy.getCanonicalType(); + if (const auto *FieldRecTy = FieldTy->getAs()) { + if (!llvm::is_contained(RecordTypeList, FieldRecTy)) + RecordTypeList.push_back(FieldRecTy); + } + } + ++NextToCheckIndex; + } + return false; +} + bool Type::isBSCFutureType() const { if (const auto *RT = getAs()) { RecordDecl *RD = RT->getAsRecordDecl(); diff --git a/clang/lib/Analysis/BSCBorrowChecker.cpp b/clang/lib/Analysis/BSCBorrowChecker.cpp new file mode 100644 index 000000000000..7d9a4e9fb65f --- /dev/null +++ b/clang/lib/Analysis/BSCBorrowChecker.cpp @@ -0,0 +1,1716 @@ +//===- BSCBorrowChecker.cpp - Borrow Check for Source CFGs -*- BSC --*--------// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements BSC borrow checker for source-level CFGs. +// +//===----------------------------------------------------------------------===// + +#if ENABLE_BSC + +#include "clang/Analysis/Analyses/BSCBorrowChecker.h" +#include "clang/AST/StmtVisitor.h" + +using namespace clang; +using namespace clang::borrow; + +static bool IsTrackedType(QualType type) { + return type.isBorrowQualified() || type->withBorrowFields(); +} + +namespace { +/// Given a statement, returns (defs, uses). +/// +/// The `defs` contains variables whose current value is completely +/// overwritten, and the `uses` contains variables whose current value is used. +/// Note that a variable may exist in both sets. +class DefUse : public clang::StmtVisitor { + enum { None, Def, Use } Action; + llvm::SmallVector defs; + llvm::SmallVector uses; + +public: + DefUse(Stmt *S) { + Action = None; + Visit(S); + } + + const llvm::SmallVector &getDefs() const { return defs; } + const llvm::SmallVector &getUses() const { return uses; } + + void VisitBinaryOperator(BinaryOperator *BO); + void VisitBinAssign(BinaryOperator *BO); + void VisitCallExpr(CallExpr *CE); + void VisitDeclRefExpr(DeclRefExpr *DRE); + void VisitDeclStmt(DeclStmt *DS); + void VisitMemberExpr(MemberExpr *ME); + void VisitReturnStmt(ReturnStmt *RS); + void VisitStmt(Stmt *S); + void VisitUnaryDeref(UnaryOperator *UO); + void VisitUnaryPostDec(UnaryOperator *UO); + void VisitUnaryPostInc(UnaryOperator *UO); + void VisitUnaryPreDec(UnaryOperator *UO); + void VisitUnaryPreInc(UnaryOperator *UO); +}; +} // namespace + +void DefUse::VisitBinaryOperator(BinaryOperator *BO) { + if ((BO->getOpcode() >= BO_Mul && BO->getOpcode() <= BO_Shr) || + (BO->getOpcode() >= BO_And && BO->getOpcode() <= BO_LOr) || + (BO->getOpcode() >= BO_LT && BO->getOpcode() <= BO_NE)) { + Action = Use; + Visit(BO->getLHS()); + Visit(BO->getRHS()); + } +} + +void DefUse::VisitBinAssign(BinaryOperator *BO) { + Action = Def; + Visit(BO->getLHS()); + Action = Use; + Visit(BO->getRHS()); +} + +void DefUse::VisitCallExpr(CallExpr *CE) { + Action = Use; + for (Expr *E : CE->arguments()) { + Visit(E); + } +} + +void DefUse::VisitDeclRefExpr(DeclRefExpr *DRE) { + if (VarDecl *VD = dyn_cast(DRE->getDecl())) { + if (Action == Def) { + defs.push_back(VD); + } else if (Action == Use) { + uses.push_back(VD); + } + } +} + +void DefUse::VisitDeclStmt(DeclStmt *DS) { + for (Decl *D : DS->decls()) { + if (VarDecl *VD = dyn_cast(D)) { + defs.push_back(VD); + if (VD->hasInit()) { + Action = Use; + Visit(VD->getInit()); + } + } + } +} + +void DefUse::VisitMemberExpr(MemberExpr *ME) { + /// When you have `p = ...`, which variable is reaasigned? + /// If `p` is `x`, then `x` is. Otherwise, nothing. + if (Action == Use) + Visit(ME->getBase()); +} + +void DefUse::VisitReturnStmt(ReturnStmt *RS) { + Action = Use; + if (Expr *RV = RS->getRetValue()) { + Visit(RV); + } +} + +void DefUse::VisitStmt(Stmt *S) { + for (auto *C : S->children()) { + if (C) + Visit(C); + } +} + +void DefUse::VisitUnaryDeref(UnaryOperator *UO) { + Action = Use; + Visit(UO->getSubExpr()); +} + +void DefUse::VisitUnaryPostDec(UnaryOperator *UO) { + Action = Def; + Visit(UO->getSubExpr()); + Action = Use; + Visit(UO->getSubExpr()); +} + +void DefUse::VisitUnaryPostInc(UnaryOperator *UO) { + Action = Def; + Visit(UO->getSubExpr()); + Action = Use; + Visit(UO->getSubExpr()); +} + +void DefUse::VisitUnaryPreDec(UnaryOperator *UO) { + Action = Def; + Visit(UO->getSubExpr()); + Action = Use; + Visit(UO->getSubExpr()); +} + +void DefUse::VisitUnaryPreInc(UnaryOperator *UO) { + Action = Def; + Visit(UO->getSubExpr()); + Action = Use; + Visit(UO->getSubExpr()); +} + +namespace { +/// Given a statement, extract and generate actions. +/// +/// Due to the complexity of AST nodes, implementing the borrow checker becomes +/// challenging, so it is necessary to parse out the corresponding actions to +/// simplify the algorithm implementations. +/// +/// We classify each statement into six type of actions, with each statement +/// corresponding to one or more actions. +class ActionExtract : public clang::StmtVisitor { + RegionCheck &rc; + std::vector> actions; + std::vector> Sources; + std::unique_ptr Src = nullptr; + std::unique_ptr Dest = nullptr; + RegionName RNL; + RegionName RNR; + borrow::BorrowKind BK; + Action::ActionKind Kind = Action::Noop; + enum { LHS, RHS } op; + unsigned pathDepth = 0; + Decl *D = nullptr; + bool isArrow = false; + bool BuildOnGet = true; + + void Reset() { + Sources.clear(); + Src = nullptr; + Dest = nullptr; + RNL = RegionName(); + RNR = RegionName(); + Kind = Action::Noop; + op = LHS; + pathDepth = 0; + D = nullptr; + isArrow = false; + } + + void BuildAction() { + switch (Kind) { + case Action::Assign: { + assert(!RNL.isInvalid() && !RNR.isInvalid() && + "do not expect a invalid region name!"); + RegionName DerefRN = RegionName::Create(); + + std::vector> DerefSources = + ProcessDeref(Dest->ty, Sources[0]); + actions.emplace_back(std::make_unique( + std::move(Dest), RNL, RNR, std::move(Sources[0]), DerefRN, + std::move(DerefSources))); + break; + } + case Action::Borrow: { + assert(!RNL.isInvalid() && !RNR.isInvalid() && + "do not expect a invalid region name!"); + actions.emplace_back(std::make_unique( + std::move(Dest), RNL, RNR, BK, std::move(Sources[0]))); + break; + } + case Action::Init: { + if (IsTrackedType(Dest->ty)) { + GenerateImplicitAssign(); + actions.insert( + actions.begin(), + std::make_unique(std::move(Dest), std::move(Sources))); + } else { + std::vector> DerefSources; + for (const std::unique_ptr &Source : Sources) { + QualType QT = Source->ty; + std::vector> Derefs = ProcessDeref(QT, Source); + DerefSources.insert(DerefSources.end(), + std::make_move_iterator(Derefs.begin()), + std::make_move_iterator(Derefs.end())); + } + actions.emplace_back(std::make_unique( + std::move(Dest), std::move(Sources), std::move(DerefSources))); + } + break; + }; + case Action::StorageDead: { + actions.emplace_back( + std::make_unique(std::move(Dest))); + break; + } + case Action::Use: { + std::vector> DerefSources; + for (const std::unique_ptr &Source : Sources) { + QualType QT = Source->ty; + std::vector> Derefs = ProcessDeref(QT, Source); + DerefSources.insert(DerefSources.end(), + std::make_move_iterator(Derefs.begin()), + std::make_move_iterator(Derefs.end())); + } + actions.emplace_back(std::make_unique( + std::move(Sources), std::move(DerefSources))); + break; + } + default: { + actions.emplace_back(std::make_unique()); + break; + } + } + + Reset(); + } + + std::vector> + ProcessDeref(QualType QT, const std::unique_ptr &path) { + std::vector> Res; + if (QT.isBorrowQualified()) { + std::unique_ptr DerefSource = std::make_unique(*path); + DerefSource = std::make_unique(std::move(DerefSource), "*", + DerefSource->ty->getPointeeType(), + DerefSource->Location); + Res.emplace_back(std::move(DerefSource)); + } else if (QT->withBorrowFields()) { + const RecordType *RT = dyn_cast(QT.getCanonicalType()); + RecursiveForFields(RT, Res, path); + } + return Res; + } + + // Traverse the field of record type and create implicit dereferences. + void RecursiveForFields(const RecordType *RT, + std::vector> &Res, + const std::unique_ptr &Base) { + for (FieldDecl *FD : RT->getDecl()->fields()) { + std::unique_ptr Deref = std::make_unique(*Base); + Deref = std::make_unique(std::move(Deref), FD->getName().str(), + FD->getType(), Deref->Location); + if (FD->getType().isBorrowQualified()) { + Deref = std::make_unique(std::move(Deref), "*", + Deref->ty->getPointeeType(), + Deref->Location); + Res.emplace_back(std::move(Deref)); + } + if (FD->getType()->withBorrowFields()) { + const RecordType *rt = + dyn_cast(FD->getType().getCanonicalType()); + RecursiveForFields(rt, Res, Deref); + } + } + } + + void GenerateImplicitAssign() { + RegionName DerefRN = RegionName::Create(); + for (const std::unique_ptr &Source : Sources) { + if (IsTrackedType(Source->ty) && Source->D != nullptr) { + std::unique_ptr DestCopy = std::make_unique(*Dest); + std::unique_ptr SourceCopy = std::make_unique(*Source); + assert(Source->D != nullptr && "expected non nullptr"); + RegionName SourceRN = rc.getRegionName(Source->D); + assert(!SourceRN.isInvalid() && "expected valid region name"); + std::vector> DerefSources = + ProcessDeref(DestCopy->ty, Source); + actions.emplace_back(std::make_unique( + std::move(DestCopy), RNL, SourceRN, std::move(SourceCopy), DerefRN, + std::move(DerefSources))); + } + } + } + +public: + ActionExtract(Stmt *S, const VarDecl *VD, SourceLocation ScopeEndLoc, + RegionCheck &rc) + : rc(rc) { + /// For CFGScopeEnd, just create an `ActionStorageDead`. + if (VD) { + Kind = Action::StorageDead; + Dest = std::make_unique(VD->getName().str(), VD->getType(), + ScopeEndLoc); + } else { + Visit(S); + } + } + + ~ActionExtract() = default; + + std::vector> GetAction() { + if (BuildOnGet) + BuildAction(); + + return std::move(actions); + } + + void VisitArraySubscriptExpr(ArraySubscriptExpr *ASE); + void VisitBinaryOperator(BinaryOperator *BO); + void VisitBinAssign(BinaryOperator *BO); + void VisitCallExpr(CallExpr *CE); + void VisitCStyleCastExpr(CStyleCastExpr *CSCE); + void VisitDeclRefExpr(DeclRefExpr *DRE); + void VisitDeclStmt(DeclStmt *DS); + void VisitInitListExpr(InitListExpr *ILE); + void VisitMemberExpr(MemberExpr *ME); + void VisitReturnStmt(ReturnStmt *RS); + void VisitStmt(Stmt *S); + void VisitUnaryAddrConst(UnaryOperator *UO); + void VisitUnaryAddrConstDeref(UnaryOperator *UO); + void VisitUnaryAddrMut(UnaryOperator *UO); + void VisitUnaryAddrMutDeref(UnaryOperator *UO); + void VisitUnaryDeref(UnaryOperator *UO); + void VisitUnaryPostDec(UnaryOperator *UO); + void VisitUnaryPostInc(UnaryOperator *UO); + void VisitUnaryPreDec(UnaryOperator *UO); + void VisitUnaryPreInc(UnaryOperator *UO); +}; +} // namespace + +void ActionExtract::VisitArraySubscriptExpr(ArraySubscriptExpr *ASE) { + Visit(ASE->getLHS()); +} + +void ActionExtract::VisitBinaryOperator(BinaryOperator *BO) { + auto Opcode = BO->getOpcode(); + if ((Opcode >= BO_Mul && Opcode <= BO_Shr) || + (Opcode >= BO_And && Opcode <= BO_LOr)) { + op = RHS; + Visit(BO->getLHS()); + Visit(BO->getRHS()); + } else if (Opcode >= BO_LT && Opcode <= BO_NE) { + Kind = Action::Use; + op = RHS; + Visit(BO->getLHS()); + Visit(BO->getRHS()); + } else if (Opcode >= BO_MulAssign && Opcode <= BO_OrAssign) { + Kind = Action::Init; + op = LHS; + Visit(BO->getLHS()); + op = RHS; + Visit(BO->getLHS()); + Visit(BO->getRHS()); + } +} + +void ActionExtract::VisitBinAssign(BinaryOperator *BO) { + if (BO->getType()->isStructureType() && IsTrackedType(BO->getType()) && + isa(BO->getRHS()->IgnoreImpCasts())) { + BuildOnGet = false; + op = LHS; + Visit(BO->getLHS()); + op = RHS; + Visit(BO->getRHS()); + } else { + Kind = Action::Assign; + op = LHS; + Visit(BO->getLHS()); + op = RHS; + Visit(BO->getRHS()); + if (RNL.isInvalid() || RNR.isInvalid() || Sources.empty()) + Kind = Action::Init; + } +} + +void ActionExtract::VisitCallExpr(CallExpr *CE) { + if (Kind == Action::Noop) + Kind = Action::Use; + else if (Dest != nullptr) + Kind = Action::Init; + op = RHS; + for (Expr *E : CE->arguments()) { + Visit(E); + } +} + +void ActionExtract::VisitCStyleCastExpr(CStyleCastExpr *CSCE) { + Visit(CSCE->getSubExpr()); + if (op == RHS) { + if (CSCE->getType().isBorrowQualified()) { + if (!Sources.empty() && Sources[0] != nullptr) { + RNR = rc.getRegionName(CSCE); + if (CSCE->getType().isConstBorrow()) { + BK = borrow::BorrowKind::Shared; + } else { + BK = borrow::BorrowKind::Mut; + } + Kind = Action::Borrow; + bool AddDeref = Sources[0]->ty != Dest->ty->getPointeeType(); + if (UnaryOperator *Sub = dyn_cast( + CSCE->getSubExpr()->IgnoreParenImpCasts())) { + if (Sub->getOpcode() == UO_AddrOf) { + // If the sub expr of a CStyleCastExpr is UO_AddrOf such as + // `(int* borrow)&x`, don't add dereference. + AddDeref = false; + } + } + if (AddDeref) { + std::unique_ptr Deref = std::make_unique( + std::move(Sources[0]), "*", CSCE->getType(), CSCE->getEndLoc()); + Sources[0] = std::move(Deref); + } + } + } + } +} + +void ActionExtract::VisitDeclRefExpr(DeclRefExpr *DRE) { + if (op == LHS) { + Dest = std::make_unique(DRE->getDecl()->getName().str(), + DRE->getType(), DRE->getLocation()); + if (isArrow) { + Dest = std::make_unique(std::move(Dest), "*", + DRE->getType()->getPointeeType(), + DRE->getLocation()); + } + if (IsTrackedType(DRE->getType())) { + RNL = rc.getRegionName(DRE->getDecl()); + } + } else if (op == RHS) { + Src = std::make_unique(DRE->getDecl()->getName().str(), + DRE->getType(), DRE->getLocation()); + // Decl of DeclRefExpr is for reborrow constraints. + if (DRE->getType().isBorrowQualified()) { + Src->setDecl(DRE->getDecl()); + } else if (IsTrackedType(DRE->getType())) { + Src->setDecl(DRE->getDecl()); + D = DRE->getDecl(); + } + if (Kind == Action::Assign && IsTrackedType(DRE->getType())) { + RNR = rc.getRegionName(DRE->getDecl()); + } + if (isArrow) { + Src = std::make_unique(std::move(Src), "*", + DRE->getType()->getPointeeType(), + DRE->getLocation()); + } + if (pathDepth == 0) + Sources.emplace_back(std::move(Src)); + } +} + +void ActionExtract::VisitDeclStmt(DeclStmt *DS) { + // Note: The construction of CFG ensures that there is only one VarDecl in + // the DeclStmt. + for (Decl *D : DS->decls()) { + if (VarDecl *VD = dyn_cast(D)) { + if (VD->getType()->isStructureType() && IsTrackedType(VD->getType()) && + isa(VD->getInit())) { + BuildOnGet = false; + Dest = std::make_unique(VD->getName().str(), VD->getType(), + VD->getLocation()); + RNL = rc.getRegionName(VD); + Visit(VD->getInit()); + } else { + Kind = Action::Assign; + Dest = std::make_unique(VD->getName().str(), VD->getType(), + VD->getLocation()); + if (IsTrackedType(VD->getType())) + RNL = rc.getRegionName(VD); + if (VD->hasInit()) { + Sources.clear(); + op = RHS; + Visit(VD->getInit()); + } + // Handle cases like `int *borrow p = (int *borrow)NULL` and + // `int *borrow p = (int *borrow)q`. + if (RNL.isInvalid() || RNR.isInvalid() || Sources.empty()) + Kind = Action::Init; + } + } + } +} + +void ActionExtract::VisitInitListExpr(InitListExpr *ILE) { + if (IsTrackedType(ILE->getType())) { + Kind = Action::Noop; + RegionName RN = RNL; + RegionName CurRN; + std::unique_ptr Base = std::make_unique(*Dest); + unsigned Index = 0; + const RecordType *RT = + dyn_cast(ILE->getType().getCanonicalType()); + for (FieldDecl *Field : RT->getDecl()->fields()) { + Kind = Action::Assign; + std::unique_ptr FieldName = std::make_unique(*Base); + FieldName = + std::make_unique(std::move(FieldName), Field->getName().str(), + Field->getType(), ILE->getBeginLoc()); + Dest = std::move(FieldName); + if (IsTrackedType(Field->getType())) + CurRN = RN; + RNL = CurRN; + op = RHS; + Visit(ILE->getInit(Index)); + if (RNL.isInvalid() || RNR.isInvalid() || Sources.empty()) + Kind = Action::Init; + if (Dest) + BuildAction(); + ++Index; + } + } else { + for (Expr *Init : ILE->inits()) { + Visit(Init); + } + } +} + +void ActionExtract::VisitMemberExpr(MemberExpr *ME) { + if (op == LHS) { + bool oldIsArrow = isArrow; + isArrow = ME->isArrow(); + Visit(ME->getBase()); + std::unique_ptr Member = std::make_unique( + std::move(Dest), ME->getMemberNameInfo().getAsString(), ME->getType(), + ME->getMemberLoc()); + if (oldIsArrow) { + Member = std::make_unique(std::move(Member), "*", + ME->getType()->getPointeeType(), + ME->getMemberLoc()); + } + isArrow = oldIsArrow; + Dest = std::move(Member); + } else if (op == RHS) { + ++pathDepth; + bool oldIsArrow = isArrow; + isArrow = ME->isArrow(); + Visit(ME->getBase()); + std::unique_ptr Member = std::make_unique( + std::move(Src), ME->getMemberNameInfo().getAsString(), ME->getType(), + ME->getMemberLoc()); + if (ME->getType().isBorrowQualified()) { + Member->setDecl(D); + } + if (oldIsArrow) { + Member = std::make_unique(std::move(Member), "*", + ME->getType()->getPointeeType(), + ME->getMemberLoc()); + } + isArrow = oldIsArrow; + Src = std::move(Member); + --pathDepth; + if (pathDepth == 0) + Sources.emplace_back(std::move(Src)); + } +} + +void ActionExtract::VisitReturnStmt(ReturnStmt *RS) { + if (!RS->getRetValue()) { + return; + } + if (!IsTrackedType(RS->getRetValue()->getType())) { + Kind = Action::Use; + op = RHS; + Visit(RS->getRetValue()); + return; + } + Kind = Action::Assign; + Dest = std::make_unique("__ret", RS->getRetValue()->getType(), + RS->getReturnLoc()); + RNL = RegionName::CreateFree(); + op = RHS; + Visit(RS->getRetValue()); + if (RNR.isInvalid()) + Kind = Action::Init; +} + +void ActionExtract::VisitStmt(Stmt *S) { + for (auto *C : S->children()) { + if (C) + Visit(C); + } +} + +void ActionExtract::VisitUnaryAddrConst(UnaryOperator *UO) { + RNR = rc.getRegionName(UO); + BK = borrow::BorrowKind::Shared; + Kind = Action::Borrow; + Visit(UO->getSubExpr()); +} + +void ActionExtract::VisitUnaryAddrConstDeref(UnaryOperator *UO) { + RNR = rc.getRegionName(UO); + BK = borrow::BorrowKind::Shared; + Kind = Action::Borrow; + Visit(UO->getSubExpr()); + if (Sources[0]->ty->isPointerType()) { + std::unique_ptr Deref = std::make_unique( + std::move(Sources[0]), "*", UO->getType(), UO->getEndLoc()); + Sources[0] = std::move(Deref); + } +} + +void ActionExtract::VisitUnaryAddrMut(UnaryOperator *UO) { + RNR = rc.getRegionName(UO); + BK = borrow::BorrowKind::Mut; + Kind = Action::Borrow; + Visit(UO->getSubExpr()); +} + +void ActionExtract::VisitUnaryAddrMutDeref(UnaryOperator *UO) { + RNR = rc.getRegionName(UO); + BK = borrow::BorrowKind::Mut; + Kind = Action::Borrow; + Visit(UO->getSubExpr()); + // Note that in some cases such as `&mut *&p`, there is no need to add + // dereference, as it allows for more accurate analysis. + if (Sources[0]->ty->isPointerType()) { + std::unique_ptr Deref = std::make_unique( + std::move(Sources[0]), "*", UO->getType(), UO->getEndLoc()); + Sources[0] = std::move(Deref); + } +} + +void ActionExtract::VisitUnaryDeref(UnaryOperator *UO) { + if (Kind == Action::Noop) { + Kind = Action::Use; + op = RHS; + } + if (op == LHS) { + Visit(UO->getSubExpr()); + std::unique_ptr Deref = std::make_unique( + std::move(Dest), "*", UO->getType(), UO->getBeginLoc()); + Dest = std::move(Deref); + } else if (op == RHS) { + ++pathDepth; + Visit(UO->getSubExpr()); + bool DontAddDeref = false; + if (UnaryOperator *Sub = + dyn_cast(UO->getSubExpr()->IgnoreParenImpCasts())) { + if (Sub->getOpcode() == UO_AddrOf) { + DontAddDeref = true; + } + } + if (!DontAddDeref) { + std::unique_ptr Deref = std::make_unique( + std::move(Src), "*", UO->getType(), UO->getBeginLoc()); + Src = std::move(Deref); + } + --pathDepth; + if (pathDepth == 0) + Sources.emplace_back(std::move(Src)); + } +} + +void ActionExtract::VisitUnaryPostDec(UnaryOperator *UO) { + if (Kind == Action::Noop) { + Kind = Action::Init; + op = LHS; + Visit(UO->getSubExpr()); + op = RHS; + Visit(UO->getSubExpr()); + } else { + op = RHS; + Visit(UO->getSubExpr()); + } +} + +void ActionExtract::VisitUnaryPostInc(UnaryOperator *UO) { + if (Kind == Action::Noop) { + Kind = Action::Init; + op = LHS; + Visit(UO->getSubExpr()); + op = RHS; + Visit(UO->getSubExpr()); + } else { + op = RHS; + Visit(UO->getSubExpr()); + } +} + +void ActionExtract::VisitUnaryPreDec(UnaryOperator *UO) { + if (Kind == Action::Noop) { + Kind = Action::Init; + op = LHS; + Visit(UO->getSubExpr()); + op = RHS; + Visit(UO->getSubExpr()); + } else { + op = RHS; + Visit(UO->getSubExpr()); + } +} + +void ActionExtract::VisitUnaryPreInc(UnaryOperator *UO) { + if (Kind == Action::Noop) { + Kind = Action::Init; + op = LHS; + Visit(UO->getSubExpr()); + op = RHS; + Visit(UO->getSubExpr()); + } else { + op = RHS; + Visit(UO->getSubExpr()); + } +} + +//===----------------------------------------------------------------------===// +// Operations on RegionName +//===----------------------------------------------------------------------===// + +unsigned RegionName::Cnt = 0; + +//===----------------------------------------------------------------------===// +// Achors for Actions +//===----------------------------------------------------------------------===// + +void Action::anchor() {} +void ActionNoop::anchor() {} +void ActionInit::anchor() {} +void ActionBorrow::anchor() {} +void ActionAssign::anchor() {} +void ActionUse::anchor() {} +void ActionStorageDead::anchor() {} + +//===----------------------------------------------------------------------===// +// Query functions on Environment +//===----------------------------------------------------------------------===// + +/// Given a point, returns the set of all its successor points in the CFG. +llvm::SmallVector Environment::SuccessorPoints(Point point) const { + llvm::SmallVector Succs; + + const CFGBlock *block = *(cfg.nodes_begin() + point.blockID); + if (point.index != block->size()) { + Succs.push_back(Point(point.blockID, point.index + 1)); + } else { + llvm::DenseSet succs(block->succ_begin(), + block->succ_end()); + bool changed = true; + while (changed) { + changed = false; + size_t oldSize = succs.size(); + // Note: to avoid iterator invalidation + llvm::DenseSet newBlocks; + for (const CFGBlock *succ : succs) { + if (succ && succ->empty()) { + newBlocks.insert(succ->succ_begin(), succ->succ_end()); + } + } + succs.insert(newBlocks.begin(), newBlocks.end()); + if (succs.size() != oldSize) + changed = true; + } + for (const CFGBlock *succ : succs) { + if (succ && !succ->empty()) + Succs.push_back(Point(succ->BlockID, 1)); + } + } + + return Succs; +} + +//===----------------------------------------------------------------------===// +// Operation functions on InferenceContext and DFS +//===----------------------------------------------------------------------===// + +void InferenceContext::AddLivePoint(RegionVariable RV, Point P) { +#if DEBUG_PRINT + llvm::outs() << "AddLivePoint: " << RV.print() << " @ " << P.print() << '\n'; +#endif + VarDefinition &definition = definitions[RV.index]; + if (definition.value.AddPoint(P)) { + if (definition.capped) { + llvm_unreachable("Free region should not grow anymore!"); + } + } +} + +/// Inference algorithm, which is implemented based on fixed-point iteration. +/// +/// During fixed-point iteration, the algorithm solves inference constraints +/// and updates the regions of region variables. +void InferenceContext::Solve(const Environment &env) { + bool changed = true; + DFS dfs(env); + while (changed) { + changed = false; + + for (Constraint &constraint : constraints) { + const Region &Sup = definitions[constraint.sup.index].value; + VarDefinition &SubDef = definitions[constraint.sub.index]; +#if DEBUG_PRINT + llvm::outs() << "constraint: " << constraint.print(); + llvm::outs() << " sup (before): " << Sup.print() << '\n'; + llvm::outs() << " sub (before): " << SubDef.value.print() << '\n'; +#endif + + // DFS from the start point of constraint. + if (dfs.Copy(Sup, SubDef.value, constraint.point)) { + changed = true; + + if (SubDef.capped) { + llvm_unreachable("Free region should not grow anymore!"); + } + } + +#if DEBUG_PRINT + llvm::outs() << " sub (after) : " << SubDef.value.print() << '\n'; + llvm::outs() << " changed : " << (changed ? "true" : "false") + << '\n'; +#endif + } +#if DEBUG_PRINT + llvm::outs() << '\n'; +#endif + } +} + +/// Update `To` using `From`, starting DFS from `StartPoint` until the visited +/// is not in `From` or has already been visited. +bool DFS::Copy(const Region &From, Region &To, Point StartPoint) { + bool changed = false; + + stack.clear(); + visited.clear(); + + stack.push_back(StartPoint); + while (!stack.empty()) { + Point p = stack.back(); + stack.pop_back(); + +#if DEBUG_PRINT + llvm::outs() << " dfs: p=" << p.print() << '\n'; +#endif + + if (!From.MayContain(p)) { +#if DEBUG_PRINT + llvm::outs() << " not in From-Region\n"; +#endif + continue; + } + + if (!visited.insert(p).second) { +#if DEBUG_PRINT + llvm::outs() << " already visited\n"; +#endif + continue; + } + + changed |= To.AddPoint(p); + + llvm::SmallVector SuccessorPoints = env.SuccessorPoints(p); + if (SuccessorPoints.empty()) { + Point endPoint(Point::EndBlockID, Point::EndIndex); + if (From.MayContain(endPoint)) { + changed |= To.AddPoint(endPoint); + } + } else { + stack.insert(stack.end(), SuccessorPoints.begin(), SuccessorPoints.end()); + } + } + + return changed; +} + +//===----------------------------------------------------------------------===// +// Liveness computations +//===----------------------------------------------------------------------===// + +/// Iterates until a fixed point, computing live variables on the entry of each +/// basic block. +void Liveness::Compute() { + LivenessFact fact; + bool changed = true; + while (changed) { + changed = false; + + for (const CFGBlock *B : env.cfg.const_nodes()) { + SimulateBlock(fact, B, + [](auto _p, auto _a, auto _s, auto _v, auto _l) {}); + changed |= SetFrom(liveness[B], fact); + } + } +} + +template +void Liveness::SimulateBlock(LivenessFact &fact, const CFGBlock *Block, + CB callback) { + fact.clear(); + + // Everything live in a successor is live at the exit of the block. + for (auto succ : Block->succs()) { + if (succ) + SetFrom(fact, liveness[succ]); + } + + // Walk backwards through the actions. + for (CFGBlock::const_reverse_iterator it = Block->rbegin(), + ei = Block->rend(); + it != ei; ++it) { + const CFGElement &elem = *it; + const Stmt *S = nullptr; + const VarDecl *ScopeEndVD = nullptr; + SourceLocation ScopeEndLoc; + + if (elem.getAs()) { + S = elem.castAs().getStmt(); + // Get the def-use information of a given statement. + DefUse DU(const_cast(S)); + const llvm::SmallVector &defs = DU.getDefs(); + const llvm::SmallVector &uses = DU.getUses(); + + // Anything we write to is no longer live. + for (VarDecl *def : defs) { + Kill(fact, def); + } + + // Any variables we read from, we make live. + for (VarDecl *use : uses) { + Gen(fact, use); + } + } + + // There is no need to handle CFGScopeEnd when calculating liveness, while + // it's necessary to handle CFGScopeEnd when populating inference, so we + // need to get the `ScopeEndVD` and `ScopeEndLoc` here. + if (elem.getAs()) { + ScopeEndVD = elem.castAs().getVarDecl(); + ScopeEndLoc = elem.castAs().getTriggerStmt()->getEndLoc(); + } + + Point point(Block->getBlockID(), std::distance(it, ei)); + + callback(point, S, fact, ScopeEndVD, ScopeEndLoc); + } +} + +/// Invokes callback once for each statement with: +/// a. the point of the statement; +/// b. the statement itself; +/// c. the set of live variables on entry to the statement. +/// +/// Note that all constraints will be generated after the walk. +template void Liveness::Walk(CB callback) { + LivenessFact fact; + + for (const CFGBlock *B : env.cfg.const_nodes()) { + SimulateBlock(fact, B, callback); + } +} + +/// Given a LivenessFact, return the corresponding set of RegionName based on +/// the type of the variables. +/// +/// Only variables of borrowed qualified type or structure type with borrowed +/// qualifed field has corresponding RegionName. +std::set Liveness::LiveRegions(const LivenessFact &liveFact) { + std::set set; + for (VarDecl *VD : liveFact) { + if (IsTrackedType(VD->getType())) { + set.insert(rc.getRegionName(VD)); + } + } + return set; +} + +//===----------------------------------------------------------------------===// +// LoansInScope computations +//===----------------------------------------------------------------------===// + +LoansInScope::LoansInScope(const Environment &env, const RegionCheck &rc) + : env(env), rc(rc) { + // Collect the full set of loans, including explicit loans created with + // `&const` and `&mut` expressions, as well as implicit loans created when + // using variables of borrow qualified types. + for (const CFGBlock *Block : env.cfg.const_reverse_nodes()) { + for (CFGBlock::const_iterator it = Block->begin(), ei = Block->end(); + it != ei; ++it) { + Point point(Block->getBlockID(), std::distance(Block->begin(), it) + 1); + if (rc.getActionMap().find(point) != rc.getActionMap().end()) { + const auto &actions = rc.getActionMap().at(point); + for (const std::unique_ptr &action : actions) { + if (action->getKind() == Action::ActionKind::Borrow) { + const ActionBorrow *AB = dyn_cast(action.get()); + const Region ®ion = rc.getRegion(AB->RNR); + loans.push_back(Loan(point, AB->Source, AB->BK, region)); + } else if (action->getKind() == Action::ActionKind::Assign) { + const ActionAssign *AA = dyn_cast(action.get()); + const Region ®ion = rc.getRegion(AA->DerefRN); + for (const std::unique_ptr &DerefSource : AA->DerefSources) { + borrow::BorrowKind BK = DerefSource->base->ty.isConstBorrow() + ? BorrowKind::Shared + : BorrowKind::Mut; + loans.push_back(Loan(point, DerefSource, BK, region)); + } + } else if (action->getKind() == Action::ActionKind::Use) { + const ActionUse *AU = dyn_cast(action.get()); + const Region ®ion = rc.getEmptyRegion(); + for (const std::unique_ptr &DerefSource : AU->DerefSources) { + borrow::BorrowKind BK = DerefSource->base->ty.isConstBorrow() + ? BorrowKind::Shared + : BorrowKind::Mut; + loans.push_back(Loan(point, DerefSource, BK, region)); + } + } else if (action->getKind() == Action::ActionKind::Init) { + const ActionInit *AI = dyn_cast(action.get()); + const Region ®ion = rc.getEmptyRegion(); + for (const std::unique_ptr &DerefSource : AI->DerefSources) { + borrow::BorrowKind BK = DerefSource->base->ty.isConstBorrow() + ? BorrowKind::Shared + : BorrowKind::Mut; + loans.push_back(Loan(point, DerefSource, BK, region)); + } + } + } + } + } + } + +#if DEBUG_PRINT + llvm::outs() << "loans: [\n"; + for (const Loan &loan : loans) { + llvm::outs() << loan.print() << ",\n"; + } + llvm::outs() << "]\n"; +#endif + + // Make a convenient hash map for getting the index of a loan based on where + // it appears. + for (const auto &IndexedLoan : llvm::enumerate(loans)) { + loansByPoint[IndexedLoan.value().point].push_back(IndexedLoan.index()); + } + + // Iterates until a fixed point. + Compute(); +} + +llvm::SmallVector +LoansInScope::LoansKilledByWriteTo(const Path *path) const { + llvm::SmallVector loanIndexes; + + // When an assignment like `a.b.c = ...` occurs, we kill all + // the loans for `a.b.c` or some subpath like `a.b.c.d`, since + // the path no longer evaluates to the same thing. + for (const auto &IndexedLoan : llvm::enumerate(loans)) { + for (const Path *p : IndexedLoan.value().path->prefixes()) { + if (p->to_string() == path->to_string()) { + loanIndexes.push_back(IndexedLoan.index()); + } + } + } + + return loanIndexes; +} + +template +void LoansInScope::SimulateBlock(LoansFact &fact, const CFGBlock *Block, + CB callback) { + fact.clear(); + + // Everything live at end of a pred is live at the entry of the block. + for (auto pred : Block->preds()) { + SetFrom(fact, loansInScopeAfterBlock[pred]); + } + + // Walk forwards through the actions on by one. + for (CFGBlock::const_iterator it = Block->begin(), ei = Block->end(); + it != ei; ++it) { + const CFGElement &elem = *it; + const Stmt *S = nullptr; + + if (elem.getAs()) + S = elem.castAs().getStmt(); + + Point point(Block->getBlockID(), std::distance(Block->begin(), it) + 1); + + // kill any loans where `point` is not in their region + for (unsigned loanIndex : LoansNotInScopeAt(point)) { + Kill(fact, loanIndex); + } + + // callback at start of the action + callback(point, S, fact); + + // bring the loan into scope after the borrow + if (loansByPoint.find(point) != loansByPoint.end()) { + Gen(fact, loansByPoint[point]); + } + + // Figure out which path is overwritten by this action; this may cancel out + // some loans. + if (S) { + const auto &actions = rc.getActionMap().at(point); + for (const std::unique_ptr &action : actions) { + llvm::Optional overwritten = action->OverWrites(); + if (overwritten.hasValue()) { + const Path *overwrittenPath = overwritten.getValue(); + for (unsigned loanIndex : LoansKilledByWriteTo(overwrittenPath)) { + Kill(fact, loanIndex); + } + } + } + } + } +} + +/// Iterates until a fixed point, computing the loans in scope after each block +/// terminates. +void LoansInScope::Compute() { + LoansFact fact; + bool changed = true; + while (changed) { + changed = false; + + for (const CFGBlock *B : env.cfg.const_reverse_nodes()) { + SimulateBlock(fact, B, [](auto _p, auto _a, auto _s) {}); + changed |= SetFrom(loansInScopeAfterBlock[B], fact); + } + } +} + +/// Invoke `callback` with the loans in scope at each point. +template void LoansInScope::Walk(CB callback) { + llvm::SmallVector loans; + LoansFact fact; + + for (const CFGBlock *B : env.cfg.const_reverse_nodes()) { + SimulateBlock(fact, B, [&](Point point, const Stmt *S, LoansFact &fact) { + // Convert from the LoansFact into a vector of loans. + loans.clear(); + for (const auto &IndexedLoan : llvm::enumerate(this->loans)) { + if (fact.find(IndexedLoan.index()) != fact.end()) + loans.push_back(IndexedLoan.value()); + } + + // Invoke the callback, check actions at each point according to `loans`. + callback(point, S, loans); + }); + } +} + +//===----------------------------------------------------------------------===// +// Check functions on BorrowCheck +//===----------------------------------------------------------------------===// + +void BorrowCheck::CheckAction(const std::unique_ptr &action) { + IsBorrow = false; +#if DEBUG_PRINT + llvm::outs() << "CheckAction(" << action->print() << ") at " << point.print() + << '\n'; +#endif + switch (action->getKind()) { + case Action::Assign: { + const ActionAssign *AA = llvm::cast(action.get()); + CheckShallowWrite(AA->Dest); + CheckRead(AA->Source); + for (const std::unique_ptr &Deref : AA->DerefSources) { + if (Deref->base->ty.isConstBorrow()) { + CheckRead(Deref); + } else { + CheckMutBorrow(Deref); + } + } + break; + } + case Action::Borrow: { + const ActionBorrow *AB = llvm::cast(action.get()); + CheckShallowWrite(AB->Dest); + IsBorrow = true; + if (AB->BK == BorrowKind::Shared) { + CheckRead(AB->Source); + } else { + CheckMutBorrow(AB->Source); + } + break; + } + case Action::Init: { + const ActionInit *AI = llvm::cast(action.get()); + CheckShallowWrite(AI->Dest); + for (const std::unique_ptr &Source : AI->Sources) { + if (Source->ty.isOwnedQualified() || Source->ty->isMoveSemanticType()) { + CheckMove(Source); + } else { + CheckRead(Source); + } + } + for (const std::unique_ptr &Deref : AI->DerefSources) { + if (Deref->base->ty.isConstBorrow()) { + CheckRead(Deref); + } else { + CheckMutBorrow(Deref); + } + } + break; + } + case Action::StorageDead: { + const ActionStorageDead *ASD = llvm::cast(action.get()); + CheckStorageDead(ASD->Var); + break; + } + case Action::Use: { + const ActionUse *AU = llvm::cast(action.get()); + for (const std::unique_ptr &Use : AU->Uses) { + if (Use->ty.isOwnedQualified() || Use->ty->isMoveSemanticType()) { + CheckMove(Use); + } else { + CheckRead(Use); + } + } + for (const std::unique_ptr &Deref : AU->DerefSources) { + if (Deref->base->ty.isConstBorrow()) { + CheckRead(Deref); + } else { + CheckMutBorrow(Deref); + } + } + break; + } + default: + break; + } +} + +void BorrowCheck::CheckBorrows(Depth depth, Mode accessMode, + const std::unique_ptr &path) { + llvm::SmallVector loans; + switch (depth) { + case Depth::Shallow: + loans = FindLoansThatFreeze(path); + break; + case Depth::Deep: + loans = FindLoansThatIntersect(path); + break; + default: + break; + } + + for (const Loan *loan : loans) { + switch (accessMode) { + case Mode::Read: { + switch (loan->kind) { + case BorrowKind::Shared: + /* Ok */ + break; + case BorrowKind::Mut: { + if (IsBorrow) { + reporter.emitDiag(BorrowDiagKind::ForImmutWhenMut, path->Location, + path.get(), loan->path->Location); + } else { + reporter.emitDiag(BorrowDiagKind::ForRead, path->Location, path.get(), + loan->path->Location, loan->path.get()); + } + return; + } + default: + break; + } + break; + } + case Mode::Write: { + if (depth == Depth::Shallow) { + reporter.emitDiag(BorrowDiagKind::ForWrite, path->Location, path.get(), + loan->path->Location); + } else { + if (loan->kind == BorrowKind::Mut) { + reporter.emitDiag(BorrowDiagKind::ForMultiMut, path->Location, + path.get(), loan->path->Location); + } else { + reporter.emitDiag(BorrowDiagKind::ForMutWhenImmut, path->Location, + path.get(), loan->path->Location); + } + } + return; + } + default: + break; + } + } +} + +/// Cannot move from a path `p` if: +/// - `p` is borrowed; +/// - some subpath `p.foo` is borrowed; +/// - some prefix of `p` is borrowed. +/// +/// Note that it is stricter than both write and storage dead. In particular, +/// you can write to a variable `x` that contains an `&mut` value when `*x` is +/// borrowed, but you cannot move `x`. This is because moving it would make the +/// `&mut` variable in the new location, but writing (and storage dead) both +/// kill it forever. +void BorrowCheck::CheckMove(const std::unique_ptr &path) { + for (const Loan *loan : FindLoansThatIntersect(path)) { + reporter.emitDiag(BorrowDiagKind::ForMove, path->Location, path.get(), + loan->path->Location, loan->path.get()); + } +} + +/// `&mut x` may mutate `x`, but it can also *read* from `x`, and mutate things +/// reachable from `x`. +void BorrowCheck::CheckMutBorrow(const std::unique_ptr &path) { + CheckBorrows(Depth::Deep, Mode::Write, path); +} + +/// `use(x)` may access `x` and (by going through the produced value) anything +/// reachable from `x`. +void BorrowCheck::CheckRead(const std::unique_ptr &path) { + CheckBorrows(Depth::Deep, Mode::Read, path); +} + +/// `x = ...` overwrites `x` (without reading it) and prevents any further +/// reads from that path. +void BorrowCheck::CheckShallowWrite(const std::unique_ptr &path) { + CheckBorrows(Depth::Shallow, Mode::Write, path); +} + +/// Cannot free a local variable `var` if: +/// -data interior to `var` is borrowed. +/// +/// In particular, having something like `*var` borrowed is ok. +void BorrowCheck::CheckStorageDead(const std::unique_ptr &path) { + for (const Loan *loan : FindLoansThatFreeze(path)) { + reporter.emitDiag(BorrowDiagKind::ForStorageDead, path->Location, + loan->path.get(), loan->path->Location); + } +} + +/// Helper for `CheckWrite` and `CheckStorageDead`. +/// +/// Finds if there is a loan that "freezes" the given path -- that is, a +/// loan that would make modifying the `path` (or freeing it) illegal. +/// This is slightly more permissive than the rules around move and reads, +/// precisely because overwriting or freeing `path` makes the previous value +/// unavailable from that point on. +llvm::SmallVector +BorrowCheck::FindLoansThatFreeze(const std::unique_ptr &path) { + llvm::SmallVector loans; + llvm::SmallVector pathPrefixes = path->prefixes(); + for (const Loan &loan : this->loans) { + bool NeedToInsert = false; + llvm::SmallVector frozenPaths = FrozenByBorrowOf(loan.path); + // If you have borrowed `a.b`, this prevents writes to `a` or `a.b`: + for (const Path *frozen : frozenPaths) { + if (frozen->to_string() == path.get()->to_string()) { + NeedToInsert = true; + break; + } + } + // If you have borrowed `a.b`, this prevents writes to `a.b.c`: + for (const Path *prefix : pathPrefixes) { + if (prefix->to_string() == loan.path->to_string()) { + NeedToInsert = true; + break; + } + } + if (NeedToInsert) { + loans.push_back(&loan); + } + } + return loans; +} + +/// A loan L *intersects* a path P if either: +/// +/// - the loan is for the path P; or, +/// - the path P can be extended to reach the data in the loan; or, +/// - the loan path can be extended to reach the data in P. +/// +/// So, for example, if the path P is `a.b.c`, then: +/// +/// - a loan of `a.b.c` intersects P; +/// - a loan of `a.b.c.d` intersects P, because (e.g.) after reading P +/// you have also read `a.b.c.d`; +/// - a loan of `a.b` intersects P, because you can use the +/// reference to access the data at P. +llvm::SmallVector +BorrowCheck::FindLoansThatIntersect(const std::unique_ptr &path) { + llvm::SmallVector loans; + llvm::SmallVector pathPrefixes = path->prefixes(); + for (const Loan &loan : this->loans) { + bool NeedToInsert = false; + // Accessing `a.b.c` intersects a loan of `a.b.c`, `a.b` and `a`. + for (const Path *prefix : pathPrefixes) { + if (prefix->to_string() == loan.path->to_string()) { + NeedToInsert = true; + break; + } + } + /// Accessing `a.b.c` also intersects a loan of `a.b.c.d`. + for (const Path *prefix : loan.path->supportingPrefixes()) { + if (prefix->to_string() == path->to_string()) { + NeedToInsert = true; + break; + } + } + if (NeedToInsert) { + loans.push_back(&loan); + } + } + return loans; +} + +/// If `path` is mutably borrowed, returns a vector of paths which -- if +/// moved or if the storage went away -- would invalidate this +/// reference. +llvm::SmallVector +BorrowCheck::FrozenByBorrowOf(const std::unique_ptr &path) { + llvm::SmallVector paths; + const Path *curPath = path.get(); + while (true) { + paths.push_back(curPath); + switch (curPath->type) { + case Path::PathType::Var: { + return paths; + } + case Path::PathType::Extension: { + const Path *basePath = curPath->base.get(); + // If you borrowed `*r`, writing to `r` does not actually affect the + // memory at `*r`, so we can stop iterating backwards now. + if (basePath->ty->isPointerType() && !basePath->ty.isOwnedQualified()) { + return paths; + } + // If you have borrowed `a.b`, then writing to `a` would overwrite `a.b`, + // which is disallowed. Besides, for owned pointer `p`, if you have + // borrowed `*p`, then writing to `p` would overwrite `*p`, which is also + // disallowed. + curPath = basePath; + break; + } + default: + break; + } + } + return paths; +} + +//===----------------------------------------------------------------------===// +// Operation and query functions on RegionCheck +//===----------------------------------------------------------------------===// + +void RegionCheck::PopulateInference(Liveness &liveness) { + // Process free region for function parameters. + PreprocessForParamAndReturn(); + + // Walk statements to generate liveness constraints, subtyping constraints + // and reborrow constraints. + liveness.Walk([&, this](Point point, const Stmt *S, + llvm::DenseSet liveOnEntry, + const VarDecl *VD, + SourceLocation ScopeEndLoc) -> void { + // To start, find every variable `x` that is live. All regions in the type + // of `x` must include `point`. + std::set liveRegionsOnEntry = liveness.LiveRegions(liveOnEntry); + for (RegionName Name : liveRegionsOnEntry) { + RegionVariable RV = getRegionVariable(Name); + infer.AddLivePoint(RV, point); + } + + // Skip CFGScopeBegin nodes. + if (!S && !VD) + return; + + // Note: Since the last statement of the CFG basic block may be a + // borrow/reborrow statement, the set of successor points needs to be + // calculated here. + llvm::SmallVector SuccPoints = env.SuccessorPoints(point); + + // Next, walk the actions and establish any additional constraints that may + // arise from subtyping. + ActionExtract AE(const_cast(S), VD, ScopeEndLoc, *this); + actionMap[point] = std::move(AE.GetAction()); + const auto &actions = actionMap[point]; + for (const std::unique_ptr &action : actions) { +#if DEBUG_PRINT + llvm::outs() << point.print() << ": " << action->print() << '\n'; +#endif + switch (action->getKind()) { + case Action::Borrow: { + const ActionBorrow *AB = llvm::dyn_cast(action.get()); + for (Point SuccPoint : SuccPoints) { + RelateRegions(SuccPoint, AB->RNR, AB->RNL); + EnsureBorrowSource(SuccPoint, AB->RNR, AB->Source); + } + break; + } + case Action::Assign: { + const ActionAssign *AA = llvm::dyn_cast(action.get()); + // Note: manually add DerefRN to regionMap. + getRegionVariable(AA->DerefRN); + for (Point SuccPoint : SuccPoints) { + RelateRegions(SuccPoint, AA->DerefRN, AA->RNL); + RelateRegions(SuccPoint, AA->RNR, AA->DerefRN); + } + break; + } + default: + break; + } + } + }); +} + +void RegionCheck::Check() { + // Compute liveness. + Liveness liveness(env, *this); +#if DEBUG_PRINT + liveness.print(); +#endif + // Add inference constraints. + PopulateInference(liveness); + + // Solve inference constraints, reporting any errors. + infer.Solve(env); + + // Compute loans in scope at each point. + LoansInScope LIS(env, *this); + +#if DEBUG_PRINT + llvm::outs() << "========== BorrowCk ==========\n"; +#endif + // Run the borrow check, reporting any errors. + BorrowCk(env, *this, LIS); +} + +RegionName RegionCheck::getRegionName(Decl *D) { + if (declToRegionNameMap.find(D) != declToRegionNameMap.end()) + return declToRegionNameMap[D]; + RegionName RN = RegionName::Create(); +#if DEBUG_PRINT + llvm::outs() << "Decl: { " << cast(D)->getName() << " } => " + << RN.print() << '\n'; +#endif + declToRegionNameMap[D] = RN; + return RN; +} + +RegionName RegionCheck::getRegionName(Stmt *S) { + if (stmtToRegionNameMap.find(S) != stmtToRegionNameMap.end()) + return stmtToRegionNameMap[S]; + RegionName RN = RegionName::Create(); +#if DEBUG_PRINT + llvm::outs() << "Stmt: { " << S->getStmtClassName() << " } => " << RN.print() + << '\n'; +#endif + stmtToRegionNameMap[S] = RN; + return RN; +} + +/// Given a RegionName, returns the corresponding RegionVariable from regionMap. +/// If not existing, insert it into regionMap and return it. +RegionVariable RegionCheck::getRegionVariable(RegionName Name) { + if (regionMap.find(Name) != regionMap.end()) + return regionMap[Name]; + RegionVariable RV = infer.AddVar(Name); + regionMap[Name] = RV; + return RV; +} + +/// Given a RegionName, returns the corresponding Region from InferenceContext. +const Region &RegionCheck::getRegion(RegionName RN) const { + assert(regionMap.find(RN) != regionMap.end() && + ("no region variable ever created with name: " + RN.print()).c_str()); + RegionVariable RV = regionMap.at(RN); + return infer.getRegion(RV); +} + +/// EnsureBorrowSource - Add any relations between regions that are needed to +/// ensures that reborrows live long enough. +/// Specifically, if we borrow something like `*r` for `'a`, +/// where `r: &'b i32`, then `'b: 'a` is required. +void RegionCheck::EnsureBorrowSource(Point SuccPoint, + RegionName BorrowRegionName, + const std::unique_ptr &SourcePath) { +#if DEBUG_PRINT + llvm::outs() << "EnsureBorrowSource(" << SuccPoint.print() << ", " + << BorrowRegionName.print() << ", " << SourcePath->print() + << ")\n"; +#endif + for (const Path *prefix : SourcePath->supportingPrefixes()) { + switch (prefix->type) { + case Path::PathType::Var: { + return; + } + case Path::PathType::Extension: { + if (prefix->base->ty.isBorrowQualified()) { + assert(prefix->base->D != nullptr && "expected non nullptr!"); + RegionName RefRegionName = getRegionName(prefix->base->D); + RegionVariable BorrowRV = getRegionVariable(BorrowRegionName); + RegionVariable RefRV = getRegionVariable(RefRegionName); + infer.AddOutLives(BorrowRV, RefRV, SuccPoint); + } + break; + } + default: + break; + } + } +} + +void RegionCheck::RelateRegions(Point SuccPoint, RegionName Sub, + RegionName Sup) { + RegionVariable SubRV = getRegionVariable(Sub); + RegionVariable SupRV = getRegionVariable(Sup); +#if DEBUG_PRINT + llvm::outs() << "RelateRegions: " << SubRV.print() << " : " << SupRV.print() + << " @ " << SuccPoint.print() << '\n'; +#endif + infer.AddOutLives(SupRV, SubRV, SuccPoint); +} + +/// If the return type of a function is borrow qualifed or has borrow fields, +/// we relate a free region to function parameters. +void RegionCheck::PreprocessForParamAndReturn() { + if (!IsTrackedType(env.fd.getReturnType())) + return; + + RegionName ParamRN = RegionName::Create(); + for (ParmVarDecl *PVD : env.fd.parameters()) { + if (IsTrackedType(PVD->getType())) { +#if DEBUG_PRINT + llvm::outs() << "Decl: { " << cast(PVD)->getName() << " } => " + << ParamRN.print() << '\n'; +#endif + declToRegionNameMap[PVD] = ParamRN; + } + } + + RegionName FreeRN = RegionName::CreateFree(); + // This is sort of a hack, but for the free region `'region_r`, we will wind + // up with a region variable. We want that region variable to be inferred to + // precisely the set: `{G, End('region_r)}`, where `G` is all the points in + // the control-flow graph, and `End('region_r)` is the end-point of + // `'region_r`. We are enforcing (in inference) that `'region_r` doesn't get + // inferred to some larger region. + RegionVariable FreeRV = getRegionVariable(FreeRN); + for (const CFGBlock *block : env.cfg.const_nodes()) { + unsigned blockID = block->getBlockID(); + for (CFGBlock::const_iterator it = block->begin(), ei = block->end(); + it != ei; ++it) { + Point point(blockID, std::distance(block->begin(), it) + 1); + infer.AddLivePoint(FreeRV, point); + } + } + infer.AddLivePoint(FreeRV, Point(Point::EndBlockID, Point::EndIndex)); + infer.CapVar(FreeRV); +} + +void clang::borrow::BorrowCk(const Environment &env, RegionCheck &rc, + LoansInScope &LIS) { + LIS.Walk([&](Point point, const Stmt *S, + const llvm::SmallVector &loans) -> void { + BorrowCheck borrowck(rc.getReporter(), env, point, loans); +#if DEBUG_PRINT + llvm::outs() << "Point: " << point.print() << '\n'; + llvm::outs() << "Loans: [\n"; + for (const Loan &loan : loans) { + llvm::outs() << loan.print() << ",\n"; + } + llvm::outs() << "]\n"; +#endif + if (rc.getActionMap().find(point) != rc.getActionMap().end()) { + const auto &actions = rc.getActionMap().at(point); + for (const std::unique_ptr &action : actions) { + borrowck.CheckAction(action); + } + } + }); +} + +// Entry point of borrow checker. +void clang::borrow::runBorrowChecker(const FunctionDecl &fd, const CFG &cfg, + ASTContext &Ctx, + BorrowDiagReporter &Reporter) { + RegionCheck RC(fd, cfg, Ctx, Reporter); + RC.Check(); +} + +#endif \ No newline at end of file diff --git a/clang/lib/Analysis/CFG.cpp b/clang/lib/Analysis/CFG.cpp index 10d20507ef50..572a27271f3f 100644 --- a/clang/lib/Analysis/CFG.cpp +++ b/clang/lib/Analysis/CFG.cpp @@ -1518,6 +1518,16 @@ std::unique_ptr CFGBuilder::buildCFG(const Decl *D, Stmt *Statement) { if (const CXXDestructorDecl *DD = dyn_cast_or_null(D)) addImplicitDtorsForDestructor(DD); +#if ENABLE_BSC + if (BuildOpts.BSCBorrowCk) { + if (const FunctionDecl *FD = dyn_cast(D)) { + for (ParmVarDecl *PVD : FD->parameters()) { + addLocalScopeForVarDecl(PVD); + } + } + } +#endif + // Visit the statements and create the CFG. CFGBlock *B = addStmt(Statement); @@ -2991,7 +3001,11 @@ CFGBlock *CFGBuilder::VisitDeclSubExpr(DeclStmt *DS) { // statement-expression. CFGBlock *LastBlock = Block; +#if ENABLE_BSC + if (Init && !BuildOpts.BSCBorrowCk) { +#else if (Init) { +#endif if (HasTemporaries) { // For expression with temporaries go directly to subexpression to omit // generating destructors for the second time. @@ -3198,8 +3212,15 @@ CFGBlock *CFGBuilder::VisitReturnStmt(Stmt *S) { // Visit children if (ReturnStmt *RS = dyn_cast(S)) { +#if ENABLE_BSC + if (!BuildOpts.BSCBorrowCk) { + if (Expr *O = RS->getRetValue()) + return Visit(O, AddStmtChoice::AlwaysAdd, /*ExternallyDestructed=*/true); + } +#else if (Expr *O = RS->getRetValue()) return Visit(O, AddStmtChoice::AlwaysAdd, /*ExternallyDestructed=*/true); +#endif return Block; } @@ -4883,6 +4904,12 @@ CFGBlock *CFGBuilder::VisitCXXTemporaryObjectExpr(CXXTemporaryObjectExpr *C, CFGBlock *CFGBuilder::VisitImplicitCastExpr(ImplicitCastExpr *E, AddStmtChoice asc) { +#if ENABLE_BSC + if (BuildOpts.BSCBorrowCk) { + if (!asc.alwaysAdd(*this, E)) + return Block; + } +#endif if (asc.alwaysAdd(*this, E)) { autoCreateBlock(); appendStmt(Block, E); diff --git a/clang/lib/Analysis/CMakeLists.txt b/clang/lib/Analysis/CMakeLists.txt index 97bcac8898d0..1a2441527ceb 100644 --- a/clang/lib/Analysis/CMakeLists.txt +++ b/clang/lib/Analysis/CMakeLists.txt @@ -10,6 +10,7 @@ add_clang_library(clangAnalysis BSCNullabilityCheck.cpp BSCOwnership.cpp BSCBorrowCheck.cpp + BSCBorrowChecker.cpp CalledOnceCheck.cpp CFG.cpp CFGReachabilityAnalysis.cpp diff --git a/clang/lib/Sema/BSC/SemaDeclBSC.cpp b/clang/lib/Sema/BSC/SemaDeclBSC.cpp index 34ad26b1b50b..cd3945035f63 100644 --- a/clang/lib/Sema/BSC/SemaDeclBSC.cpp +++ b/clang/lib/Sema/BSC/SemaDeclBSC.cpp @@ -13,14 +13,14 @@ #if ENABLE_BSC +#include "TreeTransform.h" #include "clang/AST/BSC/WalkerBSC.h" -#include "clang/Analysis/Analyses/BSCBorrowCheck.h" +#include "clang/Analysis/Analyses/BSCBorrowChecker.h" #include "clang/Analysis/Analyses/BSCNullabilityCheck.h" #include "clang/Analysis/Analyses/BSCFatPtrCheck.h" #include "clang/Analysis/Analyses/BSCOwnership.h" #include "clang/Analysis/AnalysisDeclContext.h" #include "clang/Sema/Sema.h" -#include "clang/Sema/SemaInternal.h" using namespace clang; using namespace sema; @@ -213,11 +213,10 @@ void Sema::BSCDataflowAnalysis(const FunctionDecl *FD, OwnershipDiagReporter OwnershipReporter(*this); runOwnershipAnalysis(*FD, *AC.getCFG(), AC, OwnershipReporter, Context); OwnershipReporter.flushDiagnostics(); - // Step three: Run borrow check when there is no other ownership errors in - // current function. + // Step three: Run borrow checker when there is no other ownership errors and + // nullability in current function. if (!OwnershipReporter.getNumErrors()) { - BorrowCheckDiagReporter BorrowCheckReporter(*this); - runBorrowCheck(*FD, *AC.getCFG(), BorrowCheckReporter, Context); + BSCBorrowChecker(const_cast(FD)); } } } @@ -227,4 +226,691 @@ void Sema::BSCDataflowAnalysis(const FunctionDecl *FD, runFatPtrReduntantCheck(*FD, *AC.getCFG(), AC, Context); } } + +struct ReplaceNodesMap { + llvm::DenseMap replacedExprsMap; + llvm::DenseMap replacedStmtsMap; + + bool Contains(Expr *E) const { + if (replacedExprsMap.find(E) != replacedExprsMap.end()) + return true; + return false; + } + + bool Contains(Stmt *S) const { + if (replacedStmtsMap.find(S) != replacedStmtsMap.end()) + return true; + return false; + } + + void Insert(Expr *Key, Expr *Value) { replacedExprsMap[Key] = Value; } + + void Insert(Stmt *Key, Stmt *Value) { replacedStmtsMap[Key] = Value; } + + Expr *Get(Expr *Key) { return replacedExprsMap[Key]; } + + Stmt *Get(Stmt *Key) { return replacedStmtsMap[Key]; } +}; + +class BorrowCheckerPrologue : public TreeTransform { + typedef TreeTransform BaseTransform; + + FunctionDecl *FD; + llvm::SmallVector Stmts; + bool NeedToReplace = false; // A flag indicating whether to create a temporary + // variable to replace the current CallExpr. + unsigned TempVarCounter = 0; + + ReplaceNodesMap &replacedNodesMap; + + // Replace function call expression or unary operator expression with a + // temporary variable, and return the corresponding DeclRefExpr. + DeclRefExpr *ReplaceWithTemporaryVariable(Expr *E) { + std::string Name = "_borrowck_tmp_" + std::to_string(TempVarCounter++); + VarDecl *VD = VarDecl::Create( + getSema().Context, FD, SourceLocation(), SourceLocation(), + &getSema().Context.Idents.get(Name), E->getType(), nullptr, SC_None); + VD->setInit(E); + DeclStmt *DS = new (getSema().Context) + DeclStmt(DeclGroupRef(VD), SourceLocation(), SourceLocation()); + Stmts.push_back(DS); + return DeclRefExpr::Create(getSema().Context, NestedNameSpecifierLoc(), + SourceLocation(), VD, false, E->getBeginLoc(), + E->getType(), VK_LValue); + } + + CompoundStmt *WrapWithCompoundStmt(Stmt *S) { + return CompoundStmt::Create(SemaRef.Context, S, FPOptionsOverride(), + S->getBeginLoc(), S->getEndLoc()); + } + +public: + BorrowCheckerPrologue(Sema &SemaRef, FunctionDecl *FD, + ReplaceNodesMap &replacedNodesMap) + : BaseTransform(SemaRef), FD(FD), replacedNodesMap(replacedNodesMap) {} + + // Don't redo semantic analysis to ensure that AST nodes are not rebuilt to + // affect destructor insertion. + bool AlwaysRebuild() { return false; } + + void applyTransform() { + StmtResult Res = BaseTransform::TransformStmt(FD->getBody()); + FD->setBody(Res.get()); + } + + StmtResult TransformCaseStmt(CaseStmt *CS) { + CS->setLHS(getDerived().TransformExpr(CS->getLHS()).get()); + Expr *RHS = CS->getRHS(); + if (RHS) + CS->setRHS(getDerived().TransformExpr(RHS).get()); + CS->setSubStmt(getDerived().TransformStmt(CS->getSubStmt()).get()); + return CS; + } + + StmtResult TransformCompoundStmt(CompoundStmt *CS) { + return TransformCompoundStmt(CS, false); + } + + // Transform each stmt in the CompoundStmt. + StmtResult TransformCompoundStmt(CompoundStmt *CS, bool IsStmtExpr) { + if (CS == nullptr) + return CS; + + llvm::SmallVector PrevStmts = Stmts; + llvm::SmallVector CurStmts; + Stmts = CurStmts; + // Traverse and transform all statements in the compound statement. + for (Stmt *S : CS->body()) { + StmtResult Res = BaseTransform::TransformStmt(S); + Stmts.push_back(Res.getAs()); + } + CompoundStmt *NewCS = CompoundStmt::Create( + SemaRef.Context, Stmts, FPOptionsOverride(), CS->getLBracLoc(), + CS->getRBracLoc(), CS->getSafeSpecifier(), CS->getSafeSpecifierLoc(), + CS->getCompSafeZoneSpecifier()); + Stmts = PrevStmts; + replacedNodesMap.Insert(NewCS, CS); + + return NewCS; + } + + StmtResult TransformDeclStmt(DeclStmt *DS) { + for (Decl *D : DS->decls()) { + if (VarDecl *VD = dyn_cast(D)) { + if (VD->hasInit()) { + getDerived().TransformExpr(VD->getInit()); + } + } + } + + return DS; + } + + StmtResult TransformDefaultStmt(DefaultStmt *DS) { + DS->setSubStmt(getDerived().TransformStmt(DS->getSubStmt()).get()); + return DS; + } + + StmtResult TransformDoStmt(DoStmt *DS) { + Stmt *Body = DS->getBody(); + CompoundStmt *CSBody = isa(Body) + ? dyn_cast(Body) + : WrapWithCompoundStmt(Body); + StmtResult ResBody = getDerived().TransformStmt(CSBody); + DS->setBody(ResBody.get()); + replacedNodesMap.Insert(ResBody.get(), Body); + DS->setCond(getDerived().TransformExpr(DS->getCond()).get()); + + return DS; + } + + StmtResult TransformForStmt(ForStmt *FS) { + FS->setInit(getDerived().TransformStmt(FS->getInit()).get()); + FS->setCond(getDerived().TransformExpr(FS->getCond()).get()); + FS->setInc(getDerived().TransformExpr(FS->getInc()).get()); + Stmt *Body = FS->getBody(); + CompoundStmt *CSBody = isa(Body) + ? dyn_cast(Body) + : WrapWithCompoundStmt(Body); + StmtResult ResBody = getDerived().TransformStmt(CSBody); + FS->setBody(ResBody.get()); + replacedNodesMap.Insert(ResBody.get(), Body); + + return FS; + } + + StmtResult TransformIfStmt(IfStmt *IS) { + Expr *Cond = IS->getCond(); + ExprResult ResCond = getDerived().TransformExpr(Cond); + IS->setCond(ResCond.get()); + + Stmt *Then = IS->getThen(); + CompoundStmt *CSThen = isa(Then) + ? dyn_cast(Then) + : WrapWithCompoundStmt(Then); + StmtResult ResThen = getDerived().TransformStmt(CSThen); + IS->setThen(ResThen.get()); + replacedNodesMap.Insert(ResThen.get(), Then); + + if (Stmt *Else = IS->getElse()) { + CompoundStmt *CSElse = isa(Else) + ? dyn_cast(Else) + : WrapWithCompoundStmt(Else); + StmtResult ResElse = getDerived().TransformStmt(CSElse); + IS->setElse(ResElse.get()); + replacedNodesMap.Insert(ResElse.get(), Else); + } + + return IS; + } + + StmtResult TransformReturnStmt(ReturnStmt *RS) { + if (!RS->getRetValue()) + return RS; + + bool Old = NeedToReplace; + NeedToReplace = true; + ExprResult Res = getDerived().TransformExpr(RS->getRetValue()); + Expr *E = Res.get(); + if (CallExpr *CE = dyn_cast_or_null(E)) { + DeclRefExpr *DRE = ReplaceWithTemporaryVariable(CE); + CastKind Kind = + DRE->getType()->getAsCXXRecordDecl() ? CK_NoOp : CK_LValueToRValue; + ImplicitCastExpr *ICE = + ImplicitCastExpr::Create(getSema().Context, DRE->getType(), Kind, DRE, + nullptr, VK_PRValue, FPOptionsOverride()); + replacedNodesMap.Insert(ICE, CE); + RS->setRetValue(ICE); + return RS; + } + if (UnaryOperator *UO = dyn_cast(E)) { + DeclRefExpr *DRE = ReplaceWithTemporaryVariable(UO); + CastKind Kind = + DRE->getType()->getAsCXXRecordDecl() ? CK_NoOp : CK_LValueToRValue; + ImplicitCastExpr *ICE = + ImplicitCastExpr::Create(getSema().Context, DRE->getType(), Kind, DRE, + nullptr, VK_PRValue, FPOptionsOverride()); + replacedNodesMap.Insert(ICE, UO); + RS->setRetValue(ICE); + return RS; + } + RS->setRetValue(E); + NeedToReplace = Old; + return RS; + } + + StmtResult TransformSwitchStmt(SwitchStmt *SS) { + SS->setCond(getDerived().TransformExpr(SS->getCond()).get()); + SS->setBody(getDerived().TransformStmt(SS->getBody()).get()); + return SS; + } + + StmtResult TransformWhileStmt(WhileStmt *WS) { + WS->setCond(getDerived().TransformExpr(WS->getCond()).get()); + Stmt *Body = WS->getBody(); + CompoundStmt *CSBody = isa(Body) + ? dyn_cast(Body) + : WrapWithCompoundStmt(Body); + StmtResult ResBody = getDerived().TransformStmt(CSBody); + WS->setBody(ResBody.get()); + replacedNodesMap.Insert(ResBody.get(), Body); + + return WS; + } + + ExprResult TransformBinaryOperator(BinaryOperator *BO) { + bool Old = NeedToReplace; + NeedToReplace = true; + ExprResult ResLHS = getDerived().TransformExpr(BO->getLHS()); + Expr *ELHS = ResLHS.get(); + if (CallExpr *CE = dyn_cast(ELHS)) { + DeclRefExpr *DRE = ReplaceWithTemporaryVariable(CE); + CastKind Kind = + DRE->getType()->getAsCXXRecordDecl() ? CK_NoOp : CK_LValueToRValue; + ELHS = + ImplicitCastExpr::Create(getSema().Context, DRE->getType(), Kind, DRE, + nullptr, VK_PRValue, FPOptionsOverride()); + replacedNodesMap.Insert(ELHS, CE); + } + BO->setLHS(ELHS); + NeedToReplace = Old; + ExprResult ResRHS = getDerived().TransformExpr(BO->getRHS()); + Expr *ERHS = ResRHS.get(); + if (BO->getOpcode() < BO_Assign) { + if (CallExpr *CE = dyn_cast(ERHS)) { + DeclRefExpr *DRE = ReplaceWithTemporaryVariable(CE); + CastKind Kind = + DRE->getType()->getAsCXXRecordDecl() ? CK_NoOp : CK_LValueToRValue; + ERHS = ImplicitCastExpr::Create(getSema().Context, DRE->getType(), Kind, + DRE, nullptr, VK_PRValue, + FPOptionsOverride()); + replacedNodesMap.Insert(ERHS, CE); + } + } + BO->setRHS(ERHS); + return BO; + } + + ExprResult TransformCallExpr(CallExpr *CE) { + bool Old = NeedToReplace; + for (unsigned i = 0; i < CE->getNumArgs(); ++i) { + Expr *Arg = CE->getArg(i); + NeedToReplace = true; + ExprResult Res = getDerived().TransformExpr(Arg); + Expr *E = Res.get(); + + if (CallExpr *NestedCall = dyn_cast(E)) { + E = ReplaceWithTemporaryVariable(NestedCall); + CastKind Kind = + E->getType()->getAsCXXRecordDecl() ? CK_NoOp : CK_LValueToRValue; + E = ImplicitCastExpr::Create(getSema().Context, E->getType(), Kind, E, + nullptr, VK_PRValue, FPOptionsOverride()); + replacedNodesMap.Insert(E, NestedCall); + } else if (UnaryOperator *UO = dyn_cast(E)) { + if (UO->getOpcode() >= UO_AddrMut && + UO->getOpcode() <= UO_AddrConstDeref) { + E = ReplaceWithTemporaryVariable(UO); + CastKind Kind = + E->getType()->getAsCXXRecordDecl() ? CK_NoOp : CK_LValueToRValue; + E = ImplicitCastExpr::Create(getSema().Context, E->getType(), Kind, E, + nullptr, VK_PRValue, + FPOptionsOverride()); + replacedNodesMap.Insert(E, UO); + } + } + CE->setArg(i, E); + } + NeedToReplace = Old; + return CE; + } + + ExprResult TransformCompoundLiteralExpr(CompoundLiteralExpr *CLE) { + ExprResult Res = getDerived().TransformExpr(CLE->getInitializer()); + Expr *E = Res.get(); + CLE->setInitializer(E); + return CLE; + } + + ExprResult TransformCStyleCastExpr(CStyleCastExpr *CSCE) { + ExprResult Res = getDerived().TransformExpr(CSCE->getSubExpr()); + Expr *E = Res.get(); + if (CallExpr *CE = dyn_cast(E)) { + if (NeedToReplace) { + E = ReplaceWithTemporaryVariable(CE); + replacedNodesMap.Insert(E, CE); + } + } + CSCE->setSubExpr(E); + return CSCE; + } + + ExprResult TransformImplicitCastExpr(ImplicitCastExpr *ICE) { + ExprResult Res = getDerived().TransformExpr(ICE->getSubExpr()); + Expr *E = Res.get(); + if (CallExpr *CE = dyn_cast(E)) { + if (NeedToReplace) { + DeclRefExpr *DRE = ReplaceWithTemporaryVariable(CE); + replacedNodesMap.Insert(DRE, CE); + ICE->setSubExpr(DRE); + return ICE; + } + } + ICE->setSubExpr(E); + return ICE; + } + + ExprResult TransformInitListExpr(InitListExpr *ILE) { + for (unsigned i = 0; i < ILE->getNumInits(); ++i) { + ExprResult Res = getDerived().TransformExpr(ILE->getInit(i)); + Expr *E = Res.get(); + if (CallExpr *CE = dyn_cast(E)) { + E = ReplaceWithTemporaryVariable(CE); + replacedNodesMap.Insert(E, CE); + } else if (UnaryOperator *UO = dyn_cast(E)) { + if (UO->getOpcode() >= UO_AddrMut && + UO->getOpcode() <= UO_AddrConstDeref) { + E = ReplaceWithTemporaryVariable(UO); + CastKind Kind = + E->getType()->getAsCXXRecordDecl() ? CK_NoOp : CK_LValueToRValue; + ImplicitCastExpr *ICE = ImplicitCastExpr::Create( + getSema().Context, E->getType(), Kind, E, nullptr, VK_PRValue, + FPOptionsOverride()); + E = ICE; + replacedNodesMap.Insert(E, UO); + } + } + ILE->setInit(i, E); + } + return ILE; + } + + ExprResult TransformMemberExpr(MemberExpr *ME) { + ExprResult Res = getDerived().TransformExpr(ME->getBase()); + Expr *E = Res.get(); + if (CallExpr *CE = dyn_cast(E)) { + E = ReplaceWithTemporaryVariable(CE); + CastKind Kind = + E->getType()->getAsCXXRecordDecl() ? CK_NoOp : CK_LValueToRValue; + E = ImplicitCastExpr::Create(getSema().Context, E->getType(), Kind, E, + nullptr, VK_PRValue, FPOptionsOverride()); + replacedNodesMap.Insert(E, CE); + } + ME->setBase(E); + return ME; + } + + ExprResult TransformStmtExpr(StmtExpr *SE) { + StmtResult Res = getDerived().TransformStmt(SE->getSubStmt()); + SE->setSubStmt(Res.getAs()); + return SE; + } + + ExprResult TransformUnaryOperator(UnaryOperator *UO) { + ExprResult Res = getDerived().TransformExpr(UO->getSubExpr()); + Expr *E = Res.get(); + if (UO->getOpcode() == UO_Deref) { + if (CallExpr *CE = dyn_cast(E)) { + DeclRefExpr *DRE = ReplaceWithTemporaryVariable(E); + CastKind Kind = + DRE->getType()->getAsCXXRecordDecl() ? CK_NoOp : CK_LValueToRValue; + E = ImplicitCastExpr::Create(getSema().Context, DRE->getType(), Kind, + DRE, nullptr, VK_PRValue, + FPOptionsOverride()); + replacedNodesMap.Insert(E, CE); + } + } + UO->setSubExpr(E); + return UO; + } +}; + +class BorrowCheckerEpilogue : public TreeTransform { + typedef TreeTransform BaseTransform; + + FunctionDecl *FD; + ReplaceNodesMap &replacedNodesMap; + +public: + BorrowCheckerEpilogue(Sema &SemaRef, FunctionDecl *FD, + ReplaceNodesMap &replacedNodesMap) + : BaseTransform(SemaRef), FD(FD), replacedNodesMap(replacedNodesMap) {} + + // Don't redo semantic analysis to ensure that AST nodes are not rebuilt to + // affect destructor insertion. + bool AlwaysRebuild() { return false; } + + void applyTransform() { + StmtResult Res = BaseTransform::TransformStmt(FD->getBody()); + FD->setBody(Res.get()); + } + + StmtResult TransformCaseStmt(CaseStmt *CS) { + CS->setLHS(getDerived().TransformExpr(CS->getLHS()).get()); + Expr *RHS = CS->getRHS(); + if (RHS) + CS->setRHS(getDerived().TransformExpr(RHS).get()); + CS->setSubStmt(getDerived().TransformStmt(CS->getSubStmt()).get()); + return CS; + } + + StmtResult TransformCompoundStmt(CompoundStmt *CS) { + return TransformCompoundStmt(CS, false); + } + + // Transform each stmt in the CompoundStmt. + StmtResult TransformCompoundStmt(CompoundStmt *CS, bool IsStmtExpr) { + if (CS == nullptr) + return CS; + + if (replacedNodesMap.Contains(CS)) { + CS = cast(replacedNodesMap.Get(CS)); + } + + // Traverse and transform all statements in the compound statement. + for (Stmt *S : CS->body()) { + BaseTransform::TransformStmt(S); + } + + return CS; + } + + StmtResult TransformDeclStmt(DeclStmt *DS) { + for (Decl *D : DS->decls()) { + if (VarDecl *VD = dyn_cast(D)) { + if (VD->hasInit()) { + getDerived().TransformExpr(VD->getInit()); + } + } + } + + return DS; + } + + StmtResult TransformDefaultStmt(DefaultStmt *DS) { + DS->setSubStmt(getDerived().TransformStmt(DS->getSubStmt()).get()); + return DS; + } + + StmtResult TransformDoStmt(DoStmt *DS) { + Stmt *Body = DS->getBody(); + if (replacedNodesMap.Contains(Body)) { + Body = replacedNodesMap.Get(Body); + } + StmtResult ResBody = getDerived().TransformStmt(Body); + DS->setBody(ResBody.get()); + DS->setCond(getDerived().TransformExpr(DS->getCond()).get()); + + return DS; + } + + StmtResult TransformForStmt(ForStmt *FS) { + FS->setInit(getDerived().TransformStmt(FS->getInit()).get()); + FS->setCond(getDerived().TransformExpr(FS->getCond()).get()); + FS->setInc(getDerived().TransformExpr(FS->getInc()).get()); + Stmt *Body = FS->getBody(); + if (replacedNodesMap.Contains(Body)) { + Body = replacedNodesMap.Get(Body); + } + StmtResult ResBody = getDerived().TransformStmt(Body); + FS->setBody(ResBody.get()); + + return FS; + } + + StmtResult TransformIfStmt(IfStmt *IS) { + Expr *Cond = IS->getCond(); + ExprResult ResCond = getDerived().TransformExpr(Cond); + IS->setCond(ResCond.get()); + + Stmt *Then = IS->getThen(); + if (replacedNodesMap.Contains(Then)) { + Then = replacedNodesMap.Get(Then); + } + StmtResult ResThen = getDerived().TransformStmt(Then); + IS->setThen(ResThen.get()); + + if (Stmt *Else = IS->getElse()) { + if (replacedNodesMap.Contains(Else)) { + Else = replacedNodesMap.Get(Else); + } + StmtResult ResElse = getDerived().TransformStmt(Else); + IS->setElse(ResElse.get()); + } + + return IS; + } + + StmtResult TransformReturnStmt(ReturnStmt *RS) { + if (!RS->getRetValue()) + return RS; + + if (replacedNodesMap.Contains(RS->getRetValue())) { + RS->setRetValue(replacedNodesMap.Get(RS->getRetValue())); + } + + ExprResult Res = getDerived().TransformExpr(RS->getRetValue()); + Expr *E = Res.get(); + + RS->setRetValue(E); + return RS; + } + + StmtResult TransformSwitchStmt(SwitchStmt *SS) { + SS->setCond(getDerived().TransformExpr(SS->getCond()).get()); + SS->setBody(getDerived().TransformStmt(SS->getBody()).get()); + return SS; + } + + StmtResult TransformWhileStmt(WhileStmt *WS) { + WS->setCond(getDerived().TransformExpr(WS->getCond()).get()); + Stmt *Body = WS->getBody(); + if (replacedNodesMap.Contains(Body)) { + Body = replacedNodesMap.Get(Body); + } + StmtResult Res = getDerived().TransformStmt(Body); + WS->setBody(Res.get()); + + return WS; + } + + ExprResult TransformBinaryOperator(BinaryOperator *BO) { + Expr *LHS = BO->getLHS(); + if (replacedNodesMap.Contains(LHS)) { + LHS = replacedNodesMap.Get(LHS); + } + ExprResult ResLHS = getDerived().TransformExpr(LHS); + BO->setLHS(ResLHS.get()); + Expr *RHS = BO->getRHS(); + if (replacedNodesMap.Contains(RHS)) { + RHS = replacedNodesMap.Get(RHS); + } + ExprResult ResRHS = getDerived().TransformExpr(RHS); + BO->setRHS(ResRHS.get()); + return BO; + } + + ExprResult TransformCallExpr(CallExpr *CE) { + for (unsigned i = 0; i < CE->getNumArgs(); ++i) { + Expr *Arg = CE->getArg(i); + if (replacedNodesMap.Contains(Arg)) { + Arg = replacedNodesMap.Get(Arg); + } + ExprResult Res = getDerived().TransformExpr(Arg); + Expr *E = Res.get(); + + CE->setArg(i, E); + } + return CE; + } + + ExprResult TransformCompoundLiteralExpr(CompoundLiteralExpr *CLE) { + ExprResult Res = getDerived().TransformExpr(CLE->getInitializer()); + Expr *E = Res.get(); + CLE->setInitializer(E); + return CLE; + } + + ExprResult TransformCStyleCastExpr(CStyleCastExpr *CSCE) { + Expr *Sub = CSCE->getSubExpr(); + if (replacedNodesMap.Contains(Sub)) { + Sub = replacedNodesMap.Get(Sub); + } + ExprResult Res = getDerived().TransformExpr(Sub); + CSCE->setSubExpr(Res.get()); + + return CSCE; + } + + ExprResult TransformImplicitCastExpr(ImplicitCastExpr *ICE) { + Expr *Sub = ICE->getSubExpr(); + if (replacedNodesMap.Contains(Sub)) { + Sub = replacedNodesMap.Get(Sub); + } + ExprResult Res = getDerived().TransformExpr(Sub); + ICE->setSubExpr(Res.get()); + + return ICE; + } + + ExprResult TransformInitListExpr(InitListExpr *ILE) { + for (unsigned i = 0; i < ILE->getNumInits(); ++i) { + Expr *Init = ILE->getInit(i); + if (replacedNodesMap.Contains(Init)) { + Init = replacedNodesMap.Get(Init); + } + ExprResult Res = getDerived().TransformExpr(Init); + ILE->setInit(i, Res.get()); + } + + return ILE; + } + + ExprResult TransformMemberExpr(MemberExpr *ME) { + Expr *Base = ME->getBase(); + if (replacedNodesMap.Contains(Base)) { + Base = replacedNodesMap.Get(Base); + } + ExprResult Res = getDerived().TransformExpr(Base); + ME->setBase(Res.get()); + + return ME; + } + + ExprResult TransformStmtExpr(StmtExpr *SE) { + StmtResult Res = getDerived().TransformStmt(SE->getSubStmt()); + SE->setSubStmt(Res.getAs()); + return SE; + } + + ExprResult TransformUnaryOperator(UnaryOperator *UO) { + Expr *Sub = UO->getSubExpr(); + if (UO->getOpcode() == UO_Deref) { + if (replacedNodesMap.Contains(Sub)) { + Sub = replacedNodesMap.Get(Sub); + } + } + ExprResult Res = getDerived().TransformExpr(Sub); + UO->setSubExpr(Res.get()); + + return UO; + } +}; + +void Sema::BSCBorrowChecker(FunctionDecl *FD) { + ReplaceNodesMap replacedNodesMap; + BorrowCheckerPrologue BCP(*this, FD, replacedNodesMap); + BCP.applyTransform(); + + AnalysisDeclContext AC(/* AnalysisDeclContextManager */ nullptr, FD); + AC.getCFGBuildOptions().PruneTriviallyFalseEdges = true; + AC.getCFGBuildOptions().AddEHEdges = false; + AC.getCFGBuildOptions().AddInitializers = true; + AC.getCFGBuildOptions().AddImplicitDtors = true; + AC.getCFGBuildOptions().AddTemporaryDtors = true; + AC.getCFGBuildOptions().AddScopes = true; + AC.getCFGBuildOptions().BSCMode = true; + AC.getCFGBuildOptions().BSCBorrowCk = true; + AC.getCFGBuildOptions() + .setAlwaysAdd(Stmt::BinaryOperatorClass) + .setAlwaysAdd(Stmt::BreakStmtClass) + .setAlwaysAdd(Stmt::CompoundStmtClass) + .setAlwaysAdd(Stmt::DeclStmtClass) + .setAlwaysAdd(Stmt::ForStmtClass) + .setAlwaysAdd(Stmt::IfStmtClass) + .setAlwaysAdd(Stmt::ReturnStmtClass) + .setAlwaysAdd(Stmt::SwitchStmtClass) + .setAlwaysAdd(Stmt::WhileStmtClass); + + if (AC.getCFG()) { +#if DEBUG_PRINT + AC.getCFG()->dump(LangOpts, true); +#endif + borrow::BorrowDiagReporter Reporter(*this); + borrow::runBorrowChecker(*FD, *AC.getCFG(), Context, Reporter); + } + + BorrowCheckerEpilogue BCE(*this, FD, replacedNodesMap); + BCE.applyTransform(); +} + #endif \ No newline at end of file diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index ca9200c92de0..a3848d8a1f5d 100755 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -16021,9 +16021,15 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body, getDiagnostics().getNumOwnershipErrors() + getDiagnostics().getNumBorrowCheckErrors() + getDiagnostics().getNumNullabilityCheckErrors()) { + bool DoAnalysis = true; + // Skip function template and class template + if (const auto *RD = dyn_cast(FD->getParent())) { + if (RD->getDescribedClassTemplate() != nullptr) + DoAnalysis = false; + } auto md = dyn_cast_or_null(FD); // Don't check ownership rules of destructor parameters - if (!md || !md->isDestructor()) + if (DoAnalysis && (!md || !md->isDestructor())) BSCDataflowAnalysis(FD, EnableOwnershipCheck, EnableNullabilityCheck); } diff --git a/clang/test/BSC/Negative/NullabilityCheck/while_stmt_struct.cbs b/clang/test/BSC/Negative/NullabilityCheck/while_stmt_struct.cbs index 1ba70d31d74d..09f9c74ec0e1 100644 --- a/clang/test/BSC/Negative/NullabilityCheck/while_stmt_struct.cbs +++ b/clang/test/BSC/Negative/NullabilityCheck/while_stmt_struct.cbs @@ -8,7 +8,8 @@ safe void test1(void) { int a = 10; struct S s = { .p = nullptr }; while (s.p == nullptr) { - s.p = get_random_ptr(&mut a); + s.p = get_random_ptr(&mut a); // expected-error {{cannot borrow `a` as mutable more than once at a time}} + // expected-note@-1 {{first mut borrow occurs here}} } *s.p = 20; } @@ -41,8 +42,10 @@ safe void test4(void) { struct S s1 = { .p = nullptr }; struct S s2 = { .p = nullptr }; while (s1.p == nullptr || s2.p == nullptr) { - s1.p = get_random_ptr(&mut a); - s2.p = get_random_ptr(&mut b); + s1.p = get_random_ptr(&mut a); // expected-error {{cannot borrow `a` as mutable more than once at a time}} + // expected-note@-1 {{first mut borrow occurs here}} + s2.p = get_random_ptr(&mut b); // expected-error {{cannot borrow `b` as mutable more than once at a time}} + // expected-note@-1 {{first mut borrow occurs here}} } *s1.p = 20; *s2.p = 20; diff --git a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/assign_to_borrowed_with_many_loops/assign_to_borrowed_with_many_loops.cbs b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/assign_to_borrowed_with_many_loops/assign_to_borrowed_with_many_loops.cbs new file mode 100644 index 000000000000..9abba063af89 --- /dev/null +++ b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/assign_to_borrowed_with_many_loops/assign_to_borrowed_with_many_loops.cbs @@ -0,0 +1,29 @@ +// RUN: %clang_cc1 -verify %s + +void test() { + int a = 1; + if (a == 1) { a = 1; } + if (a == 2) { a = 2; } + if (a == 3) { a = 3; } + if (a == 4) { a = 4; } + if (a == 5) { a = 5; } + if (a == 6) { a = 6; } + if (a == 7) { a = 7; } + if (a == 8) { a = 8; } + if (a == 9) { a = 9; } + if (a == 10) { a = 10; } + if (a == 11) { a = 11; } + if (a == 12) { a = 12; } + if (a == 13) { a = 13; } + if (a == 14) { a = 14; } + if (a == 15) { a = 15; } + if (a == 16) { a = 16; } + if (a == 17) { a = 17; } + if (a == 18) { a = 18; } + if (a == 19) { a = 19; } + if (a == 20) { a = 20; } + if (a == 21) { a = 21; } + int *borrow p = &mut a; // expected-note {{`a` is borrowed here}} + a = 2; // expected-error {{cannot assign to `a` because it is borrowed}} + *p = 3; +} diff --git a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/at_most_one_mut_borrow/at_most_one_mut_borrow.cbs b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/at_most_one_mut_borrow/at_most_one_mut_borrow.cbs index f0516009a18f..80410d7b65f0 100644 --- a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/at_most_one_mut_borrow/at_most_one_mut_borrow.cbs +++ b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/at_most_one_mut_borrow/at_most_one_mut_borrow.cbs @@ -1,193 +1,248 @@ -// RUN: %clang_cc1 -ast-dump -verify %s +// RUN: %clang_cc1 -verify %s -void use_mut(int *borrow p) {} -void use_immut(const int *borrow p) {} -T* owned safe_malloc(T value); -void free_owned(T* owned p); -void free(void*); +#define NULL ((void*)0) + +struct S { + int a; +}; + +owned struct M { +public: + int a; +}; + +void use_mut(int *borrow p); +void use_immut(const int *borrow p); +void use_two_borrow(int *borrow p1, int *borrow p2); +void useM_mut(M *borrow m); +void useM_immut(const M *borrow m); +T *owned safe_malloc(T value); +void free_owned(T *owned p); +void free(void *); void test1() { int local = 42; - int *borrow p1 = &mut local; - int *borrow p2 = &mut local; // expected-note {{previous borrow is here}} - use_mut(p1); // expected-error {{There should be at most one mutable borrow targeting to 'local' at the same time}} - use_mut(p2); + int *borrow p = &mut local; // expected-note {{first mut borrow occurs here}} + int *borrow q = &mut local; // expected-error {{cannot borrow `local` as mutable more than once at a time}} + use_mut(p); + use_mut(q); } void test2() { int *owned oriPtr = safe_malloc(0); - int *borrow p1 = &mut *oriPtr; - int *borrow p2 = &mut *oriPtr; // expected-note {{previous borrow is here}} - use_mut(p1); // expected-error {{There should be at most one mutable borrow targeting to 'oriPtr' at the same time}} + int *borrow p1 = &mut *oriPtr; // expected-note {{first mut borrow occurs here}} + int *borrow p2 = &mut *oriPtr; // expected-error {{cannot borrow `*oriPtr` as mutable more than once at a time}} + use_mut(p1); use_mut(p2); free_owned(oriPtr); } -void test3(int* oriPtr) { - int *borrow p1 = &mut * oriPtr; - int *borrow p2 = &mut * oriPtr; - use_mut(p1); // error +void test3(int *oriPtr) { + int *borrow p1 = &mut *oriPtr; // expected-note {{first mut borrow occurs here}} + int *borrow p2 = &mut *oriPtr; // expected-error {{cannot borrow `*oriPtr` as mutable more than once at a time}} + use_mut(p1); free(oriPtr); } void test4() { - int local = 5; - int *borrow p = &mut local; - int *borrow p1 = &mut local; - const int *borrow p2 = &const *p; // expected-note {{previous borrow is here}} - // expected-note@-1 {{previous borrow is here}} - use_mut(p); // expected-error {{There should be at most one mutable borrow targeting to 'local' at the same time}} - use_mut(p1); // expected-error {{There should be at most one mutable borrow targeting to 'local' at the same time}} + int local = 42; + int *borrow p = &mut local; // expected-note {{first mut borrow occurs here}} + int *borrow p1 = &mut local; // expected-error {{cannot borrow `local` as mutable more than once at a time}} + const int *borrow p2 = &const *p; // expected-note {{immutable borrow occurs here}} + use_mut(p); // expected-error {{cannot borrow `*p` as mutable because it is also borrowed as immutable}} + use_mut(p1); use_immut(p2); } void test5() { - int local = 5; - int *borrow p1 = &mut local; - const int *borrow p2 = &const local; // expected-note {{previous borrow is here}} - use_mut(p1); // expected-error {{There should be at most one mutable borrow targeting to 'local' at the same time}} - use_immut(p2); + int local = 42; + int *borrow p = &mut local; // expected-note {{mutable borrow occurs here}} + const int *borrow q = &const local; // expected-error {{cannot borrow `local` as immutable because it is also borrowed as mutable}} + use_mut(p); + use_immut(q); } -void test6(int* borrow p) { +void test6(int *borrow p) { int local = 5; p = &mut local; - const int *borrow p1 = &const *p; // expected-note {{previous borrow is here}} - use_mut(p); // expected-error {{There should be at most one mutable borrow targeting to 'local' at the same time}} + const int *borrow p1 = &const *p; // expected-note {{immutable borrow occurs here}} + use_mut(p); // expected-error {{cannot borrow `*p` as mutable because it is also borrowed as immutable}} use_immut(p1); } -void test7(int* borrow p) { - int *borrow p1 = p; // expected-note {{previous borrow is here}} - use_mut(p); // expected-error {{There should be at most one mutable borrow targeting to 'p' at the same time}} +void test7(int *borrow p) { + int *borrow p1 = p; // expected-note {{`*p` is borrowed here}} + // expected-note@-1 {{first mut borrow occurs here}} + use_mut(p); // expected-error {{cannot use `p` because it was mutably borrowed}} + // expected-error@-1 {{cannot borrow `*p` as mutable more than once at a time}} use_mut(p1); } void test8() { int arr[5]; - int *borrow p = &mut arr[0]; - int *borrow p1 = &mut arr[1]; // expected-note {{previous borrow is here}} - use_mut(p); // expected-error {{There should be at most one mutable borrow targeting to 'arr' at the same time}} - use_mut(p1); + int *borrow p = &mut arr[0]; // expected-note {{first mut borrow occurs here}} + int *borrow q = &mut arr[1]; // expected-error {{cannot borrow `arr` as mutable more than once at a time}} + use_mut(p); + use_mut(q); } -void use_two_borrow(int *borrow p1, int *borrow p2) {} void test9() { int local = 42; - int *borrow p1 = &mut local; - int *borrow p2 = &mut local; // expected-note {{previous borrow is here}} - use_two_borrow(p1, p2); // expected-error {{There should be at most one mutable borrow targeting to 'local' at the same time}} + int *borrow p1 = &mut local; // expected-note {{first mut borrow occurs here}} + int *borrow p2 = &mut local; // expected-error {{cannot borrow `local` as mutable more than once at a time}} + use_two_borrow(p1, p2); } void test10() { int local = 5; int *borrow p1 = &mut local; - const int *borrow p2 = &const local; // expected-note {{previous borrow is here}} + const int *borrow p2 = &const local; int *owned oriPtr = safe_malloc(0); if (local > 0) { - p1 = &mut *oriPtr; - p2 = &const *oriPtr; + p1 = &mut *oriPtr; // expected-note {{mutable borrow occurs here}} + p2 = &const *oriPtr; // expected-error {{cannot borrow `*oriPtr` as immutable because it is also borrowed as mutable}} } else { - p2 = &const *oriPtr; - p1 = &mut *oriPtr; + p2 = &const *oriPtr; // expected-note {{immutable borrow occurs here}} + p1 = &mut *oriPtr; // expected-error {{cannot borrow `*oriPtr` as mutable because it is also borrowed as immutable}} } - use_mut(p1); // expected-error {{There should be at most one mutable borrow targeting to 'oriPtr' at the same time}} - use_immut(p2); // expected-error {{Can not use 'p2' because expired}} + use_mut(p1); + use_immut(p2); free_owned(oriPtr); } void test11() { int local = 5; int *borrow p1 = &mut local; - const int *borrow p2 = &const local; // expected-note {{previous borrow is here}} + const int *borrow p2 = &const local; int *owned oriPtr = safe_malloc(0); if (1) { - p1 = &mut *oriPtr; - p2 = &const *oriPtr; - } else { //Dead code - p1 = &mut *oriPtr; - p2 = &const *oriPtr; + p1 = &mut *oriPtr; // expected-note {{mutable borrow occurs here}} + p2 = &const *oriPtr; // expected-error {{cannot borrow `*oriPtr` as immutable because it is also borrowed as mutable}} + } else { + p1 = &mut *oriPtr; // expected-note {{mutable borrow occurs here}} + p2 = &const *oriPtr; // expected-error {{cannot borrow `*oriPtr` as immutable because it is also borrowed as mutable}} } - use_mut(p1); // expected-error {{There should be at most one mutable borrow targeting to 'oriPtr' at the same time}} + use_mut(p1); use_immut(p2); free_owned(oriPtr); } -struct S { - int a; -}; - void test12() { - struct S s = { .a = 5 }; - const struct S *borrow p1 = &const s; - struct S *borrow p2 = &mut s; // expected-note {{previous borrow is here}} - struct S s1 = *p1; // expected-error {{There should be at most one mutable borrow targeting to 's' at the same time}} - struct S s2 = *p2; + struct S s = { .a = 5 }; + const struct S *borrow p1 = &const s; // expected-note {{immutable borrow occurs here}} + struct S *borrow p2 = &mut s; // expected-error {{cannot borrow `s` as mutable because it is also borrowed as immutable}} + struct S s1 = *p1; + struct S s2 = *p2; } -owned struct M { -public: - int a; -}; -void useM_mut(M * borrow m) {} -void useM_immut(const M * borrow m) {} - void test13() { - int local = 1; - M m = { .a = local }; - const M * borrow p1 = &const m; - M * borrow p2 = &mut m; // expected-note {{previous borrow is here}} - useM_immut(p1); // expected-error {{There should be at most one mutable borrow targeting to 'm' at the same time}} - useM_mut(p2); + int local = 1; + M m = { .a = local }; + const M *borrow p1 = &const m; // expected-note {{immutable borrow occurs here}} + M *borrow p2 = &mut m; // expected-error {{cannot borrow `m` as mutable because it is also borrowed as immutable}} + useM_immut(p1); + useM_mut(p2); } -#define NULL ((void*)0) void test14(int *a) { - int *borrow p1 = (int *borrow)a; - int *borrow p2 = (int *borrow)a; // expected-note {{previous borrow is here}} - use_mut(p1); // expected-error {{There should be at most one mutable borrow targeting to 'a' at the same time}} - use_mut(p2); + int *borrow p1 = (int *borrow)a; // expected-note {{first mut borrow occurs here}} + int *borrow p2 = (int *borrow)a; // expected-error {{cannot borrow `*a` as mutable more than once at a time}} + use_mut(p1); + use_mut(p2); } void test15(int *a) { - int *borrow p1 = (int *borrow)NULL; - int *borrow p2 = (int *borrow)NULL; // expected-note {{previous borrow is here}} - p1 = (int *borrow)a; - p2 = (int *borrow)a; - use_mut(p1); // expected-error {{There should be at most one mutable borrow targeting to 'a' at the same time}} - use_mut(p2); + int *borrow p1 = (int *borrow)NULL; + int *borrow p2 = (int *borrow)NULL; + p1 = (int *borrow)a; // expected-note {{first mut borrow occurs here}} + p2 = (int *borrow)a; // expected-error {{cannot borrow `*a` as mutable more than once at a time}} + use_mut(p1); + use_mut(p2); } void test16() { - int a = 5; - int *b = &a; - int *borrow p1 = &mut *(&(*(&a))); - int *borrow p2 = &mut *(&(*(&a))); // expected-note {{previous borrow is here}} - use_mut(p1); // expected-error {{There should be at most one mutable borrow targeting to 'a' at the same time}} - use_mut(p2); - int *borrow p3 = (int* borrow)(&*b); - int *borrow p4 = (int* borrow)(&*b);// expected-note {{previous borrow is here}} - use_mut(p3); // expected-error {{There should be at most one mutable borrow targeting to 'b' at the same time}} - use_mut(p4); -} - -int main() { - int local = 5; - test1(); - test2(); - test3(&local); - test4(); - test5(); - test6(&mut local); - test7(&mut local); - test8(); - test9(); - test10(); - test11(); - test12(); - test13(); - test14(&local); - test15(&local); - test16(); - return 0; + int a = 5; + int *b = &a; + int *borrow p1 = &mut *(&(*(&a))); // expected-note {{first mut borrow occurs here}} + int *borrow p2 = &mut *(&(*(&a))); // expected-error {{cannot borrow `a` as mutable more than once at a time}} + use_mut(p1); + use_mut(p2); + int *borrow p3 = (int *borrow)(&*b); // expected-note {{first mut borrow occurs here}} + int *borrow p4 = (int *borrow)(&*b); // expected-error {{cannot borrow `*b` as mutable more than once at a time}} + use_mut(p3); + use_mut(p4); +} + +void test17(int *a) { + int *borrow p1 = &mut *a; // expected-note {{first mut borrow occurs here}} + int *borrow p2 = &mut *a; // expected-error {{cannot borrow `*a` as mutable more than once at a time}} + use_mut(p1); + use_mut(p2); +} + +void test18(int *a) { + int *borrow p1 = (int *borrow)NULL; + int *borrow p2 = (int *borrow)NULL; + p1 = &mut *a; // expected-note {{first mut borrow occurs here}} + p2 = &mut *a; // expected-error {{cannot borrow `*a` as mutable more than once at a time}} + use_mut(p1); + use_mut(p2); +} + +void test19() { + int local = 42; + int *borrow p = &mut local, *borrow q = &mut local; // expected-error {{cannot borrow `local` as mutable more than once at a time}} + // expected-note@-1 {{first mut borrow occurs here}} + use_mut(p); + use_mut(q); +} + +void test20() { + int local = 42; + int *borrow p = &mut local; + int *borrow q = p; + p = q; // expected-note {{`*q` is borrowed here}} + int temp = *q; // expected-error {{cannot use `*q` because it was mutably borrowed}} +} + +void test21() { + int local = 42; + int *borrow p = &mut local; + int *borrow q = &mut *p; + p = &mut *q; // expected-note {{`*q` is borrowed here}} + int temp = *q; // expected-error {{cannot use `*q` because it was mutably borrowed}} +} + +void test22() { + int local = 42; + int temp = 2; + int *borrow p1 = &mut local; + int *borrow p2 = &mut temp; + p2 = p1; // expected-note {{`*p1` is borrowed here}} + *p1 = 2; // expected-error {{cannot assign to `*p1` because it is borrowed}} + temp = *p2; +} + +void test23() { + int local = 42; + int *borrow p = &mut local; + const int *borrow q = &const *p; // expected-note {{`*p` is borrowed here}} + *p = 3; // expected-error {{cannot assign to `*p` because it is borrowed}} + use_immut(q); +} + +void test24() { + int local = 42; + int *borrow p = &mut local; + int *borrow q = p; // expected-note {{`*p` is borrowed here}} + *p = 3; // expected-error {{cannot assign to `*p` because it is borrowed}} + use_mut(q); +} + +void test25() { + int local = 42; + int *borrow p1 = &mut local; + const int *borrow p2 = &const *p1; // expected-note {{immutable borrow occurs here}} + int *borrow p3 = p1; // expected-error {{cannot borrow `*p1` as mutable because it is also borrowed as immutable}} + use_immut(p2); } \ No newline at end of file diff --git a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/at_most_one_mut_borrow_member_func/at_most_one_mut_borrow_member_func.cbs b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/at_most_one_mut_borrow_member_func/at_most_one_mut_borrow_member_func.cbs new file mode 100644 index 000000000000..f76a3e1d068a --- /dev/null +++ b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/at_most_one_mut_borrow_member_func/at_most_one_mut_borrow_member_func.cbs @@ -0,0 +1,28 @@ +// RUN: %clang_cc1 -verify %s + +int *borrow int::get_mut_borrow(This *borrow this) { + return this; +} + +const int *borrow int::get_immut_borrow(const This *borrow this) { + return this; +} + +void use_mut(int *borrow p); +void use_immut(const int *borrow p); + +void test1() { + int a = 5; + int *borrow p = a.get_mut_borrow(); // expected-note {{first mut borrow occurs here}} + int *borrow q = a.get_mut_borrow(); // expected-error {{cannot borrow `a` as mutable more than once at a time}} + use_mut(p); + use_mut(q); +} + +void test2() { + int a = 5; + const int *borrow p = a.get_immut_borrow(); // expected-note {{immutable borrow occurs here}} + int *borrow q = a.get_mut_borrow(); // expected-error {{cannot borrow `a` as mutable because it is also borrowed as immutable}} + use_immut(p); + use_mut(q); +} \ No newline at end of file diff --git a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/at_most_one_mut_borrow_member_func/at_most_one_mut_borrowat_most_one_mut_borrow_member_func.cbs b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/at_most_one_mut_borrow_member_func/at_most_one_mut_borrowat_most_one_mut_borrow_member_func.cbs deleted file mode 100644 index b93f3653e499..000000000000 --- a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/at_most_one_mut_borrow_member_func/at_most_one_mut_borrowat_most_one_mut_borrow_member_func.cbs +++ /dev/null @@ -1,29 +0,0 @@ -// RUN: %clang_cc1 -ast-dump -verify %s - -int *borrow int::get_mut_borrow(This * borrow this) { - return this; -} - -const int *borrow int::get_immut_borrow(const This * borrow this) { - return this; -} - -void use_mut(int *borrow p) {} -void use_immut(const int *borrow p) {} - -void test1() { - int a = 5; - int * borrow p = a.get_mut_borrow(); // expected-note {{previous borrow is here}} - int * borrow q = a.get_mut_borrow(); // expected-note {{previous borrow is here}} expected-error {{Can not modify 'a' because be borrowed}} - use_mut(p); // expected-error {{There should be at most one mutable borrow targeting to 'a' at the same time}} - use_mut(q); -} - -void test2() { - int a = 5; - const int * borrow p = a.get_immut_borrow(); // expected-note {{previous borrow is here}} - int * borrow q = a.get_mut_borrow(); // expected-note {{previous borrow is here}} expected-error {{Can not modify 'a' because be borrowed}} - use_immut(p); // expected-error {{There should be at most one mutable borrow targeting to 'a' at the same time}} - use_mut(q); -} - diff --git a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/borrow_field_of_another_borrow/borrow_field_of_another_borrow.cbs b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/borrow_field_of_another_borrow/borrow_field_of_another_borrow.cbs index 0bfd27e01413..5c16737a48f9 100644 --- a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/borrow_field_of_another_borrow/borrow_field_of_another_borrow.cbs +++ b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/borrow_field_of_another_borrow/borrow_field_of_another_borrow.cbs @@ -1,60 +1,60 @@ // RUN: %clang_cc1 -verify %s -void use_mut(int *borrow p) {} -void use_immut(const int *borrow p) {} - struct V { - int a; - int *b; + int a; + int *b; }; struct K { struct V v; }; +void use_mut(int *borrow p); +void use_immut(const int *borrow p); + void test1() { - int local = 5; - struct V v = { .a = 5, .b = &local }; - struct V * borrow p = &mut v; - int *borrow q1 = &mut p->a; - int *borrow q2 = &mut *p->b; - const int *borrow q3 = &const p->a; // expected-note {{previous borrow is here}} expected-note {{previous borrow is here}} - const int *borrow q4 = &const *p->b; // expected-note {{previous borrow is here}} expected-note {{previous borrow is here}} - use_mut(q1); // expected-error {{There should be at most one mutable borrow targeting to 'v.a' at the same time}} - use_mut(q2); // expected-error {{There should be at most one mutable borrow targeting to 'v.b' at the same time}} - use_immut(q3); - use_immut(q4); + int local = 5; + struct V v = { .a = 5, .b = &local }; + struct V *borrow p = &mut v; + int *borrow q1 = &mut p->a; // expected-note {{mutable borrow occurs here}} + int *borrow q2 = &mut *p->b; // expected-note {{mutable borrow occurs here}} + const int *borrow q3 = &const p->a; // expected-error {{cannot borrow `(*p).a` as immutable because it is also borrowed as mutable}} + const int *borrow q4 = &const *p->b; // expected-error {{cannot borrow `*(*p).b` as immutable because it is also borrowed as mutable}} + use_mut(q1); + use_mut(q2); + use_immut(q3); + use_immut(q4); - struct K k = { .v = v }; - p = &mut k.v; - q1 = &mut p->a; - q2 = &mut *p->b; - q3 = &const p->a; - q4 = &const *p->b; - use_mut(q1); // expected-error {{There should be at most one mutable borrow targeting to 'k.v.a' at the same time}} - use_mut(q2); // expected-error {{There should be at most one mutable borrow targeting to 'k.v.b' at the same time}} - use_immut(q3); - use_immut(q4); + struct K k = { .v = v }; + p = &mut k.v; + q1 = &mut p->a; // expected-note {{mutable borrow occurs here}} + q2 = &mut *p->b; // expected-note {{mutable borrow occurs here}} + q3 = &const p->a; // expected-error {{cannot borrow `(*p).a` as immutable because it is also borrowed as mutable}} + q4 = &const *p->b; // expected-error {{cannot borrow `*(*p).b` as immutable because it is also borrowed as mutable}} + use_mut(q1); + use_mut(q2); + use_immut(q3); + use_immut(q4); } -void test2(struct V * borrow p) { - int *borrow q1 = &mut p->a; - int *borrow q2 = &mut *p->b; - const int *borrow q3 = &const p->a; // expected-note {{previous borrow is here}} - const int *borrow q4 = &const *p->b; // expected-note {{previous borrow is here}} - use_mut(q1); // expected-error {{There should be at most one mutable borrow targeting to 'p.a' at the same time}} - use_mut(q2); // expected-error {{There should be at most one mutable borrow targeting to 'p.b' at the same time}} - use_immut(q3); - use_immut(q4); +void test2(struct V *borrow p) { + int *borrow q1 = &mut p->a; // expected-note {{mutable borrow occurs here}} + int *borrow q2 = &mut *p->b; // expected-note {{mutable borrow occurs here}} + const int *borrow q3 = &const p->a; // expected-error {{cannot borrow `(*p).a` as immutable because it is also borrowed as mutable}} + const int *borrow q4 = &const *p->b; // expected-error {{cannot borrow `*(*p).b` as immutable because it is also borrowed as mutable}} + use_mut(q1); + use_mut(q2); + use_immut(q3); + use_immut(q4); } void struct V::foo(This *borrow this) { - int *borrow q1 = &mut this->a; - int *borrow q2 = &mut *this->b; - const int *borrow q3 = &const this->a; // expected-note {{previous borrow is here}} - const int *borrow q4 = &const *this->b; // expected-note {{previous borrow is here}} - use_mut(q1); // expected-error {{There should be at most one mutable borrow targeting to 'this.a' at the same time}} - use_mut(q2); // expected-error {{There should be at most one mutable borrow targeting to 'this.b' at the same time}} - use_immut(q3); - use_immut(q4); + int *borrow q1 = &mut this->a; // expected-note {{mutable borrow occurs here}} + int *borrow q2 = &mut *this->b; // expected-note {{mutable borrow occurs here}} + const int *borrow q3 = &const this->a; // expected-error {{cannot borrow `(*this).a` as immutable because it is also borrowed as mutable}} + const int *borrow q4 = &const *this->b; // expected-error {{cannot borrow `*(*this).b` as immutable because it is also borrowed as mutable}} + use_mut(q1); + use_mut(q2); + use_immut(q3); + use_immut(q4); } \ No newline at end of file diff --git a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/borrow_field_of_struct/borrow_field_of_struct.cbs b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/borrow_field_of_struct/borrow_field_of_struct.cbs index d2e2c1a48cdb..1acac3dbef2c 100644 --- a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/borrow_field_of_struct/borrow_field_of_struct.cbs +++ b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/borrow_field_of_struct/borrow_field_of_struct.cbs @@ -1,58 +1,77 @@ // RUN: %clang_cc1 -verify %s -void use(int *borrow p) {} typedef struct G { - int a; + int a; } G; -void test1() { - G g; - G * pg = &g; - int * borrow p1 = &mut g.a; // expected-note {{previous borrow is here}} - int * borrow p2 = &mut pg->a; // expected-note {{previous borrow is here}} - g.a = 5; // expected-error {{Can not modify 'g.a' because be borrowed}} - pg->a = 5; // expected-error {{Can not modify 'pg.a' because be borrowed}} - use(p1); - use(p2); -} - struct S { G g; }; + struct K { - G* g; + G *g; }; +struct M { + struct S *borrow ps; + struct K *borrow pk; +}; + +void use(int *borrow p) {} +void useM(struct M m) {} + +void test1() { + G g = { .a = 1 }; + int *borrow p1 = &mut g.a; // expected-note {{`g.a` is borrowed here}} + g.a = 5; // expected-error {{cannot assign to `g.a` because it is borrowed}} + use(p1); +} + void test2() { - struct S s1; - struct S *s = &s1; - int *borrow p1 = &mut s->g.a; // expected-note {{previous borrow is here}} - int *borrow p2 = &mut s1.g.a; // expected-note {{previous borrow is here}} - s->g.a = 5; // expected-error {{Can not modify 's.g.a' because be borrowed}} - s1.g.a = 5; // expected-error {{Can not modify 's1.g.a' because be borrowed}} - use(p1); - use(p2); - - struct K k1; - struct K *k = &k1; - int *borrow q1 = &mut k->g->a; // expected-note {{previous borrow is here}} - int *borrow q2 = &mut k1.g->a; // expected-note {{previous borrow is here}} - k->g->a = 5; // expected-error {{Can not modify 'k.g.a' because be borrowed}} - k1.g->a = 5; // expected-error {{Can not modify 'k1.g.a' because be borrowed}} - use(q1); - use(q2); + G g; + G *pg = &g; + int *borrow p2 = &mut pg->a; // expected-note {{`(*pg).a` is borrowed here}} + pg->a = 5; // expected-error {{cannot assign to `(*pg).a` because it is borrowed}} + use(p2); } -struct M { - struct S *borrow ps; - struct K *borrow pk; -}; -void useM(struct M m) {}; void test3() { - struct S s1; - struct K k1; - struct M m = { .ps = &mut s1, .pk = &mut k1 }; // expected-note {{previous borrow is here}} expected-note {{previous borrow is here}} - s1.g.a = 5; // expected-error {{Can not modify 's1.g.a' because be borrowed}} - k1.g->a = 5; // expected-error {{Can not modify 'k1.g.a' because be borrowed}} - useM(m); + G g; + G *pg = &g; + int *borrow p1 = &mut g.a; // expected-note {{`g.a` is borrowed here}} + int *borrow p2 = &mut pg->a; // expected-note {{`(*pg).a` is borrowed here}} + g.a = 5; // expected-error {{cannot assign to `g.a` because it is borrowed}} + pg->a = 5; // expected-error {{cannot assign to `(*pg).a` because it is borrowed}} + use(p1); + use(p2); +} + +void test4() { + struct S s1; + struct S* s = &s1; + int *borrow p1 = &mut s->g.a; // expected-note {{`(*s).g.a` is borrowed here}} + int *borrow p2 = &mut s1.g.a; // expected-note {{`s1.g.a` is borrowed here}} + s->g.a = 5; // expected-error {{cannot assign to `(*s).g.a` because it is borrowed}} + s1.g.a = 5; // expected-error {{cannot assign to `s1.g.a` because it is borrowed}} + use(p1); + use(p2); + + struct K k1; + struct K *k = &k1; + int *borrow q1 = &mut k->g->a; // expected-note {{`(*(*k).g).a` is borrowed here}} + int *borrow q2 = &mut k1.g->a; // expected-note {{`(*k1.g).a` is borrowed here}} + k->g->a = 5; // expected-error {{cannot assign to `(*(*k).g).a` because it is borrowed}} + k1.g->a = 5; // expected-error {{cannot assign to `(*k1.g).a` because it is borrowed}} + use(q1); + use(q2); +} + +void test5() { + struct S s1; + struct K k1; + struct M m = { .ps = &mut s1, .pk = &mut k1 }; // expected-note {{`s1.g.a` is borrowed here}} + // expected-note@-1 {{`(*k1.g).a` is borrowed here}} + s1.g.a = 5; // expected-error {{cannot assign to `s1.g.a` because it is borrowed}} + k1.g->a = 5; // expected-error {{cannot assign to `(*k1.g).a` because it is borrowed}} + useM(m); } \ No newline at end of file diff --git a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/borrow_live_longer/borrow_live_longer.cbs b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/borrow_live_longer/borrow_live_longer.cbs index 45fea8c9d834..015ca4eb9513 100644 --- a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/borrow_live_longer/borrow_live_longer.cbs +++ b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/borrow_live_longer/borrow_live_longer.cbs @@ -1,134 +1,138 @@ // RUN: %clang_cc1 -verify %s -void use_mut(int *borrow p) {} -T* owned safe_malloc(T value); -void free_owned(T* owned p); +struct S1 { int a; }; +struct S2 { int *owned b; }; +struct S3 { struct S1 c; }; +struct S4 { + int m; + int *borrow p; +}; + +void use_mut(int *borrow p); +void use_immut(const int *borrow p); +T *owned safe_malloc(T value); +void free_owned(T *owned p); void free(void*); void test1() { int local = 42; - int *borrow p = &mut local; // expected-error {{borrow pointer variable 'p' lives longer than target variable}} + int *borrow p = &mut local; { int local1 = 42; - p = &mut local1; - } + p = &mut local1; // expected-error {{`local1` does not live long enough}} + } // expected-note {{`local1` dropped here while still borrowed}} use_mut(p); } -void test2(int* borrow p) { // expected-error {{borrow pointer variable 'p' lives longer than target variable}} +void test2(int *borrow p) { { int local1 = 42; - p = &mut local1; - } + p = &mut local1; // expected-error {{`local1` does not live long enough}} + } // expected-note {{`local1` dropped here while still borrowed}} use_mut(p); } void test3() { int local = 42; - int *borrow p = &mut local; // expected-error {{borrow pointer variable 'p' lives longer than target variable}} + int *borrow p = &mut local; if (1) { int local1 = 42; - p = &mut local1; - } + p = &mut local1; // expected-error {{`local1` does not live long enough}} + } // expected-note {{`local1` dropped here while still borrowed}} use_mut(p); } void test4() { int local = 42; - int *borrow p = &mut local; // expected-error {{borrow pointer variable 'p' lives longer than target variable}} + int *borrow p = &mut local; if (local > 0) { int local1 = 42; - p = &mut local1; - } else { + p = &mut local1; // expected-error {{`local1` does not live long enough}} + } else { // expected-note {{`local1` dropped here while still borrowed}} int local2 = 42; - p = &mut local2; - } + p = &mut local2; // expected-error {{`local2` does not live long enough}} + } // expected-note {{`local2` dropped here while still borrowed}} use_mut(p); } void test5() { int *owned oriPtr = safe_malloc(0); - int *borrow p = &mut * oriPtr; // expected-note {{previous borrow is here}} - free_owned(oriPtr); // expected-error {{Can not modify 'oriPtr' because be borrowed}} - use_mut(p); + const int *borrow p = &const *oriPtr; // expected-note {{`*oriPtr` is borrowed here}} + free_owned(oriPtr); // expected-error {{cannot move out of `oriPtr` because it is borrowed}} + use_immut(p); } void test6(int *owned oriPtr) { - int *borrow p = &mut * oriPtr; // expected-note {{previous borrow is here}} - free_owned(oriPtr); // expected-error {{Can not modify 'oriPtr' because be borrowed}} + int *borrow p = &mut *oriPtr; // expected-note {{`*oriPtr` is borrowed here}} + free_owned(oriPtr); // expected-error {{cannot move out of `oriPtr` because it is borrowed}} use_mut(p); } void test7() { int a = 5; int *owned oriPtr = safe_malloc(0); - int *borrow p = &mut* oriPtr; // expected-note {{previous borrow is here}} - // expected-note@-1 {{previous borrow is here}} + int *borrow p = &mut *oriPtr; // expected-note {{`*oriPtr` is borrowed here}} + // expected-note@-1 {{`*oriPtr` is borrowed here}} if (a > 0) { - free_owned(oriPtr); // expected-error {{Can not modify 'oriPtr' because be borrowed}} + free_owned(oriPtr); // expected-error {{cannot move out of `oriPtr` because it is borrowed}} } else { - free_owned(oriPtr); // expected-error {{Can not modify 'oriPtr' because be borrowed}} + free_owned(oriPtr); // expected-error {{cannot move out of `oriPtr` because it is borrowed}} } use_mut(p); } -struct S1 { int a; }; void test8() { int local = 42; - int *borrow p = &mut local; // expected-error {{borrow pointer variable 'p' lives longer than target variable}} + int *borrow p = &mut local; { - struct S1 s = { .a = 42, }; - p = &mut s.a; - } + struct S1 s = { .a = 42 }; + p = &mut s.a; // expected-error {{`s.a` does not live long enough}} + } // expected-note {{`s.a` dropped here while still borrowed}} use_mut(p); } -struct S2 { int* owned b; }; void test9() { int local = 42; - int *borrow p = &mut local; // expected-note {{previous borrow is here}} expected-error {{borrow pointer variable 'p' lives longer than target variable}} + int *borrow p = &mut local; { struct S2 s = { .b = safe_malloc(5) }; - p = &mut *s.b; - free_owned(s.b); // expected-error {{Can not modify 's.b' because be borrowed}} - } + p = &mut *s.b; // expected-error {{`*s.b` does not live long enough}} expected-note {{`*s.b` is borrowed here}} + free_owned(s.b); // expected-error {{cannot move out of `s.b` because it is borrowed}} + } // expected-note {{`*s.b` dropped here while still borrowed}} use_mut(p); } -struct S3 { struct S1 c; }; void test10() { int local = 42; - int *borrow p = &mut local; // expected-error {{borrow pointer variable 'p' lives longer than target variable}} + int *borrow p = &mut local; { struct S1 s1 = { .a = 42 }; struct S3 s2 = { .c = s1 }; - p = &mut s2.c.a; - } + p = &mut s2.c.a; // expected-error {{`s2.c.a` does not live long enough}} + } // expected-note {{`s2.c.a` dropped here while still borrowed}} use_mut(p); } -struct S4 { - int m; - int * borrow p; -}; - -void test11() { - struct S4 s = { .m = 0, .p = &mut s.m };// expected-error {{borrow pointer variable 's' lives longer than target variable}} +void test11(int a) { + const int *borrow q = &const a; + { + int foo = 42; + { + const int *borrow p = &const foo; // expected-error {{`foo` does not live long enough}} + q = &const *p; + } + } // expected-note {{`foo` dropped here while still borrowed}} + use_immut(q); } -int main() { - int local = 5; - int *owned oriPtr = safe_malloc(0); - test1(); - test2(&mut local); - test3(); - test4(); - test5(); - test6(oriPtr); - test7(); - test8(); - test9(); - test10(); - test11(); - return 0; +// Note: V1.0不报错,更合理 +void test12(int foo) { + int local = 42; + const int *borrow q = &const local; + { + int *borrow p = &mut foo; // expected-note {{`foo` is borrowed here}} + q = &const *p; + } + local = foo; // expected-error {{cannot use `foo` because it was mutably borrowed}} + use_immut(q); } \ No newline at end of file diff --git a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/borrow_live_longer_loop/borrow_live_longer_loop.cbs b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/borrow_live_longer_loop/borrow_live_longer_loop.cbs index ecce012cc4de..929b5c571968 100644 --- a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/borrow_live_longer_loop/borrow_live_longer_loop.cbs +++ b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/borrow_live_longer_loop/borrow_live_longer_loop.cbs @@ -1,62 +1,68 @@ // RUN: %clang_cc1 -verify %s + +struct S1 { int a; }; +struct S2 { int *owned b; }; +struct S3 { struct S1 c; }; + int rand(); -void use_mut(int *borrow p) {} -T* owned safe_malloc(T value); -void free_owned(T* owned p); +void use_mut(int *borrow p); +T *owned safe_malloc(T value); +void free_owned(T *owned p); void free(void*); void test1() { int local = 42; - int *borrow p = &mut local; // expected-error {{borrow pointer variable 'p' lives longer than target variable}} + int *borrow p = &mut local; while (rand()) { int local1 = 42; - p = &mut local1; - } + p = &mut local1; // expected-error {{`local1` does not live long enough}} + } // expected-note {{`local1` dropped here while still borrowed}} use_mut(p); } void test2() { int local = 42; - int *borrow p = &mut local; // expected-error {{borrow pointer variable 'p' lives longer than target variable}} - for (int i = 0; i < 5; i++) { + int *borrow p = &mut local; + for (int i = 0; i < 5; ++i) { int local1 = 42; - p = &mut local1; - } + p = &mut local1; // expected-error {{`local1` does not live long enough}} + } // expected-note {{`local1` dropped here while still borrowed}} use_mut(p); } -void test3(int* borrow p) { // expected-error {{borrow pointer variable 'p' lives longer than target variable}} +void test3(int *borrow p) { while (rand()) { int local1 = 42; - p = &mut local1; - } + p = &mut local1; // expected-error {{`local1` does not live long enough}} + } // expected-note {{`local1` dropped here while still borrowed}} use_mut(p); } void test4() { int local = 42; - int *borrow p = &mut local; // expected-error {{borrow pointer variable 'p' lives longer than target variable}} + int *borrow p = &mut local; while (rand()) { if (rand()) { int local1 = 42; - p = &mut local1; - } else { + p = &mut local1; // expected-error {{`local1` does not live long enough}} + } else { // expected-note {{`local1` dropped here while still borrowed}} int local2 = 42; - p = &mut local2; - } + p = &mut local2; // expected-error {{`local2` does not live long enough}} + } // expected-note {{`local2` dropped here while still borrowed}} } use_mut(p); } void test5() { int *owned oriPtr = safe_malloc(0); - int *borrow p = &mut * oriPtr; // expected-note {{previous borrow is here}} + int *borrow p = &mut *oriPtr; // expected-note {{`*oriPtr` is borrowed here}} while (rand()) { if (rand()) { - free_owned(oriPtr); // expected-error {{Can not modify 'oriPtr' because be borrowed}} + free_owned(oriPtr); // expected-error {{cannot move out of `oriPtr` because it is borrowed}} use_mut(p); - } else + } else { free_owned(oriPtr); + } return; } free_owned(oriPtr); @@ -64,13 +70,13 @@ void test5() { void test6() { int local = 42; - int *borrow p = &mut local; // expected-error {{borrow pointer variable 'p' lives longer than target variable}} + int *borrow p = &mut local; while (rand()) { while (rand()) { - for (int i = 0; i < 5; i++) { + for (int i = 0; i < 5; ++i) { int local1 = 42; - p = &mut local1; - } + p = &mut local1; // expected-error {{`local1` does not live long enough}} + } // expected-note {{`local1` dropped here while still borrowed}} } } use_mut(p); @@ -78,70 +84,51 @@ void test6() { void test7() { int local = 42; - int *borrow p = &mut local; // expected-error {{borrow pointer variable 'p' lives longer than target variable}} + int *borrow p = &mut local; while (rand()) { if (rand()) { - for (int i = 0; i < 5; i++) { + for (int i = 0; i < 5; ++i) { int local1 = 42; - p = &mut local1; - } + p = &mut local1; // expected-error {{`local1` does not live long enough}} + } // expected-note {{`local1` dropped here while still borrowed}} } else { - for (int i = 0; i < 5; i++) { + for (int i = 0; i < 5; ++i) { int local2 = 42; - p = &mut local2; - } + p = &mut local2; // expected-error {{`local2` does not live long enough}} + } // expected-note {{`local2` dropped here while still borrowed}} } } use_mut(p); } -struct S1 { int a; }; void test8() { int local = 42; - int *borrow p = &mut local; // expected-error {{borrow pointer variable 'p' lives longer than target variable}} + int *borrow p = &mut local; while (rand()) { - struct S1 s = { .a = 42, }; - p = &mut s.a; - } + struct S1 s = { .a = 42 }; + p = &mut s.a; // expected-error {{`s.a` does not live long enough}} + } // expected-note {{`s.a` dropped here while still borrowed}} use_mut(p); } -struct S2 { int* owned b; }; void test9() { int local = 42; - int *borrow p = &mut local; /* expected-error {{borrow pointer variable 'p' lives longer than target variable}} - expected-note {{previous borrow is here}} */ + int *borrow p = &mut local; while (rand()) { struct S2 s = { .b = safe_malloc(5) }; - p = &mut *s.b; - free_owned(s.b); // expected-error {{Can not modify 's.b' because be borrowed}} - } + p = &mut *s.b; // expected-error {{`*s.b` does not live long enough}} expected-note {{`*s.b` is borrowed here}} + free_owned(s.b); // expected-error {{cannot move out of `s.b` because it is borrowed}} + } // expected-note {{`*s.b` dropped here while still borrowed}} use_mut(p); } -struct S3 { struct S1 c; }; void test10() { int local = 42; - int *borrow p = &mut local; // expected-error {{borrow pointer variable 'p' lives longer than target variable}} + int *borrow p = &mut local; while (rand()) { struct S1 s1 = { .a = 42 }; struct S3 s2 = { .c = s1 }; - p = &mut s2.c.a; - } + p = &mut s2.c.a; // expected-error {{`s2.c.a` does not live long enough}} + } // expected-note {{`s2.c.a` dropped here while still borrowed}} use_mut(p); -} - -int main() { - int local = 5; - test1(); - test2(); - test3(&mut local); - test4(); - test5(); - test6(); - test7(); - test8(); - test9(); - test10(); - return 0; } \ No newline at end of file diff --git a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/borrow_same_var_in_params/borrow_same_var_in_params.cbs b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/borrow_same_var_in_params/borrow_same_var_in_params.cbs new file mode 100644 index 000000000000..4cdfe3aefff8 --- /dev/null +++ b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/borrow_same_var_in_params/borrow_same_var_in_params.cbs @@ -0,0 +1,46 @@ +// RUN: %clang_cc1 -verify %s + +void foo1(int *borrow, int *borrow); +void foo2(int, int *borrow); +void foo3(int *borrow, int); + +void test1(int local) { + foo1(&mut local, &mut local); // expected-error {{cannot borrow `local` as mutable more than once at a time}} + // expected-note@-1 {{first mut borrow occurs here}} +} + +void test2(int local) { + int *borrow p = &mut local; // expected-note {{first mut borrow occurs here}} + int *borrow q = &mut local; // expected-error {{cannot borrow `local` as mutable more than once at a time}} + foo1(p, q); +} + +void test3(int local) { + int *borrow p = &mut local; // expected-note {{first mut borrow occurs here}} + foo1(p, &mut local); // expected-error {{cannot borrow `local` as mutable more than once at a time}} +} + +void test4(int local) { + int *borrow p = &mut local; // expected-note {{first mut borrow occurs here}} + foo1(&mut local, p); // expected-error {{cannot borrow `local` as mutable more than once at a time}} +} + +void test5(int local) { + foo2(local, &mut local); // expected-error {{cannot use `local` because it was mutably borrowed}} + // expected-note@-1 {{`local` is borrowed here}} +} + +void test6(int local) { + int *borrow p = &mut local; // expected-note {{`local` is borrowed here}} + foo2(local, p); // expected-error {{cannot use `local` because it was mutably borrowed}} +} + +void test7(int local) { + foo3(&mut local, local); // expected-error {{cannot use `local` because it was mutably borrowed}} + // expected-note@-1 {{`local` is borrowed here}} +} + +void test8(int local) { + int *borrow p = &mut local; // expected-note {{`local` is borrowed here}} + foo3(p, local); // expected-error {{cannot use `local` because it was mutably borrowed}} +} diff --git a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/borrow_struct_borrow_field/borrow_struct_borrow_field.cbs b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/borrow_struct_borrow_field/borrow_struct_borrow_field.cbs index 33c8127a8750..dd709cb4fb8a 100644 --- a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/borrow_struct_borrow_field/borrow_struct_borrow_field.cbs +++ b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/borrow_struct_borrow_field/borrow_struct_borrow_field.cbs @@ -1,98 +1,117 @@ // RUN: %clang_cc1 -verify %s typedef struct A { - int * borrow p; - int * borrow q; - int c; + int *borrow p; + int *borrow q; + int c; } A; -void useSA(A); - -void test1(A a) { - A b = a; // expected-note {{previous borrow is here}} - useSA(a); // expected-error {{There should be at most one mutable borrow targeting to 'a' at the same time}} - useSA(b); -} - typedef struct B { - A a; - int d; + A a; + int d; } B; +struct V { + int a; +}; + +struct M { + int *borrow ma; + const int *borrow na; +}; + +void useSA(A); void useSB(B); +void use_mut(int *borrow p); +void use_immut(const int *borrow p); +int get_by_mut(int *borrow p); + +void test1(A a) { + A b = a; // expected-note {{`*a.p` is borrowed here}} + // expected-note@-1 {{first mut borrow occurs here}} + // expected-note@-2 {{first mut borrow occurs here}} + useSA(a); // expected-error {{cannot use `a` because it was mutably borrowed}} + // expected-error@-1 {{cannot borrow `*a.p` as mutable more than once at a time}} + // expected-error@-2 {{cannot borrow `*a.q` as mutable more than once at a time}} + useSA(b); +} void test2(B b) { - A m = b.a; // expected-note {{previous borrow is here}} - useSB(b); // expected-error {{There should be at most one mutable borrow targeting to 'b' at the same time}} - useSA(m); + A m = b.a; // expected-note {{`*b.a.p` is borrowed here}} + // expected-note@-1 {{first mut borrow occurs here}} + // expected-note@-2 {{first mut borrow occurs here}} + useSB(b); // expected-error {{cannot use `b` because it was mutably borrowed}} + // expected-error@-1 {{cannot borrow `*b.a.p` as mutable more than once at a time}} + // expected-error@-2 {{cannot borrow `*b.a.q` as mutable more than once at a time}} + useSA(m); } void test3(B b) { - A m = b.a; // expected-note {{previous borrow is here}} - useSA(b.a); // expected-error {{There should be at most one mutable borrow targeting to 'b' at the same time}} - useSA(m); + A m = b.a; // expected-note {{`*b.a.p` is borrowed here}} + // expected-note@-1 {{first mut borrow occurs here}} + // expected-note@-2 {{first mut borrow occurs here}} + useSA(b.a); // expected-error {{cannot use `b.a` because it was mutably borrowed}} + // expected-error@-1 {{cannot borrow `*b.a.p` as mutable more than once at a time}} + // expected-error@-2 {{cannot borrow `*b.a.q` as mutable more than once at a time}} + useSA(m); } -void use_mut(int *borrow p); -void use_immut(const int *borrow p); - -struct V { - int a; -}; - -struct M { - int *borrow ma; - const int *borrow na; -}; - void test4() { - struct V v = { .a = 5 }; - struct V * borrow p = &mut v; - int *borrow q1 = &mut p->a; - const int *borrow q2 = &const p->a; - struct M m = { .ma = q1, .na = q2 }; // expected-note {{previous borrow is here}} - use_mut(m.ma); - use_immut(m.na); // expected-error {{Can not use 'm.na' because expired}} + struct V v = { .a = 5 }; + struct V *borrow p = &mut v; + int *borrow q1 = &mut p->a; // expected-note {{mutable borrow occurs here}} + const int *borrow q2 = &const p->a; // expected-error {{cannot borrow `(*p).a` as immutable because it is also borrowed as mutable}} + struct M m = { .ma = q1, .na = q2 }; + use_mut(m.ma); + use_immut(m.na); - // reassign - struct V v1 = { .a = 5 }; - p = &mut v1; - m.ma = &mut p->a; - m.na = &const p->a; - use_mut(m.ma); // expected-error {{There should be at most one mutable borrow targeting to 'v1.a' at the same time}} - use_immut(m.na); + struct V v1 = { .a = 5 }; + p = &mut v1; + m.ma = &mut p->a; // expected-note {{mutable borrow occurs here}} + m.na = &const p->a; // expected-error {{cannot borrow `(*p).a` as immutable because it is also borrowed as mutable}} + use_mut(m.ma); + use_immut(m.na); } void test5() { - struct V v = { .a = 5 }; - struct V * borrow p = &mut v; - int *borrow q1 = &mut p->a; - const int *borrow q2 = &const p->a; - struct M m = { .ma = q1, .na = q2 }; // expected-note {{previous borrow is here}} - use_mut(m.ma); - use_immut(m.na); // expected-error {{Can not use 'm.na' because expired}} + struct V v = { .a = 5 }; + struct V *borrow p = &mut v; + int *borrow q1 = &mut p->a; // expected-note {{mutable borrow occurs here}} + const int *borrow q2 = &const p->a; // expected-error {{cannot borrow `(*p).a` as immutable because it is also borrowed as mutable}} + struct M m = { .ma = q1, .na = q2 }; + use_mut(m.ma); + use_immut(m.na); - // reassign - struct V v1 = { .a = 5 }; - p = &mut v1; - m.na = &const p->a; - m.ma = &mut p->a; - use_immut(m.na); // expected-error {{There should be at most one mutable borrow targeting to 'v1.a' at the same time}} - use_mut(m.ma); + struct V v1 = { .a = 5 }; + p = &mut v1; + m.na = &const p->a; // expected-note {{immutable borrow occurs here}} + m.ma = &mut p->a; // expected-error {{cannot borrow `(*p).a` as mutable because it is also borrowed as immutable}} + use_immut(m.na); + use_mut(m.ma); } void test6() { int a = 0; int b = 0; struct M m = { &mut a, &const b }; - struct M * mp = &m; + struct M *mp = &m; int *borrow p1 = mp->ma; - int *borrow p2 = mp->ma; // expected-note {{previous borrow is here}} - use_mut(p1); // expected-error {{There should be at most one mutable borrow targeting to 'mp.ma' at the same time}} + int *borrow p2 = mp->ma; + use_mut(p1); use_mut(p2); - struct M ** mpp = ∓ + struct M **mpp = ∓ int *borrow p3 = (*mpp)->ma; - int *borrow p4 = (*mpp)->ma; // expected-note {{previous borrow is here}} - use_mut(p3); // expected-error {{There should be at most one mutable borrow targeting to 'mpp.ma' at the same time}} - use_mut(p4); + int *borrow p4 = (*mpp)->ma; + use_mut(p3); + use_mut(p4); +} + +void test7() { + int local = 2; + int *borrow p = &mut local; + int *borrow q = &mut *p; // expected-note {{`*p` is borrowed here}} + // expected-note@-1 {{first mut borrow occurs here}} + int y = get_by_mut(p) + 2; // expected-error {{cannot use `p` because it was mutably borrowed}} + // expected-error@-1 {{cannot borrow `*p` as mutable more than once at a time}} + use_mut(q); } \ No newline at end of file diff --git a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/borrow_var_assign/borrow_var_assign.cbs b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/borrow_var_assign/borrow_var_assign.cbs new file mode 100644 index 000000000000..bf3e3178e7cd --- /dev/null +++ b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/borrow_var_assign/borrow_var_assign.cbs @@ -0,0 +1,69 @@ +// RUN: %clang_cc1 -verify %s + +struct S { + int a; +}; + +void use_mut(int* borrow p); +void use_immut(const int* borrow p); +T* owned safe_malloc(T value); +void free_owned(T* owned p); +void free(void*); + +void test1() { + int local = 42; + int* borrow p = &mut local; // expected-note {{`local` is borrowed here}} + int* borrow q = p; + local = 43; // expected-error {{cannot assign to `local` because it is borrowed}} + use_mut(q); +} + +void test2() { + int* owned oriPtr = safe_malloc(0); + int* borrow p1 = &mut *oriPtr; // expected-note {{`*oriPtr` is borrowed here}} + int* borrow p2 = p1; + free_owned(oriPtr); // expected-error {{cannot move out of `oriPtr` because it is borrowed}} + use_mut(p2); +} + +void test3() { + int local = 42; + int* borrow p = &mut local; // expected-note {{first mut borrow occurs here}} + // expected-note@-1 {{`local` is borrowed here}} + int* borrow q = &mut local; // expected-error {{cannot borrow `local` as mutable more than once at a time}} + q = p; + local = 43; // expected-error {{cannot assign to `local` because it is borrowed}} + use_mut(q); +} + +void test4(int* oriPtr) { + int* borrow p1 = &mut *oriPtr; // expected-note {{first mut borrow occurs here}} + // expected-note@-1 {{`*oriPtr` is borrowed here}} + int* borrow p2 = &mut *oriPtr; // expected-error {{cannot borrow `*oriPtr` as mutable more than once at a time}} + free(oriPtr); // expected-error {{cannot use `oriPtr` because it was mutably borrowed}} + use_mut(p1); +} + +void test5() { + int local = 42; + const int* borrow p = &const local; // expected-note {{`local` is borrowed here}} + const int* borrow q = p; + local = 43; // expected-error {{cannot assign to `local` because it is borrowed}} + use_immut(q); +} + +void test6() { + int arr[5]; + int* borrow p = &mut arr[0]; // expected-note {{`arr` is borrowed here}} + int* borrow q = p; + int temp = arr[2]; // expected-error {{cannot use `arr` because it was mutably borrowed}} + use_mut(q); +} + +void test7() { + struct S s = { .a = 5 }; + struct S* borrow p1 = &mut s; // expected-note {{`s` is borrowed here}} + struct S* borrow p2 = p1; + struct S temp = s; // expected-error {{cannot use `s` because it was mutably borrowed}} + struct S s2 = *p2; +} \ No newline at end of file diff --git a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/borrow_var_expired/borrow_var_expired.cbs b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/borrow_var_expired/borrow_var_expired.cbs index 016f402082aa..122106a3b16b 100644 --- a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/borrow_var_expired/borrow_var_expired.cbs +++ b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/borrow_var_expired/borrow_var_expired.cbs @@ -1,147 +1,147 @@ -// RUN: %clang_cc1 -ast-dump -verify %s +// RUN: %clang_cc1 -verify %s -void borrow_expired_01() { - int a = 1; - int * borrow p1 = &mut a; - int * borrow p2 = &mut a; - int b = *p2; - int c = *p1; // expected-error {{Can not use 'p1' because expired}} -} - -void borrow_expired_02() { - int a = 1; - int * borrow p1 = &mut a; - const int * borrow p2 = &const a; - int b = *p2; - int c = *p1; // ok -} - -void borrow_expired_03() { - int a = 1; - const int * borrow p1 = &const a; - int * borrow p2 = &mut a; - int b = *p2; - int c = *p1; // expected-error {{Can not use 'p1' because expired}} -} - -void borrow_expired_04() { - int a = 1; - const int * borrow p1 = &const a; - const int * borrow p2 = &const a; - int b = *p2; - int c = *p1; // ok -} - -struct S -{ - int a; - int b; +struct S { + int a; + int b; }; -void borrow_expired_05() { - struct S s = {1, 1}; - int * borrow p1 = &mut s.a; - int * borrow p2 = &mut s.b; - int b = *p2; - int c = *p1; //ok -} - -void borrow_expired_06() { - struct S s = {1, 1}; - int * borrow p1 = &mut s.a; - const int * borrow p2 = &const s.b; - int b = *p2; - int c = *p1; // ok -} - -void borrow_expired_07() { - struct S s = {1, 1}; - const int * borrow p1 = &const s.a; - int * borrow p2 = &mut s.b; - int b = *p2; - int c = *p1; // ok -} - -void borrow_expired_08() { - struct S s = {1, 1}; - const int * borrow p1 = &const s.a; - const int * borrow p2 = &const s.b; - int b = *p2; - int c = *p1; // ok -} +void use_mut(int *borrow p); +void use_immut(const int *borrow p); +void use_S_mut(struct S *borrow a); +void use_S_immut(const struct S *borrow a); -void use_test01(struct S * borrow a){} -void use_test02(const struct S * borrow a){} - -void borrow_expired_09() { - struct S s = {1, 1}; - struct S * borrow p1 = &mut s; - int * borrow p2 = &mut s.b; - int b = *p2; - use_test01(p1); // expected-error {{Can not use 'p1' because expired}} +void test1() { + int a = 1; + int *borrow p1 = &mut a; // expected-note {{first mut borrow occurs here}} + int *borrow p2 = &mut a; // expected-error {{cannot borrow `a` as mutable more than once at a time}} + int b = *p2; + int c = *p1; } -void borrow_expired_10() { - struct S s = {1, 1}; - struct S * borrow p1 = &mut s; - const int * borrow p2 = &const s.b; - int b = *p2; - use_test01(p1); // ok +void test2() { + int a = 1; + int *borrow p1 = &mut a; // expected-note {{mutable borrow occurs here}} + const int *borrow p2 = &const a; // expected-error {{cannot borrow `a` as immutable because it is also borrowed as mutable}} + int b = *p2; + int c = *p1; } -void borrow_expired_11() { - struct S s = {1, 1}; - const struct S * borrow p1 = &const s; - int * borrow p2 = &mut s.b; - int b = *p2; - use_test02(p1); // expected-error {{Can not use 'p1' because expired}} +void test3() { + int a = 1; + const int *borrow p1 = &const a; // expected-note {{immutable borrow occurs here}} + int *borrow p2 = &mut a; // expected-error {{cannot borrow `a` as mutable because it is also borrowed as immutable}} + int b = *p2; + int c = *p1; } -void borrow_expired_12() { - struct S s = {1, 1}; - const struct S * borrow p1 = &const s; - const int * borrow p2 = &const s.b; - int b = *p2; // ok - use_test02(p1); +void test4() { + int a = 1; + const int *borrow p1 = &const a; + const int *borrow p2 = &const a; + int b = *p2; + int c = *p1; } -void borrow_expired_13() { - struct S s = {1, 1}; - int * borrow p1 = &mut s.b; - struct S * borrow p2 = &mut s; - use_test01(p2); - int b = *p1; // expected-error {{Can not use 'p1' because expired}} -} +void test5() { + struct S s = { 1, 1 }; + int *borrow p1 = &mut s.a; + int *borrow p2 = &mut s.b; + int b = *p2; + int c = *p1; +} + +void test6() { + struct S s = { 1, 1 }; + int *borrow p1 = &mut s.a; + const int *borrow p2 = &const s.b; + int b = *p2; + int c = *p1; +} + +void test7() { + struct S s = { 1, 1 }; + const int *borrow p1 = &const s.a; + int *borrow p2 = &mut s.b; + int b = *p2; + int c = *p1; +} + +void test8() { + struct S s = { 1, 1 }; + const int *borrow p1 = &const s.a; + const int *borrow p2 = &const s.b; + int b = *p2; + int c = *p1; +} + +void test9() { + struct S s = { 1, 1 }; + struct S *borrow p1 = &mut s; // expected-note {{first mut borrow occurs here}} + int *borrow p2 = &mut s.b; // expected-error {{cannot borrow `s.b` as mutable more than once at a time}} + int b = *p2; + use_S_mut(p1); +} + +void test10() { + struct S s = {1, 1}; + struct S *borrow p1 = &mut s; // expected-note {{mutable borrow occurs here}} + const int *borrow p2 = &const s.b; // expected-error {{cannot borrow `s.b` as immutable because it is also borrowed as mutable}} + int b = *p2; + use_S_mut(p1); +} + +void test11() { + struct S s = {1, 1}; + const struct S *borrow p1 = &const s; // expected-note {{immutable borrow occurs here}} + int *borrow p2 = &mut s.b; // expected-error {{cannot borrow `s.b` as mutable because it is also borrowed as immutable}} + int b = *p2; + use_S_immut(p1); +} + +void test12() { + struct S s = { 1, 1 }; + const struct S *borrow p1 = &const s; + const int *borrow p2 = &const s.b; + int b = *p2; + use_S_immut(p1); +} + +void test13() { + struct S s = { 1, 1 }; + int *borrow p1 = &mut s.b; // expected-note {{first mut borrow occurs here}} + struct S *borrow p2 = &mut s; // expected-error {{cannot borrow `s` as mutable more than once at a time}} + use_S_mut(p2); + int b = *p1; +} -void borrow_expired_14() { - struct S s = {1, 1}; - const int * borrow p1 = &const s.b; - struct S * borrow p2 = &mut s; - use_test01(p2); - int b = *p1; // expected-error {{Can not use 'p1' because expired}} -} +void test14() { + struct S s = { 1, 1 }; + const int *borrow p1 = &const s.b; // expected-note {{immutable borrow occurs here}} + struct S *borrow p2 = &mut s; // expected-error {{cannot borrow `s` as mutable because it is also borrowed as immutable}} + use_S_mut(p2); + int b = *p1; +} -void borrow_expired_15() { - struct S s = {1, 1}; - int * borrow p1 = &mut s.b; - const struct S * borrow p2 = &const s; - use_test02(p2); - int b = *p1; -} +void test15() { + struct S s = { 1, 1 }; + int *borrow p1 = &mut s.b; // expected-note {{mutable borrow occurs here}} + const struct S *borrow p2 = &const s; // expected-error {{cannot borrow `s` as immutable because it is also borrowed as mutable}} + use_S_immut(p2); + int b = *p1; +} -void borrow_expired_16() { - struct S s = {1, 1}; - const int * borrow p1 = &const s.b; - const struct S * borrow p2 = &const s; - use_test02(p2); - int b = *p1; // ok -} +void test16() { + struct S s = { 1, 1 }; + const int *borrow p1 = &const s.b; + const struct S *borrow p2 = &const s; + use_S_immut(p2); + int b = *p1; +} -void use_mut(int *borrow p) {} -void borrow_expired_17() { - int local = 5; - int *borrow p = &mut local; - int *borrow p1 = &mut *p; - use_mut(p1); - use_mut(&mut *p); // expected-error {{Can not use 'p' because expired}} +void test17() { + int local = 5; + int *borrow p = &mut local; + int *borrow p1 = &mut *p; + use_mut(p1); + use_mut(&mut *p); } \ No newline at end of file diff --git a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/borrow_with_complex_control_flow/borrow_with_complex_control_flow.cbs b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/borrow_with_complex_control_flow/borrow_with_complex_control_flow.cbs new file mode 100644 index 000000000000..1e5ff2523608 --- /dev/null +++ b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/borrow_with_complex_control_flow/borrow_with_complex_control_flow.cbs @@ -0,0 +1,21 @@ +// RUN: %clang_cc1 -verify %s + +void use(int *borrow); + +void test(int flag) { + int local = 3, a = 3, b = 4; + int *borrow p = &mut a; + int *borrow q = &mut b; + for (int i = 0; i < 3; ++i) { + if (flag) { + p = &mut local; // expected-error {{cannot borrow `local` as mutable more than once at a time}} + // expected-note@-1 {{first mut borrow occurs here}} + } else { + q = &mut local; // expected-error {{cannot borrow `local` as mutable more than once at a time}} + // expected-note@-1 {{first mut borrow occurs here}} + } + flag = ~flag; + } + use(p); + use(q); +} \ No newline at end of file diff --git a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/field_borrow_field/field_borrow_field.cbs b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/field_borrow_field/field_borrow_field.cbs index 141ca05dad61..63cc36093b5e 100644 --- a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/field_borrow_field/field_borrow_field.cbs +++ b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/field_borrow_field/field_borrow_field.cbs @@ -1,53 +1,53 @@ // RUN: %clang_cc1 -verify %s typedef struct R { - int a; - int b; + int a; + int b; } R; typedef struct S { - int * borrow p; - int * borrow q;; + int *borrow p; + int *borrow q; } S; -void use_mutI(int * borrow); -void use_constI(const int * borrow); +void use_mutI(int *borrow); +void use_constI(const int *borrow); void test1() { - R r = {.a = 1, .b = 2}; - S s = {.p = &mut r.a, .q = &mut r.b}; - use_mutI(s.p); - use_mutI(s.q); + R r = { .a = 1, .b = 2 }; + S s = { .p = &mut r.a, .q = &mut r.b }; + use_mutI(s.p); + use_mutI(s.q); } void test2() { - R r = {.a = 1, .b = 2}; - S s = {.p = &mut r.a, .q = &mut r.b}; - int * borrow pp = &mut r.a; // expected-note {{previous borrow is here}} - use_mutI(s.p); // expected-error {{There should be at most one mutable borrow targeting to 'r.a' at the same time}} - use_mutI(pp); + R r = { .a = 1, .b = 2 }; + S s = { .p = &mut r.a, .q = &mut r.b }; // expected-note {{first mut borrow occurs here}} + int *borrow pp = &mut r.a; // expected-error {{cannot borrow `r.a` as mutable more than once at a time}} + use_mutI(s.p); + use_mutI(pp); } void test3() { - R r = {.a = 1, .b = 2}; - S s = {.p = &mut r.a, .q = &mut r.b}; - int * borrow p1 = s.p; // expected-note {{previous borrow is here}} - use_mutI(s.p); // expected-error {{There should be at most one mutable borrow targeting to 'r.a' at the same time}} - int * borrow p2 = p1; + R r = { .a = 1, .b = 2 }; + S s = { .p = &mut r.a, .q = &mut r.b }; + int *borrow p1 = s.p; // expected-note {{`*s.p` is borrowed here}} + int temp = *s.p; // expected-error {{cannot use `*s.p` because it was mutably borrowed}} + int *borrow p2 = p1; } void test4() { - R r = {.a = 1, .b = 2}; - S s = {.p = &mut r.a, .q = &mut r.b}; // expected-note {{previous borrow is here}} - r.a = 5; // expected-error {{Can not modify 'r.a' because be borrowed}} - s.q = s.p; + R r = { .a = 1, .b = 2 }; + S s = { .p = &mut r.a, .q = &mut r.b }; // expected-note {{`r.a` is borrowed here}} + r.a = 5; // expected-error {{cannot assign to `r.a` because it is borrowed}} + s.q = s.p; } void test5() { - R r = {.a = 1, .b = 2}; - S s = {.p = &mut r.a, .q = &mut r.b}; // expected-note {{previous borrow is here}} expected-note {{previous borrow is here}} - r.a = 5; // expected-error {{Can not modify 'r.a' because be borrowed}} - s.q = s.p; - r.a = 5; // expected-error {{Can not modify 'r.a' because be borrowed}} - int *borrow p = s.q; + R r = { .a = 1, .b = 2 }; + S s = { .p = &mut r.a, .q = &mut r.b }; // expected-note {{`r.a` is borrowed here}} + r.a = 5; // expected-error {{cannot assign to `r.a` because it is borrowed}} + s.q = s.p; + r.a = 5; + int *borrow p = s.q; } \ No newline at end of file diff --git a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/field_borrow_var/field_borrow_var.cbs b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/field_borrow_var/field_borrow_var.cbs index 9aeb93889c8e..9383ec730039 100644 --- a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/field_borrow_var/field_borrow_var.cbs +++ b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/field_borrow_var/field_borrow_var.cbs @@ -1,38 +1,39 @@ // RUN: %clang_cc1 -verify %s typedef struct R { - int a; - int b; + int a; + int b; } R; typedef struct S { - R * borrow r; - int num; + R *borrow r; + int num; } S; -void use_mut(R * borrow); -void use_const(const R * borrow); +void use_mut(R *borrow); +void use_const(const R *borrow); void test1() { - R local_r = {.a = 1, .b = 2}; - S s = {.r = &mut local_r, .num = 3}; // expected-note {{previous borrow is here}} - const R * borrow const_r = &const local_r; - local_r.a = 4; // expected-error {{Can not modify 'local_r.a' because be borrowed}} - use_mut(s.r); + R local_r = { .a = 1, .b = 2 }; + S s = { .r = &mut local_r, .num = 3 }; // expected-note {{`local_r.a` is borrowed here}} + // expected-note@-1 {{mutable borrow occurs here}} + const R *borrow const_r = &const local_r; // expected-error {{cannot borrow `local_r` as immutable because it is also borrowed as mutable}} + local_r.a = 4; // expected-error {{cannot assign to `local_r.a` because it is borrowed}} + use_mut(s.r); } void test2() { - R local_r = {.a = 1, .b = 2}; - S s = {.r = &mut local_r, .num = 3}; - const R * borrow const_r = &const local_r; // expected-note {{previous borrow is here}} - local_r.a = 4; // expected-error {{Can not modify 'local_r.a' because be borrowed}} - use_const(const_r); + R local_r = { .a = 1, .b = 2 }; + S s = { .r = &mut local_r, .num = 3 }; + const R *borrow const_r = &const local_r; // expected-note {{`local_r.a` is borrowed here}} + local_r.a = 4; // expected-error {{cannot assign to `local_r.a` because it is borrowed}} + use_const(const_r); } void test3() { - R local_r = {.a = 1, .b = 2}; - S s = {.r = &mut local_r, .num = 3}; - const R * borrow const_r = &const local_r; - int y = local_r.a; - use_const(const_r); + R local_r = { .a = 1, .b = 2 }; + S s = { .r = &mut local_r, .num = 3 }; + const R *borrow const_r = &const local_r; + int y = local_r.a; + use_const(const_r); } \ No newline at end of file diff --git a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/kill_loans_by_write_to_path/kill_loans_by_write_to_path.cbs b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/kill_loans_by_write_to_path/kill_loans_by_write_to_path.cbs new file mode 100644 index 000000000000..4a71f8cba028 --- /dev/null +++ b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/kill_loans_by_write_to_path/kill_loans_by_write_to_path.cbs @@ -0,0 +1,23 @@ +// RUN: %clang_cc1 -verify %s + +struct S { + int a; +}; + +void use_mut(int* borrow p); +void use_immut(const int* borrow p); +void use_mut_s(struct S* borrow p); + +void test1() { + struct S s1 = { .a = 1 }; + struct S s2 = { .a = 2 }; + struct S* borrow sp = &mut s1; // expected-note {{`s1.a` is borrowed here}} + // expected-note@-1 {{`s1.a` is borrowed here}} + int* borrow p = &mut sp->a; + sp = &mut s2; + sp->a = 3; + s1.a = 2; // expected-error {{cannot assign to `s1.a` because it is borrowed}} + use_mut(p); + s1.a = 2; // expected-error {{cannot assign to `s1.a` because it is borrowed}} + use_mut_s(sp); +} \ No newline at end of file diff --git a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/member_function/member_function.cbs b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/member_function/member_function.cbs index 031ce34cda2d..69363fffb277 100644 --- a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/member_function/member_function.cbs +++ b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/member_function/member_function.cbs @@ -1,42 +1,38 @@ // RUN: %clang_cc1 -verify %s -void int::setA(int* this) { - *this = 2; -} +struct A { + int m; + int n; + int c; +}; -void use_mut(int * borrow); -void use_const(const int * borrow); +void int::setA(int *this); +void use_mut(int *borrow); +void use_const(const int *borrow); +void use_mut_A(struct A *borrow); -void test1() { - int a = 1; - int * borrow p = &mut a; // expected-note {{previous borrow is here}} - a.setA(); // expected-error {{Can not modify 'a' because be borrowed}} - use_mut(p); +void struct A::use(struct A *this) { + this->m = 2; + this->n = 3; } - -void test2() { - int a = 1; - const int * borrow p = &const a; // expected-note {{previous borrow is here}} - a.setA(); // expected-error {{Can not modify 'a' because be borrowed}} - use_const(p); +void test1() { + int a = 1; + int *borrow p = &mut a; // expected-note {{`a` is borrowed here}} + a.setA(); // expected-error {{cannot use `a` because it was mutably borrowed}} + use_mut(p); } -struct A { - int m; - int n; - int c; -}; - -void struct A::use(struct A* this) { - this->m = 2; - this->n = 3; +// FIXME: not proper now +void test2() { + int a = 1; + const int *borrow p = &const a; + a.setA(); + use_const(p); } -void use_mut_A(struct A * borrow); - void test3(struct A a) { - struct A * borrow p = &mut a; // expected-note {{previous borrow is here}} - a.use(); // expected-error {{Can not modify 'a' because be borrowed}} - use_mut_A(p); + struct A *borrow p = &mut a; // expected-note {{`a` is borrowed here}} + a.use(); // expected-error {{cannot use `a` because it was mutably borrowed}} + use_mut_A(p); } \ No newline at end of file diff --git a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/nested_call_and_member/nested_call_and_member.cbs b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/nested_call_and_member/nested_call_and_member.cbs index 524bdf00f2b2..21e9bf0d960d 100644 --- a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/nested_call_and_member/nested_call_and_member.cbs +++ b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/nested_call_and_member/nested_call_and_member.cbs @@ -12,28 +12,30 @@ public: owned struct OptionRefMut { public: - ListNode* borrow inner; + ListNode *borrow inner; }; -OptionRefMut socket_table_find(int* borrow); -ListNode* borrow OptionRefMut::unwrap(OptionRefMut* borrow this); -ListNode* owned AllocSocket(); -void accept(int, struct sockaddr *, int *); -void SkAcceptInfoSet(const v5_adpt_socket_t* borrow, v5_adpt_socket_t* borrow); +OptionRefMut socket_table_find(int *borrow); +ListNode *borrow OptionRefMut::unwrap(OptionRefMut *borrow this); +ListNode *owned AllocSocket(); +void accept(int, struct sockaddr *, int *); +void SkAcceptInfoSet(const v5_adpt_socket_t *borrow, v5_adpt_socket_t *borrow); void CloseSocket(v5_adpt_socket_t *); -void use(ListNode* borrow); +void use(ListNode *borrow); -void v5_accept(int fd, void* addr, int* addrLen, int* borrow p) { +void v5_accept(int fd, void *addr, int *addrLen, int *borrow p) { OptionRefMut o = socket_table_find(p); - const v5_adpt_socket_t* borrow ls = &const o.unwrap()->inner; // expected-note {{previous borrow is here}} - use(o.inner); // expected-error {{There should be at most one mutable borrow targeting to 'p' at the same time}} - ListNode* owned t = AllocSocket(); + const v5_adpt_socket_t *borrow ls = &const o.unwrap()->inner; // expected-note {{`o` is borrowed here}} + // expected-note@-1 {{first mut borrow occurs here}} + use(o.inner); // expected-error {{cannot use `o.inner` because it was mutably borrowed}} + // expected-error@-1 {{cannot borrow `*o.inner` as mutable more than once at a time}} + ListNode *owned t = AllocSocket(); { - v5_adpt_socket_t* borrow s = &mut t->inner; + v5_adpt_socket_t *borrow s = &mut t->inner; accept(fd, (struct sockaddr *)addr, addrLen); SkAcceptInfoSet(ls, s); } - v5_adpt_socket_t* borrow s = &mut t->inner; - ListNode* t1 = (ListNode *)t; - CloseSocket((v5_adpt_socket_t*)s); + v5_adpt_socket_t *borrow s = &mut t->inner; // expected-note {{`(*t).inner` is borrowed here}} + ListNode *t1 = (ListNode *)t; // expected-error {{cannot move out of `t` because it is borrowed}} + CloseSocket((v5_adpt_socket_t *)s); } diff --git a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/return_borrow/return_borrow.cbs b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/return_borrow/return_borrow.cbs index e688d6b91de3..b99876c87afe 100644 --- a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/return_borrow/return_borrow.cbs +++ b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/return_borrow/return_borrow.cbs @@ -1,60 +1,83 @@ // RUN: %clang_cc1 -verify %s -T* owned safe_malloc(T value); -void free_owned(T* owned p); -void free(void*); +struct S { + int *borrow p; +}; + +struct G { + struct S s; +}; + +struct P { + int *b; +}; + +struct Q { + struct P g; +}; + +T *owned safe_malloc(T value); +void free_owned(T *owned p); +void free(void *); int *borrow test1(int *borrow p) { int local = 42; - return &mut local; //expected-error{{Return value cannot be a borrow from local variable 'local'}} + return &mut local; // expected-error {{`local` does not live long enough}} + // expected-note@-1 {{`local` dropped here while still borrowed}} } int *borrow test2(int *borrow p) { int local = 42; - p = &mut local; - return p; //expected-error{{Return value cannot be a borrow from local variable 'local'}} + p = &mut local; // expected-error {{`local` does not live long enough}} + return p; // expected-note {{`local` dropped here while still borrowed}} } +// FIXME: parameters lakes CFGScopeEnd int *borrow test3(int *borrow p, int a) { - return &mut a; //expected-error{{Return value cannot be a borrow from local variable 'a'}} + return &mut a; // expected-error {{`a` does not live long enough}} + // expected-note@-1 {{`a` dropped here while still borrowed}} } +// FIXME: parameters lakes CFGScopeEnd int *borrow test4(int *borrow p, int a) { - p = &mut a; - return p; //expected-error{{Return value cannot be a borrow from local variable 'a'}} + p = &mut a; // expected-error {{`a` does not live long enough}} + return p; // expected-note {{`a` dropped here while still borrowed}} } int *borrow test5(int *borrow p) { int local = 5; - p = &mut local; - int* borrow p1 = &mut* p; - return p1; //expected-error{{Return value cannot be a borrow from local variable 'local'}} + p = &mut local; // expected-error {{`local` does not live long enough}} + int* borrow p1 = &mut *p; + return p1; // expected-note {{`local` dropped here while still borrowed}} } int *borrow test6(int *borrow p) { int local = 5; - p = &mut local; - return &mut* p; //expected-error{{Return value cannot be a borrow from local variable 'local'}} + p = &mut local; // expected-error {{`local` does not live long enough}} + return &mut *p; // expected-note {{`local` dropped here while still borrowed}} } -int* borrow test7(int* borrow p) { // expected-note {{previous borrow is here}} +int *borrow test7(int *borrow p) { int *owned oriPtr = safe_malloc(0); - p = &mut * oriPtr; - free_owned(oriPtr); // expected-error {{Can not modify 'oriPtr' because be borrowed}} - return p; // expected-error {{Return value cannot be a borrow from local variable 'oriPtr'}} + p = &mut *oriPtr; // expected-error {{`*oriPtr` does not live long enough}} + // expected-note@-1 {{`*oriPtr` is borrowed here}} + free_owned(oriPtr); // expected-error {{cannot move out of `oriPtr` because it is borrowed}} + return p; // expected-note {{`*oriPtr` dropped here while still borrowed}} } -int* borrow test8(int *owned oriPtr, int* borrow p) { // expected-note {{previous borrow is here}} - p = &mut * oriPtr; - free_owned(oriPtr); // expected-error {{Can not modify 'oriPtr' because be borrowed}} - return p; // expected-error {{Return value cannot be a borrow from local variable 'oriPtr'}} +// FIXME: parameters lakes CFGScopeEnd +int *borrow test8(int *owned oriPtr, int *borrow p) { + p = &mut *oriPtr; // expected-note {{`*oriPtr` is borrowed here}} + // expected-error@-1 {{`*oriPtr` does not live long enough}} + free_owned(oriPtr); // expected-error {{cannot move out of `oriPtr` because it is borrowed}} + return p; // expected-note {{`*oriPtr` dropped here while still borrowed}} } int *borrow test9(int *borrow p) { int local = 42; if (local > 0) { - p = &mut local; - return &mut *p; //expected-error{{Return value cannot be a borrow from local variable 'local'}} + p = &mut local; // expected-error {{`local` does not live long enough}} + return &mut *p; // expected-note {{`local` dropped here while still borrowed}} } else return p; } @@ -62,10 +85,11 @@ int *borrow test9(int *borrow p) { int *borrow test10(int *borrow p) { int local = 42; if (local > 0) - return &mut local; //expected-error{{Return value cannot be a borrow from local variable 'local'}} + return &mut local; // expected-error {{`local` does not live long enough}} + // expected-note@-1 {{`local` dropped here while still borrowed}} else { - p = &mut local; - return p; //expected-error{{Return value cannot be a borrow from local variable 'local'}} + p = &mut local; // expected-error {{`local` does not live long enough}} + return p; // expected-note {{`local` dropped here while still borrowed}} } } @@ -74,78 +98,70 @@ int *borrow test11(int *borrow p) { if (local > 0) { int local2 = 42; if (local2 > 0) - return &mut local2; //expected-error{{Return value cannot be a borrow from local variable 'local2'}} + return &mut local2; // expected-error {{`local2` does not live long enough}} + // expected-note@-1 {{`local2` dropped here while still borrowed}} else return p; } else { int local3 = 42; if (local3 > 0) - return &mut local3; //expected-error{{Return value cannot be a borrow from local variable 'local3'}} + return &mut local3; // expected-error {{`local3` does not live long enough}} + // expected-note@-1 {{`local3` dropped here while still borrowed}} else return p; } } -int* borrow test12(int* borrow p) { +int *borrow test12(int *borrow p) { int local = 42; int *owned oriPtr = safe_malloc(0); if (local > 0) { free_owned(oriPtr); - return &mut * oriPtr; // expected-error {{use of moved value: `oriPtr`}} + return &mut *oriPtr; // expected-error {{use of moved value: `oriPtr`}} } free_owned(oriPtr); return p; } -int* borrow test13(int* borrow p) { +int *borrow test13(int *borrow p) { int *owned oriPtr = safe_malloc(0); if (1) { free_owned(oriPtr); - return &mut * oriPtr; // expected-error {{use of moved value: `oriPtr`}} + return &mut *oriPtr; // expected-error {{use of moved value: `oriPtr`}} } - return &mut * oriPtr; //Dead Code + return &mut *oriPtr; } -struct S { int * borrow p; }; struct S test14(struct S s) { int local = 5; - s.p = &mut local; - return s; //expected-error{{Return value cannot be a borrow from local variable 'local'}} + s.p = &mut local; // expected-error {{`local` does not live long enough}} + return s; // expected-note {{`local` dropped here while still borrowed}} } -struct G { struct S s; }; struct S test15(struct G g) { int local = 5; - g.s.p = &mut local; - return g.s; //expected-error{{Return value cannot be a borrow from local variable 'local'}} + g.s.p = &mut local; // expected-error {{`local` does not live long enough}} + return g.s; // expected-note {{`local` dropped here while still borrowed}} } struct G test16(struct G g) { int local = 5; - g.s.p = &mut local; - return g; //expected-error{{Return value cannot be a borrow from local variable 'local'}} + g.s.p = &mut local; // expected-error {{`local` does not live long enough}} + return g; // expected-note {{`local` dropped here while still borrowed}} } -int main() { - int local = 5; - int *owned oriPtr = safe_malloc(0); - test1(&mut local); - test2(&mut local); - test3(&mut local, 5); - test4(&mut local, 5); - test5(&mut local); - test6(&mut local); - test7(&mut local); - test8(oriPtr, &mut local); - test9(&mut local); - test10(&mut local); - test11(&mut local); - test12(&mut local); - test13(&mut local); - struct S s = { .p = &mut local }; - struct G g = { .s = s }; // expected-note {{previous borrow is here}} - test14(s); // expected-error {{There should be at most one mutable borrow targeting to 'local' at the same time}} - test15(g); - test16(g); - return 0; +int *borrow test17(int *borrow p, int *borrow q) { + int local = 42; + p = &mut local; // expected-error {{`local` does not live long enough}} + int a = 3; + q = &mut a; // expected-error {{`a` does not live long enough}} + return p; // expected-note {{`a` dropped here while still borrowed}} + // expected-note@-1 {{`local` dropped here while still borrowed}} +} + +int *borrow test18(int *borrow p) { + struct Q q; + struct Q *borrow q1 = &mut q; // expected-error {{`q` does not live long enough}} + int *borrow r = &mut *q1->g.b; + return r; // expected-note {{`q` dropped here while still borrowed}} } \ No newline at end of file diff --git a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/return_borrow_loop/return_borrow_loop.cbs b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/return_borrow_loop/return_borrow_loop.cbs index b5cbd07e7a9d..87d598b4439a 100644 --- a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/return_borrow_loop/return_borrow_loop.cbs +++ b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/return_borrow_loop/return_borrow_loop.cbs @@ -1,79 +1,81 @@ // RUN: %clang_cc1 -verify %s + +struct S { + int *borrow p; +}; + +struct G { + struct S s; +}; + int rand(); -T* owned safe_malloc(T value); -void free_owned(T* owned p); -void free(void*); +T *owned safe_malloc(T value); +void free_owned(T *owned p); +void free(void *); int *borrow test1(int *borrow p) { int local = 42; while (rand()) - return &mut local; //expected-error{{Return value cannot be a borrow from local variable 'local'}} - return &mut local; //expected-error{{Return value cannot be a borrow from local variable 'local'}} + return &mut local; // expected-error {{`local` does not live long enough}} + // expected-note@-1 {{`local` dropped here while still borrowed}} + return &mut local; // expected-error {{`local` does not live long enough}} + // expected-note@-1 {{`local` dropped here while still borrowed}} } int *borrow test2(int *borrow p) { int local = 42; while (0) - return &mut local; - return &mut local; //expected-error{{Return value cannot be a borrow from local variable 'local'}} + return &mut local; // expected-error {{`local` does not live long enough}} + // expected-note@-1 {{`local` dropped here while still borrowed}} + return &mut local; // expected-error {{`local` does not live long enough}} + // expected-note@-1 {{`local` dropped here while still borrowed}} } int *borrow test3(int *borrow p) { int local = 42; while (rand()) - p = &mut local; - return p; //expected-error{{Return value cannot be a borrow from local variable 'local'}} + p = &mut local; // expected-error {{`local` does not live long enough}} + return p; // expected-note {{`local` dropped here while still borrowed}} } -int *borrow test4(int *borrow p) { //expected-error{{borrow pointer variable 'p' lives longer than target variable}} +int *borrow test4(int *borrow p) { while (rand()) { for (int i = 0; i < 5; i++) { if (rand()) { int local1 = 5; - p = &mut local1; - } else { + p = &mut local1; // expected-error {{`local1` does not live long enough}} + } else { // expected-note {{`local1` dropped here while still borrowed}} int local2 = 5; - p = &mut local2; - } + p = &mut local2; // expected-error {{`local2` does not live long enough}} + } // expected-note {{`local2` dropped here while still borrowed}} } } - return p; /* expected-error{{Return value cannot be a borrow from local variable 'local1'}} - expected-error{{Return value cannot be a borrow from local variable 'local2'}} */ + return p; } -struct S { int * borrow p; }; struct S test5(struct S s) { int local = 5; while (rand()) - s.p = &mut local; - return s; //expected-error{{Return value cannot be a borrow from local variable 'local'}} + s.p = &mut local; // expected-error {{`local` does not live long enough}} + // expected-error@-1 {{cannot borrow `local` as mutable more than once at a time}} + // expected-note@-2 {{first mut borrow occurs here}} + return s; // expected-note {{`local` dropped here while still borrowed}} } -struct G { struct S s; }; struct S test6(struct G g) { int local = 5; while (rand()) - g.s.p = &mut local; - return g.s; //expected-error{{Return value cannot be a borrow from local variable 'local'}} + g.s.p = &mut local; // expected-error {{`local` does not live long enough}} + // expected-error@-1 {{cannot borrow `local` as mutable more than once at a time}} + // expected-note@-2 {{first mut borrow occurs here}} + return g.s; // expected-note {{`local` dropped here while still borrowed}} } struct G test7(struct G g) { int local = 5; while (rand()) - g.s.p = &mut local; - return g; //expected-error{{Return value cannot be a borrow from local variable 'local'}} -} - -int main() { - int local = 5; - test1(&mut local); - test2(&mut local); - test3(&mut local); - test4(&mut local); - struct S s = { .p = &mut local }; - struct G g = { .s = s }; // expected-note {{previous borrow is here}} - test5(s); // expected-error {{There should be at most one mutable borrow targeting to 'local' at the same time}} - test6(g); - test7(g); - return 0; + g.s.p = &mut local; // expected-error {{`local` does not live long enough}} + // expected-error@-1 {{cannot borrow `local` as mutable more than once at a time}} + // expected-note@-2 {{first mut borrow occurs here}} + return g; // expected-note {{`local` dropped here while still borrowed}} } \ No newline at end of file diff --git a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/struct_has_own_struct_type_as_field/struct_has_own_struct_type_as_field.cbs b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/struct_has_own_struct_type_as_field/struct_has_own_struct_type_as_field.cbs index 0d6ae04d905f..675377a104b2 100644 --- a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/struct_has_own_struct_type_as_field/struct_has_own_struct_type_as_field.cbs +++ b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/struct_has_own_struct_type_as_field/struct_has_own_struct_type_as_field.cbs @@ -2,76 +2,86 @@ owned struct G; owned struct G { - public: - G* g; + public: + G *g; }; -void use_G_mut(G * borrow g); -void use_G_const(const G * borrow g); - -void test1(G g1) { - G g = { .g = &g1 }; - G *borrow p1 = &mut g; - const G *borrow p2 = &const g; - G *borrow p3 = &mut *g.g; - const G *borrow p4 = &const *g.g; - G *borrow p5 = &mut *g.g->g; // expected-note {{previous borrow is here}} expected-note {{previous borrow is here}} - const G *borrow p6 = &const *g.g->g; // expected-note {{previous borrow is here}} expected-note {{previous borrow is here}} expected-note {{previous borrow is here}} - use_G_mut(p1); // expected-error {{There should be at most one mutable borrow targeting to 'g' at the same time}} - use_G_const(p2); // expected-error {{There should be at most one mutable borrow targeting to 'g' at the same time}} - use_G_mut(p3); // expected-error {{There should be at most one mutable borrow targeting to 'g.g.g' at the same time}} - use_G_const(p4);// expected-error {{There should be at most one mutable borrow targeting to 'g.g.g' at the same time}} - use_G_mut(p5); // expected-error {{There should be at most one mutable borrow targeting to 'g.g.g' at the same time}} - use_G_const(p6); -} - -void test2(G g1) { - G g = { .g = &g1 }; - G *borrow p1 = &mut g; - G *borrow p2 = &mut *g.g; - G *borrow p3 = &mut *g.g->g; // expected-note {{previous borrow is here}} expected-note {{previous borrow is here}} - use_G_mut(p1); // expected-error {{There should be at most one mutable borrow targeting to 'g' at the same time}} - use_G_mut(p2); // expected-error {{There should be at most one mutable borrow targeting to 'g.g.g' at the same time}} - use_G_mut(p3); -} - owned struct K; owned struct S { - public: - K* k; + public: + K *k; }; + owned struct K { - public: - S* s; + public: + S *s; }; -void use_S_mut(S * borrow s); -void use_S_const(const S * borrow s); -void use_K_mut(K * borrow k); -void use_K_const(const K * borrow k); +void use_G_mut(G *borrow g); +void use_G_const(const G *borrow g); +void use_S_mut(S *borrow s); +void use_S_const(const S *borrow s); +void use_K_mut(K *borrow k); +void use_K_const(const K *borrow k); + +void test1(G g1) { + G g = { .g = &g1 }; + G *borrow p1 = &mut g; // expected-note {{mutable borrow occurs here}} + // expected-note@-1 {{first mut borrow occurs here}} + // expected-note@-2 {{mutable borrow occurs here}} + // expected-note@-3 {{first mut borrow occurs here}} + // expected-note@-4 {{mutable borrow occurs here}} + const G *borrow p2 = &const g; // expected-error {{cannot borrow `g` as immutable because it is also borrowed as mutable}} + G *borrow p3 = &mut *g.g; // expected-error {{cannot borrow `*g.g` as mutable more than once at a time}} + const G *borrow p4 = &const *g.g; // expected-error {{cannot borrow `*g.g` as immutable because it is also borrowed as mutable}} + G *borrow p5 = &mut *g.g->g; // expected-error {{cannot borrow `*(*g.g).g` as mutable more than once at a time}} + const G *borrow p6 = &const *g.g->g; // expected-error {{cannot borrow `*(*g.g).g` as immutable because it is also borrowed as mutable}} + use_G_mut(p1); + use_G_const(p2); + use_G_mut(p3); + use_G_const(p4); + use_G_mut(p5); + use_G_const(p6); +} + +void test2(G g1) { + G g = { .g = &g1 }; + G *borrow p1 = &mut g; // expected-note {{first mut borrow occurs here}} + // expected-note@-1 {{first mut borrow occurs here}} + G *borrow p2 = &mut *g.g; // expected-error {{cannot borrow `*g.g` as mutable more than once at a time}} + G *borrow p3 = &mut *g.g->g; // expected-error {{cannot borrow `*(*g.g).g` as mutable more than once at a time}} + use_G_mut(p1); + use_G_mut(p2); + use_G_mut(p3); +} void test3(S s1) { - K k = { .s = &s1 }; - K *borrow p1 = &mut k; - const K *borrow p2 = &const k; - S *borrow p3 = &mut *k.s; - const S *borrow p4 = &const *k.s; - K *borrow p5 = &mut *k.s->k;// expected-note {{previous borrow is here}} expected-note {{previous borrow is here}} - const K *borrow p6 = &const *k.s->k;// expected-note {{previous borrow is here}} expected-note {{previous borrow is here}} expected-note {{previous borrow is here}} - use_K_mut(p1); // expected-error {{There should be at most one mutable borrow targeting to 'k' at the same time}} - use_K_const(p2);// expected-error {{There should be at most one mutable borrow targeting to 'k' at the same time}} - use_S_mut(p3); // expected-error {{There should be at most one mutable borrow targeting to 'k.s.k' at the same time}} - use_S_const(p4);// expected-error {{There should be at most one mutable borrow targeting to 'k.s.k' at the same time}} - use_K_mut(p5); // expected-error {{There should be at most one mutable borrow targeting to 'k.s.k' at the same time}} - use_K_const(p6); + K k = { .s = &s1 }; + K *borrow p1 = &mut k; // expected-note {{mutable borrow occurs here}} + // expected-note@-1 {{first mut borrow occurs here}} + // expected-note@-2 {{mutable borrow occurs here}} + // expected-note@-3 {{first mut borrow occurs here}} + // expected-note@-4 {{mutable borrow occurs here}} + const K *borrow p2 = &const k; // expected-error {{cannot borrow `k` as immutable because it is also borrowed as mutable}} + S *borrow p3 = &mut *k.s; // expected-error {{cannot borrow `*k.s` as mutable more than once at a time}} + const S *borrow p4 = &const *k.s; // expected-error {{cannot borrow `*k.s` as immutable because it is also borrowed as mutable}} + K *borrow p5 = &mut *k.s->k; // expected-error {{cannot borrow `*(*k.s).k` as mutable more than once at a time}} + const K *borrow p6 = &const *k.s->k; // expected-error {{cannot borrow `*(*k.s).k` as immutable because it is also borrowed as mutable}} + use_K_mut(p1); + use_K_const(p2); + use_S_mut(p3); + use_S_const(p4); + use_K_mut(p5); + use_K_const(p6); } void test4(S s1) { - K k = { .s = &s1 }; - K *borrow p1 = &mut k; - S *borrow p2 = &mut *k.s; - K *borrow p3 = &mut *k.s->k;// expected-note {{previous borrow is here}} expected-note {{previous borrow is here}} - use_K_mut(p1); // expected-error {{There should be at most one mutable borrow targeting to 'k' at the same time}} - use_S_mut(p2); // expected-error {{There should be at most one mutable borrow targeting to 'k.s.k' at the same time}} - use_K_mut(p3); + K k = { .s = &s1 }; + K *borrow p1 = &mut k; // expected-note {{first mut borrow occurs here}} + // expected-note@-1 {{first mut borrow occurs here}} + S *borrow p2 = &mut *k.s; // expected-error {{cannot borrow `*k.s` as mutable more than once at a time}} + K *borrow p3 = &mut *k.s->k; // expected-error {{cannot borrow `*(*k.s).k` as mutable more than once at a time}} + use_K_mut(p1); + use_S_mut(p2); + use_K_mut(p3); } \ No newline at end of file diff --git a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/struct_with_borrow_fields/struct_with_borrow_fields.cbs b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/struct_with_borrow_fields/struct_with_borrow_fields.cbs new file mode 100644 index 000000000000..9b9d001e75a0 --- /dev/null +++ b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/struct_with_borrow_fields/struct_with_borrow_fields.cbs @@ -0,0 +1,181 @@ +// RUN: %clang_cc1 -verify %s + +typedef struct A { + int *borrow p; +} A; + +typedef struct B { + int m; + int n; +} B; + +typedef struct C { + B b; + int *borrow p; +} C; + +typedef struct D { + B b; + int *borrow p; + int *borrow q; +} D; + +typedef struct E { + C c; + int x; + int *borrow p; +} E; + +void use_mut(int *borrow); +void useE(E); +void useC(C); + +void test1() { + int local = 42; + A a = { .p = &mut local }; // expected-note {{`local` is borrowed here}} + local = 43; // expected-error {{cannot assign to `local` because it is borrowed}} + use_mut(a.p); +} + +void test2(A a) { + int local = 42; + a.p = &mut local; // expected-note {{`local` is borrowed here}} + local = 43; // expected-error {{cannot assign to `local` because it is borrowed}} + int x = *a.p; +} + +void test3() { + int local = 42; + C c = { .p = &mut local }; // expected-note {{`local` is borrowed here}} + local = 43; // expected-error {{cannot assign to `local` because it is borrowed}} + int temp = c.b.m; +} + +void test4(C c) { + int local = 42; + c = (struct C) { .p = &mut local }; // expected-note {{`local` is borrowed here}} + local = 43; // expected-error {{cannot assign to `local` because it is borrowed}} + int temp = c.b.m; +} + +void test5() { + int local = 42; + D d = { .p = &mut local, .q = &mut local }; // expected-error {{cannot borrow `local` as mutable more than once at a time}} + // expected-note@-1 {{`local` is borrowed here}} + // expected-note@-2 {{first mut borrow occurs here}} + local = 43; // expected-error {{cannot assign to `local` because it is borrowed}} + int temp = d.b.m; +} + +void test6() { + int local = 42; + E e = { .c = (struct C) { .p = &mut local }, .p = &mut local }; // expected-error {{cannot borrow `local` as mutable more than once at a time}} + // expected-note@-1 {{`local` is borrowed here}} + // expected-note@-2 {{first mut borrow occurs here}} + local = 43; // expected-error {{cannot assign to `local` because it is borrowed}} + int temp = e.c.b.m; +} + +void test7(A a) { + { + int local = 42; + a = (struct A) { .p = &mut local }; // expected-error {{`local` does not live long enough}} + } // expected-note {{`local` dropped here while still borrowed}} + use_mut(a.p); +} + +void test8(A a) { + { + int local = 42; + a = (struct A) { .p = &mut local }; // expected-note {{`local` is borrowed here}} + local = 43; // expected-error {{cannot assign to `local` because it is borrowed}} + } + use_mut(a.p); +} + +void test9(A a1) { + int local = 42; + A a2 = { .p = &mut local }; // expected-note {{`local` is borrowed here}} + a1 = a2; + local = 43; // expected-error {{cannot assign to `local` because it is borrowed}} + use_mut(a1.p); +} + +void test10() { + int local = 42; + A a1 = { .p = &mut local }; // expected-note {{`local` is borrowed here}} + A a2 = a1; + local = 43; // expected-error {{cannot assign to `local` because it is borrowed}} + use_mut(a2.p); +} + +void test11() { + int local = 42; + C c = { .p = &mut local }; + int *borrow q = c.p; // expected-note {{`*c.p` is borrowed here}} + int temp = *c.p; // expected-error {{cannot use `*c.p` because it was mutably borrowed}} + use_mut(q); +} + +void test12() { + int local = 42; + C c1 = { .p = &mut local }; + C c2 = c1; // expected-note {{`*c1.p` is borrowed here}} + int temp = *c1.p; // expected-error {{cannot use `*c1.p` because it was mutably borrowed}} + use_mut(c2.p); +} + +void test13() { + int local1 = 42; + int local2 = 42; + E e1 = { .c = (struct C) { .p = &mut local1 }, .p = &mut local2 }; + E e2 = e1; // expected-note {{`*e1.c.p` is borrowed here}} + // expected-note@-1 {{first mut borrow occurs here}} + // expected-note@-2 {{first mut borrow occurs here}} + E temp = e1; // expected-error {{cannot use `e1` because it was mutably borrowed}} + // expected-error@-1 {{cannot borrow `*e1.c.p` as mutable more than once at a time}} + // expected-error@-2 {{cannot borrow `*e1.p` as mutable more than once at a time}} + useE(e2); +} + +void test14() { + int local1 = 42; + int local2 = 42; + E e1 = { .c = (struct C) { .p = &mut local1 }, .p = &mut local2 }; + C c = e1.c; + useE(e1); +} + +void test15() { + int local1 = 42; + int local2 = 42; + int local3 = 42; + E e1 = { .c = (struct C) { .p = &mut local1 }, .p = &mut local2 }; + C c = e1.c; + e1.c = (struct C) { .p = &mut local3 }; + useE(e1); +} + +void test16() { + int local1 = 42; + int local2 = 42; + E e1 = { .c = (struct C) { .p = &mut local1 }, .p = &mut local2 }; + int *borrow p = e1.p; + useE(e1); +} + +void test17() { + int local1 = 42; + int local2 = 42; + E e = { .c = (struct C) { .p = &mut local1 }, .p = &mut local2 }; + C c = e.c; // expected-note {{`*e.c.p` is borrowed here}} + // expected-note@-1 {{first mut borrow occurs here}} + int *borrow p1 = e.p; // expected-note {{`*e.p` is borrowed here}} + // expected-note@-1 {{first mut borrow occurs here}} + int *borrow p2 = e.p; // expected-error {{cannot use `e.p` because it was mutably borrowed}} + // expected-error@-1 {{cannot borrow `*e.p` as mutable more than once at a time}} + use_mut(p1); + useE(e); // expected-error {{cannot use `e` because it was mutably borrowed}} + // expected-error@-1 {{cannot borrow `*e.c.p` as mutable more than once at a time}} + useC(c); +} \ No newline at end of file diff --git a/clang/test/BSC/Positive/Ownership/borrow_check_rules/target_is_raw_pointer/target_is_raw_pointer.cbs b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/target_is_raw_pointer/target_is_raw_pointer.cbs similarity index 33% rename from clang/test/BSC/Positive/Ownership/borrow_check_rules/target_is_raw_pointer/target_is_raw_pointer.cbs rename to clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/target_is_raw_pointer/target_is_raw_pointer.cbs index 940b493a6a0d..f4a1cfd4bab2 100644 --- a/clang/test/BSC/Positive/Ownership/borrow_check_rules/target_is_raw_pointer/target_is_raw_pointer.cbs +++ b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/target_is_raw_pointer/target_is_raw_pointer.cbs @@ -1,27 +1,35 @@ -// RUN: %clang %s -o %test.output -// RUN: %test.output -// RUN: %clang -rewrite-bsc %s -o %t-rw.c -// RUN: %clang %t-rw.c -o %t-rw.output -// RUN: %t-rw.output -// expected-no-diagnostics +// RUN: %clang_cc1 -verify %s + +struct N { + int b; + int *owned c; +}; + +struct M { + int *a; + struct N *n; +}; + +struct K { + int *borrow p; +}; + +void use_mut(int *borrow p) {} -void use_mut(int* borrow p) {} -struct N { int b; int *owned c; }; -struct M { int* a; struct N* n; }; -struct K { int* borrow p; }; void test() { int a = 5; int *borrow p1 = &mut a; int *borrow p2 = &mut a; - struct K k = { .p = &mut a }; - int* owned o = (int* owned)&a; + struct K k = { .p = &mut a }; // expected-note {{`a` is borrowed here}} + // expected-note@-1 {{`a` is borrowed here}} + int *owned o = (int* owned)&a; // expected-error {{cannot use `a` because it was mutably borrowed}} { struct N n = { .b = 5, .c = o }; - struct M m = { .a = &a, .n = &n }; + struct M m = { .a = &a, .n = &n }; // expected-error {{cannot use `a` because it was mutably borrowed}} p1 = &mut *m.a; p2 = &mut m.n->b; k.p = &mut *m.n->c; - int * d = (int*)n.c; + int *d = (int *)n.c; } use_mut(p1); use_mut(k.p); diff --git a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/use_borrow_in_condition/use_borrow_in_condition.cbs b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/use_borrow_in_condition/use_borrow_in_condition.cbs index 75215394becd..a6e95ab638cb 100644 --- a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/use_borrow_in_condition/use_borrow_in_condition.cbs +++ b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/use_borrow_in_condition/use_borrow_in_condition.cbs @@ -1,142 +1,150 @@ -// RUN: %clang_cc1 -ast-dump -verify %s +// RUN: %clang_cc1 -verify %s -void use_mut(int *borrow p) {} -void use_immut(const int *borrow p) {} -T* owned safe_malloc(T value); -void free_owned(T* owned p); -void free(void*); +struct S { + int a; + int *owned b; +}; + +void use_mut(int *borrow p); +void use_immut(const int *borrow p); +T *owned safe_malloc(T value); +void free_owned(T *owned p); void test1() { - int e = 1; - const int * borrow p1 = &const e; // expected-note {{previous borrow is here}} - e = 2; //expected-error{{Can not modify 'e' because be borrowed}} - if (*p1 == 2) { - e = 5; - } + int e = 1; + const int *borrow p1 = &const e; // expected-note {{`e` is borrowed here}} + e = 2; // expected-error {{cannot assign to `e` because it is borrowed}} + if (*p1 == 2) { + e = 5; + } - int f = 1; - const int * borrow p2 = &const f; // expected-note {{previous borrow is here}} - f = 2; //expected-error{{Can not modify 'f' because be borrowed}} - if (*p2) { - f = 5; - } + int f = 1; + const int *borrow p2 = &const f; // expected-note {{`f` is borrowed here}} + f = 2; // expected-error {{cannot assign to `f` because it is borrowed}} + if (*p2) { + f = 5; + } - int g = 1; - const int * borrow p3 = &const g; // expected-note {{previous borrow is here}} - g = 2; //expected-error{{Can not modify 'g' because be borrowed}} - if (*p3 < 0 || *p3 > 5 || *p3 + *p3 > 10) { - g = 5; - } + int g = 1; + const int *borrow p3 = &const g; // expected-note {{`g` is borrowed here}} + g = 2; // expected-error {{cannot assign to `g` because it is borrowed}} + if (*p3 < 0 || *p3 > 5 || *p3 + *p3 > 10) { + g = 5; + } - int h = 1; - int * borrow p4 = &mut h; // expected-note {{previous borrow is here}} - int k = h; //expected-error{{Can not read 'h' because be mut borrowed}} - if (*p4) { - h = 5; - } + int h = 1; + int *borrow p4 = &mut h; // expected-note {{`h` is borrowed here}} + int k = h; // expected-error {{cannot use `h` because it was mutably borrowed}} + if (*p4) { + h = 5; + } } -struct S { - int a; - int *owned b; -}; void test2() { - int local = 5; - struct S s = { .a = 1, .b = safe_malloc(1) }; - const struct S* borrow p = &const s;// expected-note {{previous borrow is here}} expected-note {{previous borrow is here}} - s.a = 2; //expected-error{{Can not modify 's.a' because be borrowed}} - free_owned(s.b); //expected-error{{Can not modify 's.b' because be borrowed}} - if (p->a == 2 && *(p->b) == 2) { - s.a = 5; - } + int local = 5; + struct S s = { .a = 1, .b = safe_malloc(1) }; + const struct S *borrow p = &const s; // expected-note {{`s.a` is borrowed here}} + // expected-note@-1 {{`s` is borrowed here}} + s.a = 2; // expected-error {{cannot assign to `s.a` because it is borrowed}} + free_owned(s.b); // expected-error {{cannot move out of `s.b` because it is borrowed}} + if (p->a == 2 && *(p->b) == 2) { + s.a = 5; + } } void test3() { - int e = 1; - const int * borrow p = &const e; // expected-note {{previous borrow is here}} expected-note {{previous borrow is here}} - e = 2; //expected-error{{Can not modify 'e' because be borrowed}} - while (*p > 0) { - e--; //expected-error{{Can not modify 'e' because be borrowed}} - } + int e = 1; + int *borrow p = &mut e; // expected-note {{`e` is borrowed here}} + // expected-note@-1 {{`e` is borrowed here}} + while (*p > 0) { + --e; // expected-error {{cannot assign to `e` because it is borrowed}} + // expected-error@-1 {{cannot use `e` because it was mutably borrowed}} + } } void test4() { - int e = 1; - const int * borrow p = &const e; // expected-note {{previous borrow is here}} expected-note {{previous borrow is here}} - e = 2; //expected-error{{Can not modify 'e' because be borrowed}} - for (int i = 0; i < *p; i++) { - e--; //expected-error{{Can not modify 'e' because be borrowed}} - } + int e = 1; + const int *borrow p = &const e; // expected-note {{`e` is borrowed here}} + for (int i = 0; i < *p; ++i) { + --e; // expected-error {{cannot assign to `e` because it is borrowed}} + } } void test5() { - int e = 1; - const int * borrow p = &const e; // expected-note {{previous borrow is here}} expected-note {{previous borrow is here}} - e = 2; //expected-error{{Can not modify 'e' because be borrowed}} - do { - e--; //expected-error{{Can not modify 'e' because be borrowed}} - } while (*p > 0); + int e = 1; + const int *borrow p = &const e; // expected-note {{`e` is borrowed here}} + e = 2; // expected-error {{cannot assign to `e` because it is borrowed}} + do { + --e; + } while (*p > 0); } void test6() { - int e = 1; - int f = 2; - const int * borrow p1 = &const e; // expected-note {{previous borrow is here}} - const int * borrow p2 = &const f; - e = 2; //expected-error{{Can not modify 'e' because be borrowed}} - for ( ; ; ) { - while (p2 == p1) - return; - } + int e = 1; + int f = 2; + const int *borrow p1 = &const e; // expected-note {{`e` is borrowed here}} + const int *borrow p2 = &const f; + e = 2; // expected-error {{cannot assign to `e` because it is borrowed}} + for ( ; ; ) { + while (p2 == p1) + return; + } } void test7() { - int e = 1; - int f = 2; - const int * borrow p1 = &const e; // expected-note {{previous borrow is here}} - const int * borrow p2 = &const f; - e = 2; //expected-error{{Can not modify 'e' because be borrowed}} - for ( ; ; ) { - while (p2 == p1) - return; - } + int e = 1; + const int *borrow p = &const e; // expected-note {{`e` is borrowed here}} + e = 2; // expected-error {{cannot assign to `e` because it is borrowed}} + while (*p > 0) { + --e; + } } void test8() { - int e = 1; - int f = 2; - const int * borrow p1 = &const e; // expected-note {{previous borrow is here}} - const int * borrow p2 = &const f; // expected-note {{previous borrow is here}} - e = 2; //expected-error{{Can not modify 'e' because be borrowed}} - for (p2 = p1; *p2 > 0; ) { - e--; //expected-error{{Can not modify 'e' because be borrowed}} - } + int e = 1; + int f = 2; + const int *borrow p1 = &const e; // expected-note {{`e` is borrowed here}} + const int *borrow p2 = &const f; + for (p2 = p1; *p2 > 0; ) { + --e; // expected-error {{cannot assign to `e` because it is borrowed}} + } } void test9() { - int e = 1; - int f = 2; - const int * borrow p1 = &const e; - const int * borrow p2 = &const f; // expected-note {{previous borrow is here}} - for ( ; e = 2, f = 2; ) { //expected-error{{Can not modify 'f' because be borrowed}} - if (e) { - return; - } else { - p1 = p2; - } + int e = 1; + int f = 2; + const int *borrow p1 = &const e; + const int *borrow p2 = &const f; // expected-note {{`f` is borrowed here}} + for ( ; e = 2, f = 2; ) { // expected-error {{cannot assign to `f` because it is borrowed}} + if (e) { + return; + } else { + p1 = p2; } + } } void test10() { - int e = 1; - int f = 2; - const int * borrow p1 = &const e; // expected-note {{previous borrow is here}} - const int * borrow p2 = &const f; // expected-note {{previous borrow is here}} - for ( ; e = 2, f = 2; ) { //expected-error{{Can not modify 'f' because be borrowed}} expected-error{{Can not modify 'e' because be borrowed}} - if (p1 == p2) { - return; - } else { - p1 = p2; - } + int e = 1; + int f = 2; + const int *borrow p1 = &const e; // expected-note {{`e` is borrowed here}} + const int *borrow p2 = &const f; // expected-note {{`f` is borrowed here}} + for ( ; e = 2, f = 2; ) { // expected-error {{cannot assign to `e` because it is borrowed}} + // expected-error@-1 {{cannot assign to `f` because it is borrowed}} + if (p1 == p2) { + return; + } else { + p1 = p2; } + } +} + +void test11() { + int local = 42; + int *borrow p = &mut local; + int *borrow q = &mut *p; + while (1) { + use_mut(p); + } + use_mut(q); } \ No newline at end of file diff --git a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/var_borrow_field/var_borrow_field.cbs b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/var_borrow_field/var_borrow_field.cbs index 18697a85ad76..66e58b2d8c31 100644 --- a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/var_borrow_field/var_borrow_field.cbs +++ b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/var_borrow_field/var_borrow_field.cbs @@ -1,38 +1,64 @@ // RUN: %clang_cc1 -verify %s +#define NULL ((void *)0) + typedef struct S { - int a; - int b; + int a; + int b; } S; -void use_mutI(int * borrow); -void use_constI(const int * borrow); -void use_mutS(S * borrow); -void use_constS(const S * borrow); +typedef struct Foo { + int *borrow field; +} Foo; + +void use_mutI(int *borrow); +void use_constI(const int *borrow); +void use_mutS(S *borrow); +void use_constS(const S *borrow); T* owned safe_malloc(T value); void free_owned(T* owned p); -void free(void*); +void free(void *); void test1() { - S local = {.a = 1, .b = 2}; - int * borrow p = &mut local.a; - S * borrow s = &mut local; // expected-note {{previous borrow is here}} - use_mutI(p); // expected-error {{There should be at most one mutable borrow targeting to 'local' at the same time}} - use_mutS(s); + S local = { .a = 1, .b = 2 }; + int *borrow p = &mut local.a; // expected-note {{first mut borrow occurs here}} + S *borrow s = &mut local; // expected-error {{cannot borrow `local` as mutable more than once at a time}} + use_mutI(p); + use_mutS(s); } void test2() { - S local = {.a = 1, .b = 2}; - const int * borrow p = &const local.a; - S * borrow s = &mut local; // expected-note {{previous borrow is here}} - use_constI(p); // expected-error {{There should be at most one mutable borrow targeting to 'local' at the same time}} - use_mutS(s); + S local = { .a = 1, .b = 2 }; + const int *borrow p = &const local.a; // expected-note {{immutable borrow occurs here}} + S *borrow s = &mut local; // expected-error {{cannot borrow `local` as mutable because it is also borrowed as immutable}} + use_constI(p); + use_mutS(s); } void test3() { - S local = {.a = 1, .b = 2}; - int * borrow p = &mut local.a; - const S * borrow s = &const local; // expected-note {{previous borrow is here}} - use_mutI(p); // expected-error {{There should be at most one mutable borrow targeting to 'local' at the same time}} - use_constS(s); + S local = { .a = 1, .b = 2 }; + int *borrow p = &mut local.a; // expected-note {{mutable borrow occurs here}} + const S *borrow s = &const local; // expected-error {{cannot borrow `local` as immutable because it is also borrowed as mutable}} + use_mutI(p); + use_constS(s); +} + +void test4(Foo a) { + int *borrow b = &mut *a.field; // expected-note {{`*a.field` is borrowed here}} + // expected-note@-1 {{first mut borrow occurs here}} + Foo temp = a; // expected-error {{cannot use `a` because it was mutably borrowed}} + // expected-error@-1 {{cannot borrow `*a.field` as mutable more than once at a time}} + use_mutI(b); +} + +void test5(Foo a) { + int *borrow b = &mut *a.field; + a = (Foo) { .field = (int *borrow)NULL }; + use_mutI(b); +} + +void test6(Foo a) { + const int *borrow b = &const *a.field; + a = (Foo) { .field = (int *borrow)NULL }; + use_constI(b); } \ No newline at end of file diff --git a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/var_borrow_struct/var_borrow_struct.cbs b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/var_borrow_struct/var_borrow_struct.cbs index 71c2b91dd430..7da40fbe7984 100644 --- a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/var_borrow_struct/var_borrow_struct.cbs +++ b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/var_borrow_struct/var_borrow_struct.cbs @@ -1,98 +1,106 @@ // RUN: %clang_cc1 -verify %s typedef struct S { - int a; - int b; + int a; + int b; } S; -void use_mut(S * borrow s); -void use_const(const S * borrow s); -T* owned safe_malloc(T value); -void free_owned(T* owned p); +void use_mut(S *borrow s); +void use_const(const S *borrow s); +T *owned safe_malloc(T value); +void free_owned(T *owned p); void free(void*); void test1() { - S s = { .a = 1, .b = 2 }; - S * borrow p = &mut s; - S * borrow q = &mut s; // expected-note {{previous borrow is here}} - use_mut(p); // expected-error {{There should be at most one mutable borrow targeting to 's' at the same time}} - use_mut(q); + S s = { .a = 1, .b = 2 }; + S *borrow p = &mut s; // expected-note {{first mut borrow occurs here}} + S *borrow q = &mut s; // expected-error {{cannot borrow `s` as mutable more than once at a time}} + use_mut(p); + use_mut(q); } void test2() { S tmp = {.a = 1, .b = 2}; S *owned oriPtr = safe_malloc(tmp); - S *borrow p1 = &mut *oriPtr; - S *borrow p2 = &mut *oriPtr; // expected-note {{previous borrow is here}} - use_mut(p1); // expected-error {{There should be at most one mutable borrow targeting to 'oriPtr' at the same time}} + S *borrow p1 = &mut *oriPtr; // expected-note {{first mut borrow occurs here}} + S *borrow p2 = &mut *oriPtr; // expected-error {{cannot borrow `*oriPtr` as mutable more than once at a time}} + use_mut(p1); use_mut(p2); free_owned(oriPtr); } -void test3(S * oriPtr) { - S *borrow p1 = &mut * oriPtr; - S *borrow p2 = &mut * oriPtr; +void test3(S *oriPtr) { + S *borrow p1 = &mut *oriPtr; // expected-note {{first mut borrow occurs here}} + S *borrow p2 = &mut *oriPtr; // expected-error {{cannot borrow `*oriPtr` as mutable more than once at a time}} use_mut(p1); free(oriPtr); } void test4() { - S local = {.a = 1, .b = 2}; - S *borrow p = &mut local; - S *borrow p1 = &mut local; - const S *borrow p2 = &const *p; // expected-note {{previous borrow is here}} - // expected-note@-1 {{previous borrow is here}} - use_mut(p); // expected-error {{There should be at most one mutable borrow targeting to 'local' at the same time}} - use_mut(p1); // expected-error {{There should be at most one mutable borrow targeting to 'local' at the same time}} + S local = { .a = 1, .b = 2 }; + S *borrow p = &mut local; // expected-note {{first mut borrow occurs here}} + S *borrow p1 = &mut local; // expected-error {{cannot borrow `local` as mutable more than once at a time}} + const S *borrow p2 = &const *p; // expected-note {{immutable borrow occurs here}} + use_mut(p); // expected-error {{cannot borrow `*p` as mutable because it is also borrowed as immutable}} + use_mut(p1); use_const(p2); } void test5() { - S local = {.a = 1, .b = 2}; - S *borrow p1 = &mut local; - const S *borrow p2 = &const local; // expected-note {{previous borrow is here}} - use_mut(p1); // expected-error {{There should be at most one mutable borrow targeting to 'local' at the same time}} + S local = { .a = 1, .b = 2 }; + S *borrow p1 = &mut local; // expected-note {{mutable borrow occurs here}} + const S *borrow p2 = &const local; // expected-error {{cannot borrow `local` as immutable because it is also borrowed as mutable}} + use_mut(p1); use_const(p2); } -void test6(S* borrow p) { - S local = {.a = 1, .b = 2}; +void test6(S *borrow p) { + S local = { .a = 1, .b = 2 }; p = &mut local; - const S *borrow p1 = &const *p; // expected-note {{previous borrow is here}} - use_mut(p); // expected-error {{There should be at most one mutable borrow targeting to 'local' at the same time}} + const S *borrow p1 = &const *p; // expected-note {{immutable borrow occurs here}} + use_mut(p); // expected-error {{cannot borrow `*p` as mutable because it is also borrowed as immutable}} use_const(p1); } -void test7(S* borrow p) { - S *borrow p1 = p; // expected-note {{previous borrow is here}} - use_mut(p); // expected-error {{There should be at most one mutable borrow targeting to 'p' at the same time}} +void test7(S *borrow p) { + S *borrow p1 = p; // expected-note {{`*p` is borrowed here}} + // expected-note@-1 {{first mut borrow occurs here}} + use_mut(p); // expected-error {{cannot use `p` because it was mutably borrowed}} + // expected-error@-1 {{cannot borrow `*p` as mutable more than once at a time}} use_mut(p1); } void test8() { - S local = {.a = 1, .b = 2}; - S * borrow p = &mut local; // expected-note {{previous borrow is here}} - local.b = 3; // expected-error {{Can not modify 'local.b' because be borrowed}} + S local = { .a = 1, .b = 2 }; + S *borrow p = &mut local; // expected-note {{`local.b` is borrowed here}} + local.b = 3; // expected-error {{cannot assign to `local.b` because it is borrowed}} use_mut(p); } void test9() { - S local = {.a = 1, .b = 2}; - const S * borrow p = &const local; // expected-note {{previous borrow is here}} - local.b = 3; // expected-error {{Can not modify 'local.b' because be borrowed}} + S local = { .a = 1, .b = 2}; + const S *borrow p = &const local; // expected-note {{`local.b` is borrowed here}} + local.b = 3; // expected-error {{cannot assign to `local.b` because it is borrowed}} use_const(p); } void test10() { - S local = {.a = 1, .b = 2}; - S * borrow p = &mut local; // expected-note {{previous borrow is here}} - int x = local.b; // expected-error {{Can not modify 'local.b' because be borrowed}} + S local = { .a = 1, .b = 2 }; + S *borrow p = &mut local; // expected-note {{`local` is borrowed here}} + int x = local.b; // expected-error {{cannot use `local.b` because it was mutably borrowed}} use_mut(p); } void test11() { - S local = {.a = 1, .b = 2}; - int * borrow p1 = &mut local.a; // expected-note {{previous borrow is here}} - local.a = 5; // expected-error {{Can not modify 'local.a' because be borrowed}} - int * borrow p2 = p1; + S local = { .a = 1, .b = 2 }; + int *borrow p1 = &mut local.a; // expected-note {{`local.a` is borrowed here}} + local.a = 5; // expected-error {{cannot assign to `local.a` because it is borrowed}} + int *borrow p2 = p1; +} + +void test12() { + S foo = { .a = 1, .b = 2 }; + S *borrow p = &mut foo; + use_mut(p); + S temp = foo; } \ No newline at end of file diff --git a/clang/test/BSC/Negative/Ownership/Borrow/borrow_expr_freeze/borrow_arr_freeze/borrow_arr_freeze.cbs b/clang/test/BSC/Negative/Ownership/Borrow/borrow_expr_freeze/borrow_arr_freeze/borrow_arr_freeze.cbs index b00d2d0f5a97..e3e6186b89fa 100644 --- a/clang/test/BSC/Negative/Ownership/Borrow/borrow_expr_freeze/borrow_arr_freeze/borrow_arr_freeze.cbs +++ b/clang/test/BSC/Negative/Ownership/Borrow/borrow_expr_freeze/borrow_arr_freeze/borrow_arr_freeze.cbs @@ -4,16 +4,14 @@ void test1(int *p) {} void test_freeze_arrow_var1() { int arr[10] = {0}; - int * borrow c = &mut arr[1]; // expected-note {{previous borrow is here}} - // expected-note@-1 {{previous borrow is here}} - // expected-note@-2 {{previous borrow is here}} - // expected-note@-3 {{previous borrow is here}} - // expected-note@-4 {{previous borrow is here}} - int a = arr[1]; // expected-error {{Can not read 'arr' because be mut borrowed}} - int b = arr[2]; // expected-error {{Can not read 'arr' because be mut borrowed}} - arr[1] = 2; // expected-error {{Can not modify 'arr' because be borrowed}} - arr[2] = 2; // expected-error {{Can not modify 'arr' because be borrowed}} - test1(arr); // expected-error {{Can not modify 'arr' because be borrowed}} + int * borrow c = &mut arr[1]; // expected-note {{`arr` is borrowed here}} + // expected-note@-1 {{`arr` is borrowed here}} + // expected-note@-2 {{`arr` is borrowed here}} + int a = arr[1]; // expected-error {{cannot use `arr` because it was mutably borrowed}} + int b = arr[2]; // expected-error {{cannot use `arr` because it was mutably borrowed}} + arr[1] = 2; // expected-error {{cannot assign to `arr` because it is borrowed}} + arr[2] = 2; + test1(arr); int arr2[10] = {}; arr2[arr[1]] = 5; int m = *c; @@ -23,22 +21,19 @@ void test2(int p[10][10]) {} void test_freeze_arrow_var2() { int arr[10][10] = {0}; - int * borrow k = &mut arr[1][1]; // expected-note {{previous borrow is here}} - // expected-note@-1 {{previous borrow is here}} - // expected-note@-2 {{previous borrow is here}} - // expected-note@-3 {{previous borrow is here}} - // expected-note@-4 {{previous borrow is here}} - // expected-note@-5 {{previous borrow is here}} - // expected-note@-6 {{previous borrow is here}} - // expected-note@-7 {{previous borrow is here}} - int a = arr[1][1]; // expected-error {{Can not read 'arr' because be mut borrowed}} - int b = arr[1][2]; // expected-error {{Can not read 'arr' because be mut borrowed}} - int c = arr[2][2]; // expected-error {{Can not read 'arr' because be mut borrowed}} - test1(arr[1]); // expected-error {{Can not modify 'arr' because be borrowed}} - arr[1][1] = 2; // expected-error {{Can not modify 'arr' because be borrowed}} - arr[1][2] = 2; // expected-error {{Can not modify 'arr' because be borrowed}} - arr[2][2] = 2; // expected-error {{Can not modify 'arr' because be borrowed}} - test2(arr); // expected-error {{Can not modify 'arr' because be borrowed}} + int * borrow k = &mut arr[1][1]; // expected-note {{`arr` is borrowed here}} + // expected-note@-1 {{`arr` is borrowed here}} + // expected-note@-2 {{`arr` is borrowed here}} + // expected-note@-3 {{`arr` is borrowed here}} + // expected-note@-4 {{`arr` is borrowed here}} + int a = arr[1][1]; // expected-error {{cannot use `arr` because it was mutably borrowed}} + int b = arr[1][2]; // expected-error {{cannot use `arr` because it was mutably borrowed}} + int c = arr[2][2]; // expected-error {{cannot use `arr` because it was mutably borrowed}} + test1(arr[1]); // expected-error {{cannot use `arr` because it was mutably borrowed}} + arr[1][1] = 2; // expected-error {{cannot assign to `arr` because it is borrowed}} + arr[1][2] = 2; + arr[2][2] = 2; + test2(arr); int m = *k; } diff --git a/clang/test/BSC/Negative/Ownership/Borrow/borrow_expr_freeze/borrow_ident_freeze/borrow_ident_freeze.cbs b/clang/test/BSC/Negative/Ownership/Borrow/borrow_expr_freeze/borrow_ident_freeze/borrow_ident_freeze.cbs index ce997bcece21..6fcbbc2d3100 100644 --- a/clang/test/BSC/Negative/Ownership/Borrow/borrow_expr_freeze/borrow_ident_freeze/borrow_ident_freeze.cbs +++ b/clang/test/BSC/Negative/Ownership/Borrow/borrow_expr_freeze/borrow_ident_freeze/borrow_ident_freeze.cbs @@ -1,140 +1,140 @@ // RUN: %clang_cc1 -ast-dump -verify %s void test_freeze_ident1() { - int a = 1; - int * borrow c = &mut a; - // we can modify "a" when c life time is end; - a = 2; + int a = 1; + int *borrow c = &mut a; + // we can modify "a" when c life time is end; + a = 2; } void test_freeze_ident2() { - int a = 1; - int * borrow c = &mut a; // expected-note {{previous borrow is here}} - a = 2; // expected-error {{Can not modify 'a' because be borrowed}} - int d = *c; + int a = 1; + int *borrow c = &mut a; // expected-note {{`a` is borrowed here}} + a = 2; // expected-error {{cannot assign to `a` because it is borrowed}} + int d = *c; } void test_freeze_ident2_0() { - int a = 1; - int * borrow c = &mut a; // expected-note {{previous borrow is here}} - int m = a; // expected-error {{Can not read 'a' because be mut borrowed}} - int d = *c; + int a = 1; + int *borrow c = &mut a; // expected-note {{`a` is borrowed here}} + int m = a; // expected-error {{cannot use `a` because it was mutably borrowed}} + int d = *c; } void test_freeze_ident3() { - int a = 1; - int b = 2; - int * borrow c = &mut a; - // "c" life time is end at before, and reactivate at after; - a = 2; - c = &mut b; + int a = 1; + int b = 2; + int *borrow c = &mut a; + // "c" life time is end at before, and reactivate at after; + a = 2; + c = &mut b; } void test_freeze_ident4() { - int a = 1; - int * borrow c = &mut a; - int * borrow d = &mut a; + int a = 1; + int *borrow c = &mut a; + int *borrow d = &mut a; } void test_freeze_ident5() { - int a = 1; - int * borrow c = &mut a; - int * borrow d = &mut a; - int e = *c; + int a = 1; + int *borrow c = &mut a; // expected-note {{first mut borrow occurs here}} + int *borrow d = &mut a; // expected-error {{cannot borrow `a` as mutable more than once at a time}} + int e = *c; } void test_freeze_ident6() { - int a = 1; - int * borrow c = &mut a; - int * borrow d = &mut a; - int e = *d; + int a = 1; + int *borrow c = &mut a; + int *borrow d = &mut a; + int e = *d; } void test_freeze_ident6_0() { - int a = 1; - int * borrow c = &mut a; - int * borrow d = &mut a; - int e = *d; - int f = *c; // expected-error {{Can not use 'c' because expired}} + int a = 1; + int *borrow c = &mut a; // expected-note {{first mut borrow occurs here}} + int *borrow d = &mut a; // expected-error {{cannot borrow `a` as mutable more than once at a time}} + int e = *d; + int f = *c; } void test_freeze_ident6_1() { - int a = 1; - int * borrow c = &mut a; - int * borrow d = &mut a; // expected-note {{previous borrow is here}} - int e = *c; // expected-error {{There should be at most one mutable borrow targeting to 'a' at the same time}} - int f = *d; + int a = 1; + int *borrow c = &mut a; // expected-note {{first mut borrow occurs here}} + int *borrow d = &mut a; // expected-error {{cannot borrow `a` as mutable more than once at a time}} + int e = *c; + int f = *d; } void test_freeze_ident6_2() { - int a = 1; - int * borrow c = &mut a; - int * borrow d = c; // expected-note {{previous borrow is here}} - int e = *c; // expected-error {{There should be at most one mutable borrow targeting to 'a' at the same time}} - int f = *d; + int a = 1; + int *borrow c = &mut a; + int *borrow d = c; // expected-note {{`*c` is borrowed here}} + int e = *c; // expected-error {{cannot use `*c` because it was mutably borrowed}} + int f = *d; } -void use(int * borrow a) {} +void use(int *borrow a) {} void test_freeze_ident6_3() { - int a = 1; - int * borrow c = &mut a; - int * borrow d = c; // expected-note {{previous borrow is here}} - int e = *c; // expected-error {{There should be at most one mutable borrow targeting to 'a' at the same time}} - use(d); + int a = 1; + int *borrow c = &mut a; + int *borrow d = c; // expected-note {{`*c` is borrowed here}} + int e = *c; // expected-error {{cannot use `*c` because it was mutably borrowed}} + use(d); } void test_freeze_ident7() { - int a = 1; - int * borrow c = &mut a; - const int * borrow d = &const a; + int a = 1; + int *borrow c = &mut a; + const int *borrow d = &const a; } void test_freeze_ident8() { - int a = 1; - int * borrow c = &mut a; - const int * borrow d = &const a; - int e = *c; + int a = 1; + int *borrow c = &mut a; // expected-note {{mutable borrow occurs here}} + const int *borrow d = &const a; // expected-error {{cannot borrow `a` as immutable because it is also borrowed as mutable}} + int e = *c; } void test_freeze_ident9() { - int a = 1; - int * borrow c = &mut a; - const int * borrow d = &const a; - int e = *d; + int a = 1; + int *borrow c = &mut a; + const int *borrow d = &const a; + int e = *d; } void test_freeze_ident10() { - int a = 1; - int * borrow c = &mut a; - const int * borrow d = &const a; - int b = *d; - int e = *c; + int a = 1; + int *borrow c = &mut a; // expected-note {{mutable borrow occurs here}} + const int *borrow d = &const a; // expected-error {{cannot borrow `a` as immutable because it is also borrowed as mutable}} + int b = *d; + int e = *c; } -void const_use(const int * borrow a) {}; +void const_use(const int *borrow a) {}; void test_freeze_ident11() { - int a = 1; - int * borrow c = &mut a; - const int * borrow d = &const a; // expected-note {{previous borrow is here}} - int b = *c; // expected-error {{There should be at most one mutable borrow targeting to 'a' at the same time}} - const_use(d); + int a = 1; + int *borrow c = &mut a; // expected-note {{mutable borrow occurs here}} + const int *borrow d = &const a; // expected-error {{cannot borrow `a` as immutable because it is also borrowed as mutable}} + int b = *c; + const_use(d); } void test_freeze_ident12() { - int a = 1; - const int * borrow c = &const a; - const int * borrow d = &const a; - int b = *c; - int e = *d; + int a = 1; + const int *borrow c = &const a; + const int *borrow d = &const a; + int b = *c; + int e = *d; } void test_freeze_ident13() { - int a = 1; - const int * borrow c = &const a; // expected-note {{previous borrow is here}} - a = 2; // expected-error {{Can not modify 'a' because be borrowed}} - int b = *c; + int a = 1; + const int *borrow c = &const a; // expected-note {{`a` is borrowed here}} + a = 2; // expected-error {{cannot assign to `a` because it is borrowed}} + int b = *c; } int test_freeze_ident14(){ @@ -149,156 +149,156 @@ int test_freeze_ident14(){ return 0; } -int test_freeze_ident15(int * borrow m){ +int test_freeze_ident15(int *borrow m){ int a = 1; int b = 2; int *c, *d; c = &a; d = &b; - int * borrow e = &mut *c; - int * borrow f = &mut *c; // expected-note {{previous borrow is here}} - int g = f == e ? *f : *e; // expected-error {{There should be at most one mutable borrow targeting to 'c' at the same time}} + int *borrow e = &mut *c; // expected-note {{first mut borrow occurs here}} + int *borrow f = &mut *c; // expected-error {{cannot borrow `*c` as mutable more than once at a time}} + int g = f == e ? *f : *e; use(f); - use(e); // expected-error {{Can not use 'e' because expired}} + use(e); return 0; } struct S { int a; - int * borrow b; + int *borrow b; }; void test_freeze_ident17(){ - int a = 1; - int *owned b = (int *owned)&a; - int *borrow c = &mut *b; - int *d = (int *)b; + int a = 1; + int *owned b = (int *owned)&a; + int *borrow c = &mut *b; + int *d = (int *)b; } void test_freeze_ident18(){ - int a = 1; - int *owned b = (int *owned)&a; - int *borrow c = &mut *b; // expected-note {{previous borrow is here}} - int *d = (int *)b; // expected-error {{Can not read 'b' because be mut borrowed}} - use(c); + int a = 1; + int *owned b = (int *owned)&a; + int *borrow c = &mut *b; // expected-note {{`*b` is borrowed here}} + int *d = (int *)b; // expected-error {{cannot move out of `b` because it is borrowed}} + use(c); } void test_freeze_ident19(){ - int a = 1; - int *owned b = (int *owned)&a; - int *borrow c = &mut *b; // expected-note {{previous borrow is here}} - int *owned d = b; // expected-error {{Can not modify 'b' because be borrowed}} - use(c); - int *e = (int *)d; + int a = 1; + int *owned b = (int *owned)&a; + int *borrow c = &mut *b; // expected-note {{`*b` is borrowed here}} + int *owned d = b; // expected-error {{cannot move out of `b` because it is borrowed}} + use(c); + int *e = (int *)d; } void test_freeze_ident20(){ - int a = 1; - int *owned b = (int *owned)&a; - const int *borrow c = &const *b; // expected-note {{previous borrow is here}} - int *owned d = b; // expected-error {{Can not modify 'b' because be borrowed}} - const_use(c); - int * e = (int *)d; -} - -owned struct F -{ - public: - int a; + int a = 1; + int *owned b = (int *owned)&a; + const int *borrow c = &const *b; // expected-note {{`*b` is borrowed here}} + int *owned d = b; // expected-error {{cannot move out of `b` because it is borrowed}} + const_use(c); + int * e = (int *)d; +} + +owned struct F { +public: + int a; }; -void owend_struct_use1(F *borrow b){} +void owned_struct_use1(F *borrow b){} void test_freeze_ident21(){ - F f = {1}; - F *borrow b = &mut f; // expected-note {{previous borrow is here}} - F k = f; // expected-error {{Can not modify 'f' because be borrowed}} - owend_struct_use1(b); + F f = {1}; + F *borrow b = &mut f; // expected-note {{`f` is borrowed here}} + F k = f; // expected-error {{cannot move out of `f` because it is borrowed}} + owned_struct_use1(b); } -void owend_struct_use2(const F *borrow b){} +void owned_struct_use2(const F *borrow b){} void test_freeze_ident22(){ - F f = {1}; - const F *borrow b = &const f; // expected-note {{previous borrow is here}} - F k = f; // expected-error {{Can not modify 'f' because be borrowed}} - owend_struct_use2(b); + F f = {1}; + const F *borrow b = &const f; // expected-note {{`f` is borrowed here}} + F k = f; // expected-error {{cannot move out of `f` because it is borrowed}} + owned_struct_use2(b); } -struct H -{ - int *owned a; - int b; +struct H { + int *owned a; + int b; }; -void owend_struct_use3(struct H *borrow b){} +void owned_struct_use3(struct H *borrow b){} void test_freeze_ident23(){ - int a = 1; - struct H h = {(int *owned)&a, 2}; - struct H *borrow b = &mut h; // expected-note {{previous borrow is here}} - struct H g = h; // expected-error {{Can not modify 'h' because be borrowed}} - owend_struct_use3(b); - int *c = (int *)g.a; + int a = 1; + struct H h = {(int *owned)&a, 2}; + struct H *borrow b = &mut h; // expected-note {{`h` is borrowed here}} + struct H g = h; // expected-error {{cannot move out of `h` because it is borrowed}} + owned_struct_use3(b); + int *c = (int *)g.a; } -void owend_struct_use4(const struct H *borrow b){} +void owned_struct_use4(const struct H *borrow b){} void test_freeze_ident24(){ - int a = 1; - struct H h = {(int *owned)&a, 2}; - const struct H * borrow b = &const h; // expected-note {{previous borrow is here}} - struct H g = h; // expected-error {{Can not modify 'h' because be borrowed}} - owend_struct_use4(b); - int *c = (int *)g.a; + int a = 1; + struct H h = {(int *owned)&a, 2}; + const struct H *borrow b = &const h; // expected-note {{`h` is borrowed here}} + struct H g = h; // expected-error {{cannot move out of `h` because it is borrowed}} + owned_struct_use4(b); + int *c = (int *)g.a; } void test_freeze_ident25(){ - int a = 1; - int *borrow p1 = &mut a; // expected-note {{previous borrow is here}} - a = 5; // expected-error {{Can not modify 'a' because be borrowed}} - int *borrow p2 = p1; + int a = 1; + int *borrow p1 = &mut a; // expected-note {{`a` is borrowed here}} + a = 5; // expected-error {{cannot assign to `a` because it is borrowed}} + int *borrow p2 = p1; } void test_freeze_ident26(){ - int a = 1; - int *owned b = (int *owned)&a; - int *borrow p1 = &mut *b; // expected-note {{previous borrow is here}} - int *d = (int *)b; // expected-error {{Can not read 'b' because be mut borrowed}} - int *borrow p2 = p1; + int a = 1; + int *owned b = (int *owned)&a; + int *borrow p1 = &mut *b; // expected-note {{`*b` is borrowed here}} + int *d = (int *)b; // expected-error {{cannot move out of `b` because it is borrowed}} + int *borrow p2 = p1; } void test_freeze_ident27(){ - int a = 1; - int *owned b = (int *owned)&a; - int *borrow p1 = &mut *b; - int *borrow p2 = p1; // expected-note {{previous borrow is here}} - int *d = (int *)b; // expected-error {{Can not read 'b' because be mut borrowed}} - use(p2); + int a = 1; + int *owned b = (int *owned)&a; + int *borrow p1 = &mut *b; // expected-note {{`*b` is borrowed here}} + int *borrow p2 = p1; + int *d = (int *)b; // expected-error {{cannot move out of `b` because it is borrowed}} + use(p2); } void test_freeze_ident28(){ - int a = 1; - int *owned b = (int *owned)&a; - int *borrow p1 = &mut *b; // expected-note {{previous borrow is here}} - int *d = (int *)b; // expected-error {{Can not read 'b' because be mut borrowed}} - int *borrow p2 = p1; // expected-note {{previous borrow is here}} expected-note {{previous borrow is here}} - b = (int *owned)&a; // expected-error {{Can not modify 'b' because be borrowed}} - d = (int *)b; // expected-error {{Can not read 'b' because be mut borrowed}} - use(p2); + int a = 1; + int *owned b = (int *owned)&a; + int *borrow p1 = &mut *b; // expected-note {{`*b` is borrowed here}} + // expected-note@-1 {{`b` is borrowed here}} + int *d = (int *)b; // expected-error {{cannot move out of `b` because it is borrowed}} + int *borrow p2 = p1; + b = (int *owned)&a; // expected-error {{cannot assign to `b` because it is borrowed}} + d = (int *)b; + use(p2); } void test_freeze_ident29(){ - int a = 1; - int *borrow p1 = &mut a; // expected-note {{previous borrow is here}} - int *borrow p2 = &mut a; - a = 5; // expected-error {{Can not modify 'a' because be borrowed}} - p2 = p1; + int a = 1; + int *borrow p1 = &mut a; // expected-note {{first mut borrow occurs here}} + // expected-note@-1 {{`a` is borrowed here}} + int *borrow p2 = &mut a; // expected-error {{cannot borrow `a` as mutable more than once at a time}} + a = 5; // expected-error {{cannot assign to `a` because it is borrowed}} + p2 = p1; } void test_freeze_ident30(){ - int local = 5; - int *borrow p = &mut local; // expected-note {{previous borrow is here}} - use(&mut local); // expected-error {{Can not modify 'local' because be borrowed}} - use(p); + int local = 5; + int *borrow p = &mut local; // expected-note {{first mut borrow occurs here}} + use(&mut local); // expected-error {{cannot borrow `local` as mutable more than once at a time}} + use(p); } \ No newline at end of file diff --git a/clang/test/BSC/Negative/Ownership/Borrow/borrow_expr_freeze/borrow_star_freeze/borrow_star_freeze.cbs b/clang/test/BSC/Negative/Ownership/Borrow/borrow_expr_freeze/borrow_star_freeze/borrow_star_freeze.cbs index b450b3017def..131b3d3e04f4 100644 --- a/clang/test/BSC/Negative/Ownership/Borrow/borrow_expr_freeze/borrow_star_freeze/borrow_star_freeze.cbs +++ b/clang/test/BSC/Negative/Ownership/Borrow/borrow_expr_freeze/borrow_star_freeze/borrow_star_freeze.cbs @@ -1,13 +1,14 @@ // RUN: %clang_cc1 -ast-dump -verify %s +// expected-no-diagnostics void test1(int *p) {} void test_freeze_star_var1() { int a = 1; int * b = &a; - int * borrow c = &mut *b; // expected-note {{previous borrow is here}} + int * borrow c = &mut *b; a = 2; // ok; - b = &a; // expected-error {{Can not modify 'b' because be borrowed}} + b = &a; int m = *c; } diff --git a/clang/test/BSC/Negative/Ownership/Borrow/borrow_expr_freeze/member_func_call_freeze/member_func_call_freeze.cbs b/clang/test/BSC/Negative/Ownership/Borrow/borrow_expr_freeze/member_func_call_freeze/member_func_call_freeze.cbs index ff20d89994f0..f0aec908e481 100644 --- a/clang/test/BSC/Negative/Ownership/Borrow/borrow_expr_freeze/member_func_call_freeze/member_func_call_freeze.cbs +++ b/clang/test/BSC/Negative/Ownership/Borrow/borrow_expr_freeze/member_func_call_freeze/member_func_call_freeze.cbs @@ -2,30 +2,31 @@ void use_mut(int *borrow p) {} void use_immut(const int *borrow p) {} -void free(void*); +void free(void *); int* malloc_int(int value); struct A { int *p; }; -int * borrow struct A::get_p_mut(This * borrow this) { +int * borrow struct A::get_p_mut(This *borrow this) { return &mut *(this->p); } -const int * borrow struct A::get_p_immut(This * borrow this) { +const int *borrow struct A::get_p_immut(This *borrow this) { return &const *(this->p); } -void struct A::free_p(This * borrow this) { - free((int*)this->p); +void struct A::free_p(This *borrow this) { + free((int *)this->p); } void test1() { struct A a = { .p = malloc_int(5) }; *a.p = 6; - int * borrow q1 = a.get_p_mut(); // expected-note {{previous borrow is here}} - const int * borrow q2 = a.get_p_immut(); // expected-note {{previous borrow is here}} expected-error {{Can not modify 'a' because be borrowed}} - a.free_p(); // expected-error {{Can not modify 'a' because be borrowed}} + int *borrow q1 = a.get_p_mut(); // expected-note {{first mut borrow occurs here}} + // expected-note@-1 {{first mut borrow occurs here}} + const int *borrow q2 = a.get_p_immut(); // expected-error {{cannot borrow `a` as mutable more than once at a time}} + a.free_p(); // expected-error {{cannot borrow `a` as mutable more than once at a time}} int tmp2 = *q2; int tmp1 = *q1; } @@ -33,7 +34,7 @@ void test1() { void test2() { struct A a = { .p = malloc_int(5) }; *a.p = 6; - int * borrow q = a.get_p_mut(); // expected-note {{previous borrow is here}} - a.free_p(); // expected-error {{Can not modify 'a' because be borrowed}} + int *borrow q = a.get_p_mut(); // expected-note {{first mut borrow occurs here}} + a.free_p(); // expected-error {{cannot borrow `a` as mutable more than once at a time}} int tmp = *q; } \ No newline at end of file diff --git a/clang/test/BSC/Negative/Ownership/Borrow/borrow_trait/borrow_trait.cbs b/clang/test/BSC/Negative/Ownership/Borrow/borrow_trait/borrow_trait.cbs index 5eabbefda410..5535d154a181 100644 --- a/clang/test/BSC/Negative/Ownership/Borrow/borrow_trait/borrow_trait.cbs +++ b/clang/test/BSC/Negative/Ownership/Borrow/borrow_trait/borrow_trait.cbs @@ -1,4 +1,5 @@ // RUN: %clang_cc1 -verify %s +// expected-no-diagnostics trait T { static int f(This* this); @@ -13,9 +14,9 @@ impl trait T for int; int main() { int a = 1; int* t = &a; - trait T* borrow p = &mut *t; // expected-note {{previous borrow is here}} + trait T* borrow p = &mut *t; a = 2; - t = &a; // expected-error {{Can not modify 't' because be borrowed}} + t = &a; p->f(); return 0; } \ No newline at end of file diff --git a/clang/test/BSC/Positive/Driver/rewrite-bsc/Ownership/rewrite_bsc_borrow/rewrite_bsc_borrow.cbs b/clang/test/BSC/Positive/Driver/rewrite-bsc/Ownership/rewrite_bsc_borrow/rewrite_bsc_borrow.cbs index 43b6165ff56e..5d21dbcedd28 100644 --- a/clang/test/BSC/Positive/Driver/rewrite-bsc/Ownership/rewrite_bsc_borrow/rewrite_bsc_borrow.cbs +++ b/clang/test/BSC/Positive/Driver/rewrite-bsc/Ownership/rewrite_bsc_borrow/rewrite_bsc_borrow.cbs @@ -19,15 +19,15 @@ void test1() { int x1 = 0; int x = 0; float y = 1.0; + const int * borrow p2 = &const x; + const int * borrow p4= (const int * borrow)&x; + int * borrow p7 = &mut *(&x); int * borrow p = &mut x; float * borrow p1 = &mut y; - const int * borrow p2 = &const x; myInt p3 = p; - const int * borrow p4= (const int * borrow)&x; int * owned p5 = (int * owned)&x1; const int * borrow b6 = &const *p3; - int * borrow p7 = &mut *(&x); p7 = &mut *p; int * p8 = (int *)p5; } @@ -59,14 +59,14 @@ int main() { // CHECK-NEXT: int x1 = 0; // CHECK-NEXT: int x = 0; // CHECK-NEXT: float y = 1.; +// CHECK-NEXT: const int * p2 = &x; +// CHECK-NEXT: const int * p4 = (const int *)&x; +// CHECK-NEXT: int * p7 = &*(&x); // CHECK-NEXT: int * p = &x; // CHECK-NEXT: float * p1 = &y; -// CHECK-NEXT: const int * p2 = &x; // CHECK-NEXT: myInt p3 = p; -// CHECK-NEXT: const int * p4 = (const int *)&x; // CHECK-NEXT: int * p5 = (int *)&x1; // CHECK-NEXT: const int * b6 = &*p3; -// CHECK-NEXT: int * p7 = &*(&x); // CHECK-NEXT: p7 = &*p; // CHECK-NEXT: int *p8 = (int *)p5; // CHECK-NEXT: } diff --git a/clang/test/BSC/Positive/OperatorOverload/Subscript/Subscript.cbs b/clang/test/BSC/Positive/OperatorOverload/Subscript/Subscript.cbs index c866c90474a0..b2630f15530d 100644 --- a/clang/test/BSC/Positive/OperatorOverload/Subscript/Subscript.cbs +++ b/clang/test/BSC/Positive/OperatorOverload/Subscript/Subscript.cbs @@ -150,7 +150,9 @@ int test5() { struct DataArray dataArray; dataArray[0].data = 1; dataArray[1].data = 2; - if (dataArray[0].data + dataArray[1].data != 3) { + int data1 = dataArray[0].data; + int data2 = dataArray[1].data; + if (data1 + data2 != 3) { return -1; } return 0; @@ -309,7 +311,9 @@ int main() { // CHECK-NEXT: struct DataArray_int dataArray; // CHECK-NEXT: (*deref5_int(&dataArray, 0)).data = 1; // CHECK-NEXT: (*deref5_int(&dataArray, 1)).data = 2; -// CHECK-NEXT: if ((*deref5_int(&dataArray, 0)).data + (*deref5_int(&dataArray, 1)).data != 3) { +// CHECK-NEXT: int data1 = (*deref5_int(&dataArray, 0)).data; +// CHECK-NEXT: int data2 = (*deref5_int(&dataArray, 1)).data; +// CHECK-NEXT: if (data1 + data2 != 3) { // CHECK-NEXT: return -1; // CHECK-NEXT: } // CHECK-NEXT: return 0; diff --git a/clang/test/BSC/Positive/OperatorOverload/deref/deref.cbs b/clang/test/BSC/Positive/OperatorOverload/deref/deref.cbs index 04f9e4652105..e73b847fa9c6 100644 --- a/clang/test/BSC/Positive/OperatorOverload/deref/deref.cbs +++ b/clang/test/BSC/Positive/OperatorOverload/deref/deref.cbs @@ -176,14 +176,12 @@ int main() { // CHECK-DAG: struct I { // CHECK-DAG-NEXT: int a; // CHECK-DAG-NEXT: }; -// CHECK: struct Rc_int { -// CHECK-NEXT: struct RcData_int *ptr; -// CHECK-NEXT: }; -// CHECK-EMPTY: -// CHECK: struct RcData_int { -// CHECK-NEXT: int data; -// CHECK-NEXT: }; -// CHECK-EMPTY: +// CHECK-DAG: struct Rc_int { +// CHECK-DAG-NEXT: struct RcData_int *ptr; +// CHECK-DAG-NEXT: }; +// CHECK-DAG: struct RcData_int { +// CHECK-DAG-NEXT: int data; +// CHECK-DAG-NEXT: }; // CHECK-DAG: struct F { // CHECK-DAG-NEXT: struct E e; // CHECK-DAG-NEXT: }; diff --git a/clang/test/BSC/Positive/OperatorOverload/member_ref/member_ref.cbs b/clang/test/BSC/Positive/OperatorOverload/member_ref/member_ref.cbs index e5aea663a858..eb67c744f55a 100644 --- a/clang/test/BSC/Positive/OperatorOverload/member_ref/member_ref.cbs +++ b/clang/test/BSC/Positive/OperatorOverload/member_ref/member_ref.cbs @@ -224,40 +224,35 @@ int main(){ // CHECK-NEXT: struct RcData_int; // CHECK-NEXT: struct Rc_int; // CHECK-NEXT: struct RcRecord_int; -// CHECK-DAG: struct A { +// CHECK-DAG: struct A { // CHECK-DAG-NEXT: int m; // CHECK-DAG-NEXT: }; // CHECK-DAG: struct AP { // CHECK-DAG-NEXT: struct A *a; // CHECK-DAG-NEXT: }; -// CHECK-DAG: struct B { -// CHECK-DAG-NEXT: int x; -// CHECK-DAG-NEXT: int y; -// CHECK-DAG-NEXT: }; +// CHECK-DAG-: struct B { +// CHECK-DAG--NEXT: int x; +// CHECK-DAG--NEXT: int y; +// CHECK-DAG--NEXT: }; // CHECK-DAG: struct BP { // CHECK-DAG-NEXT: struct A *a; // CHECK-DAG-NEXT: struct B *b; // CHECK-DAG-NEXT: }; -// CHECK: struct CP { -// CHECK-NEXT: struct A * a; -// CHECK-NEXT: }; -// CHECK-EMPTY: -// CHECK: struct DP { -// CHECK-NEXT: struct A * a; -// CHECK-NEXT: }; -// CHECK-EMPTY: -// CHECK: struct FP { -// CHECK-NEXT: struct A* a; -// CHECK-NEXT: }; -// CHECK-EMPTY: -// CHECK: struct Rc_int { -// CHECK-NEXT: struct RcData_int *ptr; -// CHECK-NEXT: }; -// CHECK-EMPTY: -// CHECK: struct RcData_int { -// CHECK-NEXT: int data; -// CHECK-NEXT: }; -// CHECK-EMPTY: +// CHECK-DAG: struct CP { +// CHECK-DAG-NEXT: struct A * a; +// CHECK-DAG-NEXT: }; +// CHECK-DAG: struct DP { +// CHECK-DAG-NEXT: struct A * a; +// CHECK-DAG-NEXT: }; +// CHECK-DAG: struct FP { +// CHECK-DAG-NEXT: struct A* a; +// CHECK-DAG-NEXT: }; +// CHECK-DAG: struct Rc_int { +// CHECK-DAG-NEXT: struct RcData_int *ptr; +// CHECK-DAG-NEXT: }; +// CHECK-DAG: struct RcData_int { +// CHECK-DAG-NEXT: int data; +// CHECK-DAG-NEXT: }; // CHECK: struct GP { // CHECK-NEXT: struct FP fp; // CHECK-NEXT: }; diff --git a/clang/test/BSC/Positive/Ownership/borrow_check_rules/at_most_one_mut_borrow/at_most_one_mut_borrow.cbs b/clang/test/BSC/Positive/Ownership/borrow_check_rules/at_most_one_mut_borrow/at_most_one_mut_borrow.cbs index 800404859dea..2e689a722a86 100644 --- a/clang/test/BSC/Positive/Ownership/borrow_check_rules/at_most_one_mut_borrow/at_most_one_mut_borrow.cbs +++ b/clang/test/BSC/Positive/Ownership/borrow_check_rules/at_most_one_mut_borrow/at_most_one_mut_borrow.cbs @@ -43,11 +43,11 @@ void test2() { void test3() { int a = 5; - int b = 5; + int b = 5, c = 5; int *owned oriPtr = safe_malloc(0); int *borrow p1 = &mut a; int *borrow p2 = &mut b; - if (a > 0) + if (c > 0) p1 = &mut * oriPtr; else p2 = &mut * oriPtr; @@ -66,8 +66,8 @@ void test4() { p2 = &mut * oriPtr; p1 = &mut * oriPtr; } else { // Dead Code - p1 = &mut * oriPtr; p2 = &mut * oriPtr; + p1 = &mut * oriPtr; } use_mut(p1); // use_mut(p2); @@ -85,6 +85,7 @@ void test5() { // use_mut(p1); } + // CHECK-L: #line 11 "{{.*}}.cbs" // CHECK-L-NEXT: void use_mut(int * p) { // CHECK-L-NEXT: } @@ -116,13 +117,13 @@ void test5() { // CHECK-L: #line 44 "{{.*}}.cbs" // CHECK-L-NEXT: void test3(void) { // CHECK-L-NEXT: int a = 5; -// CHECK-L-NEXT: int b = 5; +// CHECK-L-NEXT: int b = 5, c = 5; // CHECK-L-NEXT: int * oriPtr = safe_malloc_int(0); // CHECK-L-NEXT: int * p1 = &a; // CHECK-L-NEXT: int * p2 = &b; -// CHECK-L-NEXT: if (a > 0) +// CHECK-L-NEXT: if (c > 0) // CHECK-L-NEXT: p1 = &*oriPtr; -// CHECK-L-NEXT: else +// CHECK-L-NEXT: else // CHECK-L-NEXT: p2 = &*oriPtr; // CHECK-L-NEXT: use_mut(p1); // CHECK-L-NEXT: use_mut(p2); @@ -140,8 +141,8 @@ void test5() { // CHECK-L-NEXT: p2 = &*oriPtr; // CHECK-L-NEXT: p1 = &*oriPtr; // CHECK-L-NEXT: } else { -// CHECK-L-NEXT: p1 = &*oriPtr; // CHECK-L-NEXT: p2 = &*oriPtr; +// CHECK-L-NEXT: p1 = &*oriPtr; // CHECK-L-NEXT: } // CHECK-L-NEXT: use_mut(p1); // CHECK-L-NEXT: free_owned_int(oriPtr); diff --git a/clang/test/BSC/Positive/Ownership/borrow_check_rules/borrow_with_assert/borrow_with_assert.cbs b/clang/test/BSC/Positive/Ownership/borrow_check_rules/borrow_with_assert/borrow_with_assert.cbs new file mode 100644 index 000000000000..554bc18c8dda --- /dev/null +++ b/clang/test/BSC/Positive/Ownership/borrow_check_rules/borrow_with_assert/borrow_with_assert.cbs @@ -0,0 +1,20 @@ +// RUN: %clang %s -o %test.output +// RUN: %test.output +// RUN: %clang -rewrite-bsc %s -o %t-rw.c +// RUN: %clang %t-rw.c -o %t-rw.output +// RUN: %t-rw.output +// expected-no-diagnostics + +#include + +void test() { + int x = 1; + int* borrow p = &mut x; + *p = 2; + assert(*p == 2); +} + +int main() { + test(); + return 0; +} diff --git a/clang/test/BSC/Positive/Ownership/borrow_check_rules/read_when_mut_borrowed/read_when_mut_borrowed.cbs b/clang/test/BSC/Positive/Ownership/borrow_check_rules/read_when_mut_borrowed/read_when_mut_borrowed.cbs new file mode 100644 index 000000000000..736c8111aff7 --- /dev/null +++ b/clang/test/BSC/Positive/Ownership/borrow_check_rules/read_when_mut_borrowed/read_when_mut_borrowed.cbs @@ -0,0 +1,24 @@ +// RUN: %clang %s -o %test.output +// RUN: %test.output +// RUN: %clang -rewrite-bsc %s -o %t-rw.c +// RUN: %clang %t-rw.c -o %t-rw.output +// RUN: %t-rw.output +// expected-no-diagnostics + +#include + +struct Foo { + int a; +}; + +void use_immut(const struct Foo *borrow p) { + int b = p->a; +} + +int main() { + struct Foo foo = {.a = 1}; + const struct Foo *borrow foo_const_mut = &const foo; + printf("%d\n", foo.a); + use_immut(foo_const_mut); + return 0; +} \ No newline at end of file diff --git a/clang/test/BSC/Positive/Ownership/borrow_check_rules/return_borrow/return_borrow.cbs b/clang/test/BSC/Positive/Ownership/borrow_check_rules/return_borrow/return_borrow.cbs index b9bf76fe82ce..df163c431250 100644 --- a/clang/test/BSC/Positive/Ownership/borrow_check_rules/return_borrow/return_borrow.cbs +++ b/clang/test/BSC/Positive/Ownership/borrow_check_rules/return_borrow/return_borrow.cbs @@ -72,7 +72,7 @@ int* borrow test11(int *borrow p, int a) { if (1) { return p; } - return &mut a; // Dead Code, this ReturnStmt is unreachable. + // return &mut a; // Dead Code, this ReturnStmt is unreachable. } int* borrow test12(int* borrow p) { @@ -81,7 +81,7 @@ int* borrow test12(int* borrow p) { free_owned(oriPtr); return p; } - return &mut * oriPtr; // Dead Code, this ReturnStmt is unreachable. +// return &mut * oriPtr; // Dead Code, this ReturnStmt is unreachable. } struct S { int * borrow p; }; @@ -96,16 +96,17 @@ struct S test14(struct S s, int* borrow p) { int main() { int local = 5; + int *p = &local; test1(&const local); test2(&mut local); test3(&const local); test4(&mut local); test5(&mut local); - test6(&mut local, &local); - test7(&mut local, &local); + test6(&mut local, p); + test7(&mut local, p); test8(&mut local); test9(&mut local); - test10(&mut local, &local); + test10(&mut local, p); test11(&mut local, 5); test12(&mut local); struct S s = { .p = &mut local }; diff --git a/clang/test/BSC/Positive/Ownership/borrow_check_rules/return_borrow/return_not_local_borrow.cbs b/clang/test/BSC/Positive/Ownership/borrow_check_rules/return_borrow/return_not_local_borrow.cbs index 6c5f9b336bf7..b642b6835cf6 100644 --- a/clang/test/BSC/Positive/Ownership/borrow_check_rules/return_borrow/return_not_local_borrow.cbs +++ b/clang/test/BSC/Positive/Ownership/borrow_check_rules/return_borrow/return_not_local_borrow.cbs @@ -7,11 +7,11 @@ struct S { - int* borrow a; + int *borrow a; }; struct S f(struct S ss) { - struct S* ptr = &ss; + struct S *ptr = &ss; struct S t = *ptr; return t; } @@ -26,4 +26,4 @@ int main() { struct S k = *m; *k.a = 10; struct S j = **n; -} +} \ No newline at end of file diff --git a/clang/test/BSC/Positive/Ownership/borrow_check_rules/return_borrow_from_raw_pointer/return_borrow_from_raw_pointer.cbs b/clang/test/BSC/Positive/Ownership/borrow_check_rules/return_borrow_from_raw_pointer/return_borrow_from_raw_pointer.cbs index 48b98d930446..e163e8ead79b 100644 --- a/clang/test/BSC/Positive/Ownership/borrow_check_rules/return_borrow_from_raw_pointer/return_borrow_from_raw_pointer.cbs +++ b/clang/test/BSC/Positive/Ownership/borrow_check_rules/return_borrow_from_raw_pointer/return_borrow_from_raw_pointer.cbs @@ -14,108 +14,101 @@ struct Q { }; int *borrow test1(struct P *borrow p) { - return &mut *p->b; + return &mut *p->b; } int *borrow test2(struct P *p, int *borrow p1) { - return &mut *p->b; + return &mut *p->b; } int *borrow test3(struct P p, int *borrow p1) { - return &mut *p.b; + return &mut *p.b; } int *borrow test4(struct Q *borrow q) { - return &mut *q->g.b; + return &mut *q->g.b; } int *borrow test5(struct Q *q, int *borrow p) { - return &mut *q->g.b; + return &mut *q->g.b; } int *borrow test6(struct Q q, int *borrow p) { - return &mut *q.g.b; + return &mut *q.g.b; } int *borrow test7(int *borrow p) { - struct Q q; - int *borrow r = &mut *q.g.b; - return r; + struct Q q; + int *borrow r = &mut *q.g.b; + return r; } int *borrow test8(int *borrow p) { - struct Q q; - struct Q *q1 = &q; - int *borrow r = &mut *q1->g.b; - return r; -} - -int *borrow test9(int *borrow p) { - struct Q q; - struct Q *borrow q1 = &mut q; - int *borrow r = &mut *q1->g.b; - return r; + struct Q q; + struct Q *q1 = &q; + int *borrow r = &mut *q1->g.b; + return r; } int *borrow test10(struct P *borrow p, int index) { - return &mut p->b[index]; + return &mut p->b[index]; } int *borrow test11(struct P *p, int index, int *borrow p1) { - return &mut p->b[index]; + return &mut p->b[index]; } -int *borrow test12(struct P p, int index, int *borrow p1) { - return &mut p.b[index]; -} +// int *borrow test12(struct P p, int index, int *borrow p1) { +// return &mut p.b[index]; +// } int *borrow test13(struct Q *borrow q, int index) { - return &mut q->g.b[index]; + return &mut q->g.b[index]; } int *borrow test14(struct Q *q, int index, int *borrow p) { - return &mut q->g.b[index]; + return &mut q->g.b[index]; } -int *borrow test15(struct Q q, int index, int *borrow p) { - return &mut q.g.b[index]; -} +// int *borrow test15(struct Q q, int index, int *borrow p) { +// return &mut q.g.b[index]; +// } struct M { - int* owned a; + int *owned a; }; struct N { - struct M* m; + struct M *m; }; int *borrow test16(struct N n, int *borrow p) { - return &mut *n.m->a; + return &mut *n.m->a; } int *owned *borrow test17(struct N n, int *borrow p) { - return &mut n.m->a; + return &mut n.m->a; } int *borrow struct N::test18(This* borrow this) { - return &mut *this->m->a; + return &mut *this->m->a; } int *borrow struct N::test19(This* this, int* borrow p) { - return &mut *this->m->a; + return &mut *this->m->a; } owned struct S { public: - int *owned *borrow a; //成员变量为 owned borrow - int *owned *borrow test20(S *this) { - int *owned *borrow a = this->a; - return a; - } + int *owned *borrow a; + int *owned *borrow test20(S *this) { + int *owned *borrow a = this->a; + return a; + } }; int *owned *borrow S::test21(This* this) { - return this->a; + return this->a; } int main() { @@ -130,14 +123,13 @@ int main() { test6(q, &mut local); test7(&mut local); test8(&mut local); - test9(&mut local); test10(&mut p, 5); test11(&p, 5, &mut local); - test12(p, 5, &mut local); + // test12(p, 5, &mut local); test13(&mut q, 5); test14(&q, 5, &mut local); - test15(q, 5, &mut local); - int *owned c = (int* owned)&local; + // test15(q, 5, &mut local); + int *owned c = (int *owned)&local; struct M m = { .a = c }; struct N n = { .m = &m }; test16(n, &mut local); @@ -147,6 +139,6 @@ int main() { S s = { .a = &mut m.a }; s.test20(); s.test21(); - int* d = (int*) m.a; + int *d = (int *) m.a; return 0; } \ No newline at end of file diff --git a/clang/test/BSC/Positive/Ownership/borrow_check_rules/struct_with_borrow_init_by_func_call/struct_with_borrow_init_by_func_call.cbs b/clang/test/BSC/Positive/Ownership/borrow_check_rules/struct_with_borrow_init_by_func_call/struct_with_borrow_init_by_func_call.cbs index 9829bf01c94d..b29e02bab755 100644 --- a/clang/test/BSC/Positive/Ownership/borrow_check_rules/struct_with_borrow_init_by_func_call/struct_with_borrow_init_by_func_call.cbs +++ b/clang/test/BSC/Positive/Ownership/borrow_check_rules/struct_with_borrow_init_by_func_call/struct_with_borrow_init_by_func_call.cbs @@ -6,37 +6,39 @@ // expected-no-diagnostics #include + struct S { - int *borrow p1; - const int *borrow p2; + int *borrow p1; + const int *borrow p2; }; struct S struct S::new(int *borrow p1, const int *borrow p2) { - struct S s = { .p1 = p1, .p2 = p2 }; - return s; + struct S s = { .p1 = p1, .p2 = p2 }; + return s; } + void use(struct S s) {} int main() { - int a = 5; - int b = 10; - int* p1 = &a; - int* p2 = &b; - int* owned p3 = (int *owned)malloc(sizeof(int)); - int* owned p4 = (int *owned)malloc(sizeof(int)); - struct S s1 = struct S::new(&mut a, &const b); - struct S s2 = struct S::new(&mut *p1, &const *p2); - struct S s3 = struct S::new(&mut *p3, &const *p4); - use(s1); - use(s2); - use(s3); - s1 = struct S::new(&mut a, &const b); - s2 = struct S::new(&mut *p1, &const *p2); - s3 = struct S::new(&mut *p3, &const *p4); - use(s1); - use(s2); - use(s3); - free((int*)p3); - free((int*)p4); - return 0; + int a = 5; + int b = 10; + int *p1 = &a; + int *p2 = &b; + int *owned p3 = (int *owned)malloc(sizeof(int)); + int *owned p4 = (int *owned)malloc(sizeof(int)); + struct S s1 = struct S::new(&mut a, &const b); + struct S s2 = struct S::new(&mut *p1, &const *p2); + struct S s3 = struct S::new(&mut *p3, &const *p4); + use(s1); + use(s2); + use(s3); + s1 = struct S::new(&mut a, &const b); + s2 = struct S::new(&mut *p1, &const *p2); + s3 = struct S::new(&mut *p3, &const *p4); + use(s1); + use(s2); + use(s3); + free((int *)p3); + free((int *)p4); + return 0; } \ No newline at end of file diff --git a/clang/test/BSC/Positive/Ownership/borrow_check_rules/struct_with_borrow_init_by_func_call2/struct_with_borrow_init_by_func_call2.cbs b/clang/test/BSC/Positive/Ownership/borrow_check_rules/struct_with_borrow_init_by_func_call2/struct_with_borrow_init_by_func_call2.cbs index 610b39620784..d1d882934698 100644 --- a/clang/test/BSC/Positive/Ownership/borrow_check_rules/struct_with_borrow_init_by_func_call2/struct_with_borrow_init_by_func_call2.cbs +++ b/clang/test/BSC/Positive/Ownership/borrow_check_rules/struct_with_borrow_init_by_func_call2/struct_with_borrow_init_by_func_call2.cbs @@ -6,38 +6,37 @@ // expected-no-diagnostics #include + owned struct Option { public: - T * owned inner; - ~Option(This this) { - free((T*)this.inner); - } + T *owned inner; + ~Option(This this) { + free((T *)this.inner); + } }; owned struct OptionRefMut { public: - T *borrow p; + T *borrow p; }; safe OptionRefMut OptionRefMut::Some(T *borrow p) { - OptionRefMut rm = { .p = p }; - return rm; + OptionRefMut rm = { .p = p }; + return rm; }; -safe OptionRefMut Option::as_mut_1(Option* borrow this) -{ - OptionRefMut r = OptionRefMut::Some(&mut *this->inner); - return r; +safe OptionRefMut Option::as_mut_1(Option *borrow this) { + OptionRefMut r = OptionRefMut::Some(&mut *this->inner); + return r; } -safe OptionRefMut Option::as_mut_2(Option* borrow this) -{ - return OptionRefMut::Some(&mut *this->inner); +safe OptionRefMut Option::as_mut_2(Option *borrow this) { + return OptionRefMut::Some(&mut *this->inner); } int main() { - Option op = { .inner = (int *owned)malloc(sizeof(int)) }; - OptionRefMut rm1 = op.as_mut_1(); - OptionRefMut rm2 = op.as_mut_2(); - return 0; + Option op = { .inner = (int *owned)malloc(sizeof(int)) }; + OptionRefMut rm1 = op.as_mut_1(); + OptionRefMut rm2 = op.as_mut_2(); + return 0; } \ No newline at end of file diff --git a/clang/test/BSC/Positive/Ownership/borrow_check_rules/virtual_target_live_longer/virtual_target_live_longer.cbs b/clang/test/BSC/Positive/Ownership/borrow_check_rules/virtual_target_live_longer/virtual_target_live_longer.cbs index 778ef8403155..1c89e1837609 100644 --- a/clang/test/BSC/Positive/Ownership/borrow_check_rules/virtual_target_live_longer/virtual_target_live_longer.cbs +++ b/clang/test/BSC/Positive/Ownership/borrow_check_rules/virtual_target_live_longer/virtual_target_live_longer.cbs @@ -4,37 +4,45 @@ // RUN: %clang %t-rw.c -o %t-rw.output // RUN: %t-rw.output // expected-no-diagnostics + #include + +struct S { + int a; + int *borrow p1; + int *owned p2; +}; + void use_mut(int *borrow p) {} -T* owned safe_malloc(T value) { - T * p = (T *) malloc( sizeof(T) ); + +T *owned safe_malloc(T value) { + T *p = (T *) malloc( sizeof(T) ); *p = value; - return (T* owned)p; + return (T *owned)p; } -void free_owned(T* owned p) { - free( (T*)p ); +void free_owned(T *owned p) { + free( (T *)p ); } void test1() { int *a = (int *)malloc(sizeof(int)); - int *borrow p = &mut * a; + int *borrow p = &mut *a; use_mut(p); free(a); } -void test2(int* a) { - int *borrow p = &mut * a; +void test2(int *a) { + int *borrow p = &mut *a; use_mut(p); free(a); } void test3(int *borrow p) { - int *borrow p1 = &mut * p; + int *borrow p1 = &mut *p; use_mut(p1); } -struct S { int a; int * borrow p1; int* owned p2; }; void test4(struct S s) { int *borrow p3 = &mut s.a; use_mut(p3); diff --git a/clang/test/BSC/Positive/Ownership/borrow_serialization/borrow_serialization.cbs b/clang/test/BSC/Positive/Ownership/borrow_serialization/borrow_serialization.cbs index 9d9d7e050170..0ca8227f8903 100644 --- a/clang/test/BSC/Positive/Ownership/borrow_serialization/borrow_serialization.cbs +++ b/clang/test/BSC/Positive/Ownership/borrow_serialization/borrow_serialization.cbs @@ -13,30 +13,30 @@ // RUN: | FileCheck --strict-whitespace %s struct R { - int * borrow p1; - const int * borrow p2; + int *borrow p1; + const int *borrow p2; }; -void int::foo(This * borrow this) {} +void int::foo(This *borrow this) {} -typedef int * borrow myInt; -typedef int* borrow (*PF2)(struct R); +typedef int *borrow myInt; +typedef int *borrow (*PF2)(struct R); void test() { - int x1 = 0; - int x = 0; - float y = 1.0; - int * borrow p = &mut x; - float * borrow p1 = &mut y; - const int * borrow p2 = &const x; - myInt p3 = p; - const int * borrow p4= (const int * borrow)&x; + int x1 = 0; + int x = 0; + float y = 1.0; + const int *borrow p4= (const int *borrow)&x; + const int *borrow p2 = &const x; + int *borrow p7 = &mut *(&x); + int *borrow p = &mut x; + float *borrow p1 = &mut y; + myInt p3 = p; - int * owned p5 = (int * owned)&x1; - const int * borrow b6 = &const *p3; - int * borrow p7 = &mut *(&x); - p7 = &mut *p; - int * p8 = (int *)p5; + int *owned p5 = (int *owned)&x1; + const int *borrow b6 = &const *p3; + p7 = &mut *p; + int *p8 = (int *)p5; } int main() { @@ -45,52 +45,52 @@ int main() { // CHECK: FunctionDecl 0x{{[^ ]*}} line:[[@LINE-21]]:6 test 'void (void)' // CHECK-NEXT: CompoundStmt 0x{{[^ ]*}} -// CHECK-NEXT: DeclStmt 0x{{[^ ]*}} -// CHECK-NEXT: VarDecl 0x{{[^ ]*}} col:9 used x1 'int' cinit -// CHECK-NEXT: IntegerLiteral 0x{{[^ ]*}} 'int' 0 -// CHECK-NEXT: DeclStmt 0x{{[^ ]*}} -// CHECK-NEXT: VarDecl 0x{{[^ ]*}} col:9 used x 'int' cinit -// CHECK-NEXT: IntegerLiteral 0x{{[^ ]*}} 'int' 0 -// CHECK-NEXT: DeclStmt 0x{{[^ ]*}} -// CHECK-NEXT: VarDecl 0x{{[^ ]*}} col:11 used y 'float' cinit -// CHECK-NEXT: ImplicitCastExpr 0x{{[^ ]*}} 'float' -// CHECK-NEXT: FloatingLiteral 0x{{[^ ]*}} 'double' 1.000000e+00 -// CHECK-NEXT: DeclStmt 0x{{[^ ]*}} -// CHECK-NEXT: VarDecl 0x{{[^ ]*}} col:18 used p 'int *borrow' cinit -// CHECK-NEXT: UnaryOperator 0x{{[^ ]*}} 'int *borrow' prefix '&' cannot overflow -// CHECK-NEXT: DeclRefExpr 0x{{[^ ]*}} 'int' lvalue Var 0x{{[^ ]*}} 'x' 'int' -// CHECK-NEXT: DeclStmt 0x{{[^ ]*}} -// CHECK-NEXT: VarDecl 0x{{[^ ]*}} col:20 p1 'float *borrow' cinit -// CHECK-NEXT: UnaryOperator 0x{{[^ ]*}} 'float *borrow' prefix '&' cannot overflow -// CHECK-NEXT: DeclRefExpr 0x{{[^ ]*}} 'float' lvalue Var 0x{{[^ ]*}} 'y' 'float' -// CHECK-NEXT: DeclStmt 0x{{[^ ]*}} -// CHECK-NEXT: VarDecl 0x{{[^ ]*}} col:24 p2 'const int *borrow' cinit -// CHECK-NEXT: UnaryOperator 0x{{[^ ]*}} 'const int *borrow' prefix '&' cannot overflow -// CHECK-NEXT: DeclRefExpr 0x{{[^ ]*}} 'int' lvalue Var 0x{{[^ ]*}} 'x' 'int' -// CHECK-NEXT: DeclStmt 0x{{[^ ]*}} -// CHECK-NEXT: VarDecl 0x{{[^ ]*}} col:11 used p3 'myInt':'int *borrow' cinit -// CHECK-NEXT: ImplicitCastExpr 0x{{[^ ]*}} 'int *borrow' -// CHECK-NEXT: DeclRefExpr 0x{{[^ ]*}} 'int *borrow' lvalue Var 0x{{[^ ]*}} 'p' 'int *borrow' -// CHECK-NEXT: DeclStmt 0x{{[^ ]*}} -// CHECK-NEXT: VarDecl 0x{{[^ ]*}} col:24 p4 'const int *borrow' cinit -// CHECK-NEXT: CStyleCastExpr 0x{{[^ ]*}} 'const int *borrow' -// CHECK-NEXT: UnaryOperator 0x{{[^ ]*}} 'int *' prefix '&' cannot overflow -// CHECK-NEXT: DeclRefExpr 0x{{[^ ]*}} 'int' lvalue Var 0x{{[^ ]*}} 'x' 'int' -// CHECK-NEXT: DeclStmt 0x{{[^ ]*}} -// CHECK-NEXT: VarDecl 0x{{[^ ]*}} col:17 used p5 'int *owned' cinit -// CHECK-NEXT: CStyleCastExpr 0x{{[^ ]*}} 'int *owned' -// CHECK-NEXT: UnaryOperator 0x{{[^ ]*}} 'int *' prefix '&' cannot overflow -// CHECK-NEXT: DeclRefExpr 0x{{[^ ]*}} 'int' lvalue Var 0x{{[^ ]*}} 'x1' 'int' -// CHECK-NEXT: DeclStmt 0x{{[^ ]*}} -// CHECK-NEXT: VarDecl 0x{{[^ ]*}} col:24 b6 'const int *borrow' cinit -// CHECK-NEXT: ImplicitCastExpr 0x{{[^ ]*}} 'int *borrow' -// CHECK-NEXT: DeclRefExpr 0x{{[^ ]*}} 'myInt':'int *borrow' lvalue Var 0x{{[^ ]*}} 'p3' 'myInt':'int *borrow' -// CHECK-NEXT: DeclStmt 0x{{[^ ]*}} -// CHECK-NEXT: VarDecl 0x{{[^ ]*}} col:18 used p7 'int *borrow' cinit -// CHECK-NEXT: ParenExpr 0x{{[^ ]*}} 'int *' -// CHECK-NEXT: UnaryOperator 0x{{[^ ]*}} 'int *' prefix '&' cannot overflow -// CHECK-NEXT: DeclRefExpr 0x{{[^ ]*}} 'int' lvalue Var 0x{{[^ ]*}} 'x' 'int' -// CHECK-NEXT: BinaryOperator 0x{{[^ ]*}} 'int *borrow' '=' -// CHECK-NEXT: DeclRefExpr 0x{{[^ ]*}} 'int *borrow' lvalue Var 0x{{[^ ]*}} 'p7' 'int *borrow' -// CHECK-NEXT: ImplicitCastExpr 0x{{[^ ]*}} 'int *borrow' -// CHECK-NEXT: DeclRefExpr 0x{{[^ ]*}} 'int *borrow' lvalue Var 0x{{[^ ]*}} 'p' 'int *borrow' \ No newline at end of file +// CHECK-NEXT: DeclStmt 0x{{[^ ]*}} +// CHECK-NEXT: VarDecl 0x{{[^ ]*}} col:7 used x1 'int' cinit +// CHECK-NEXT: IntegerLiteral 0x{{[^ ]*}} 'int' 0 +// CHECK-NEXT: DeclStmt 0x{{[^ ]*}} +// CHECK-NEXT: VarDecl 0x{{[^ ]*}} col:7 used x 'int' cinit +// CHECK-NEXT: IntegerLiteral 0x{{[^ ]*}} 'int' 0 +// CHECK-NEXT: DeclStmt 0x{{[^ ]*}} +// CHECK-NEXT: VarDecl 0x{{[^ ]*}} col:9 used y 'float' cinit +// CHECK-NEXT: ImplicitCastExpr 0x{{[^ ]*}} 'float' +// CHECK-NEXT: FloatingLiteral 0x{{[^ ]*}} 'double' 1.000000e+00 +// CHECK-NEXT: DeclStmt 0x{{[^ ]*}} +// CHECK-NEXT: VarDecl 0x{{[^ ]*}} col:21 p4 'const int *borrow' cinit +// CHECK-NEXT: CStyleCastExpr 0x{{[^ ]*}} 'const int *borrow' +// CHECK-NEXT: UnaryOperator 0x{{[^ ]*}} 'int *' prefix '&' cannot overflow +// CHECK-NEXT: DeclRefExpr 0x{{[^ ]*}} 'int' lvalue Var 0x{{[^ ]*}} 'x' 'int' +// CHECK-NEXT: DeclStmt 0x{{[^ ]*}} +// CHECK-NEXT: VarDecl 0x{{[^ ]*}} col:21 p2 'const int *borrow' cinit +// CHECK-NEXT: UnaryOperator 0x{{[^ ]*}} 'const int *borrow' prefix '&' cannot overflow +// CHECK-NEXT: DeclRefExpr 0x{{[^ ]*}} 'int' lvalue Var 0x{{[^ ]*}} 'x' 'int' +// CHECK-NEXT: DeclStmt 0x{{[^ ]*}} +// CHECK-NEXT: VarDecl 0x{{[^ ]*}} col:15 used p7 'int *borrow' cinit +// CHECK-NEXT: ParenExpr 0x{{[^ ]*}} 'int *' +// CHECK-NEXT: UnaryOperator 0x{{[^ ]*}} 'int *' prefix '&' cannot overflow +// CHECK-NEXT: DeclRefExpr 0x{{[^ ]*}} 'int' lvalue Var 0x{{[^ ]*}} 'x' 'int' +// CHECK-NEXT: DeclStmt 0x{{[^ ]*}} +// CHECK-NEXT: VarDecl 0x{{[^ ]*}} col:15 used p 'int *borrow' cinit +// CHECK-NEXT: UnaryOperator 0x{{[^ ]*}} 'int *borrow' prefix '&' cannot overflow +// CHECK-NEXT: DeclRefExpr 0x{{[^ ]*}} 'int' lvalue Var 0x{{[^ ]*}} 'x' 'int' +// CHECK-NEXT: DeclStmt 0x{{[^ ]*}} +// CHECK-NEXT: VarDecl 0x{{[^ ]*}} col:17 p1 'float *borrow' cinit +// CHECK-NEXT: UnaryOperator 0x{{[^ ]*}} 'float *borrow' prefix '&' cannot overflow +// CHECK-NEXT: DeclRefExpr 0x{{[^ ]*}} 'float' lvalue Var 0x{{[^ ]*}} 'y' 'float' +// CHECK-NEXT: DeclStmt 0x{{[^ ]*}} +// CHECK-NEXT: VarDecl 0x{{[^ ]*}} col:9 used p3 'myInt':'int *borrow' cinit +// CHECK-NEXT: ImplicitCastExpr 0x{{[^ ]*}} 'int *borrow' +// CHECK-NEXT: DeclRefExpr 0x{{[^ ]*}} 'int *borrow' lvalue Var 0x{{[^ ]*}} 'p' 'int *borrow' +// CHECK-NEXT: DeclStmt 0x{{[^ ]*}} +// CHECK-NEXT: VarDecl 0x{{[^ ]*}} col:14 used p5 'int *owned' cinit +// CHECK-NEXT: CStyleCastExpr 0x{{[^ ]*}} 'int *owned' +// CHECK-NEXT: UnaryOperator 0x{{[^ ]*}} 'int *' prefix '&' cannot overflow +// CHECK-NEXT: DeclRefExpr 0x{{[^ ]*}} 'int' lvalue Var 0x{{[^ ]*}} 'x1' 'int' +// CHECK-NEXT: DeclStmt 0x{{[^ ]*}} +// CHECK-NEXT: VarDecl 0x{{[^ ]*}} col:21 b6 'const int *borrow' cinit +// CHECK-NEXT: ImplicitCastExpr 0x{{[^ ]*}} 'int *borrow' +// CHECK-NEXT: DeclRefExpr 0x{{[^ ]*}} 'myInt':'int *borrow' lvalue Var 0x{{[^ ]*}} 'p3' 'myInt':'int *borrow' +// CHECK-NEXT: BinaryOperator 0x{{[^ ]*}} 'int *borrow' '=' +// CHECK-NEXT: DeclRefExpr 0x{{[^ ]*}} 'int *borrow' lvalue Var 0x{{[^ ]*}} 'p7' 'int *borrow' +// CHECK-NEXT: ImplicitCastExpr 0x{{[^ ]*}} 'int *borrow' +// CHECK-NEXT: DeclRefExpr 0x{{[^ ]*}} 'int *borrow' lvalue Var 0x{{[^ ]*}} 'p' 'int *borrow' \ No newline at end of file -- Gitee From e1e00c203dec0efa59678aac48e937c728d2e9d9 Mon Sep 17 00:00:00 2001 From: zhangziyao Date: Fri, 28 Feb 2025 11:08:01 +0800 Subject: [PATCH 09/29] fixed some libcbs test cases fails and code review comments --- .../Analysis/Analyses/BSCBorrowChecker.h | 23 +++ clang/lib/Analysis/BSCBorrowChecker.cpp | 59 ++++---- clang/lib/Analysis/CFG.cpp | 9 ++ clang/lib/Sema/BSC/SemaDeclBSC.cpp | 133 ++++++++++++++---- 4 files changed, 160 insertions(+), 64 deletions(-) diff --git a/clang/include/clang/Analysis/Analyses/BSCBorrowChecker.h b/clang/include/clang/Analysis/Analyses/BSCBorrowChecker.h index 67196a6b3108..49c093f5853c 100644 --- a/clang/include/clang/Analysis/Analyses/BSCBorrowChecker.h +++ b/clang/include/clang/Analysis/Analyses/BSCBorrowChecker.h @@ -247,6 +247,7 @@ private: virtual void anchor(); }; +/// ActionNoop represents statement that does not use any variables. struct ActionNoop : public Action { ActionNoop() : Action(Noop) {} @@ -260,6 +261,9 @@ private: void anchor() override; }; +/// ActionInit represents a variable declaration or assignment statement. +/// +/// Note that the declared or assigned variable is of a non-borrow type. struct ActionInit : public Action { std::unique_ptr Dest; std::vector> Sources; @@ -296,6 +300,8 @@ private: void anchor() override; }; +/// ActionBorrow represents a statement that explicity borrows a variable using +/// `&mut`, `&const`, `&mut *`, or `&const *`. struct ActionBorrow : public Action { std::unique_ptr Dest; RegionName RNL; @@ -334,6 +340,7 @@ private: void anchor() override; }; +/// ActionAssign represents an assignment between variables of borrow types. struct ActionAssign : public Action { std::unique_ptr Dest; RegionName RNL; @@ -372,6 +379,8 @@ private: void anchor() override; }; +/// ActionUse represents the usage of a vairble, including reading, writing, or +/// transferring its ownership. struct ActionUse : public Action { std::vector> Uses; std::vector> DerefSources; @@ -401,6 +410,8 @@ private: void anchor() override; }; +/// ActionStorageDead represents leaving the lexical scope of a variable, +/// meaning it is destroyed on the stack. struct ActionStorageDead : public Action { std::unique_ptr Var; @@ -524,11 +535,14 @@ public: llvm::SmallVector SuccessorPoints(Point point) const; }; +/// All the information required for inference solving, including the +/// definition of region variables and all the constraints in the function. class InferenceContext { private: llvm::SmallVector definitions; llvm::SmallVector constraints; + /// Used for implicit borrows. Region emptyRegion; public: @@ -584,6 +598,9 @@ private: const Environment &env; RegionCheck &rc; + + /// For a given key, which is a basic block, the value is the set of all live + /// variables at the entry of the block. llvm::DenseMap liveness; void Kill(LivenessFact &fact, VarDecl *D) { fact.erase(D); } @@ -658,8 +675,14 @@ private: const Environment &env; const RegionCheck &rc; + + /// All loans in the function. llvm::SmallVector loans; + llvm::DenseMap loansInScopeAfterBlock; + + /// For a given key, which is a point of the CFG, the value is a vector of + /// the index of all loans at the entry of the point. std::map> loansByPoint; void Kill(LoansFact &fact, unsigned index) { fact.erase(index); } diff --git a/clang/lib/Analysis/BSCBorrowChecker.cpp b/clang/lib/Analysis/BSCBorrowChecker.cpp index 7d9a4e9fb65f..e8596e7d0fa6 100644 --- a/clang/lib/Analysis/BSCBorrowChecker.cpp +++ b/clang/lib/Analysis/BSCBorrowChecker.cpp @@ -23,7 +23,7 @@ static bool IsTrackedType(QualType type) { } namespace { -/// Given a statement, returns (defs, uses). +/// Given a statement, returns the corresponding (defs, uses). /// /// The `defs` contains variables whose current value is completely /// overwritten, and the `uses` contains variables whose current value is used. @@ -51,17 +51,21 @@ public: void VisitReturnStmt(ReturnStmt *RS); void VisitStmt(Stmt *S); void VisitUnaryDeref(UnaryOperator *UO); - void VisitUnaryPostDec(UnaryOperator *UO); - void VisitUnaryPostInc(UnaryOperator *UO); - void VisitUnaryPreDec(UnaryOperator *UO); - void VisitUnaryPreInc(UnaryOperator *UO); + void VisitUnaryOperator(UnaryOperator *UO); }; } // namespace void DefUse::VisitBinaryOperator(BinaryOperator *BO) { - if ((BO->getOpcode() >= BO_Mul && BO->getOpcode() <= BO_Shr) || - (BO->getOpcode() >= BO_And && BO->getOpcode() <= BO_LOr) || - (BO->getOpcode() >= BO_LT && BO->getOpcode() <= BO_NE)) { + auto Opcode = BO->getOpcode(); + if ((Opcode >= BO_Mul && Opcode <= BO_Shr) || + (Opcode >= BO_And && Opcode <= BO_LOr) || + (Opcode >= BO_LT && Opcode <= BO_NE)) { + Action = Use; + Visit(BO->getLHS()); + Visit(BO->getRHS()); + } else if (Opcode >= BO_MulAssign && Opcode <= BO_OrAssign) { + Action = Def; + Visit(BO->getLHS()); Action = Use; Visit(BO->getLHS()); Visit(BO->getRHS()); @@ -130,32 +134,15 @@ void DefUse::VisitUnaryDeref(UnaryOperator *UO) { Visit(UO->getSubExpr()); } -void DefUse::VisitUnaryPostDec(UnaryOperator *UO) { - Action = Def; - Visit(UO->getSubExpr()); - Action = Use; - Visit(UO->getSubExpr()); -} - -void DefUse::VisitUnaryPostInc(UnaryOperator *UO) { - Action = Def; - Visit(UO->getSubExpr()); - Action = Use; - Visit(UO->getSubExpr()); -} - -void DefUse::VisitUnaryPreDec(UnaryOperator *UO) { - Action = Def; - Visit(UO->getSubExpr()); - Action = Use; - Visit(UO->getSubExpr()); -} - -void DefUse::VisitUnaryPreInc(UnaryOperator *UO) { - Action = Def; - Visit(UO->getSubExpr()); - Action = Use; - Visit(UO->getSubExpr()); +void DefUse::VisitUnaryOperator(UnaryOperator *UO) { + if (UO->isIncrementDecrementOp()) { + Action = Def; + Visit(UO->getSubExpr()); + Action = Use; + Visit(UO->getSubExpr()); + } else { + Visit(UO->getSubExpr()); + } } namespace { @@ -908,6 +895,8 @@ bool DFS::Copy(const Region &From, Region &To, Point StartPoint) { /// Iterates until a fixed point, computing live variables on the entry of each /// basic block. +/// +/// Note that an empty callback is sufficient when computing liveness. void Liveness::Compute() { LivenessFact fact; bool changed = true; @@ -1683,6 +1672,8 @@ void RegionCheck::PreprocessForParamAndReturn() { infer.CapVar(FreeRV); } +/// Traverse each statement in the CFG, check the corresponding actions and +/// report erros according to the loans in scope information. void clang::borrow::BorrowCk(const Environment &env, RegionCheck &rc, LoansInScope &LIS) { LIS.Walk([&](Point point, const Stmt *S, diff --git a/clang/lib/Analysis/CFG.cpp b/clang/lib/Analysis/CFG.cpp index 572a27271f3f..844fa6eaacd0 100644 --- a/clang/lib/Analysis/CFG.cpp +++ b/clang/lib/Analysis/CFG.cpp @@ -2592,7 +2592,12 @@ CFGBlock *CFGBuilder::VisitBinaryOperator(BinaryOperator *B, if (B->getOpcode() == BO_Comma) { // , autoCreateBlock(); +#if ENABLE_BSC + if (!BuildOpts.BSCBorrowCk) + appendStmt(Block, B); +#else appendStmt(Block, B); +#endif addStmt(B->getRHS()); return addStmt(B->getLHS()); } @@ -4318,7 +4323,11 @@ CFGBlock *CFGBuilder::VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *E, /// VisitStmtExpr - Utility method to handle (nested) statement /// expressions (a GCC extension). CFGBlock *CFGBuilder::VisitStmtExpr(StmtExpr *SE, AddStmtChoice asc) { +#if ENABLE_BSC + if (!BuildOpts.BSCBorrowCk && asc.alwaysAdd(*this, SE)) { +#else if (asc.alwaysAdd(*this, SE)) { +#endif autoCreateBlock(); appendStmt(Block, SE); } diff --git a/clang/lib/Sema/BSC/SemaDeclBSC.cpp b/clang/lib/Sema/BSC/SemaDeclBSC.cpp index cd3945035f63..bc2cb704c36c 100644 --- a/clang/lib/Sema/BSC/SemaDeclBSC.cpp +++ b/clang/lib/Sema/BSC/SemaDeclBSC.cpp @@ -228,19 +228,19 @@ void Sema::BSCDataflowAnalysis(const FunctionDecl *FD, } struct ReplaceNodesMap { + /// When we replace an AST node `p` with an AST node `q`, we use `q` as value + /// and use `p` as key and insert into the map. + /// When we execute BorrowCheckerEpilogue, when we find the key of an AST + /// node, we replace it with the corresponding value. llvm::DenseMap replacedExprsMap; llvm::DenseMap replacedStmtsMap; bool Contains(Expr *E) const { - if (replacedExprsMap.find(E) != replacedExprsMap.end()) - return true; - return false; + return replacedExprsMap.find(E) != replacedExprsMap.end(); } bool Contains(Stmt *S) const { - if (replacedStmtsMap.find(S) != replacedStmtsMap.end()) - return true; - return false; + return replacedStmtsMap.find(S) != replacedStmtsMap.end(); } void Insert(Expr *Key, Expr *Value) { replacedExprsMap[Key] = Value; } @@ -252,6 +252,15 @@ struct ReplaceNodesMap { Stmt *Get(Stmt *Key) { return replacedStmtsMap[Key]; } }; +/// Before running borrow checker, introduce some temporary variables to adjust +/// FunctionDecl in the AST, replacing nested function calls and complex +/// expressions. +/// +/// The complexity for AST nodes presents significant challenges for +/// implementing the borrow checker. For example, scenarios like `foo(bar())` +/// are not convenient for analysis. To handle such cases, we use a temporary +/// variable to store the return value of `bar()` before passing it as an +/// argument to `foo()`, ensuring a semantically equivalent transformation. class BorrowCheckerPrologue : public TreeTransform { typedef TreeTransform BaseTransform; @@ -279,7 +288,9 @@ class BorrowCheckerPrologue : public TreeTransform { E->getType(), VK_LValue); } - CompoundStmt *WrapWithCompoundStmt(Stmt *S) { + CompoundStmt *GetOrWrapWithCompoundStmt(Stmt *S) { + if (isa(S)) + return dyn_cast(S); return CompoundStmt::Create(SemaRef.Context, S, FPOptionsOverride(), S->getBeginLoc(), S->getEndLoc()); } @@ -300,10 +311,14 @@ public: StmtResult TransformCaseStmt(CaseStmt *CS) { CS->setLHS(getDerived().TransformExpr(CS->getLHS()).get()); - Expr *RHS = CS->getRHS(); - if (RHS) + if (Expr *RHS = CS->getRHS()) CS->setRHS(getDerived().TransformExpr(RHS).get()); - CS->setSubStmt(getDerived().TransformStmt(CS->getSubStmt()).get()); + if (Stmt *Sub = CS->getSubStmt()) { + CompoundStmt *SubBody = GetOrWrapWithCompoundStmt(Sub); + StmtResult ResSub = getDerived().TransformStmt(SubBody); + CS->setSubStmt(ResSub.get()); + replacedNodesMap.Insert(ResSub.get(), Sub); + } return CS; } @@ -347,15 +362,18 @@ public: } StmtResult TransformDefaultStmt(DefaultStmt *DS) { - DS->setSubStmt(getDerived().TransformStmt(DS->getSubStmt()).get()); + if (Stmt *Sub = DS->getSubStmt()) { + CompoundStmt *SubBody = GetOrWrapWithCompoundStmt(Sub); + StmtResult ResSub = getDerived().TransformStmt(SubBody); + DS->setSubStmt(ResSub.get()); + replacedNodesMap.Insert(ResSub.get(), Sub); + } return DS; } StmtResult TransformDoStmt(DoStmt *DS) { Stmt *Body = DS->getBody(); - CompoundStmt *CSBody = isa(Body) - ? dyn_cast(Body) - : WrapWithCompoundStmt(Body); + CompoundStmt *CSBody = GetOrWrapWithCompoundStmt(Body); StmtResult ResBody = getDerived().TransformStmt(CSBody); DS->setBody(ResBody.get()); replacedNodesMap.Insert(ResBody.get(), Body); @@ -369,9 +387,7 @@ public: FS->setCond(getDerived().TransformExpr(FS->getCond()).get()); FS->setInc(getDerived().TransformExpr(FS->getInc()).get()); Stmt *Body = FS->getBody(); - CompoundStmt *CSBody = isa(Body) - ? dyn_cast(Body) - : WrapWithCompoundStmt(Body); + CompoundStmt *CSBody = GetOrWrapWithCompoundStmt(Body); StmtResult ResBody = getDerived().TransformStmt(CSBody); FS->setBody(ResBody.get()); replacedNodesMap.Insert(ResBody.get(), Body); @@ -385,17 +401,13 @@ public: IS->setCond(ResCond.get()); Stmt *Then = IS->getThen(); - CompoundStmt *CSThen = isa(Then) - ? dyn_cast(Then) - : WrapWithCompoundStmt(Then); + CompoundStmt *CSThen = GetOrWrapWithCompoundStmt(Then); StmtResult ResThen = getDerived().TransformStmt(CSThen); IS->setThen(ResThen.get()); replacedNodesMap.Insert(ResThen.get(), Then); if (Stmt *Else = IS->getElse()) { - CompoundStmt *CSElse = isa(Else) - ? dyn_cast(Else) - : WrapWithCompoundStmt(Else); + CompoundStmt *CSElse = GetOrWrapWithCompoundStmt(Else); StmtResult ResElse = getDerived().TransformStmt(CSElse); IS->setElse(ResElse.get()); replacedNodesMap.Insert(ResElse.get(), Else); @@ -448,9 +460,7 @@ public: StmtResult TransformWhileStmt(WhileStmt *WS) { WS->setCond(getDerived().TransformExpr(WS->getCond()).get()); Stmt *Body = WS->getBody(); - CompoundStmt *CSBody = isa(Body) - ? dyn_cast(Body) - : WrapWithCompoundStmt(Body); + CompoundStmt *CSBody = GetOrWrapWithCompoundStmt(Body); StmtResult ResBody = getDerived().TransformStmt(CSBody); WS->setBody(ResBody.get()); replacedNodesMap.Insert(ResBody.get(), Body); @@ -539,6 +549,11 @@ public: E = ReplaceWithTemporaryVariable(CE); replacedNodesMap.Insert(E, CE); } + } else if (UnaryOperator *UO = dyn_cast(E)) { + if (NeedToReplace) { + E = ReplaceWithTemporaryVariable(UO); + replacedNodesMap.Insert(E, UO); + } } CSCE->setSubExpr(E); return CSCE; @@ -599,6 +614,31 @@ public: return ME; } + ExprResult TransformParenExpr(ParenExpr *PE) { + ExprResult Res = getDerived().TransformExpr(PE->getSubExpr()); + Expr *E = Res.get(); + if (CallExpr *CE = dyn_cast(E)) { + DeclRefExpr *DRE = ReplaceWithTemporaryVariable(E); + CastKind Kind = + DRE->getType()->getAsCXXRecordDecl() ? CK_NoOp : CK_LValueToRValue; + E = ImplicitCastExpr::Create(getSema().Context, DRE->getType(), Kind, + DRE, nullptr, VK_PRValue, + FPOptionsOverride()); + replacedNodesMap.Insert(E, CE); + } else if (BinaryOperator *BO = dyn_cast(E)) { + DeclRefExpr *DRE = ReplaceWithTemporaryVariable(E); + CastKind Kind = + DRE->getType()->getAsCXXRecordDecl() ? CK_NoOp : CK_LValueToRValue; + E = ImplicitCastExpr::Create(getSema().Context, DRE->getType(), Kind, + DRE, nullptr, VK_PRValue, + FPOptionsOverride()); + replacedNodesMap.Insert(E, BO); + } + PE->setSubExpr(E); + + return PE; + } + ExprResult TransformStmtExpr(StmtExpr *SE) { StmtResult Res = getDerived().TransformStmt(SE->getSubStmt()); SE->setSubStmt(Res.getAs()); @@ -617,6 +657,14 @@ public: DRE, nullptr, VK_PRValue, FPOptionsOverride()); replacedNodesMap.Insert(E, CE); + } else if (BinaryOperator *BO = dyn_cast(E)) { + DeclRefExpr *DRE = ReplaceWithTemporaryVariable(E); + CastKind Kind = + DRE->getType()->getAsCXXRecordDecl() ? CK_NoOp : CK_LValueToRValue; + E = ImplicitCastExpr::Create(getSema().Context, DRE->getType(), Kind, + DRE, nullptr, VK_PRValue, + FPOptionsOverride()); + replacedNodesMap.Insert(E, BO); } } UO->setSubExpr(E); @@ -624,6 +672,8 @@ public: } }; +/// After running borrow checker, restore the AST to its original form to avoid +/// any impact on other compiler phases caused by AST transformations. class BorrowCheckerEpilogue : public TreeTransform { typedef TreeTransform BaseTransform; @@ -646,10 +696,15 @@ public: StmtResult TransformCaseStmt(CaseStmt *CS) { CS->setLHS(getDerived().TransformExpr(CS->getLHS()).get()); - Expr *RHS = CS->getRHS(); - if (RHS) + if (Expr *RHS = CS->getRHS()) CS->setRHS(getDerived().TransformExpr(RHS).get()); - CS->setSubStmt(getDerived().TransformStmt(CS->getSubStmt()).get()); + if (Stmt *Sub = CS->getSubStmt()) { + if (replacedNodesMap.Contains(Sub)) { + Sub = replacedNodesMap.Get(Sub); + } + StmtResult ResSub = getDerived().TransformStmt(Sub); + CS->setSubStmt(ResSub.get()); + } return CS; } @@ -687,7 +742,13 @@ public: } StmtResult TransformDefaultStmt(DefaultStmt *DS) { - DS->setSubStmt(getDerived().TransformStmt(DS->getSubStmt()).get()); + if (Stmt *Sub = DS->getSubStmt()) { + if (replacedNodesMap.Contains(Sub)) { + Sub = replacedNodesMap.Get(Sub); + } + StmtResult ResSub = getDerived().TransformStmt(Sub); + DS->setSubStmt(ResSub.get()); + } return DS; } @@ -856,6 +917,17 @@ public: return ME; } + ExprResult TransformParenExpr(ParenExpr *PE) { + Expr *Sub = PE->getSubExpr(); + if (replacedNodesMap.Contains(Sub)) { + Sub = replacedNodesMap.Get(Sub); + } + ExprResult Res = getDerived().TransformExpr(Sub); + PE->setSubExpr(Res.get()); + + return PE; + } + ExprResult TransformStmtExpr(StmtExpr *SE) { StmtResult Res = getDerived().TransformStmt(SE->getSubStmt()); SE->setSubStmt(Res.getAs()); @@ -895,6 +967,7 @@ void Sema::BSCBorrowChecker(FunctionDecl *FD) { .setAlwaysAdd(Stmt::BreakStmtClass) .setAlwaysAdd(Stmt::CompoundStmtClass) .setAlwaysAdd(Stmt::DeclStmtClass) + .setAlwaysAdd(Stmt::DoStmtClass) .setAlwaysAdd(Stmt::ForStmtClass) .setAlwaysAdd(Stmt::IfStmtClass) .setAlwaysAdd(Stmt::ReturnStmtClass) -- Gitee From f11204a98be1cb52646ef75049d6040d1954ec8f Mon Sep 17 00:00:00 2001 From: zhangziyao Date: Wed, 5 Mar 2025 15:13:23 +0800 Subject: [PATCH 10/29] [safezone] allow goto statement and label statement in safe zone --- clang/lib/Parse/BSC/ParseStmtBSC.cpp | 9 --------- .../SafeZone/unsafe_stmt_or_decl/unsafe_stmt_or_decl.cbs | 4 ++-- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/clang/lib/Parse/BSC/ParseStmtBSC.cpp b/clang/lib/Parse/BSC/ParseStmtBSC.cpp index 659ea1cfd227..7e729c8cd160 100644 --- a/clang/lib/Parse/BSC/ParseStmtBSC.cpp +++ b/clang/lib/Parse/BSC/ParseStmtBSC.cpp @@ -22,15 +22,6 @@ void Parser::CheckStmtTokInSafeZone(tok::TokenKind Kind) { return; } switch (Kind) { - case tok::identifier: { - Token Next = NextToken(); - if (Next.is(tok::colon)) - Diag(Tok, diag::err_unsafe_action) << "label statement"; - break; - } - case tok::kw_goto: - Diag(Tok, diag::err_unsafe_action) << "goto statement"; - break; case tok::kw_asm: Diag(Tok, diag::err_unsafe_action) << "asm statement"; break; diff --git a/clang/test/BSC/Negative/SafeZone/unsafe_stmt_or_decl/unsafe_stmt_or_decl.cbs b/clang/test/BSC/Negative/SafeZone/unsafe_stmt_or_decl/unsafe_stmt_or_decl.cbs index 6dfcb05b0ed4..06c53b819d2c 100644 --- a/clang/test/BSC/Negative/SafeZone/unsafe_stmt_or_decl/unsafe_stmt_or_decl.cbs +++ b/clang/test/BSC/Negative/SafeZone/unsafe_stmt_or_decl/unsafe_stmt_or_decl.cbs @@ -3,10 +3,10 @@ safe int main(void) { int a; // expected-error {{uninitialized declarator is forbidden in the safe zone}} a = 0; - lab: // expected-error {{label statement is forbidden in the safe zone}} + lab: if (a < 10) { a = a + 1; - goto lab; // expected-error {{goto statement is forbidden in the safe zone}} + goto lab; } switch (a) { int b = 1; // expected-error {{variable declaration in the top-level switch block is forbidden in the safe zone}} -- Gitee From c693fea91f001d9617bc34f55654fcea45999cdf Mon Sep 17 00:00:00 2001 From: zhangziyao Date: Fri, 7 Mar 2025 09:44:42 +0800 Subject: [PATCH 11/29] [user manual] update user manual of borrow feature according to new borrow checker --- clang/docs/BiShengCLanguageUserManual.md | 128 ++++++++++------------- 1 file changed, 54 insertions(+), 74 deletions(-) diff --git a/clang/docs/BiShengCLanguageUserManual.md b/clang/docs/BiShengCLanguageUserManual.md index 2ceb09558656..241014dc2160 100644 --- a/clang/docs/BiShengCLanguageUserManual.md +++ b/clang/docs/BiShengCLanguageUserManual.md @@ -2802,9 +2802,9 @@ void use_mut(int *borrow p); void test() { int local = 5; int *borrow p1 = &mut local; //p1是可变借用指针,借用了local - const int *borrow p2 = &const local; //p2是不可变借用指针,也借用了local - use_immut(p2); use_mut(p1); + const int *borrow p2 = &const local; //p2是不可变借用指针,借用了local + use_immut(p2); } ``` 另外,表达式 e 如果是指针的解引用表达式,`&mut *p`和`&const *p`分别可以看作对地址 p 中存放的值,也就是`*p`,取可变借用和不可变借用,这一操作不为`*p`产生临时变量。其中,p可以是裸指针、owned指针和其它借用指针。例如: @@ -2812,14 +2812,14 @@ void test() { void test() { int *x1 = malloc(sizeof(int)); *x1 = 42; - int *borrow p1 = &mut *x1; //p1借用了*x1,但由于我们没有办法跟踪裸指针所指向的内存,所以我们假设x1和*x1是一一对应的,等价于p1借用了x1 + int *borrow p1 = &mut *x1; //p1借用了*x1 int* owned x2 = safe_malloc(); - int *borrow p2 = &mut *x2; //p2借用了*x2,由于owned指针与它所指向的内存是一一对应的,等价于p2借用了x2 + int *borrow p2 = &mut *x2; //p2借用了*x2 int local = 5; int *borrow x3 = &mut local; - int *borrow p3 = &mut *x3; //p3借用了*x3,由于x3借用了local,所以我们认为p3也借用了local + int *borrow p3 = &mut *x3; //p3借用了*x3 } ``` #### 1.2 借用的作用 @@ -2861,7 +2861,7 @@ void other_operation(MyFile* borrow p) { //其他文件操作函数,对文件 int main(void) { MyFile* owned p = create_file(); - insert_str(&mut *p, str); //不取得文件指针p的所有权,而是借用p,后续p可以继续被使用 + insert_str(&mut *p, str); //不取得文件指针p的所有权,而是借用*p,后续p可以继续被使用 other_operation(&mut *p); safe_free(p); return 0; @@ -2902,7 +2902,7 @@ void test() { int local1 = 5; p = &mut local1; //对 p 进行再赋值之后,p 不再借用 local,而是借用 local1 } - use(p); // error,借用变量 p 的生命周期比被借用对象 local1 的生命周期长 + use(p); // error,local1 的生命周期不够长 } ``` @@ -2914,16 +2914,16 @@ void test(int a, int *owned b, int *c, struct S d) { // 被借用对象是普通局部变量 int local = 5; int *borrow p1 = &mut local; //p1的被借用对象是local - int *borrow p2 = &mut *p1; //p2的被借用对象是local,因为p1的借用对象是local - int *borrow p3 = p1; //p3的被借用对象是local,因为p1的借用对象是local + int *borrow p2 = &mut *p1; //p2的被借用对象是*p1 + int *borrow p3 = p1; //p3的被借用对象是*p1 // 被借用对象是owned变量 int *owned x1 = safe_malloc(2); - int *borrow p4 = &mut *x1; //p4的被借用对象是x1 + int *borrow p4 = &mut *x1; //p4的被借用对象是*x1 // 被借用对象是裸指针变量 int *x2 = malloc(sizeof(int)); - int *borrow p5 = &mut *x2; //p5的被借用对象是x2 + int *borrow p5 = &mut *x2; //p5的被借用对象是*x2 // 被借用对象是结构体的某个字段 struct S s = { .a = 5 }; @@ -2938,14 +2938,14 @@ void test(int a, int *owned b, int *c, struct S d) { // 被借用对象是函数入参 int *borrow p9 = &mut a; //p9的被借用对象是a - int *borrow p10 = &mut *b; //p10的被借用对象是b - int *borrow p11 = &mut *c; //p11的被借用对象是c + int *borrow p10 = &mut *b; //p10的被借用对象是*b + int *borrow p11 = &mut *c; //p11的被借用对象是*c int *borrow p12 = &mut d.a; //p12的被借用对象是d.a } ``` ##### 2.3 借用变量的 Non-Lexical Lifetime -一个变量的生命周期从它的声明开始,到当前整个语句块结束,这个设计被称为Lexical Lifetime,因为变量的生命周期是严格和词法中的作用域范围绑定的。这个策略实现起来非常简单,但它可能过于保守了,某些情况下借用变量的作用范围被过度拉长了,以至于某些实质上是安全的代码也被阻止了,这在一定程度上限制了程序员的发挥。因此,毕昇 C 为借用变量引入 Non-Lexical Lifetime(简写为NLL),用更精细的手段计算借用变量真正起作用的范围,**借用变量的 NLL 范围为:从借用处开始,一直持续到最后一次使用的地方**。具体的,它是**从借用变量定义或被再赋值开始,到被再赋值之前最后一次被使用结束**。 +一个变量的生命周期从它的声明开始,到当前整个语句块结束,这个设计被称为Lexical Lifetime,因为变量的生命周期是严格和词法中的作用域范围绑定的。这个策略实现起来非常简单,但它可能过于保守了,某些情况下借用变量的作用范围被过度拉长了,以至于某些实质上是安全的代码也被阻止了,这在一定程度上限制了程序员能编写出的代码。因此,毕昇 C 为借用变量引入 Non-Lexical Lifetime(简写为NLL),用更精细的手段计算借用变量真正起作用的范围,**借用变量的 NLL 范围为:从借用处开始,一直持续到最后一次使用的地方**。具体的,它是**从借用变量定义或被再赋值开始,到被再赋值之前最后一次被使用结束**。 其中,以下场景属于对借用变量p的使用: 1. 函数调用use(p)或use(&mut*p) @@ -3033,7 +3033,7 @@ void test4() { use(p); //#10 } -//本例中,p的生命周期为[2,4],被借用对象x的生命周期为[1,3],不满足生命周期约束,error +//本例中,p的生命周期为[2,4],被借用对象*x的生命周期为[1,3],不满足生命周期约束,error void test5() { int *owned x = safe_malloc(5); //#1 int *borrow p = &mut *x; //#2 @@ -3079,78 +3079,44 @@ void test() { read_a(p2); //该函数会读取 a 所指向的内存 } ``` -由于借用本质上也是指针,所以为了避免上述问题,毕昇 C 规定,**同一时刻,对于同一个对象,只能拥有要么一个可变借用, 要么任意多个不可变借用**。 +由于借用本质上也是指针,所以为了避免上述问题,毕昇 C 规定,**同一时刻,对于同一个对象,要么只能拥有一个可变借用, 要么任意多个不可变借用**。 ```C void test1() { int local = 5; int *borrow p1 = &mut local; - int *borrow p2 = &mut local; - modify(p1); //error,同一时刻最多只能有一个指向local的可变借用变量 + int *borrow p2 = &mut local; //error,同一时刻最多只能有一个指向local的可变借用变量 + modify(p1); modify(p2); } void test2() { int local = 1; - int * borrow p1 = &mut local; - const int * borrow p2 = &const local; - use(p1); //error,指向local的可变和不可变借用不能同时存在 + int *borrow p1 = &mut local; + int *borrow p2 = &mut local; //error,同一时刻最多只能有一个指向local的可变借用变量 use(p2); + use(p1); } -``` -由于不可变借用不会导致被借用对象被修改,因此同一时刻可以拥有任意多个不可变借用,例如: -```C -void test() { - int local = 5; - const int *borrow p1 = &const local; - const int *borrow p2 = &const local; - read(p1); //ok,同一时刻可以拥有任意多个不可变借用 - read(p2); -} -``` -##### 3.3 定义可变借用会使在它之前定义的其它借用被冻结 -定义一个可变借用变量,会导致在它之前定义的其它借用变量(前提是这些借用变量都借用了同一个对象)处于冻结状态,我们认为借用在被冻结期间不存在,也就不能被使用。例如: -```C -void test1() { +void test3() { int local = 1; int * borrow p1 = &mut local; - int * borrow p2 = &mut local; //p2生命周期开始,p1被p2冻结 - use(p1); //error,p2生命周期此时没有结束,因此p1仍然处于被冻结状态,不能使用p1 + const int * borrow p2 = &const local; //error,指向local的可变和不可变借用不能同时存在 + use(p1); use(p2); } -void test2() { +void test4() { int local = 1; const int * borrow p1 = &const local; - int * borrow p2 = &mut local; //p2生命周期开始,p1被p2冻结 - use(p1); //error,p2生命周期此时没有结束,因此p1仍然处于被冻结状态,不能使用p1 + int * borrow p2 = &mut local; // error,指向local的可变和不可变借用不能同时存在 + use(p1); use(p2); } ``` -这条规则实际上是 3.2 中“可变借用同时只能存在一个”规则的补充,它保证了同一时刻,最多只有一个可变借用处于活跃状态。 - -##### 3.4 使用可变借用会使在它之前定义的其它借用失效 -可变借用变量能够使在它之前被定义的其它借用变量(前提是这些借用变量都借用了同一个对象)失效,我们认为失效的借用不存在,也就无法再被使用。例如: -```C -void test1() { - int local = 1; - int * borrow p1 = &mut local; - int * borrow p2 = &mut local; - use(p2); //使用p2,会使p1失效 - use(p1); //error,p1已经失效,无法再被使用 -} - -void test2() { - int local = 1; - const int * borrow p1 = &mut local; - int * borrow p2 = &mut local; - use(p2); //使用p2,会使p1失效 - use_immut(p1); //error,p1已经失效,无法再被使用 -} -``` -使用可变借用,可能会导致被借用对象的内存状态发生改变,如果后续通过其它借用变量访问这块被修改的内存,可能会导致未定义行为。 +如果同时存在对一个变量的可变借用和不可变借用,可能会出现通过可变借用修改被借用对象的内存状态,然后再使用不可借用访问被修改的内存,从而导致未定义行为的情况。 例如: + ```C struct A { int *p; @@ -3172,8 +3138,20 @@ int main() { return 0; } ``` + 上述代码中,`a.free_p()`实际上使用了一个指向 a 的可变借用,该可变借用会使在它之前被定义的借用 q 失效,由于`printf("%d", *q)`使用了失效的 q,毕昇 C 编译器会报错,也就阻止了不安全行为的发生。 +由于不可变借用不会导致被借用对象被修改,因此同一时刻可以拥有任意多个不可变借用,例如: +```C +void test() { + int local = 5; + const int *borrow p1 = &const local; + const int *borrow p2 = &const local; + read(p1); //ok,同一时刻可以拥有任意多个不可变借用 + read(p2); +} +``` + #### 4.借用对被借用对象的影响 ##### 4.1 不可变借用对被借用对象的影响 对表达式 e 做不可变借用, 即`&const e`,在这个不可变借用的生命周期结束之前,e 只能读不能修改,也不能对 e 创建可变借用。 @@ -3185,7 +3163,7 @@ int main() { | &const e->field | e->field 进入 “只读” 状态,也不允许整体修改 *e。但允许修改 e 指向的其它成员,或者对其它成员做可变借用 | | &const e.field | e.field 进入 “只读” 状态,也不允许整体修改 e。但允许修改 e 的其它成员,或者对其它成员做可变借用 | | &const e[index] | e 进入 “只读” 状态,不允许修改 e 及其直接或间接成员,或者对其它成员做可变借用 | -| &const *e | e 进入 “只读” 状态,不允许修改 e 及其直接或间接成员,或者对其它成员做可变借用 | +| &const *e | *e 进入 “只读” 状态,不允许修改 *e 及其直接或间接成员,或者对其它成员做可变借用,如果 e 是 owned 指针类型,则 e 也进入只读状态 | ##### 4.2 可变借用对被借用对象的影响 对表达式 e 做可变借用, 即`&mut e`,表达式 e 进入 “冻结” 状态。在这个可变借用的生命周期结束之前,e 不能读,不能修改(包含被move),也不能被借用。 @@ -3197,7 +3175,7 @@ int main() { | &mut e->field | e->field 被冻结,不允许读写 e->field,不允许整体修改 *e,但允许修改 e 指向的其它成员,或者对其它成员做可变借用 | | &mut e.field | e.field 被冻结,不允许读写 e.field,不允许整体修改 e,但允许修改 e 的其它成员,或者对其它成员做可变借用 | | &mut e[index] | e 被冻结,不允许读写 e 以及它的成员 | -| &mut *e | e 被冻结,不允许读写 e 以及它的成员 | +| &mut *e | *e 被冻结,不允许读写 *e 以及它的成员,如果 e 是 owned 指针类型,则也不允许读写 e | #### 5. 函数定义中包含借用类型 1. 不允许函数参数中没有借用类型的参数,但是函数返回是借用类型。 @@ -3398,25 +3376,27 @@ void int *borrow::f() {} //error 8. union 的成员不允许是借用类型。 ```C union MyUnion { - int *borrow p;//error,借用指针不允许作为泛型实参 + int *borrow p;//error,借用指针不允许作为union成员 }; ``` -9. 借用指针变量不支持索引运算。 +9. 借用指针类型不能是泛型实参。 + +10. 借用指针变量不支持索引运算。 -10. 借用指针变量不支持算术运算。 +11. 借用指针变量不支持算术运算。 -11. 允许同类型的借用变量之间,使用 `==`、`!=`、`>`、`<`、`<=`、`>=` 等比较运算符操作。 +12. 允许同类型的借用变量之间,使用 `==`、`!=`、`>`、`<`、`<=`、`>=` 等比较运算符操作。 -12. 允许对借用类型使用 `sizeof`、`alignof`操作符,并且有: +13. 允许对借用类型使用 `sizeof`、`alignof`操作符,并且有: sizeof(T* borrow) == sizeof(T*) _Alignof(T* borrow) == _Alignof(T*) -13. 允许对借用类型使用一元的`&`、`!`及二元的`&&`、`||`运算符。 +14. 允许对借用类型使用一元的`&`、`!`及二元的`&&`、`||`运算符。 -14. 不允许对借用类型使用一元的`-`、`~`、`&const`、`&mut`、`[]`、`++`、`--`运算符,也不允许对借用类型使用二元的`*`、`/`、`%`、`&`、`|`、`<<`、`>>`、`+`、`-`运算符。 +15. 不允许对借用类型使用一元的`-`、`~`、`&const`、`&mut`、`[]`、`++`、`--`运算符,也不允许对借用类型使用二元的`*`、`/`、`%`、`&`、`|`、`<<`、`>>`、`+`、`-`运算符。 -15. 如果一个借用指针变量指向的是函数,那么可以通过这个借用指针变量来调用函数。 +16. 如果一个借用指针变量指向的是函数,那么可以通过这个借用指针变量来调用函数。 ```C int f() {} void test() { @@ -3425,7 +3405,7 @@ void test() { } ``` -16. 不允许对函数做可变借用,只能做只读借用。 +17. 不允许对函数做可变借用,只能做只读借用。 ### 安全区 -- Gitee From 7774757e3cdfeb2d8187f9abe09973bfb6200c2e Mon Sep 17 00:00:00 2001 From: zhangziyao Date: Fri, 7 Mar 2025 10:01:20 +0800 Subject: [PATCH 12/29] [dir adjust] create BSC directory in analysis and move four files to it --- .../clang/Analysis/Analyses/{ => BSC}/BSCBorrowCheck.h | 0 .../Analysis/Analyses/{ => BSC}/BSCBorrowChecker.h | 0 .../clang/Analysis/Analyses/{ => BSC}/BSCFatPtrCheck.h | 0 .../Analysis/Analyses/{ => BSC}/BSCNullabilityCheck.h | 0 .../clang/Analysis/Analyses/{ => BSC}/BSCOwnership.h | 0 clang/lib/Analysis/{ => BSC}/BSCBorrowCheck.cpp | 2 +- clang/lib/Analysis/{ => BSC}/BSCBorrowChecker.cpp | 2 +- clang/lib/Analysis/{ => BSC}/BSCFatPtrCheck.cpp | 2 +- clang/lib/Analysis/{ => BSC}/BSCNullabilityCheck.cpp | 2 +- clang/lib/Analysis/{ => BSC}/BSCOwnership.cpp | 2 +- clang/lib/Analysis/CMakeLists.txt | 10 +++++----- clang/lib/Sema/BSC/SemaDeclBSC.cpp | 8 ++++---- 12 files changed, 14 insertions(+), 14 deletions(-) rename clang/include/clang/Analysis/Analyses/{ => BSC}/BSCBorrowCheck.h (100%) rename clang/include/clang/Analysis/Analyses/{ => BSC}/BSCBorrowChecker.h (100%) rename clang/include/clang/Analysis/Analyses/{ => BSC}/BSCFatPtrCheck.h (100%) rename clang/include/clang/Analysis/Analyses/{ => BSC}/BSCNullabilityCheck.h (100%) rename clang/include/clang/Analysis/Analyses/{ => BSC}/BSCOwnership.h (100%) rename clang/lib/Analysis/{ => BSC}/BSCBorrowCheck.cpp (99%) rename clang/lib/Analysis/{ => BSC}/BSCBorrowChecker.cpp (99%) rename clang/lib/Analysis/{ => BSC}/BSCFatPtrCheck.cpp (99%) rename clang/lib/Analysis/{ => BSC}/BSCNullabilityCheck.cpp (99%) rename clang/lib/Analysis/{ => BSC}/BSCOwnership.cpp (99%) diff --git a/clang/include/clang/Analysis/Analyses/BSCBorrowCheck.h b/clang/include/clang/Analysis/Analyses/BSC/BSCBorrowCheck.h similarity index 100% rename from clang/include/clang/Analysis/Analyses/BSCBorrowCheck.h rename to clang/include/clang/Analysis/Analyses/BSC/BSCBorrowCheck.h diff --git a/clang/include/clang/Analysis/Analyses/BSCBorrowChecker.h b/clang/include/clang/Analysis/Analyses/BSC/BSCBorrowChecker.h similarity index 100% rename from clang/include/clang/Analysis/Analyses/BSCBorrowChecker.h rename to clang/include/clang/Analysis/Analyses/BSC/BSCBorrowChecker.h diff --git a/clang/include/clang/Analysis/Analyses/BSCFatPtrCheck.h b/clang/include/clang/Analysis/Analyses/BSC/BSCFatPtrCheck.h similarity index 100% rename from clang/include/clang/Analysis/Analyses/BSCFatPtrCheck.h rename to clang/include/clang/Analysis/Analyses/BSC/BSCFatPtrCheck.h diff --git a/clang/include/clang/Analysis/Analyses/BSCNullabilityCheck.h b/clang/include/clang/Analysis/Analyses/BSC/BSCNullabilityCheck.h similarity index 100% rename from clang/include/clang/Analysis/Analyses/BSCNullabilityCheck.h rename to clang/include/clang/Analysis/Analyses/BSC/BSCNullabilityCheck.h diff --git a/clang/include/clang/Analysis/Analyses/BSCOwnership.h b/clang/include/clang/Analysis/Analyses/BSC/BSCOwnership.h similarity index 100% rename from clang/include/clang/Analysis/Analyses/BSCOwnership.h rename to clang/include/clang/Analysis/Analyses/BSC/BSCOwnership.h diff --git a/clang/lib/Analysis/BSCBorrowCheck.cpp b/clang/lib/Analysis/BSC/BSCBorrowCheck.cpp similarity index 99% rename from clang/lib/Analysis/BSCBorrowCheck.cpp rename to clang/lib/Analysis/BSC/BSCBorrowCheck.cpp index 7b4d8d3d47f8..3ba19a587308 100644 --- a/clang/lib/Analysis/BSCBorrowCheck.cpp +++ b/clang/lib/Analysis/BSC/BSCBorrowCheck.cpp @@ -12,7 +12,7 @@ #if ENABLE_BSC -#include "clang/Analysis/Analyses/BSCBorrowCheck.h" +#include "clang/Analysis/Analyses/BSC/BSCBorrowCheck.h" #include "clang/AST/StmtVisitor.h" #include "clang/Analysis/AnalysisDeclContext.h" #include "clang/Analysis/CFG.h" diff --git a/clang/lib/Analysis/BSCBorrowChecker.cpp b/clang/lib/Analysis/BSC/BSCBorrowChecker.cpp similarity index 99% rename from clang/lib/Analysis/BSCBorrowChecker.cpp rename to clang/lib/Analysis/BSC/BSCBorrowChecker.cpp index e8596e7d0fa6..4d3ff316ed87 100644 --- a/clang/lib/Analysis/BSCBorrowChecker.cpp +++ b/clang/lib/Analysis/BSC/BSCBorrowChecker.cpp @@ -12,7 +12,7 @@ #if ENABLE_BSC -#include "clang/Analysis/Analyses/BSCBorrowChecker.h" +#include "clang/Analysis/Analyses/BSC/BSCBorrowChecker.h" #include "clang/AST/StmtVisitor.h" using namespace clang; diff --git a/clang/lib/Analysis/BSCFatPtrCheck.cpp b/clang/lib/Analysis/BSC/BSCFatPtrCheck.cpp similarity index 99% rename from clang/lib/Analysis/BSCFatPtrCheck.cpp rename to clang/lib/Analysis/BSC/BSCFatPtrCheck.cpp index e93ba320e750..e4a6e6d0e742 100644 --- a/clang/lib/Analysis/BSCFatPtrCheck.cpp +++ b/clang/lib/Analysis/BSC/BSCFatPtrCheck.cpp @@ -1,6 +1,6 @@ #if ENABLE_BSC -#include "clang/Analysis/Analyses/BSCFatPtrCheck.h" +#include "clang/Analysis/Analyses/BSC/BSCFatPtrCheck.h" #include "clang/AST/ParentMap.h" #include "clang/AST/StmtVisitor.h" #include "clang/Analysis/AnalysisDeclContext.h" diff --git a/clang/lib/Analysis/BSCNullabilityCheck.cpp b/clang/lib/Analysis/BSC/BSCNullabilityCheck.cpp similarity index 99% rename from clang/lib/Analysis/BSCNullabilityCheck.cpp rename to clang/lib/Analysis/BSC/BSCNullabilityCheck.cpp index fac9bc2c1bfe..ebe781adcee2 100644 --- a/clang/lib/Analysis/BSCNullabilityCheck.cpp +++ b/clang/lib/Analysis/BSC/BSCNullabilityCheck.cpp @@ -12,7 +12,7 @@ #if ENABLE_BSC -#include "clang/Analysis/Analyses/BSCNullabilityCheck.h" +#include "clang/Analysis/Analyses/BSC/BSCNullabilityCheck.h" #include "clang/AST/StmtVisitor.h" #include "clang/Analysis/AnalysisDeclContext.h" #include "clang/Analysis/CFG.h" diff --git a/clang/lib/Analysis/BSCOwnership.cpp b/clang/lib/Analysis/BSC/BSCOwnership.cpp similarity index 99% rename from clang/lib/Analysis/BSCOwnership.cpp rename to clang/lib/Analysis/BSC/BSCOwnership.cpp index f240816e9502..749359e9256a 100644 --- a/clang/lib/Analysis/BSCOwnership.cpp +++ b/clang/lib/Analysis/BSC/BSCOwnership.cpp @@ -12,7 +12,7 @@ #if ENABLE_BSC -#include "clang/Analysis/Analyses/BSCOwnership.h" +#include "clang/Analysis/Analyses/BSC/BSCOwnership.h" #include "clang/AST/StmtVisitor.h" #include "clang/Analysis/AnalysisDeclContext.h" #include "clang/Analysis/CFG.h" diff --git a/clang/lib/Analysis/CMakeLists.txt b/clang/lib/Analysis/CMakeLists.txt index 1a2441527ceb..387bcd09a808 100644 --- a/clang/lib/Analysis/CMakeLists.txt +++ b/clang/lib/Analysis/CMakeLists.txt @@ -6,11 +6,11 @@ set(LLVM_LINK_COMPONENTS add_clang_library(clangAnalysis AnalysisDeclContext.cpp BodyFarm.cpp - BSCFatPtrCheck.cpp - BSCNullabilityCheck.cpp - BSCOwnership.cpp - BSCBorrowCheck.cpp - BSCBorrowChecker.cpp + BSC/BSCBorrowCheck.cpp + BSC/BSCBorrowChecker.cpp + BSC/BSCFatPtrCheck.cpp + BSC/BSCNullabilityCheck.cpp + BSC/BSCOwnership.cpp CalledOnceCheck.cpp CFG.cpp CFGReachabilityAnalysis.cpp diff --git a/clang/lib/Sema/BSC/SemaDeclBSC.cpp b/clang/lib/Sema/BSC/SemaDeclBSC.cpp index bc2cb704c36c..fe82b6f447c6 100644 --- a/clang/lib/Sema/BSC/SemaDeclBSC.cpp +++ b/clang/lib/Sema/BSC/SemaDeclBSC.cpp @@ -15,10 +15,10 @@ #include "TreeTransform.h" #include "clang/AST/BSC/WalkerBSC.h" -#include "clang/Analysis/Analyses/BSCBorrowChecker.h" -#include "clang/Analysis/Analyses/BSCNullabilityCheck.h" -#include "clang/Analysis/Analyses/BSCFatPtrCheck.h" -#include "clang/Analysis/Analyses/BSCOwnership.h" +#include "clang/Analysis/Analyses/BSC/BSCBorrowChecker.h" +#include "clang/Analysis/Analyses/BSC/BSCFatPtrCheck.h" +#include "clang/Analysis/Analyses/BSC/BSCNullabilityCheck.h" +#include "clang/Analysis/Analyses/BSC/BSCOwnership.h" #include "clang/Analysis/AnalysisDeclContext.h" #include "clang/Sema/Sema.h" -- Gitee From 312dae5b6d071fa93007661e4b4036ab4389bb95 Mon Sep 17 00:00:00 2001 From: wuhuiquan Date: Wed, 19 Feb 2025 16:25:52 +0800 Subject: [PATCH 13/29] [Diag] Add error blocking compilation options --- clang/docs/BiShengCLanguageUserManual.md | 97 ++++++++++++-- .../Analysis/Analyses/BSC/BSCBorrowChecker.h | 21 ++++ .../Analyses/BSC/BSCNullabilityCheck.h | 46 +++---- .../Analysis/Analyses/BSC/BSCOwnership.h | 97 +++++++------- .../Basic/BSC/DiagnosticBSCFrontendKinds.td | 3 + .../clang/Basic/BSC/DiagnosticBSCGroups.td | 50 +++++++- .../clang/Basic/BSC/DiagnosticBSCSemaKinds.td | 100 ++++++++++----- .../clang/Basic/DiagnosticFrontendKinds.td | 5 + clang/include/clang/Basic/DiagnosticIDs.h | 4 + clang/include/clang/Basic/DiagnosticOptions.h | 5 + clang/include/clang/Driver/BSC/BSCOptions.td | 7 +- clang/lib/Basic/Diagnostic.cpp | 4 + clang/lib/Basic/DiagnosticIDs.cpp | 10 ++ clang/lib/Basic/Warnings.cpp | 44 ++++++- clang/lib/Driver/ToolChains/Clang.cpp | 3 + clang/lib/Frontend/CompilerInvocation.cpp | 9 ++ clang/lib/Frontend/TextDiagnosticPrinter.cpp | 18 ++- clang/lib/Lex/Pragma.cpp | 14 ++- .../BSC/Negative/NullabilityCheck/if_stmt.cbs | 4 +- .../NullabilityCheck/nested_struct.cbs | 14 +-- .../Negative/NullabilityCheck/while_stmt.cbs | 2 +- .../assign_borrowed/assign_borrowed.cbs | 23 ++++ .../move_borrowed/move_borrowed.cbs | 21 ++++ .../repeated_borrow/repeated_borrow.cbs | 57 +++++++++ .../short_life_borrow/short_life_borrow.cbs | 22 ++++ .../use_mutably_borrowed.cbs | 19 +++ .../Mixed/bsc_borrow/bsc_borrow.cbs | 41 ++++++ .../Mixed/bsc_nullability/bsc_nullability.cbs | 60 +++++++++ .../Mixed/bsc_ownership/bsc_ownership.cbs | 59 +++++++++ .../bsc_safety_check/bsc_safety_check.cbs | 27 ++++ .../assign_nonnull/assign_nonnull.cbs | 33 +++++ .../assign_nullable/assign_nullable.cbs | 32 +++++ .../cast_nullable/cast_nullable.cbs | 31 +++++ .../deref_nullable/deref_nullable.cbs | 33 +++++ .../pass_nullable/pass_nullable.cbs | 31 +++++ .../return_nullable/return_nullable.cbs | 12 ++ .../Ownership/assign_owned/assign_owned.cbs | 26 ++++ .../Ownership/cast_owned/cast_owned.cbs | 26 ++++ .../Ownership/memory_leak/memory_leak.cbs | 70 +++++++++++ .../use_moved_owned/use_moved_owned.cbs | 113 +++++++++++++++++ .../use_uninit_owned/use_uninit_owned.cbs | 13 ++ .../assign_borrowed/assign_borrowed.cbs | 26 ++++ .../move_borrowed/move_borrowed.cbs | 24 ++++ .../repeated_borrow/repeated_borrow.cbs | 66 ++++++++++ .../short_life_borrow/short_life_borrow.cbs | 25 ++++ .../use_mutably_borrowed.cbs | 22 ++++ .../Pragma/Mixed/bsc_borrow/bsc_borrow.cbs | 47 +++++++ .../Mixed/bsc_nullability/bsc_nullability.cbs | 66 ++++++++++ .../Mixed/bsc_ownership/bsc_ownership.cbs | 68 ++++++++++ .../bsc_safety_check/bsc_safety_check.cbs | 30 +++++ .../assign_nonnull/assign_nonnull.cbs | 39 ++++++ .../assign_nullable/assign_nullable.cbs | 35 ++++++ .../cast_nullable/cast_nullable.cbs | 34 +++++ .../deref_nullable/deref_nullable.cbs | 36 ++++++ .../pass_nullable/pass_nullable.cbs | 34 +++++ .../return_nullable/return_nullable.cbs | 14 +++ .../Ownership/assign_owned/assign_owned.cbs | 29 +++++ .../Ownership/cast_owned/cast_owned.cbs | 29 +++++ .../Ownership/memory_leak/memory_leak.cbs | 76 +++++++++++ .../use_moved_owned/use_moved_owned.cbs | 119 ++++++++++++++++++ .../use_uninit_owned/use_uninit_owned.cbs | 16 +++ .../TableGen/ClangDiagnosticsEmitter.cpp | 12 +- 62 files changed, 2011 insertions(+), 142 deletions(-) create mode 100644 clang/include/clang/Basic/BSC/DiagnosticBSCFrontendKinds.td create mode 100644 clang/test/BSC/Positive/BlockingError/CompOptions/BorrowCheck/assign_borrowed/assign_borrowed.cbs create mode 100644 clang/test/BSC/Positive/BlockingError/CompOptions/BorrowCheck/move_borrowed/move_borrowed.cbs create mode 100644 clang/test/BSC/Positive/BlockingError/CompOptions/BorrowCheck/repeated_borrow/repeated_borrow.cbs create mode 100644 clang/test/BSC/Positive/BlockingError/CompOptions/BorrowCheck/short_life_borrow/short_life_borrow.cbs create mode 100644 clang/test/BSC/Positive/BlockingError/CompOptions/BorrowCheck/use_mutably_borrowed/use_mutably_borrowed.cbs create mode 100644 clang/test/BSC/Positive/BlockingError/CompOptions/Mixed/bsc_borrow/bsc_borrow.cbs create mode 100644 clang/test/BSC/Positive/BlockingError/CompOptions/Mixed/bsc_nullability/bsc_nullability.cbs create mode 100644 clang/test/BSC/Positive/BlockingError/CompOptions/Mixed/bsc_ownership/bsc_ownership.cbs create mode 100644 clang/test/BSC/Positive/BlockingError/CompOptions/Mixed/bsc_safety_check/bsc_safety_check.cbs create mode 100644 clang/test/BSC/Positive/BlockingError/CompOptions/Nullability/assign_nonnull/assign_nonnull.cbs create mode 100644 clang/test/BSC/Positive/BlockingError/CompOptions/Nullability/assign_nullable/assign_nullable.cbs create mode 100644 clang/test/BSC/Positive/BlockingError/CompOptions/Nullability/cast_nullable/cast_nullable.cbs create mode 100644 clang/test/BSC/Positive/BlockingError/CompOptions/Nullability/deref_nullable/deref_nullable.cbs create mode 100644 clang/test/BSC/Positive/BlockingError/CompOptions/Nullability/pass_nullable/pass_nullable.cbs create mode 100644 clang/test/BSC/Positive/BlockingError/CompOptions/Nullability/return_nullable/return_nullable.cbs create mode 100644 clang/test/BSC/Positive/BlockingError/CompOptions/Ownership/assign_owned/assign_owned.cbs create mode 100644 clang/test/BSC/Positive/BlockingError/CompOptions/Ownership/cast_owned/cast_owned.cbs create mode 100644 clang/test/BSC/Positive/BlockingError/CompOptions/Ownership/memory_leak/memory_leak.cbs create mode 100644 clang/test/BSC/Positive/BlockingError/CompOptions/Ownership/use_moved_owned/use_moved_owned.cbs create mode 100644 clang/test/BSC/Positive/BlockingError/CompOptions/Ownership/use_uninit_owned/use_uninit_owned.cbs create mode 100644 clang/test/BSC/Positive/BlockingError/Pragma/BorrowCheck/assign_borrowed/assign_borrowed.cbs create mode 100644 clang/test/BSC/Positive/BlockingError/Pragma/BorrowCheck/move_borrowed/move_borrowed.cbs create mode 100644 clang/test/BSC/Positive/BlockingError/Pragma/BorrowCheck/repeated_borrow/repeated_borrow.cbs create mode 100644 clang/test/BSC/Positive/BlockingError/Pragma/BorrowCheck/short_life_borrow/short_life_borrow.cbs create mode 100644 clang/test/BSC/Positive/BlockingError/Pragma/BorrowCheck/use_mutably_borrowed/use_mutably_borrowed.cbs create mode 100644 clang/test/BSC/Positive/BlockingError/Pragma/Mixed/bsc_borrow/bsc_borrow.cbs create mode 100644 clang/test/BSC/Positive/BlockingError/Pragma/Mixed/bsc_nullability/bsc_nullability.cbs create mode 100644 clang/test/BSC/Positive/BlockingError/Pragma/Mixed/bsc_ownership/bsc_ownership.cbs create mode 100644 clang/test/BSC/Positive/BlockingError/Pragma/Mixed/bsc_safety_check/bsc_safety_check.cbs create mode 100644 clang/test/BSC/Positive/BlockingError/Pragma/Nullability/assign_nonnull/assign_nonnull.cbs create mode 100644 clang/test/BSC/Positive/BlockingError/Pragma/Nullability/assign_nullable/assign_nullable.cbs create mode 100644 clang/test/BSC/Positive/BlockingError/Pragma/Nullability/cast_nullable/cast_nullable.cbs create mode 100644 clang/test/BSC/Positive/BlockingError/Pragma/Nullability/deref_nullable/deref_nullable.cbs create mode 100644 clang/test/BSC/Positive/BlockingError/Pragma/Nullability/pass_nullable/pass_nullable.cbs create mode 100644 clang/test/BSC/Positive/BlockingError/Pragma/Nullability/return_nullable/return_nullable.cbs create mode 100644 clang/test/BSC/Positive/BlockingError/Pragma/Ownership/assign_owned/assign_owned.cbs create mode 100644 clang/test/BSC/Positive/BlockingError/Pragma/Ownership/cast_owned/cast_owned.cbs create mode 100644 clang/test/BSC/Positive/BlockingError/Pragma/Ownership/memory_leak/memory_leak.cbs create mode 100644 clang/test/BSC/Positive/BlockingError/Pragma/Ownership/use_moved_owned/use_moved_owned.cbs create mode 100644 clang/test/BSC/Positive/BlockingError/Pragma/Ownership/use_uninit_owned/use_uninit_owned.cbs diff --git a/clang/docs/BiShengCLanguageUserManual.md b/clang/docs/BiShengCLanguageUserManual.md index 241014dc2160..7ade6b5507b5 100644 --- a/clang/docs/BiShengCLanguageUserManual.md +++ b/clang/docs/BiShengCLanguageUserManual.md @@ -5673,19 +5673,17 @@ int main() { #line 12 "basic_math.hbs" int struct_MyStruct_divide(int a, int b); - - - #endif ``` + `calc_demo.c` ```c #include "basic_math.h" - + static int min_int(int a, int b); - + static int max_int(int a, int b); - + #line 3 "calc_demo.cbs" int main(void) { struct MyStruct s = {4, 2}; @@ -5697,12 +5695,12 @@ int main() { int c2 = min_int(s.a, s.b); return 0; } - + #line 14 "./basic_math.hbs" static int min_int(int a, int b) { return a > b ? b : a; } - + #line 18 "./basic_math.hbs" static int max_int(int a, int b) { return a > b ? a : b; @@ -5742,6 +5740,89 @@ int main() { - 调试中显示的代码位置指向原始cbs文件,支持多源文件的调试跳转。 - 当源源变换前后代码行数存在差异、无法逐行映射时,例如owned struct析构函数、trait等会生成新代码的特性,显示的调试位置可能不准确,需要开发者注意。 +## 编译错误屏蔽 + +### 概述 + +​ 开发者可以针对性的让编译器在编译时不显示某些已知且暂时无需关注的错误,从而避免编译过程被这些错误打断,提高开发效率。当前只支持屏蔽 Nullability、Owned、Borrow 数据流安全分析过程中的报错,不支持屏蔽其他语法语义报错。在某些场景中,这些安全规则可能会过于严格,开发者可以针对这些过于严格的报错进行屏蔽。但需注意,屏蔽错误并不意味着错误不存在,只是在编译阶段不展示相关提示。过度使用错误屏蔽功能可能会导致一些严重问题被忽视。 + +### 使用方式 + +- 使用编译选项屏蔽错误:毕昇 C 编译器新增了 `-Eno-xxx` 错误屏蔽编译选项。这里的`xxx`代表具体的**错误类型标识**。 + + 示例: 对于如下代码,使用毕昇编译器编译时会上报注释中所述错误,我们通过在编译命令中添加 `-Eno-repeated-borrow`可关闭此类错误提示。 + + ```c++ + // file: test1.cbs + // clang -Eno-repeated-borrow test1.cbs + void use(int * borrow a){} + int main() { + int local = 1; + int * borrow p1 = &mut local; + int * borrow p2 = &mut local; // error: cannot borrow `local` as mutable more than once at a time + use(p2); + use(p1); + return 0; + } + ``` + +- 屏蔽代码片段中的错误:通过在代码片段中添加`#pragma` 预处理指令管理编译过程中的报错行为。 + + `#pragma GCC diagnostic push`保存当前的诊断状态。 + + `#pragma GCC diagnostic ignored "-Exxx"`告诉编译器忽略`xxx`错误类型的报错。这里的`xxx`代表具体的**错误类型标识**。 + + `#pragma GCC diagnostic pop`恢复到之前保存的诊断状态。 + + 示例:对于如下代码,编译器会忽略`*p1 = 2;`触发的`assign-borrowed`错误类型的报错。 + + ```c++ + int main() { + int local = 42; + int temp = 2; + int *borrow p1 = &mut local; + int *borrow p2 = &mut temp; + p2 = p1; + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Eassign-borrowed" + *p1 = 2; // error: cannot assign to `*p1` because it is borrowed + #pragma GCC diagnostic pop + temp = *p2; + return 0; + } + ``` +### 可被屏蔽的错误 + + 具体错误类型标识对应的错误日志详情见下表: + +| **错误类型标识** | 可屏蔽错误日志 (日志中%0、%1为您所写的代码中变量的属性。) | +| -------------------- | ------------------------------------------------------------ | +| deref-nullable | "nullable pointer cannot be dereferenced" | +| pass-nullable | "cannot pass nullable pointer argument" | +| return-nullable | "cannot return nullable pointer type" | +| cast-nullable | "cannot cast nullable pointer to nonnull type" | +| assign-nullable | "cannot access member through nullable pointer" | +| assign-nonnull | "nonnull pointer cannot be assigned by nullable pointer" | +| bsc-nullability | 可屏蔽所有 Nullability 数据流分析过程的报错,
包括 deref-nullable、pass-nullable、return-nullable、cast-nullable、
assign-nullable、assign-nonnull 错误类型标识。 | +| use-moved-owned | "use of moved value: \`%0\`“
"use of partially moved value: \`%0\`, %1 moved"
"use of all moved value: \`%0\`" | +| use-uninit-owned | "use of uninitialized value: \`%0\`"
"use of possibly uninitialized value: \`%0\`" | +| use-owned | 包括use-moved-owned、use-uninit-owned 错误类型标识可屏蔽的错误日志 | +| assign-moved-owned | "assign to partially moved value: \`%0\`, %1 moved"
"assign to possibly partially moved value: \`%0\`, %1 possibly moved"
"assign to all moved value: \`%0\`"
"assign to part of moved value: \`%0\`" | +| assign-uninit-owned | "assign to part of uninitialized value: \`%0\`" | +| assign-owned | "assign to owned value: \`%0\`"
"assign to part of owned value: \`%0\`"
"assign to subfield owned value: \`%0\`, %1 owned"
还包括assign-moved-owned、assign-uninit-owned 错误类型标识可屏蔽的错误日志 | +| cast-moved-owned | "invalid cast to `void * owned` of moved value: \`%0\`" | +| cast-owned | "invalid cast to `void * owned` of owned value: \`%0\`"
"invalid cast to `void * owned` of uninit value: \`%0\`"
"invalid cast to `void * owned` of not all moved value: \`%0\`, %1 owned"
"invalid cast to `void * owned` of moved value:\`%0\`" | +| check-memory-leak | "field memory leak of value: `%0`, %1 leak"
"memory leak of value: `%0` | +| bsc-ownership | 可屏蔽所有 owned 数据流分析过程的报错,
包括 use-owned、assign-owned、cast-owned、check-memory-leak 错误类型标识可屏蔽的错误日志。 | +| assign-borrowed | "cannot assign to \`%0\` because it is borrowed" | +| move-borrowed | "cannot move out of \`%0\` because it is borrowed" | +| use-mutably-borrowed | "cannot use \`%0\` because it was mutably borrowed" | +| repeated-borrow | "cannot borrow \`%0\` as mutable more than once at a time"
"cannot borrow \`%0\` as immutable because it is also borrowed as mutable"
"cannot borrow \`%0\` as mutable because it is also borrowed as immutable" | +| return-local-borrow | "cannot return reference to local variable \`%0\`" | +| short-life-borrow | "\`%0\` does not live long enough" | +| bsc-borrow | 可屏蔽所有 borrow 数据流分析过程的报错,
包括assign-borrowed、move-borrowed、use-mutably-borrowed、
repeated-borrow、return-local-borrow、short-life-borrow错误类型标识可屏蔽的错误日志。 | +| bsc-safety-check | 可屏蔽所有 Nullability、owned、borrow 数据流分析过程的报错,
包括bsc-nullability、bsc-ownership、bsc-borrow错误类型标识可屏蔽的错误日志。 | + diff --git a/clang/include/clang/Analysis/Analyses/BSC/BSCBorrowChecker.h b/clang/include/clang/Analysis/Analyses/BSC/BSCBorrowChecker.h index 49c093f5853c..7fbca6ef0317 100644 --- a/clang/include/clang/Analysis/Analyses/BSC/BSCBorrowChecker.h +++ b/clang/include/clang/Analysis/Analyses/BSC/BSCBorrowChecker.h @@ -734,8 +734,18 @@ enum class BorrowDiagKind { ForRead, ForWrite, ForStorageDead, + BorrowMaxDiagKind }; +const unsigned BorrowDiagIdList[] = { + diag::err_borrow_immut_borrow_when_mut_borrowed, + diag::err_borrow_move_when_borrowed, + diag::err_borrow_mut_borrow_more_than_once, + diag::err_borrow_mut_borrow_when_immut_borrowed, + diag::err_borrow_use_when_mut_borrowed, + diag::err_borrow_not_live_long, + diag::err_borrow_assign_when_borrowed}; + struct BorrowDiagInfo { BorrowDiagKind Kind; SourceLocation Location; @@ -846,8 +856,19 @@ public: path->to_string(), LoanLoc)); } + unsigned getBorrowDiagID(BorrowDiagKind Kind) { + unsigned index = static_cast(Kind); + assert(index < static_cast(BorrowDiagKind::BorrowMaxDiagKind) && + "Unknown error type"); + return BorrowDiagIdList[index]; + } + void emitDiag(BorrowDiagKind Kind, SourceLocation Location, const Path *path, SourceLocation LoanLoc, const Path *loanPath = nullptr) { + if (S.getDiagnostics().getDiagnosticLevel( + getBorrowDiagID(Kind), Location) == DiagnosticsEngine::Ignored) { + return; + } switch (Kind) { case BorrowDiagKind::ForImmutWhenMut: ForImmutWhenMut(Location, path, LoanLoc); diff --git a/clang/include/clang/Analysis/Analyses/BSC/BSCNullabilityCheck.h b/clang/include/clang/Analysis/Analyses/BSC/BSCNullabilityCheck.h index d7cec1207cd5..7e862348f0b4 100644 --- a/clang/include/clang/Analysis/Analyses/BSC/BSCNullabilityCheck.h +++ b/clang/include/clang/Analysis/Analyses/BSC/BSCNullabilityCheck.h @@ -28,8 +28,17 @@ enum NullabilityCheckDiagKind { NullableCastNonnull, NullablePointerDereference, NullablePointerAccessMember, + NullabilityMaxDiagKind }; +const unsigned NullabilityDiagIdList[] = { + diag::err_nonnull_assigned_by_nullable, + diag::err_pass_nullable_argument, + diag::err_return_nullable, + diag::err_nullable_cast_nonnull, + diag::err_nullable_pointer_dereference, + diag::err_nullable_pointer_access_member}; + struct NullabilityCheckDiagInfo { SourceLocation Loc; NullabilityCheckDiagKind Kind; @@ -60,9 +69,22 @@ public: if (DI == *it) return; } + if (S.getDiagnostics().getDiagnosticLevel(getNullabilityDiagID(DI.Kind), + DI.Loc) == + DiagnosticsEngine::Ignored) { + return; + } DIV.push_back(DI); } + unsigned getNullabilityDiagID(NullabilityCheckDiagKind Kind) { + unsigned index = static_cast(Kind); + assert(index < static_cast( + NullabilityCheckDiagKind::NullabilityMaxDiagKind) && + "Unknown error type"); + return NullabilityDiagIdList[index]; + } + void flushDiagnostics() { // Sort the diag info by SourceLocation. While not strictly // guaranteed to produce them in line/column order, this will provide @@ -75,29 +97,7 @@ public: }); for (const NullabilityCheckDiagInfo &DI : DIV) { - switch (DI.Kind) { - case NonnullAssignedByNullable: - S.Diag(DI.Loc, diag::err_nonnull_assigned_by_nullable); - break; - case PassNullableArgument: - S.Diag(DI.Loc, diag::err_pass_nullable_argument); - break; - case ReturnNullable: - S.Diag(DI.Loc, diag::err_return_nullable); - break; - case NullableCastNonnull: - S.Diag(DI.Loc, diag::err_nullable_cast_nonnull); - break; - case NullablePointerDereference: - S.Diag(DI.Loc, diag::err_nullable_pointer_dereference); - break; - case NullablePointerAccessMember: - S.Diag(DI.Loc, diag::err_nullable_pointer_access_member); - break; - default: - llvm_unreachable("Unknown error type"); - break; - } + S.Diag(DI.Loc, getNullabilityDiagID(DI.Kind)); S.getDiagnostics().increaseNullabilityCheckErrors(); } } diff --git a/clang/include/clang/Analysis/Analyses/BSC/BSCOwnership.h b/clang/include/clang/Analysis/Analyses/BSC/BSCOwnership.h index a2c673e6f5ed..f84f3ba72087 100644 --- a/clang/include/clang/Analysis/Analyses/BSC/BSCOwnership.h +++ b/clang/include/clang/Analysis/Analyses/BSC/BSCOwnership.h @@ -186,8 +186,30 @@ enum OwnershipDiagKind { InvalidCastFieldOwned, FieldMemoryLeak, MemoryLeak, + OwnershipMaxDiagKind }; +const unsigned OwnershipDiagIdList[] = { + diag::err_ownership_use_moved, + diag::err_ownership_use_partially_moved, + diag::err_ownership_use_all_moved, + diag::err_ownership_use_possibly_uninit, + diag::err_ownership_use_uninit, + diag::err_ownership_assign_owned, + diag::err_ownership_assign_partially_moved, + diag::err_ownership_assign_possibly_partially_moved, + diag::err_ownership_assign_all_moved, + diag::err_ownership_assign_field_uninit, + diag::err_ownership_assign_field_owned, + diag::err_ownership_assign_field_moved, + diag::err_ownership_assign_field_subfield_owned, + diag::err_ownership_cast_moved, + diag::err_ownership_cast_owned, + diag::err_ownership_cast_uninit, + diag::err_ownership_cast_subfield_owned, + diag::err_ownership_memory_leak_field, + diag::err_ownership_memory_leak}; + class OwnershipDiagInfo { public: SourceLocation Loc; @@ -235,9 +257,22 @@ public: if (DI == *it) return; } + if (S.getDiagnostics().getDiagnosticLevel(getOwnershipDiagID(DI.Kind), + DI.Loc) == + DiagnosticsEngine::Ignored) { + return; + } DIV.push_back(DI); } + unsigned getOwnershipDiagID(OwnershipDiagKind Kind) { + unsigned index = static_cast(Kind); + assert(index < + static_cast(OwnershipDiagKind::OwnershipMaxDiagKind) && + "Unknown error type"); + return OwnershipDiagIdList[index]; + } + void flushDiagnostics() { // Sort the diag info by SourceLocation. While not strictly // guaranteed to produce them in line/column order, this will provide @@ -250,68 +285,28 @@ public: for (const OwnershipDiagInfo &DI : DIV) { switch (DI.Kind) { - case InvalidUseOfMoved: - S.Diag(DI.Loc, diag::err_ownership_use_moved) << DI.Name; - break; - case InvalidUseOfPartiallyMoved: - S.Diag(DI.Loc, diag::err_ownership_use_partially_moved) - << DI.Name << DI.Fields; - break; - case InvalidUseOfAllMoved: - S.Diag(DI.Loc, diag::err_ownership_use_all_moved) << DI.Name; - break; - case InvalidUseOfUninit: - S.Diag(DI.Loc, diag::err_ownership_use_uninit) << DI.Name; - break; - case InvalidUseOfPossiblyUninit: - S.Diag(DI.Loc, diag::err_ownership_use_possibly_uninit) << DI.Name; - break; case InvalidAssignOfOwned: - S.Diag(DI.Loc, diag::err_ownership_assign_owned) << DI.Name; - break; - case InvalidAssignOfPartiallyMoved: - S.Diag(DI.Loc, diag::err_ownership_assign_partially_moved) - << DI.Name << DI.Fields; - break; - case InvalidAssignOfPossiblyPartiallyMoved: - S.Diag(DI.Loc, diag::err_ownership_assign_possibly_partially_moved) - << DI.Name << DI.Fields; - break; case InvalidAssignOfAllMoved: - S.Diag(DI.Loc, diag::err_ownership_assign_all_moved) << DI.Name; - break; case InvalidAssignFieldOfUninit: - S.Diag(DI.Loc, diag::err_ownership_assign_field_uninit) << DI.Name; - break; case InvalidAssignFieldOfOwned: - S.Diag(DI.Loc, diag::err_ownership_assign_field_owned) << DI.Name; - break; case InvalidAssignFieldOfMoved: - S.Diag(DI.Loc, diag::err_ownership_assign_field_moved) << DI.Name; - break; - case InvalidAssignSubFieldOwned: - S.Diag(DI.Loc, diag::err_ownership_assign_field_subfield_owned) - << DI.Name << DI.Fields; - break; case InvalidCastMoved: - S.Diag(DI.Loc, diag::err_ownership_cast_moved) << DI.Name; - break; case InvalidCastOwned: - S.Diag(DI.Loc, diag::err_ownership_cast_owned) << DI.Name; - break; case InvalidCastUninit: - S.Diag(DI.Loc, diag::err_ownership_cast_uninit) << DI.Name; + case InvalidUseOfAllMoved: + case InvalidUseOfMoved: + case InvalidUseOfPossiblyUninit: + case InvalidUseOfUninit: + case MemoryLeak: + S.Diag(DI.Loc, getOwnershipDiagID(DI.Kind)) << DI.Name; break; + case InvalidAssignOfPartiallyMoved: + case InvalidAssignOfPossiblyPartiallyMoved: + case InvalidAssignSubFieldOwned: case InvalidCastFieldOwned: - S.Diag(DI.Loc, diag::err_ownership_cast_subfield_owned) - << DI.Name << DI.Fields; - break; + case InvalidUseOfPartiallyMoved: case FieldMemoryLeak: - S.Diag(DI.Loc, diag::err_ownership_memory_leak_field) - << DI.Name << DI.Fields; - break; - case MemoryLeak: - S.Diag(DI.Loc, diag::err_ownership_memory_leak) << DI.Name; + S.Diag(DI.Loc, getOwnershipDiagID(DI.Kind)) << DI.Name << DI.Fields; break; default: llvm_unreachable("Unknown error type"); diff --git a/clang/include/clang/Basic/BSC/DiagnosticBSCFrontendKinds.td b/clang/include/clang/Basic/BSC/DiagnosticBSCFrontendKinds.td new file mode 100644 index 000000000000..57dd04ad5de8 --- /dev/null +++ b/clang/include/clang/Basic/BSC/DiagnosticBSCFrontendKinds.td @@ -0,0 +1,3 @@ +def warn_unknown_bsc_diag_option : Warning< + "unknown %select{warning|remark|error}0 option '%1'%select{|; did you mean '%3'?}2">, + InGroup; \ No newline at end of file diff --git a/clang/include/clang/Basic/BSC/DiagnosticBSCGroups.td b/clang/include/clang/Basic/BSC/DiagnosticBSCGroups.td index 59f8b42f811d..1ad004445485 100644 --- a/clang/include/clang/Basic/BSC/DiagnosticBSCGroups.td +++ b/clang/include/clang/Basic/BSC/DiagnosticBSCGroups.td @@ -1,2 +1,50 @@ def BSCTraitMissing : DiagGroup<"bsc-trait-missing">; -def BSCOwnedStruct : DiagGroup<"bsc-owned-struct">; \ No newline at end of file +def BSCOwnedStruct : DiagGroup<"bsc-owned-struct">; + +def DerefNullable : DiagGroup<"deref-nullable">; +def PassNullable : DiagGroup<"pass-nullable">; +def ReturnNullable : DiagGroup<"return-nullable">; +def CastNullable : DiagGroup<"cast-nullable">; +def AssignNullable : DiagGroup<"assign-nullable">; +def AssignNonnull : DiagGroup<"assign-nonnull">; +def BSCNullability : DiagGroup<"bsc-nullability", + [DerefNullable, + PassNullable, + ReturnNullable, + CastNullable, + AssignNullable, + AssignNonnull]>; + +def UseMovedOwned : DiagGroup<"use-moved-owned">; +def UseUninitOwned : DiagGroup<"use-uninit-owned">; +def UseOwned : DiagGroup<"use-owned",[UseMovedOwned, UseUninitOwned]>; +def AssignMovedOwned : DiagGroup<"assign-moved-owned">; +def AssignUninitOwned : DiagGroup<"assign-uninit-owned">; +def AssignOwned : DiagGroup<"assign-owned",[AssignUninitOwned, AssignMovedOwned]>; +def CastMovedOwned : DiagGroup<"cast-moved-owned">; +def CastOwned : DiagGroup<"cast-owned", [CastMovedOwned]>; +def CheckMemoryLeak : DiagGroup<"check-memory-leak">; +def BSCOwnership : DiagGroup<"bsc-ownership", + [UseOwned, + AssignOwned, + CastOwned, + CheckMemoryLeak]>; + +def AssignBorrowed : DiagGroup<"assign-borrowed">; +def MoveBorrowed : DiagGroup<"move-borrowed">; +def UseMutablyBorrowed : DiagGroup<"use-mutably-borrowed">; +def RepeatedBorrow : DiagGroup<"repeated-borrow">; +def ReturnLocalBorrow : DiagGroup<"return-local-borrow">; +def ShortLifeBorrow : DiagGroup<"short-life-borrow">; +def BSCBorrow : DiagGroup<"bsc-borrow", + [AssignBorrowed, + MoveBorrowed, + UseMutablyBorrowed, + RepeatedBorrow, + ReturnLocalBorrow, + ShortLifeBorrow]>; + +def BSCSafetyCheck : DiagGroup<"bsc-safety-check", + [BSCNullability, + BSCOwnership, + BSCBorrow]>; \ No newline at end of file diff --git a/clang/include/clang/Basic/BSC/DiagnosticBSCSemaKinds.td b/clang/include/clang/Basic/BSC/DiagnosticBSCSemaKinds.td index 0ea50fbc5b61..c70e09f31e8b 100644 --- a/clang/include/clang/Basic/BSC/DiagnosticBSCSemaKinds.td +++ b/clang/include/clang/Basic/BSC/DiagnosticBSCSemaKinds.td @@ -138,50 +138,68 @@ def err_owned_struct_destructor_body : Error<"destructor must have a function bo // BSC Ownership Analysis errors. // use def err_ownership_use_moved : Error< - "use of moved value: `%0`">; + "use of moved value: `%0`">, + InGroup; def err_ownership_use_partially_moved : Error< - "use of partially moved value: `%0`, %1 moved">; + "use of partially moved value: `%0`, %1 moved">, + InGroup; def err_ownership_use_all_moved: Error< - "use of all moved value: `%0`">; + "use of all moved value: `%0`">, + InGroup; def err_ownership_use_uninit : Error< - "use of uninitialized value: `%0`">; + "use of uninitialized value: `%0`">, + InGroup; def err_ownership_use_possibly_uninit : Error< - "use of possibly uninitialized value: `%0`">; + "use of possibly uninitialized value: `%0`">, + InGroup; // assign def err_ownership_assign_owned : Error< - "assign to owned value: `%0`">; + "assign to owned value: `%0`">, + InGroup; def err_ownership_assign_partially_moved : Error< - "assign to partially moved value: `%0`, %1 moved">; + "assign to partially moved value: `%0`, %1 moved">, + InGroup; def err_ownership_assign_possibly_partially_moved : Error< - "assign to possibly partially moved value: `%0`, %1 possibly moved">; + "assign to possibly partially moved value: `%0`, %1 possibly moved">, + InGroup; def err_ownership_assign_all_moved : Error< - "assign to all moved value: `%0`">; + "assign to all moved value: `%0`">, + InGroup; // field assign def err_ownership_assign_field_uninit : Error< - "assign to part of uninitialized value: `%0`">; + "assign to part of uninitialized value: `%0`">, + InGroup; def err_ownership_assign_field_owned : Error< - "assign to part of owned value: `%0`">; + "assign to part of owned value: `%0`">, + InGroup; def err_ownership_assign_field_moved : Error< - "assign to part of moved value: `%0`">; + "assign to part of moved value: `%0`">, + InGroup; def err_ownership_assign_field_subfield_owned : Error< - "assign to subfield owned value: `%0`, %1 owned">; + "assign to subfield owned value: `%0`, %1 owned">, + InGroup; // cast to `void * owned` def err_ownership_cast_moved : Error< - "invalid cast to `void * owned` of moved value: `%0`">; + "invalid cast to `void * owned` of moved value: `%0`">, + InGroup; def err_ownership_cast_owned : Error< - "invalid cast to `void * owned` of owned value: `%0`">; + "invalid cast to `void * owned` of owned value: `%0`">, + InGroup; def err_ownership_cast_uninit: Error< - "invalid cast to `void * owned` of uninit value: `%0`">; + "invalid cast to `void * owned` of uninit value: `%0`">, + InGroup; def err_ownership_cast_subfield_owned : Error< - "invalid cast to `void * owned` of not all moved value: `%0`, %1 owned">; - + "invalid cast to `void * owned` of not all moved value: `%0`, %1 owned">, + InGroup; // memory leak def err_ownership_memory_leak_field : Error< - "field memory leak of value: `%0`, %1 leak">; + "field memory leak of value: `%0`, %1 leak">, + InGroup; def err_ownership_memory_leak : Error< - "memory leak of value: `%0`">; + "memory leak of value: `%0`">, + InGroup; // BSC Borrow Checker errors. def err_borrow_live_longer_than_target_var : Error< @@ -200,25 +218,33 @@ def err_use_expired_borrow_var : Error< // New BSC Borrow Checker errors. def err_borrow_assign_when_borrowed : Error< - "cannot assign to `%0` because it is borrowed">; + "cannot assign to `%0` because it is borrowed">, + InGroup; def err_borrow_move_when_borrowed : Error< - "cannot move out of `%0` because it is borrowed">; + "cannot move out of `%0` because it is borrowed">, + InGroup; def err_borrow_use_when_mut_borrowed : Error< - "cannot use `%0` because it was mutably borrowed">; + "cannot use `%0` because it was mutably borrowed">, + InGroup; def note_borrowed_here : Note<"`%0` is borrowed here">; def err_borrow_mut_borrow_more_than_once : Error< - "cannot borrow `%0` as mutable more than once at a time">; + "cannot borrow `%0` as mutable more than once at a time">, + InGroup; def note_first_mut_borrow_occurs_here : Note<"first mut borrow occurs here">; def err_borrow_return_local_borrow : Error< - "cannot return reference to local variable `%0`">; + "cannot return reference to local variable `%0`">, + InGroup; def err_borrow_not_live_long : Error< - "`%0` does not live long enough">; + "`%0` does not live long enough">, + InGroup; def note_dropped_while_borrowed: Note<"`%0` dropped here while still borrowed">; def err_borrow_immut_borrow_when_mut_borrowed : Error< - "cannot borrow `%0` as immutable because it is also borrowed as mutable">; + "cannot borrow `%0` as immutable because it is also borrowed as mutable">, + InGroup; def note_mutable_borrow_occurs_here : Note<"mutable borrow occurs here">; def err_borrow_mut_borrow_when_immut_borrowed : Error< - "cannot borrow `%0` as mutable because it is also borrowed as immutable">; + "cannot borrow `%0` as mutable because it is also borrowed as immutable">, + InGroup; def note_immutable_borrow_occurs_here : Note<"immutable borrow occurs here">; // BSC operator overload errors. @@ -228,17 +254,23 @@ def err_operator_overload_needs_point : Error< // BSC Pointer Nullability Check errors. def err_nullable_pointer_dereference : Error< - "nullable pointer cannot be dereferenced">; + "nullable pointer cannot be dereferenced">, + InGroup; def err_pass_nullable_argument : Error< - "cannot pass nullable pointer argument">; + "cannot pass nullable pointer argument">, + InGroup; def err_return_nullable : Error< - "cannot return nullable pointer type">; + "cannot return nullable pointer type">, + InGroup; def err_nullable_cast_nonnull : Error< - "cannot cast nullable pointer to nonnull type">; + "cannot cast nullable pointer to nonnull type">, + InGroup; def err_nullable_pointer_access_member : Error< - "cannot access member througn nullable pointer">; + "cannot access member through nullable pointer">, + InGroup; def err_nonnull_assigned_by_nullable : Error< - "nonnull pointer cannot be assigned by nullable pointer">; + "nonnull pointer cannot be assigned by nullable pointer">, + InGroup; // BSC Fat Pointer errors. def err_fat_ptr_checked_attr_global_array_var : Error< diff --git a/clang/include/clang/Basic/DiagnosticFrontendKinds.td b/clang/include/clang/Basic/DiagnosticFrontendKinds.td index 50f7e2b9221d..2e4f250ee33f 100644 --- a/clang/include/clang/Basic/DiagnosticFrontendKinds.td +++ b/clang/include/clang/Basic/DiagnosticFrontendKinds.td @@ -322,4 +322,9 @@ def warn_profile_data_misexpect : Warning< InGroup; } // end of instrumentation issue category +// BSC warnings and errors +#ifdef BSC +include "clang/Basic/BSC/DiagnosticBSCFrontendKinds.td" +#endif + } diff --git a/clang/include/clang/Basic/DiagnosticIDs.h b/clang/include/clang/Basic/DiagnosticIDs.h index 91b180f8004d..48a2ccde73ec 100644 --- a/clang/include/clang/Basic/DiagnosticIDs.h +++ b/clang/include/clang/Basic/DiagnosticIDs.h @@ -95,6 +95,10 @@ namespace clang { ///< problem. Can be made fatal by -Werror. Remark ///< A diagnostic that indicates normal progress through ///< compilation. +#if ENABLE_BSC + , + Error +#endif }; } diff --git a/clang/include/clang/Basic/DiagnosticOptions.h b/clang/include/clang/Basic/DiagnosticOptions.h index c4134835b5de..4c049ce7cf8e 100644 --- a/clang/include/clang/Basic/DiagnosticOptions.h +++ b/clang/include/clang/Basic/DiagnosticOptions.h @@ -121,6 +121,11 @@ public: /// The prefixes for comment directives sought by -verify ("expected" by /// default). std::vector VerifyPrefixes; +#if ENABLE_BSC + /// The list of -E... options used to alter the diagnostic mappings, with the + /// prefixes removed. + std::vector Errors; +#endif public: // Define accessors/mutators for diagnostic options of enumeration type. diff --git a/clang/include/clang/Driver/BSC/BSCOptions.td b/clang/include/clang/Driver/BSC/BSCOptions.td index 6c782e581767..9c6266b558e8 100644 --- a/clang/include/clang/Driver/BSC/BSCOptions.td +++ b/clang/include/clang/Driver/BSC/BSCOptions.td @@ -12,4 +12,9 @@ def nullability_check : Joined<["-"], "nullability-check=">, Flags<[CC1Option]>, HelpText<"Select Nullability Check Enable Zone">, Values<"none,safeonly,all">, NormalizedValuesScope<"LangOptions">, NormalizedValues<["NC_NONE", "NC_SAFE", "NC_ALL"]>, - MarshallingInfoEnum, "NC_SAFE">; \ No newline at end of file + MarshallingInfoEnum, "NC_SAFE">; +def E_Group : OptionGroup<"">, Group, DocFlatten; +def E_value_Group : OptionGroup<"">, Group, + DocFlatten; +def E_Joined : Joined<["-"], "E">, Group, Flags<[CC1Option, CoreOption]>, + MetaVarName<"">, HelpText<"Enable the specified error">; \ No newline at end of file diff --git a/clang/lib/Basic/Diagnostic.cpp b/clang/lib/Basic/Diagnostic.cpp index 1d4c0cdb21d6..39f3e6d26ace 100644 --- a/clang/lib/Basic/Diagnostic.cpp +++ b/clang/lib/Basic/Diagnostic.cpp @@ -364,9 +364,13 @@ void DiagnosticsEngine::setSeverity(diag::kind Diag, diag::Severity Map, SourceLocation L) { assert(Diag < diag::DIAG_UPPER_LIMIT && "Can only map builtin diagnostics"); +#if ENABLE_BSC + // BSC allows modification of error log levels +#else assert((Diags->isBuiltinWarningOrExtension(Diag) || (Map == diag::Severity::Fatal || Map == diag::Severity::Error)) && "Cannot map errors into warnings!"); +#endif assert((L.isInvalid() || SourceMgr) && "No SourceMgr for valid location"); // Don't allow a mapping to a warning override an error/fatal mapping. diff --git a/clang/lib/Basic/DiagnosticIDs.cpp b/clang/lib/Basic/DiagnosticIDs.cpp index 8e2593b103d1..36a31107fbed 100644 --- a/clang/lib/Basic/DiagnosticIDs.cpp +++ b/clang/lib/Basic/DiagnosticIDs.cpp @@ -134,6 +134,16 @@ struct StaticDiagInfoRec { } diag::Flavor getFlavor() const { +#if ENABLE_BSC + switch (Class) { + case CLASS_ERROR: + return diag::Flavor::Error; + case CLASS_REMARK: + return diag::Flavor::Remark; + default: + return diag::Flavor::WarningOrError; + } +#endif return Class == CLASS_REMARK ? diag::Flavor::Remark : diag::Flavor::WarningOrError; } diff --git a/clang/lib/Basic/Warnings.cpp b/clang/lib/Basic/Warnings.cpp index cc8c138233ca..ffd38eda0856 100644 --- a/clang/lib/Basic/Warnings.cpp +++ b/clang/lib/Basic/Warnings.cpp @@ -35,8 +35,20 @@ static void EmitUnknownDiagWarning(DiagnosticsEngine &Diags, diag::Flavor Flavor, StringRef Prefix, StringRef Opt) { StringRef Suggestion = DiagnosticIDs::getNearestOption(Flavor, Opt); - Diags.Report(diag::warn_unknown_diag_option) - << (Flavor == diag::Flavor::WarningOrError ? 0 : 1) + Diags.Report( +#if ENABLE_BSC + diag::warn_unknown_bsc_diag_option +#else + diag::warn_unknown_diag_option +#endif + ) + << ( +#if ENABLE_BSC + Flavor == diag::Flavor::Error + ? 2 + : +#endif + Flavor == diag::Flavor::WarningOrError ? 0 : 1) << (Prefix.str() += std::string(Opt)) << !Suggestion.empty() << (Prefix.str() += std::string(Suggestion)); } @@ -198,6 +210,34 @@ void clang::ProcessWarningOptions(DiagnosticsEngine &Diags, } } +#if ENABLE_BSC + for (unsigned i = 0, e = Opts.Errors.size(); i != e; ++i) { + const auto Flavor = diag::Flavor::Error; + StringRef Opt = Opts.Errors[i]; + + // Check to see if this error starts with "no-", if so, this is a + // negative form of the option. + bool isPositive = true; + if (Opt.startswith("no-")) { + isPositive = false; + Opt = Opt.substr(3); + } + + // Figure out how this option affects the error. If -Efoo, map the + // diagnostic to a error, if -Eno-foo, map it to ignore. + diag::Severity Mapping = + isPositive ? diag::Severity::Error : diag::Severity::Ignored; + + if (Report) { + if (DiagIDs->getDiagnosticsInGroup(Flavor, Opt, _Diags)) + EmitUnknownDiagWarning(Diags, Flavor, isPositive ? "-E" : "-Eno-", + Opt); + } else { + Diags.setSeverityForGroup(Flavor, Opt, Mapping); + } + } +#endif + for (unsigned i = 0, e = Opts.Remarks.size(); i != e; ++i) { StringRef Opt = Opts.Remarks[i]; const auto Flavor = diag::Flavor::Remark; diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index 543964111f2c..ca169a639f95 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -5748,6 +5748,9 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, claimNoWarnArgs(Args); Args.AddAllArgs(CmdArgs, options::OPT_R_Group); +#if ENABLE_BSC + Args.AddAllArgs(CmdArgs, options::OPT_E_Group); +#endif for (const Arg *A : Args.filtered(options::OPT_W_Group, options::OPT__SLASH_wd)) { diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index 68aaf9cc2628..55b0018cacbe 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -2324,6 +2324,12 @@ void CompilerInvocation::GenerateDiagnosticArgs( Args.push_back(SA(StringRef("-W") + Warning)); } +#if ENABLE_BSC + for (const auto &Error : Opts.Errors) { + Args.push_back(SA(StringRef("-E") + Error)); + } +#endif + for (const auto &Remark : Opts.Remarks) { // These arguments are generated from OptimizationRemark fields of // CodeGenOptions. @@ -2409,6 +2415,9 @@ bool clang::ParseDiagnosticArgs(DiagnosticOptions &Opts, ArgList &Args, addDiagnosticArgs(Args, OPT_W_Group, OPT_W_value_Group, Opts.Warnings); addDiagnosticArgs(Args, OPT_R_Group, OPT_R_value_Group, Opts.Remarks); +#if ENABLE_BSC + addDiagnosticArgs(Args, OPT_E_Group, OPT_E_value_Group, Opts.Errors); +#endif return Diags->getNumErrors() == NumErrorsBefore; } diff --git a/clang/lib/Frontend/TextDiagnosticPrinter.cpp b/clang/lib/Frontend/TextDiagnosticPrinter.cpp index 0ff5376098ff..444b71ec5d99 100644 --- a/clang/lib/Frontend/TextDiagnosticPrinter.cpp +++ b/clang/lib/Frontend/TextDiagnosticPrinter.cpp @@ -78,12 +78,18 @@ static void printDiagnosticOptions(raw_ostream &OS, StringRef Opt = DiagnosticIDs::getWarningOptionForDiag(Info.getID()); if (!Opt.empty()) { - OS << (Started ? "," : " [") - << (Level == DiagnosticsEngine::Remark ? "-R" : "-W") << Opt; - StringRef OptValue = Info.getDiags()->getFlagValue(); - if (!OptValue.empty()) - OS << "=" << OptValue; - Started = true; +#if ENABLE_BSC + if (!DiagnosticIDs::isDefaultMappingAsError(Info.getID())) { +#endif + OS << (Started ? "," : " [") + << (Level == DiagnosticsEngine::Remark ? "-R" : "-W") << Opt; + StringRef OptValue = Info.getDiags()->getFlagValue(); + if (!OptValue.empty()) + OS << "=" << OptValue; + Started = true; +#if ENABLE_BSC + } +#endif } } diff --git a/clang/lib/Lex/Pragma.cpp b/clang/lib/Lex/Pragma.cpp index fb4f2dc45758..38238ee3529a 100644 --- a/clang/lib/Lex/Pragma.cpp +++ b/clang/lib/Lex/Pragma.cpp @@ -1285,13 +1285,21 @@ public: } if (WarningName.size() < 3 || WarningName[0] != '-' || - (WarningName[1] != 'W' && WarningName[1] != 'R')) { + (WarningName[1] != 'W' && WarningName[1] != 'R' +#if ENABLE_BSC + && WarningName[1] != 'E' +#endif + )) { PP.Diag(StringLoc, diag::warn_pragma_diagnostic_invalid_option); return; } - diag::Flavor Flavor = WarningName[1] == 'W' ? diag::Flavor::WarningOrError - : diag::Flavor::Remark; + diag::Flavor Flavor = WarningName[1] == 'W' + ? diag::Flavor::WarningOrError +#if ENABLE_BSC + : WarningName[1] == 'E' ? diag::Flavor::Error +#endif + : diag::Flavor::Remark; StringRef Group = StringRef(WarningName).substr(2); bool unknownDiag = false; if (Group == "everything") { diff --git a/clang/test/BSC/Negative/NullabilityCheck/if_stmt.cbs b/clang/test/BSC/Negative/NullabilityCheck/if_stmt.cbs index 7b38218ff2d6..229e9e52f5d5 100644 --- a/clang/test/BSC/Negative/NullabilityCheck/if_stmt.cbs +++ b/clang/test/BSC/Negative/NullabilityCheck/if_stmt.cbs @@ -246,7 +246,7 @@ safe void test17(void) { if (p != nullptr) { p->a = 10; } else { - p->a = 20; // expected-error {{cannot access member througn nullable pointer}} + p->a = 20; // expected-error {{cannot access member through nullable pointer}} } - p->a = 30; // expected-error {{cannot access member througn nullable pointer}} + p->a = 30; // expected-error {{cannot access member through nullable pointer}} } \ No newline at end of file diff --git a/clang/test/BSC/Negative/NullabilityCheck/nested_struct.cbs b/clang/test/BSC/Negative/NullabilityCheck/nested_struct.cbs index b06640b6fe37..5f6b80e48f59 100644 --- a/clang/test/BSC/Negative/NullabilityCheck/nested_struct.cbs +++ b/clang/test/BSC/Negative/NullabilityCheck/nested_struct.cbs @@ -10,8 +10,8 @@ struct N { safe void test1(void) { struct N n1 = { .b = nullptr }; - if (n1.b->a != nullptr) // expected-error {{cannot access member througn nullable pointer}} - *n1.b->a = 10; // expected-error {{cannot access member througn nullable pointer}} + if (n1.b->a != nullptr) // expected-error {{cannot access member through nullable pointer}} + *n1.b->a = 10; // expected-error {{cannot access member through nullable pointer}} struct M m = { .a = nullptr }; struct N n2 = { .b = &mut m }; if (n2.b->a != nullptr) @@ -26,7 +26,7 @@ safe void test2(void) { safe void test3(void) { struct N n1 = { .b = nullptr }; - if (n1.b->a != nullptr && n1.b != nullptr) // expected-error {{cannot access member througn nullable pointer}} + if (n1.b->a != nullptr && n1.b != nullptr) // expected-error {{cannot access member through nullable pointer}} *n1.b->a = 10; } @@ -43,16 +43,16 @@ safe struct N *owned _Nullable safe_malloc(struct N n); safe void test5(void) { struct N n = { .b = nullptr }; struct N *owned _Nullable p = safe_malloc(n); - if (p->b->a != nullptr) // expected-error {{cannot access member througn nullable pointer}} - *p->b->a = 10; // expected-error {{cannot access member througn nullable pointer}} + if (p->b->a != nullptr) // expected-error {{cannot access member through nullable pointer}} + *p->b->a = 10; // expected-error {{cannot access member through nullable pointer}} safe_free(p); } safe void test6(void) { struct N n = { .b = nullptr }; struct N *owned _Nullable p = safe_malloc(n); - if (p->b != nullptr && p->b->a != nullptr) // expected-error {{cannot access member througn nullable pointer}} expected-error {{cannot access member througn nullable pointer}} - *p->b->a = 10; // expected-error {{cannot access member througn nullable pointer}} + if (p->b != nullptr && p->b->a != nullptr) // expected-error {{cannot access member through nullable pointer}} expected-error {{cannot access member through nullable pointer}} + *p->b->a = 10; // expected-error {{cannot access member through nullable pointer}} safe_free(p); } diff --git a/clang/test/BSC/Negative/NullabilityCheck/while_stmt.cbs b/clang/test/BSC/Negative/NullabilityCheck/while_stmt.cbs index 14b930f10504..448b7b8d20b4 100644 --- a/clang/test/BSC/Negative/NullabilityCheck/while_stmt.cbs +++ b/clang/test/BSC/Negative/NullabilityCheck/while_stmt.cbs @@ -79,5 +79,5 @@ safe void test7(void) { p->a = 10; p = get_random_ptr_S(&mut a); } - p->a = 20; // expected-error {{cannot access member througn nullable pointer}} + p->a = 20; // expected-error {{cannot access member through nullable pointer}} } \ No newline at end of file diff --git a/clang/test/BSC/Positive/BlockingError/CompOptions/BorrowCheck/assign_borrowed/assign_borrowed.cbs b/clang/test/BSC/Positive/BlockingError/CompOptions/BorrowCheck/assign_borrowed/assign_borrowed.cbs new file mode 100644 index 000000000000..e139316b9ea0 --- /dev/null +++ b/clang/test/BSC/Positive/BlockingError/CompOptions/BorrowCheck/assign_borrowed/assign_borrowed.cbs @@ -0,0 +1,23 @@ +// RUN: %clang -Eno-assign-borrowed -rewrite-bsc %s -o %t-rw.c +// RUN: FileCheck --input-file=%t-rw.c %s +// expected-no-diagnostics + +void test1() { + int local = 42; + int temp = 2; + int *borrow p1 = &mut local; + int *borrow p2 = &mut temp; + p2 = p1; + *p1 = 2; // cannot assign to `*p1` because it is borrowed + temp = *p2; +} + +// CHECK: void test1(void) { +// CHECK-NEXT: int local = 42; +// CHECK-NEXT: int temp = 2; +// CHECK-NEXT: int * p1 = &local; +// CHECK-NEXT: int * p2 = &temp; +// CHECK-NEXT: p2 = p1; +// CHECK-NEXT: *p1 = 2; +// CHECK-NEXT: temp = *p2; +// CHECK-NEXT: } diff --git a/clang/test/BSC/Positive/BlockingError/CompOptions/BorrowCheck/move_borrowed/move_borrowed.cbs b/clang/test/BSC/Positive/BlockingError/CompOptions/BorrowCheck/move_borrowed/move_borrowed.cbs new file mode 100644 index 000000000000..39202faee35f --- /dev/null +++ b/clang/test/BSC/Positive/BlockingError/CompOptions/BorrowCheck/move_borrowed/move_borrowed.cbs @@ -0,0 +1,21 @@ +// RUN: %clang -Eno-move-borrowed -rewrite-bsc %s -o %t-rw.c +// RUN: FileCheck --input-file=%t-rw.c %s +// expected-no-diagnostics + +void use_mut(int *borrow p); +void free_owned(T* owned p); +void test1(int* owned oriPtr) { + int *borrow p = &mut *oriPtr; + free_owned(oriPtr); // cannot move out of `oriPtr` because it is borrowed + use_mut(p); +} + +// CHECK: static void free_owned_int(int * p); +// CHECK-EMPTY: +// CHECK: void use_mut(int * p); +// CHECK-EMPTY: +// CHECK: void test1(int * oriPtr) { +// CHECK-NEXT: int * p = &*oriPtr; +// CHECK-NEXT: free_owned_int(oriPtr); +// CHECK-NEXT: use_mut(p); +// CHECK-NEXT: } diff --git a/clang/test/BSC/Positive/BlockingError/CompOptions/BorrowCheck/repeated_borrow/repeated_borrow.cbs b/clang/test/BSC/Positive/BlockingError/CompOptions/BorrowCheck/repeated_borrow/repeated_borrow.cbs new file mode 100644 index 000000000000..4d8f7b4bba11 --- /dev/null +++ b/clang/test/BSC/Positive/BlockingError/CompOptions/BorrowCheck/repeated_borrow/repeated_borrow.cbs @@ -0,0 +1,57 @@ +// RUN: %clang -Eno-repeated-borrow -rewrite-bsc %s -o %t-rw.c +// RUN: FileCheck --input-file=%t-rw.c %s +// expected-no-diagnostics + +void use_mut(int *borrow p); +void use_immut(const int *borrow p); +void test1() { + int local = 42; + int *borrow p = &mut local; + int *borrow q = &mut local; // cannot borrow `local` as mutable more than once at a time + use_mut(p); + use_mut(q); +} + +void test2() { + int local = 42; + int *borrow p = &mut local; + const int *borrow q = &const local; // cannot borrow `local` as immutable because it is also borrowed as mutable + use_mut(p); + use_immut(q); +} + +void test3(int *borrow p) { + int local = 5; + p = &mut local; + const int *borrow p1 = &const *p; + use_mut(p); // cannot borrow `*p` as mutable because it is also borrowed as immutable + use_immut(p1); +} + +// CHECK: void use_mut(int * p); +// CHECK-EMPTY: +// CHECK: void use_immut(const int * p); +// CHECK-EMPTY: +// CHECK: void test1(void) { +// CHECK-NEXT: int local = 42; +// CHECK-NEXT: int * p = &local; +// CHECK-NEXT: int * q = &local; +// CHECK-NEXT: use_mut(p); +// CHECK-NEXT: use_mut(q); +// CHECK-NEXT: } +// CHECK-EMPTY: +// CHECK: void test2(void) { +// CHECK-NEXT: int local = 42; +// CHECK-NEXT: int * p = &local; +// CHECK-NEXT: const int * q = &local; +// CHECK-NEXT: use_mut(p); +// CHECK-NEXT: use_immut(q); +// CHECK-NEXT: } +// CHECK-EMPTY: +// CHECK: void test3(int * p) { +// CHECK-NEXT: int local = 5; +// CHECK-NEXT: p = &local; +// CHECK-NEXT: const int * p1 = &*p; +// CHECK-NEXT: use_mut(p); +// CHECK-NEXT: use_immut(p1); +// CHECK-NEXT: } diff --git a/clang/test/BSC/Positive/BlockingError/CompOptions/BorrowCheck/short_life_borrow/short_life_borrow.cbs b/clang/test/BSC/Positive/BlockingError/CompOptions/BorrowCheck/short_life_borrow/short_life_borrow.cbs new file mode 100644 index 000000000000..5e770cff26f0 --- /dev/null +++ b/clang/test/BSC/Positive/BlockingError/CompOptions/BorrowCheck/short_life_borrow/short_life_borrow.cbs @@ -0,0 +1,22 @@ +// RUN: %clang -Eno-short-life-borrow -rewrite-bsc %s -o %t-rw.c +// RUN: FileCheck --input-file=%t-rw.c %s +// expected-no-diagnostics + +void use_mut(int *borrow p); +void test1(int *borrow p) { + { + int local1 = 42; + p = &mut local1; // `local1` does not live long enough + } + use_mut(p); +} + +// CHECK: void use_mut(int * p); +// CHECK-EMPTY: +// CHECK: void test1(int * p) { +// CHECK-NEXT: { +// CHECK-NEXT: int local1 = 42; +// CHECK-NEXT: p = &local1; +// CHECK-NEXT: } +// CHECK-NEXT: use_mut(p); +// CHECK-NEXT: } diff --git a/clang/test/BSC/Positive/BlockingError/CompOptions/BorrowCheck/use_mutably_borrowed/use_mutably_borrowed.cbs b/clang/test/BSC/Positive/BlockingError/CompOptions/BorrowCheck/use_mutably_borrowed/use_mutably_borrowed.cbs new file mode 100644 index 000000000000..7041fc73cf96 --- /dev/null +++ b/clang/test/BSC/Positive/BlockingError/CompOptions/BorrowCheck/use_mutably_borrowed/use_mutably_borrowed.cbs @@ -0,0 +1,19 @@ +// RUN: %clang -Eno-use-mutably-borrowed -rewrite-bsc %s -o %t-rw.c +// RUN: FileCheck --input-file=%t-rw.c %s +// expected-no-diagnostics + +void test1() { + int local = 42; + int *borrow p = &mut local; + int *borrow q = p; + p = q; + int temp = *q; // cannot use `*q` because it was mutably borrowed +} + +// CHECK: void test1(void) { +// CHECK-NEXT: int local = 42; +// CHECK-NEXT: int * p = &local; +// CHECK-NEXT: int * q = p; +// CHECK-NEXT: p = q; +// CHECK-NEXT: int temp = *q; +// CHECK-NEXT: } diff --git a/clang/test/BSC/Positive/BlockingError/CompOptions/Mixed/bsc_borrow/bsc_borrow.cbs b/clang/test/BSC/Positive/BlockingError/CompOptions/Mixed/bsc_borrow/bsc_borrow.cbs new file mode 100644 index 000000000000..e37bc061f569 --- /dev/null +++ b/clang/test/BSC/Positive/BlockingError/CompOptions/Mixed/bsc_borrow/bsc_borrow.cbs @@ -0,0 +1,41 @@ +// RUN: %clang -Eno-bsc-borrow -rewrite-bsc %s -o %t-rw.c +// RUN: FileCheck --input-file=%t-rw.c %s +// expected-no-diagnostics + +void test1() { + int local = 42; + int temp = 2; + int *borrow p1 = &mut local; + int *borrow p2 = &mut temp; + p2 = p1; + *p1 = 2; // cannot assign to `*p1` because it is borrowed + temp = *p2; +} + +void use_mut(int *borrow p); +void free_owned(T* owned p); +void test2(int* owned oriPtr) { + int *borrow p = &mut *oriPtr; + free_owned(oriPtr); // cannot move out of `oriPtr` because it is borrowed + use_mut(p); +} + +// CHECK: static void free_owned_int(int * p); +// CHECK-EMPTY: +// CHECK: void test1(void) { +// CHECK-NEXT: int local = 42; +// CHECK-NEXT: int temp = 2; +// CHECK-NEXT: int * p1 = &local; +// CHECK-NEXT: int * p2 = &temp; +// CHECK-NEXT: p2 = p1; +// CHECK-NEXT: *p1 = 2; +// CHECK-NEXT: temp = *p2; +// CHECK-NEXT: } +// CHECK-EMPTY: +// CHECK: void use_mut(int * p); +// CHECK-EMPTY: +// CHECK: void test2(int * oriPtr) { +// CHECK-NEXT: int * p = &*oriPtr; +// CHECK-NEXT: free_owned_int(oriPtr); +// CHECK-NEXT: use_mut(p); +// CHECK-NEXT: } diff --git a/clang/test/BSC/Positive/BlockingError/CompOptions/Mixed/bsc_nullability/bsc_nullability.cbs b/clang/test/BSC/Positive/BlockingError/CompOptions/Mixed/bsc_nullability/bsc_nullability.cbs new file mode 100644 index 000000000000..b826c0c484a1 --- /dev/null +++ b/clang/test/BSC/Positive/BlockingError/CompOptions/Mixed/bsc_nullability/bsc_nullability.cbs @@ -0,0 +1,60 @@ +// RUN: %clang -Eno-bsc-nullability -rewrite-bsc %s -o %t-rw.c +// RUN: FileCheck --input-file=%t-rw.c %s +// expected-no-diagnostics + +safe int *borrow _Nullable foo1(int* borrow a); +safe void test1(void) { + int a = 10; + int *borrow p1 = foo1(&mut a); + int *borrow _Nullable p2 = foo1(&mut a); +} + +void test2(void) { + int *borrow p = nullptr; + unsafe { + int *borrow p2 = nullptr; + } +} + +struct S { + int a; +}; +safe void test3(void) { + struct S *borrow _Nullable p = nullptr; + if (p != nullptr) { + p->a = 10; + } else { + p->a = 20; + } + p->a = 30; +} + +// CHECK: struct S; +// CHECK-NEXT: struct S { +// CHECK-NEXT: int a; +// CHECK-NEXT: }; +// CHECK-EMPTY: +// CHECK: int * foo1(int * a); +// CHECK-EMPTY: +// CHECK: void test1(void) { +// CHECK-NEXT: int a = 10; +// CHECK-NEXT: int * p1 = foo1(&a); +// CHECK-NEXT: int * p2 = foo1(&a); +// CHECK-NEXT: } +// CHECK-EMPTY: +// CHECK: void test2(void) { +// CHECK-NEXT: int * p = 0; +// CHECK-NEXT: { +// CHECK-NEXT: int * p2 = 0; +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-EMPTY: +// CHECK: void test3(void) { +// CHECK-NEXT: struct S * p = 0; +// CHECK-NEXT: if (p != 0) { +// CHECK-NEXT: p->a = 10; +// CHECK-NEXT: } else { +// CHECK-NEXT: p->a = 20; +// CHECK-NEXT: } +// CHECK-NEXT: p->a = 30; +// CHECK-NEXT: } \ No newline at end of file diff --git a/clang/test/BSC/Positive/BlockingError/CompOptions/Mixed/bsc_ownership/bsc_ownership.cbs b/clang/test/BSC/Positive/BlockingError/CompOptions/Mixed/bsc_ownership/bsc_ownership.cbs new file mode 100644 index 000000000000..5ab69f47ea7a --- /dev/null +++ b/clang/test/BSC/Positive/BlockingError/CompOptions/Mixed/bsc_ownership/bsc_ownership.cbs @@ -0,0 +1,59 @@ +// RUN: %clang -Eno-bsc-ownership -rewrite-bsc %s -o %t-rw.c +// RUN: FileCheck --input-file=%t-rw.c %s +// expected-no-diagnostics + +void free_owned_new(void * owned p); + +typedef struct A { + int * owned a; +} A; + +void test1(A * owned p) { + free_owned_new((void * owned)p); // invalid cast to `void * owned` of not all moved value: `p`, p.a is owned +} + +int * owned test2() { + int * owned a; + return a; // use of uninitialized value: `a` +} + +safe T *owned safe_malloc(T t); +safe void test3(void) { + int *owned _Nullable p = nullptr; + int cond = 1; + if (cond) { + p = safe_malloc(10); + *p = 10; + } + p = nullptr; //assign to owned value: `p` +} + +// CHECK: struct A; +// CHECK-NEXT: typedef struct A A; +// CHECK-EMPTY: +// CHECK: struct A { +// CHECK-NEXT: int * a; +// CHECK-NEXT: }; +// CHECK-EMPTY: +// CHECK: static int * safe_malloc_int(int t); +// CHECK-EMPTY: +// CHECK: void free_owned_new(void * p); +// CHECK-EMPTY: +// CHECK: void test1(A * p) { +// CHECK-NEXT: free_owned_new((void *)p); +// CHECK-NEXT: } +// CHECK-EMPTY: +// CHECK: int * test2(void) { +// CHECK-NEXT: int * a; +// CHECK-NEXT: return a; +// CHECK-NEXT: } +// CHECK-EMPTY: +// CHECK: void test3(void) { +// CHECK-NEXT: int * p = 0; +// CHECK-NEXT: int cond = 1; +// CHECK-NEXT: if (cond) { +// CHECK-NEXT: p = safe_malloc_int(10); +// CHECK-NEXT: *p = 10; +// CHECK-NEXT: } +// CHECK-NEXT: p = 0; +// CHECK-NEXT: } diff --git a/clang/test/BSC/Positive/BlockingError/CompOptions/Mixed/bsc_safety_check/bsc_safety_check.cbs b/clang/test/BSC/Positive/BlockingError/CompOptions/Mixed/bsc_safety_check/bsc_safety_check.cbs new file mode 100644 index 000000000000..695729652344 --- /dev/null +++ b/clang/test/BSC/Positive/BlockingError/CompOptions/Mixed/bsc_safety_check/bsc_safety_check.cbs @@ -0,0 +1,27 @@ +// RUN: %clang -Eno-bsc-safety-check -rewrite-bsc %s -o %t-rw.c +// RUN: FileCheck --input-file=%t-rw.c %s +// expected-no-diagnostics + +safe int *owned _Nonnull safe_malloc(int a); +safe void test1(void) { + int *owned _Nullable p1 = nullptr; + int *owned _Nonnull p2 = p1; // nonnull pointer cannot be assigned by nullable pointer + int *owned _Nullable p3 = safe_malloc(5); + int *owned _Nonnull p4 = p3; + int *owned _Nonnull p5 = safe_malloc(5); // nonnull pointer cannot be assigned by nullable pointer + p5 = p1; // use of moved value: `p1` + p5 = p3; // memory leak of value: `p5` +} + +// CHECK: int * safe_malloc(int a); +// CHECK-EMPTY: +// CHECK: void test1(void) { +// CHECK-NEXT: int * p1 = 0; +// CHECK-NEXT: int * p2 = p1; +// CHECK-NEXT: int * p3 = safe_malloc(5); +// CHECK-NEXT: int * p4 = p3; +// CHECK-NEXT: int * p5 = safe_malloc(5); +// CHECK-NEXT: p5 = p1; +// CHECK-NEXT: p5 = p3; +// CHECK-NEXT: } +// CHECK-EMPTY: \ No newline at end of file diff --git a/clang/test/BSC/Positive/BlockingError/CompOptions/Nullability/assign_nonnull/assign_nonnull.cbs b/clang/test/BSC/Positive/BlockingError/CompOptions/Nullability/assign_nonnull/assign_nonnull.cbs new file mode 100644 index 000000000000..bfd88822dfc5 --- /dev/null +++ b/clang/test/BSC/Positive/BlockingError/CompOptions/Nullability/assign_nonnull/assign_nonnull.cbs @@ -0,0 +1,33 @@ +// RUN: %clang -Eno-assign-nonnull -nullability-check=all -rewrite-bsc %s -o %t-rw.c +// RUN: FileCheck --input-file=%t-rw.c %s +// expected-no-diagnostics + +safe int *borrow _Nullable foo1(int* borrow a); +safe void test1(void) { + int a = 10; + int *borrow p1 = foo1(&mut a); // nonnull pointer cannot be assigned by nullable pointer + int *borrow _Nullable p2 = foo1(&mut a); +} + +void test2(void) { + int *borrow p = nullptr; // nonnull pointer cannot be assigned by nullable pointer + unsafe { + int *borrow p2 = nullptr; // nonnull pointer cannot be assigned by nullable pointer + } +} + +// CHECK: int * foo1(int * a); +// CHECK-EMPTY: +// CHECK: void test1(void) { +// CHECK-NEXT: int a = 10; +// CHECK-NEXT: int * p1 = foo1(&a); +// CHECK-NEXT: int * p2 = foo1(&a); +// CHECK-NEXT: } +// CHECK-EMPTY: +// CHECK: void test2(void) { +// CHECK-NEXT: int * p = 0; +// CHECK-NEXT: { +// CHECK-NEXT: int * p2 = 0; +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-EMPTY: \ No newline at end of file diff --git a/clang/test/BSC/Positive/BlockingError/CompOptions/Nullability/assign_nullable/assign_nullable.cbs b/clang/test/BSC/Positive/BlockingError/CompOptions/Nullability/assign_nullable/assign_nullable.cbs new file mode 100644 index 000000000000..887db09ec779 --- /dev/null +++ b/clang/test/BSC/Positive/BlockingError/CompOptions/Nullability/assign_nullable/assign_nullable.cbs @@ -0,0 +1,32 @@ +// RUN: %clang -Eno-assign-nullable -rewrite-bsc %s -o %t-rw.c +// RUN: FileCheck --input-file=%t-rw.c %s +// expected-no-diagnostics + +struct S { + int a; +}; +safe void test1(void) { + struct S *borrow _Nullable p = nullptr; + if (p != nullptr) { + p->a = 10; + } else { + p->a = 20; // cannot access member through nullable pointer + } + p->a = 30; // cannot access member through nullable pointer +} + +// CHECK: struct S; +// CHECK-NEXT: struct S { +// CHECK-NEXT: int a; +// CHECK-NEXT: }; +// CHECK-EMPTY: +// CHECK: void test1(void) { +// CHECK-NEXT: struct S * p = 0; +// CHECK-NEXT: if (p != 0) { +// CHECK-NEXT: p->a = 10; +// CHECK-NEXT: } else { +// CHECK-NEXT: p->a = 20; +// CHECK-NEXT: } +// CHECK-NEXT: p->a = 30; +// CHECK-NEXT: } +// CHECK-EMPTY: \ No newline at end of file diff --git a/clang/test/BSC/Positive/BlockingError/CompOptions/Nullability/cast_nullable/cast_nullable.cbs b/clang/test/BSC/Positive/BlockingError/CompOptions/Nullability/cast_nullable/cast_nullable.cbs new file mode 100644 index 000000000000..e8018e27822f --- /dev/null +++ b/clang/test/BSC/Positive/BlockingError/CompOptions/Nullability/cast_nullable/cast_nullable.cbs @@ -0,0 +1,31 @@ +// RUN: %clang -Eno-cast-nullable -rewrite-bsc %s -o %t-rw.c +// RUN: FileCheck --input-file=%t-rw.c %s +// expected-no-diagnostics + +safe T *owned safe_malloc(T t); +safe void safe_free(void *owned p); + +safe void test1(void) { + int *owned _Nullable p = nullptr; + int cond = 1; + if (cond) { + p = safe_malloc(10); + *p = 10; + } + safe_free((void *owned)p); // cannot cast nullable pointer to nonnull type +} + +// CHECK: static int * safe_malloc_int(int t); +// CHECK-EMPTY: +// CHECK: void safe_free(void * p); +// CHECK-EMPTY: +// CHECK: void test1(void) { +// CHECK-NEXT: int * p = 0; +// CHECK-NEXT: int cond = 1; +// CHECK-NEXT: if (cond) { +// CHECK-NEXT: p = safe_malloc_int(10); +// CHECK-NEXT: *p = 10; +// CHECK-NEXT: } +// CHECK-NEXT: safe_free((void *)p); +// CHECK-NEXT: } +// CHECK-EMPTY: \ No newline at end of file diff --git a/clang/test/BSC/Positive/BlockingError/CompOptions/Nullability/deref_nullable/deref_nullable.cbs b/clang/test/BSC/Positive/BlockingError/CompOptions/Nullability/deref_nullable/deref_nullable.cbs new file mode 100644 index 000000000000..e72c5b980f33 --- /dev/null +++ b/clang/test/BSC/Positive/BlockingError/CompOptions/Nullability/deref_nullable/deref_nullable.cbs @@ -0,0 +1,33 @@ +// RUN: %clang -Eno-deref-nullable -rewrite-bsc %s -o %t-rw.c +// RUN: FileCheck --input-file=%t-rw.c %s +// expected-no-diagnostics + +struct S { + int *borrow _Nullable p; +}; + +safe void test1(void) { + struct S s = { .p = nullptr }; + if (s.p != nullptr) { + *s.p = 10; + } else { + *s.p = 20; // nullable pointer cannot be dereferenced + } + *s.p = 30; // nullable pointer cannot be dereferenced +} + +// CHECK: struct S; +// CHECK-NEXT: struct S { +// CHECK-NEXT: int * p; +// CHECK-NEXT: }; +// CHECK-EMPTY: +// CHECK: void test1(void) { +// CHECK-NEXT: struct S s = {.p = 0}; +// CHECK-NEXT: if (s.p != 0) { +// CHECK-NEXT: *s.p = 10; +// CHECK-NEXT: } else { +// CHECK-NEXT: *s.p = 20; +// CHECK-NEXT: } +// CHECK-NEXT: *s.p = 30; +// CHECK-NEXT: } +// CHECK-EMPTY: \ No newline at end of file diff --git a/clang/test/BSC/Positive/BlockingError/CompOptions/Nullability/pass_nullable/pass_nullable.cbs b/clang/test/BSC/Positive/BlockingError/CompOptions/Nullability/pass_nullable/pass_nullable.cbs new file mode 100644 index 000000000000..df0047f5a002 --- /dev/null +++ b/clang/test/BSC/Positive/BlockingError/CompOptions/Nullability/pass_nullable/pass_nullable.cbs @@ -0,0 +1,31 @@ +// RUN: %clang -Eno-pass-nullable -rewrite-bsc %s -o %t-rw.c +// RUN: FileCheck --input-file=%t-rw.c %s +// expected-no-diagnostics + +safe void foo1(int *borrow a); +safe void test1(void) { + int a = 10; + int *borrow p1 = &mut a; + foo1(p1); + int *borrow _Nullable p2 = &mut a; + foo1(p2); + int *borrow _Nullable p3 = nullptr; + foo1(p3); // cannot pass nullable pointer argument + foo1(&mut a); + foo1(nullptr); // cannot pass nullable pointer argument +} + +// CHECK: void foo1(int * a); +// CHECK-EMPTY: +// CHECK: void test1(void) { +// CHECK-NEXT: int a = 10; +// CHECK-NEXT: int * p1 = &a; +// CHECK-NEXT: foo1(p1); +// CHECK-NEXT: int * p2 = &a; +// CHECK-NEXT: foo1(p2); +// CHECK-NEXT: int * p3 = 0; +// CHECK-NEXT: foo1(p3); +// CHECK-NEXT: foo1(&a); +// CHECK-NEXT: foo1(0); +// CHECK-NEXT: } +// CHECK-EMPTY: \ No newline at end of file diff --git a/clang/test/BSC/Positive/BlockingError/CompOptions/Nullability/return_nullable/return_nullable.cbs b/clang/test/BSC/Positive/BlockingError/CompOptions/Nullability/return_nullable/return_nullable.cbs new file mode 100644 index 000000000000..ae342573b36f --- /dev/null +++ b/clang/test/BSC/Positive/BlockingError/CompOptions/Nullability/return_nullable/return_nullable.cbs @@ -0,0 +1,12 @@ +// RUN: %clang -Eno-return-nullable -rewrite-bsc %s -o %t-rw.c +// RUN: FileCheck --input-file=%t-rw.c %s +// expected-no-diagnostics + +safe int *borrow foo1(int *borrow _Nullable a) { + return a; // cannot return nullable pointer type +} + +// CHECK: int * foo1(int * a) { +// CHECK-NEXT: return a; +// CHECK-NEXT: } +// CHECK-EMPTY: diff --git a/clang/test/BSC/Positive/BlockingError/CompOptions/Ownership/assign_owned/assign_owned.cbs b/clang/test/BSC/Positive/BlockingError/CompOptions/Ownership/assign_owned/assign_owned.cbs new file mode 100644 index 000000000000..772480d26c8a --- /dev/null +++ b/clang/test/BSC/Positive/BlockingError/CompOptions/Ownership/assign_owned/assign_owned.cbs @@ -0,0 +1,26 @@ +// RUN: %clang -Eno-assign-owned -rewrite-bsc %s -o %t-rw.c +// RUN: FileCheck --input-file=%t-rw.c %s +// expected-no-diagnostics + +safe T *owned safe_malloc(T t); +safe void test1(void) { + int *owned _Nullable p = nullptr; + int cond = 1; + if (cond) { + p = safe_malloc(10); + *p = 10; + } + p = nullptr; // assign to owned value: `p` +} + +// CHECK: static int * safe_malloc_int(int t); +// CHECK-EMPTY: +// CHECK: void test1(void) { +// CHECK-NEXT: int * p = 0; +// CHECK-NEXT: int cond = 1; +// CHECK-NEXT: if (cond) { +// CHECK-NEXT: p = safe_malloc_int(10); +// CHECK-NEXT: *p = 10; +// CHECK-NEXT: } +// CHECK-NEXT: p = 0; +// CHECK-NEXT: } diff --git a/clang/test/BSC/Positive/BlockingError/CompOptions/Ownership/cast_owned/cast_owned.cbs b/clang/test/BSC/Positive/BlockingError/CompOptions/Ownership/cast_owned/cast_owned.cbs new file mode 100644 index 000000000000..090435aabd64 --- /dev/null +++ b/clang/test/BSC/Positive/BlockingError/CompOptions/Ownership/cast_owned/cast_owned.cbs @@ -0,0 +1,26 @@ +// RUN: %clang -Eno-cast-owned -rewrite-bsc %s -o %t-rw.c +// RUN: FileCheck --input-file=%t-rw.c %s +// expected-no-diagnostics + +void free_owned_new(void * owned p); + +typedef struct A { + int * owned a; +} A; + +void test1(A * owned p) { + free_owned_new((void * owned)p); // invalid cast to `void * owned` of not all moved value: `p`, p.a is owned +} + +// CHECK: struct A; +// CHECK-NEXT: typedef struct A A; +// CHECK-EMPTY: +// CHECK: struct A { +// CHECK-NEXT: int * a; +// CHECK-NEXT: }; +// CHECK-EMPTY: +// CHECK: void free_owned_new(void * p); +// CHECK-EMPTY: +// CHECK: void test1(A * p) { +// CHECK-NEXT: free_owned_new((void *)p); +// CHECK-NEXT: } diff --git a/clang/test/BSC/Positive/BlockingError/CompOptions/Ownership/memory_leak/memory_leak.cbs b/clang/test/BSC/Positive/BlockingError/CompOptions/Ownership/memory_leak/memory_leak.cbs new file mode 100644 index 000000000000..a37aee8c9ec9 --- /dev/null +++ b/clang/test/BSC/Positive/BlockingError/CompOptions/Ownership/memory_leak/memory_leak.cbs @@ -0,0 +1,70 @@ +// RUN: %clang -Eno-check-memory-leak -rewrite-bsc %s -o %t-rw.c +// RUN: FileCheck --input-file=%t-rw.c %s +// expected-no-diagnostics + +typedef int* owned myInt; +void test1() { + int x = 10; + int* p = &x; + myInt p1 = (myInt)p; +} // memory leak of value: `p1` + +owned struct S { +public: + int a; +}; + +trait T { +}; + +impl trait T for S; + +void test2(S* owned sp) { + trait T* owned t = sp; +} // field memory leak of value: `t`, t.data is leak + +// CHECK: struct S; +// CHECK-NEXT: struct __Trait_T_Vtable; +// CHECK-NEXT: struct __Trait_T; +// CHECK-NEXT: struct __Trait_T_Owned; +// CHECK-NEXT: struct __Trait_T_Borrow; +// CHECK-NEXT: typedef int * myInt; +// CHECK-EMPTY: +// CHECK: struct S { +// CHECK-NEXT: int a; +// CHECK-NEXT: }; +// CHECK-EMPTY: +// CHECK: struct __Trait_T_Vtable { +// CHECK-NEXT: }; +// CHECK-EMPTY: +// CHECK: struct __Trait_T { +// CHECK-NEXT: void *data; +// CHECK-NEXT: struct __Trait_T_Vtable *vtable; +// CHECK-NEXT: }; +// CHECK-EMPTY: +// CHECK: struct __Trait_T_Owned { +// CHECK-NEXT: void * data; +// CHECK-NEXT: struct __Trait_T_Vtable *vtable; +// CHECK-NEXT: }; +// CHECK-EMPTY: +// CHECK: struct __Trait_T_Borrow { +// CHECK-NEXT: void * data; +// CHECK-NEXT: struct __Trait_T_Vtable *vtable; +// CHECK-NEXT: }; +// CHECK-EMPTY: +// CHECK: void struct_S_D( struct S this); +// CHECK-EMPTY: +// CHECK: void test1(void) { +// CHECK-NEXT: int x = 10; +// CHECK-NEXT: int *p = &x; +// CHECK-NEXT: myInt p1 = (myInt)p; +// CHECK-NEXT: } +// CHECK-EMPTY: +// CHECK: struct __Trait_T_Vtable __owned_struct_S_trait_T = {}; +// CHECK-EMPTY: +// CHECK: void test2( struct S * sp) { +// CHECK-NEXT: struct __Trait_T_Owned t = {sp, &__owned_struct_S_trait_T}; +// CHECK-NEXT: } +// CHECK-EMPTY: +// CHECK: void struct_S_D( struct S this) { +// CHECK-NEXT: } diff --git a/clang/test/BSC/Positive/BlockingError/CompOptions/Ownership/use_moved_owned/use_moved_owned.cbs b/clang/test/BSC/Positive/BlockingError/CompOptions/Ownership/use_moved_owned/use_moved_owned.cbs new file mode 100644 index 000000000000..213843827ad4 --- /dev/null +++ b/clang/test/BSC/Positive/BlockingError/CompOptions/Ownership/use_moved_owned/use_moved_owned.cbs @@ -0,0 +1,113 @@ +// RUN: %clang -Eno-use-moved-owned -rewrite-bsc %s -o %t-rw.c +// RUN: FileCheck --input-file=%t-rw.c %s +// expected-no-diagnostics + +owned struct A { + public: + int x; + int y ; + ~A(A this){ + } +}; +int f(A a){ return 1;} + +int test1() { + A a = {1, 2}; + A b = a; + f(a); // use of moved value: `a` + return 0; +} + +owned struct S { +public: + int a; + int b; +}; + +owned struct T { +public: + S s; +}; + +struct B { + T t; + int c; +}; + +void test2(struct B sb) { + S s = sb.t.s; + T t = sb.t; // use of partially moved value: `sb.t`, sb.t.s is moved +} + +// CHECK: struct A; +// CHECK-NEXT: struct S; +// CHECK-NEXT: struct T; +// CHECK-NEXT: struct B; +// CHECK-NEXT: struct A { +// CHECK-NEXT: int x; +// CHECK-NEXT: int y; +// CHECK-NEXT: }; +// CHECK-EMPTY: +// CHECK: struct S { +// CHECK-NEXT: int a; +// CHECK-NEXT: int b; +// CHECK-NEXT: }; +// CHECK-EMPTY: +// CHECK: struct T { +// CHECK-NEXT: struct S s; +// CHECK-NEXT: }; +// CHECK-EMPTY: +// CHECK: struct B { +// CHECK-NEXT: struct T t; +// CHECK-NEXT: int c; +// CHECK-NEXT: }; +// CHECK-EMPTY: +// CHECK: static void struct_A_D( struct A this); +// CHECK-EMPTY: +// CHECK: void struct_S_D( struct S this); +// CHECK-EMPTY: +// CHECK: void struct_T_D( struct T this); +// CHECK-EMPTY: +// CHECK: int f( struct A a) { +// CHECK-NEXT: _Bool a_is_moved = 0; +// CHECK-NEXT: if (!a_is_moved) +// CHECK-NEXT: struct_A_D(a); +// CHECK-NEXT: return 1; +// CHECK-NEXT: } +// CHECK-EMPTY: +// CHECK: int test1(void) { +// CHECK-NEXT: struct A a = {1, 2}; +// CHECK-NEXT: _Bool a_is_moved = 0; +// CHECK-NEXT: struct A b = a; +// CHECK-NEXT: _Bool b_is_moved = 0; +// CHECK-NEXT: a_is_moved = 1; +// CHECK-NEXT: f(a); +// CHECK-NEXT: a_is_moved = 1; +// CHECK-NEXT: if (!b_is_moved) +// CHECK-NEXT: struct_A_D(b); +// CHECK-NEXT: if (!a_is_moved) +// CHECK-NEXT: struct_A_D(a); +// CHECK-NEXT: return 0; +// CHECK-NEXT: } +// CHECK-EMPTY: +// CHECK: void test2(struct B sb) { +// CHECK-NEXT: struct S s = sb.t.s; +// CHECK-NEXT: _Bool s_is_moved = 0; +// CHECK-NEXT: struct T t = sb.t; +// CHECK-NEXT: _Bool t_is_moved = 0; +// CHECK-NEXT: if (!t_is_moved) +// CHECK-NEXT: struct_T_D(t); +// CHECK-NEXT: if (!s_is_moved) +// CHECK-NEXT: struct_S_D(s); +// CHECK-NEXT: } +// CHECK-EMPTY: +// CHECK: static void struct_A_D( struct A this) { +// CHECK-NEXT: _Bool this_is_moved = 0; +// CHECK-NEXT: } +// CHECK-EMPTY: +// CHECK: void struct_S_D( struct S this) { +// CHECK-NEXT: } +// CHECK-EMPTY: +// CHECK: void struct_T_D( struct T this) { +// CHECK-NEXT: struct_S_D(this.s); +// CHECK-NEXT: } diff --git a/clang/test/BSC/Positive/BlockingError/CompOptions/Ownership/use_uninit_owned/use_uninit_owned.cbs b/clang/test/BSC/Positive/BlockingError/CompOptions/Ownership/use_uninit_owned/use_uninit_owned.cbs new file mode 100644 index 000000000000..e783c03c2712 --- /dev/null +++ b/clang/test/BSC/Positive/BlockingError/CompOptions/Ownership/use_uninit_owned/use_uninit_owned.cbs @@ -0,0 +1,13 @@ +// RUN: %clang -Eno-use-uninit-owned -rewrite-bsc %s -o %t-rw.c +// RUN: FileCheck --input-file=%t-rw.c %s +// expected-no-diagnostics + +int * owned test() { + int * owned a; + return a; // use of uninitialized value: `a` +} + +// CHECK: int * test(void) { +// CHECK-NEXT: int * a; +// CHECK-NEXT: return a; +// CHECK-NEXT: } diff --git a/clang/test/BSC/Positive/BlockingError/Pragma/BorrowCheck/assign_borrowed/assign_borrowed.cbs b/clang/test/BSC/Positive/BlockingError/Pragma/BorrowCheck/assign_borrowed/assign_borrowed.cbs new file mode 100644 index 000000000000..d1ef1e2e94a7 --- /dev/null +++ b/clang/test/BSC/Positive/BlockingError/Pragma/BorrowCheck/assign_borrowed/assign_borrowed.cbs @@ -0,0 +1,26 @@ +// RUN: %clang -rewrite-bsc %s -o %t-rw.c +// RUN: FileCheck --input-file=%t-rw.c %s +// expected-no-diagnostics + +void test1() { + int local = 42; + int temp = 2; + int *borrow p1 = &mut local; + int *borrow p2 = &mut temp; + p2 = p1; + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Eassign-borrowed" + *p1 = 2; // cannot assign to `*p1` because it is borrowed + #pragma GCC diagnostic pop + temp = *p2; +} + +// CHECK: void test1(void) { +// CHECK-NEXT: int local = 42; +// CHECK-NEXT: int temp = 2; +// CHECK-NEXT: int * p1 = &local; +// CHECK-NEXT: int * p2 = &temp; +// CHECK-NEXT: p2 = p1; +// CHECK-NEXT: *p1 = 2; +// CHECK-NEXT: temp = *p2; +// CHECK-NEXT: } diff --git a/clang/test/BSC/Positive/BlockingError/Pragma/BorrowCheck/move_borrowed/move_borrowed.cbs b/clang/test/BSC/Positive/BlockingError/Pragma/BorrowCheck/move_borrowed/move_borrowed.cbs new file mode 100644 index 000000000000..a3211e6abfd1 --- /dev/null +++ b/clang/test/BSC/Positive/BlockingError/Pragma/BorrowCheck/move_borrowed/move_borrowed.cbs @@ -0,0 +1,24 @@ +// RUN: %clang -rewrite-bsc %s -o %t-rw.c +// RUN: FileCheck --input-file=%t-rw.c %s +// expected-no-diagnostics + +void use_mut(int *borrow p); +void free_owned(T* owned p); +void test1(int* owned oriPtr) { + int *borrow p = &mut *oriPtr; + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Emove-borrowed" + free_owned(oriPtr); // cannot move out of `oriPtr` because it is borrowed + #pragma GCC diagnostic pop + use_mut(p); +} + +// CHECK: static void free_owned_int(int * p); +// CHECK-EMPTY: +// CHECK: void use_mut(int * p); +// CHECK-EMPTY: +// CHECK: void test1(int * oriPtr) { +// CHECK-NEXT: int * p = &*oriPtr; +// CHECK-NEXT: free_owned_int(oriPtr); +// CHECK-NEXT: use_mut(p); +// CHECK-NEXT: } diff --git a/clang/test/BSC/Positive/BlockingError/Pragma/BorrowCheck/repeated_borrow/repeated_borrow.cbs b/clang/test/BSC/Positive/BlockingError/Pragma/BorrowCheck/repeated_borrow/repeated_borrow.cbs new file mode 100644 index 000000000000..887e88d5095e --- /dev/null +++ b/clang/test/BSC/Positive/BlockingError/Pragma/BorrowCheck/repeated_borrow/repeated_borrow.cbs @@ -0,0 +1,66 @@ +// RUN: %clang -rewrite-bsc %s -o %t-rw.c +// RUN: FileCheck --input-file=%t-rw.c %s +// expected-no-diagnostics + +void use_mut(int *borrow p); +void use_immut(const int *borrow p); +void test1() { + int local = 42; + int *borrow p = &mut local; + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Erepeated-borrow" + int *borrow q = &mut local; // cannot borrow `local` as mutable more than once at a time + #pragma GCC diagnostic pop + use_mut(p); + use_mut(q); +} + +void test2() { + int local = 42; + int *borrow p = &mut local; + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Erepeated-borrow" + const int *borrow q = &const local; // cannot borrow `local` as immutable because it is also borrowed as mutable + use_mut(p); + #pragma GCC diagnostic pop + use_immut(q); +} + +void test3(int *borrow p) { + int local = 5; + p = &mut local; + const int *borrow p1 = &const *p; + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Erepeated-borrow" + use_mut(p); // cannot borrow `*p` as mutable because it is also borrowed as immutable + #pragma GCC diagnostic pop + use_immut(p1); +} + +// CHECK: void use_mut(int * p); +// CHECK-EMPTY: +// CHECK: void use_immut(const int * p); +// CHECK-EMPTY: +// CHECK: void test1(void) { +// CHECK-NEXT: int local = 42; +// CHECK-NEXT: int * p = &local; +// CHECK-NEXT: int * q = &local; +// CHECK-NEXT: use_mut(p); +// CHECK-NEXT: use_mut(q); +// CHECK-NEXT: } +// CHECK-EMPTY: +// CHECK: void test2(void) { +// CHECK-NEXT: int local = 42; +// CHECK-NEXT: int * p = &local; +// CHECK-NEXT: const int * q = &local; +// CHECK-NEXT: use_mut(p); +// CHECK-NEXT: use_immut(q); +// CHECK-NEXT: } +// CHECK-EMPTY: +// CHECK: void test3(int * p) { +// CHECK-NEXT: int local = 5; +// CHECK-NEXT: p = &local; +// CHECK-NEXT: const int * p1 = &*p; +// CHECK-NEXT: use_mut(p); +// CHECK-NEXT: use_immut(p1); +// CHECK-NEXT: } diff --git a/clang/test/BSC/Positive/BlockingError/Pragma/BorrowCheck/short_life_borrow/short_life_borrow.cbs b/clang/test/BSC/Positive/BlockingError/Pragma/BorrowCheck/short_life_borrow/short_life_borrow.cbs new file mode 100644 index 000000000000..0d79bafe81f5 --- /dev/null +++ b/clang/test/BSC/Positive/BlockingError/Pragma/BorrowCheck/short_life_borrow/short_life_borrow.cbs @@ -0,0 +1,25 @@ +// RUN: %clang -rewrite-bsc %s -o %t-rw.c +// RUN: FileCheck --input-file=%t-rw.c %s +// expected-no-diagnostics + +void use_mut(int *borrow p); +void test1(int *borrow p) { + { + int local1 = 42; + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Eshort-life-borrow" + p = &mut local1; // `local1` does not live long enough + #pragma GCC diagnostic pop + } + use_mut(p); +} + +// CHECK: void use_mut(int * p); +// CHECK-EMPTY: +// CHECK: void test1(int * p) { +// CHECK-NEXT: { +// CHECK-NEXT: int local1 = 42; +// CHECK-NEXT: p = &local1; +// CHECK-NEXT: } +// CHECK-NEXT: use_mut(p); +// CHECK-NEXT: } diff --git a/clang/test/BSC/Positive/BlockingError/Pragma/BorrowCheck/use_mutably_borrowed/use_mutably_borrowed.cbs b/clang/test/BSC/Positive/BlockingError/Pragma/BorrowCheck/use_mutably_borrowed/use_mutably_borrowed.cbs new file mode 100644 index 000000000000..e23459c3f2b1 --- /dev/null +++ b/clang/test/BSC/Positive/BlockingError/Pragma/BorrowCheck/use_mutably_borrowed/use_mutably_borrowed.cbs @@ -0,0 +1,22 @@ +// RUN: %clang -rewrite-bsc %s -o %t-rw.c +// RUN: FileCheck --input-file=%t-rw.c %s +// expected-no-diagnostics + +void test1() { + int local = 42; + int *borrow p = &mut local; + int *borrow q = p; + p = q; + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Euse-mutably-borrowed" + int temp = *q; // cannot use `*q` because it was mutably borrowed + #pragma GCC diagnostic pop +} + +// CHECK: void test1(void) { +// CHECK-NEXT: int local = 42; +// CHECK-NEXT: int * p = &local; +// CHECK-NEXT: int * q = p; +// CHECK-NEXT: p = q; +// CHECK-NEXT: int temp = *q; +// CHECK-NEXT: } diff --git a/clang/test/BSC/Positive/BlockingError/Pragma/Mixed/bsc_borrow/bsc_borrow.cbs b/clang/test/BSC/Positive/BlockingError/Pragma/Mixed/bsc_borrow/bsc_borrow.cbs new file mode 100644 index 000000000000..b35e3ef0111c --- /dev/null +++ b/clang/test/BSC/Positive/BlockingError/Pragma/Mixed/bsc_borrow/bsc_borrow.cbs @@ -0,0 +1,47 @@ +// RUN: %clang -rewrite-bsc %s -o %t-rw.c +// RUN: FileCheck --input-file=%t-rw.c %s +// expected-no-diagnostics + +void test1() { + int local = 42; + int temp = 2; + int *borrow p1 = &mut local; + int *borrow p2 = &mut temp; + p2 = p1; + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Ebsc-borrow" + *p1 = 2; // cannot assign to `*p1` because it is borrowed + temp = *p2; + #pragma GCC diagnostic pop +} + +void use_mut(int *borrow p); +void free_owned(T* owned p); +void test2(int* owned oriPtr) { + int *borrow p = &mut *oriPtr; + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Ebsc-borrow" + free_owned(oriPtr); // cannot move out of `oriPtr` because it is borrowed + use_mut(p); + #pragma GCC diagnostic pop +} + +// CHECK: static void free_owned_int(int * p); +// CHECK-EMPTY: +// CHECK: void test1(void) { +// CHECK-NEXT: int local = 42; +// CHECK-NEXT: int temp = 2; +// CHECK-NEXT: int * p1 = &local; +// CHECK-NEXT: int * p2 = &temp; +// CHECK-NEXT: p2 = p1; +// CHECK-NEXT: *p1 = 2; +// CHECK-NEXT: temp = *p2; +// CHECK-NEXT: } +// CHECK-EMPTY: +// CHECK: void use_mut(int * p); +// CHECK-EMPTY: +// CHECK: void test2(int * oriPtr) { +// CHECK-NEXT: int * p = &*oriPtr; +// CHECK-NEXT: free_owned_int(oriPtr); +// CHECK-NEXT: use_mut(p); +// CHECK-NEXT: } diff --git a/clang/test/BSC/Positive/BlockingError/Pragma/Mixed/bsc_nullability/bsc_nullability.cbs b/clang/test/BSC/Positive/BlockingError/Pragma/Mixed/bsc_nullability/bsc_nullability.cbs new file mode 100644 index 000000000000..f1a872a2c88d --- /dev/null +++ b/clang/test/BSC/Positive/BlockingError/Pragma/Mixed/bsc_nullability/bsc_nullability.cbs @@ -0,0 +1,66 @@ +// RUN: %clang -rewrite-bsc %s -o %t-rw.c +// RUN: FileCheck --input-file=%t-rw.c %s +// expected-no-diagnostics + +safe int *borrow _Nullable foo1(int* borrow a); +safe void test1(void) { + int a = 10; + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Ebsc-nullability" + int *borrow p1 = foo1(&mut a); // nonnull pointer cannot be assigned by nullable pointer + int *borrow _Nullable p2 = foo1(&mut a); + #pragma GCC diagnostic pop +} + +void test2(void) { + int *borrow p = nullptr; + unsafe { + int *borrow p2 = nullptr; + } +} + +struct S { + int a; +}; +safe void test3(void) { + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Ebsc-nullability" + struct S *borrow _Nullable p = nullptr; + if (p != nullptr) { + p->a = 10; // cannot access member through nullable pointer + } else { + p->a = 20; + } + p->a = 30; // cannot access member through nullable pointer + #pragma GCC diagnostic pop +} + +// CHECK: struct S; +// CHECK-NEXT: struct S { +// CHECK-NEXT: int a; +// CHECK-NEXT: }; +// CHECK-EMPTY: +// CHECK: int * foo1(int * a); +// CHECK-EMPTY: +// CHECK: void test1(void) { +// CHECK-NEXT: int a = 10; +// CHECK-NEXT: int * p1 = foo1(&a); +// CHECK-NEXT: int * p2 = foo1(&a); +// CHECK-NEXT: } +// CHECK-EMPTY: +// CHECK: void test2(void) { +// CHECK-NEXT: int * p = 0; +// CHECK-NEXT: { +// CHECK-NEXT: int * p2 = 0; +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-EMPTY: +// CHECK: void test3(void) { +// CHECK-NEXT: struct S * p = 0; +// CHECK-NEXT: if (p != 0) { +// CHECK-NEXT: p->a = 10; +// CHECK-NEXT: } else { +// CHECK-NEXT: p->a = 20; +// CHECK-NEXT: } +// CHECK-NEXT: p->a = 30; +// CHECK-NEXT: } \ No newline at end of file diff --git a/clang/test/BSC/Positive/BlockingError/Pragma/Mixed/bsc_ownership/bsc_ownership.cbs b/clang/test/BSC/Positive/BlockingError/Pragma/Mixed/bsc_ownership/bsc_ownership.cbs new file mode 100644 index 000000000000..444f38a93571 --- /dev/null +++ b/clang/test/BSC/Positive/BlockingError/Pragma/Mixed/bsc_ownership/bsc_ownership.cbs @@ -0,0 +1,68 @@ +// RUN: %clang -rewrite-bsc %s -o %t-rw.c +// RUN: FileCheck --input-file=%t-rw.c %s +// expected-no-diagnostics + +void free_owned_new(void * owned p); + +typedef struct A { + int * owned a; +} A; + +void test1(A * owned p) { + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Ebsc-ownership" + free_owned_new((void * owned)p); // invalid cast to `void * owned` of not all moved value: `p`, p.a is owned + #pragma GCC diagnostic pop +} + +int * owned test2() { + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Ebsc-ownership" + int * owned a; + return a; // use of uninitialized value: `a` + #pragma GCC diagnostic pop +} + +safe T *owned safe_malloc(T t); +safe void test3(void) { + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Ebsc-ownership" + int *owned _Nullable p = nullptr; + int cond = 1; + if (cond) { + p = safe_malloc(10); + *p = 10; + } + p = nullptr; //assign to owned value: `p` + #pragma GCC diagnostic pop +} + +// CHECK: struct A; +// CHECK-NEXT: typedef struct A A; +// CHECK-EMPTY: +// CHECK: struct A { +// CHECK-NEXT: int * a; +// CHECK-NEXT: }; +// CHECK-EMPTY: +// CHECK: static int * safe_malloc_int(int t); +// CHECK-EMPTY: +// CHECK: void free_owned_new(void * p); +// CHECK-EMPTY: +// CHECK: void test1(A * p) { +// CHECK-NEXT: free_owned_new((void *)p); +// CHECK-NEXT: } +// CHECK-EMPTY: +// CHECK: int * test2(void) { +// CHECK-NEXT: int * a; +// CHECK-NEXT: return a; +// CHECK-NEXT: } +// CHECK-EMPTY: +// CHECK: void test3(void) { +// CHECK-NEXT: int * p = 0; +// CHECK-NEXT: int cond = 1; +// CHECK-NEXT: if (cond) { +// CHECK-NEXT: p = safe_malloc_int(10); +// CHECK-NEXT: *p = 10; +// CHECK-NEXT: } +// CHECK-NEXT: p = 0; +// CHECK-NEXT: } diff --git a/clang/test/BSC/Positive/BlockingError/Pragma/Mixed/bsc_safety_check/bsc_safety_check.cbs b/clang/test/BSC/Positive/BlockingError/Pragma/Mixed/bsc_safety_check/bsc_safety_check.cbs new file mode 100644 index 000000000000..aa447a142010 --- /dev/null +++ b/clang/test/BSC/Positive/BlockingError/Pragma/Mixed/bsc_safety_check/bsc_safety_check.cbs @@ -0,0 +1,30 @@ +// RUN: %clang -Eno-bsc-safety-check -rewrite-bsc %s -o %t-rw.c +// RUN: FileCheck --input-file=%t-rw.c %s +// expected-no-diagnostics + +safe int *owned _Nonnull safe_malloc(int a); +safe void test1(void) { + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Ebsc-safety-check" + int *owned _Nullable p1 = nullptr; + int *owned _Nonnull p2 = p1; // nonnull pointer cannot be assigned by nullable pointer + int *owned _Nullable p3 = safe_malloc(5); + int *owned _Nonnull p4 = p3; + int *owned _Nonnull p5 = safe_malloc(5); // nonnull pointer cannot be assigned by nullable pointer + p5 = p1; // use of moved value: `p1` + p5 = p3; // memory leak of value: `p5` + #pragma GCC diagnostic pop +} + +// CHECK: int * safe_malloc(int a); +// CHECK-EMPTY: +// CHECK: void test1(void) { +// CHECK-NEXT: int * p1 = 0; +// CHECK-NEXT: int * p2 = p1; +// CHECK-NEXT: int * p3 = safe_malloc(5); +// CHECK-NEXT: int * p4 = p3; +// CHECK-NEXT: int * p5 = safe_malloc(5); +// CHECK-NEXT: p5 = p1; +// CHECK-NEXT: p5 = p3; +// CHECK-NEXT: } +// CHECK-EMPTY: \ No newline at end of file diff --git a/clang/test/BSC/Positive/BlockingError/Pragma/Nullability/assign_nonnull/assign_nonnull.cbs b/clang/test/BSC/Positive/BlockingError/Pragma/Nullability/assign_nonnull/assign_nonnull.cbs new file mode 100644 index 000000000000..f640d9fa9f5b --- /dev/null +++ b/clang/test/BSC/Positive/BlockingError/Pragma/Nullability/assign_nonnull/assign_nonnull.cbs @@ -0,0 +1,39 @@ +// RUN: %clang -nullability-check=all -rewrite-bsc %s -o %t-rw.c +// RUN: FileCheck --input-file=%t-rw.c %s +// expected-no-diagnostics + +safe int *borrow _Nullable foo1(int* borrow a); +safe void test1(void) { + int a = 10; + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Eassign-nonnull" + int *borrow p1 = foo1(&mut a); // nonnull pointer cannot be assigned by nullable pointer + int *borrow _Nullable p2 = foo1(&mut a); + #pragma GCC diagnostic pop +} + +void test2(void) { + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Eassign-nonnull" + int *borrow p = nullptr; // nonnull pointer cannot be assigned by nullable pointer + unsafe { + int *borrow p2 = nullptr; // nonnull pointer cannot be assigned by nullable pointer + } + #pragma GCC diagnostic pop +} + +// CHECK: int * foo1(int * a); +// CHECK-EMPTY: +// CHECK: void test1(void) { +// CHECK-NEXT: int a = 10; +// CHECK-NEXT: int * p1 = foo1(&a); +// CHECK-NEXT: int * p2 = foo1(&a); +// CHECK-NEXT: } +// CHECK-EMPTY: +// CHECK: void test2(void) { +// CHECK-NEXT: int * p = 0; +// CHECK-NEXT: { +// CHECK-NEXT: int * p2 = 0; +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-EMPTY: \ No newline at end of file diff --git a/clang/test/BSC/Positive/BlockingError/Pragma/Nullability/assign_nullable/assign_nullable.cbs b/clang/test/BSC/Positive/BlockingError/Pragma/Nullability/assign_nullable/assign_nullable.cbs new file mode 100644 index 000000000000..c379cab98057 --- /dev/null +++ b/clang/test/BSC/Positive/BlockingError/Pragma/Nullability/assign_nullable/assign_nullable.cbs @@ -0,0 +1,35 @@ +// RUN: %clang -rewrite-bsc %s -o %t-rw.c +// RUN: FileCheck --input-file=%t-rw.c %s +// expected-no-diagnostics + +struct S { + int a; +}; +safe void test1(void) { + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Eassign-nullable" + struct S *borrow _Nullable p = nullptr; + if (p != nullptr) { + p->a = 10; + } else { + p->a = 20; // cannot access member through nullable pointer + } + p->a = 30; // cannot access member through nullable pointer + #pragma GCC diagnostic pop +} + +// CHECK: struct S; +// CHECK-NEXT: struct S { +// CHECK-NEXT: int a; +// CHECK-NEXT: }; +// CHECK-EMPTY: +// CHECK: void test1(void) { +// CHECK-NEXT: struct S * p = 0; +// CHECK-NEXT: if (p != 0) { +// CHECK-NEXT: p->a = 10; +// CHECK-NEXT: } else { +// CHECK-NEXT: p->a = 20; +// CHECK-NEXT: } +// CHECK-NEXT: p->a = 30; +// CHECK-NEXT: } +// CHECK-EMPTY: \ No newline at end of file diff --git a/clang/test/BSC/Positive/BlockingError/Pragma/Nullability/cast_nullable/cast_nullable.cbs b/clang/test/BSC/Positive/BlockingError/Pragma/Nullability/cast_nullable/cast_nullable.cbs new file mode 100644 index 000000000000..f027ea98a167 --- /dev/null +++ b/clang/test/BSC/Positive/BlockingError/Pragma/Nullability/cast_nullable/cast_nullable.cbs @@ -0,0 +1,34 @@ +// RUN: %clang -rewrite-bsc %s -o %t-rw.c +// RUN: FileCheck --input-file=%t-rw.c %s +// expected-no-diagnostics + +safe T *owned safe_malloc(T t); +safe void safe_free(void *owned p); + +safe void test1(void) { + int *owned _Nullable p = nullptr; + int cond = 1; + if (cond) { + p = safe_malloc(10); + *p = 10; + } + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Ecast-nullable" + safe_free((void *owned)p); // cannot cast nullable pointer to nonnull type + #pragma GCC diagnostic pop +} + +// CHECK: static int * safe_malloc_int(int t); +// CHECK-EMPTY: +// CHECK: void safe_free(void * p); +// CHECK-EMPTY: +// CHECK: void test1(void) { +// CHECK-NEXT: int * p = 0; +// CHECK-NEXT: int cond = 1; +// CHECK-NEXT: if (cond) { +// CHECK-NEXT: p = safe_malloc_int(10); +// CHECK-NEXT: *p = 10; +// CHECK-NEXT: } +// CHECK-NEXT: safe_free((void *)p); +// CHECK-NEXT: } +// CHECK-EMPTY: \ No newline at end of file diff --git a/clang/test/BSC/Positive/BlockingError/Pragma/Nullability/deref_nullable/deref_nullable.cbs b/clang/test/BSC/Positive/BlockingError/Pragma/Nullability/deref_nullable/deref_nullable.cbs new file mode 100644 index 000000000000..125e86949b30 --- /dev/null +++ b/clang/test/BSC/Positive/BlockingError/Pragma/Nullability/deref_nullable/deref_nullable.cbs @@ -0,0 +1,36 @@ +// RUN: %clang -rewrite-bsc %s -o %t-rw.c +// RUN: FileCheck --input-file=%t-rw.c %s +// expected-no-diagnostics + +struct S { + int *borrow _Nullable p; +}; + +safe void test1(void) { + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Ederef-nullable" + struct S s = { .p = nullptr }; + if (s.p != nullptr) { + *s.p = 10; + } else { + *s.p = 20; // nullable pointer cannot be dereferenced + } + *s.p = 30; // nullable pointer cannot be dereferenced + #pragma GCC diagnostic pop +} + +// CHECK: struct S; +// CHECK-NEXT: struct S { +// CHECK-NEXT: int * p; +// CHECK-NEXT: }; +// CHECK-EMPTY: +// CHECK: void test1(void) { +// CHECK-NEXT: struct S s = {.p = 0}; +// CHECK-NEXT: if (s.p != 0) { +// CHECK-NEXT: *s.p = 10; +// CHECK-NEXT: } else { +// CHECK-NEXT: *s.p = 20; +// CHECK-NEXT: } +// CHECK-NEXT: *s.p = 30; +// CHECK-NEXT: } +// CHECK-EMPTY: \ No newline at end of file diff --git a/clang/test/BSC/Positive/BlockingError/Pragma/Nullability/pass_nullable/pass_nullable.cbs b/clang/test/BSC/Positive/BlockingError/Pragma/Nullability/pass_nullable/pass_nullable.cbs new file mode 100644 index 000000000000..35f82e859a84 --- /dev/null +++ b/clang/test/BSC/Positive/BlockingError/Pragma/Nullability/pass_nullable/pass_nullable.cbs @@ -0,0 +1,34 @@ +// RUN: %clang -rewrite-bsc %s -o %t-rw.c +// RUN: FileCheck --input-file=%t-rw.c %s +// expected-no-diagnostics + +safe void foo1(int *borrow a); +safe void test1(void) { + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Epass-nullable" + int a = 10; + int *borrow p1 = &mut a; + foo1(p1); + int *borrow _Nullable p2 = &mut a; + foo1(p2); + int *borrow _Nullable p3 = nullptr; + foo1(p3); // cannot pass nullable pointer argument + foo1(&mut a); + foo1(nullptr); // cannot pass nullable pointer argument + #pragma GCC diagnostic pop +} + +// CHECK: void foo1(int * a); +// CHECK-EMPTY: +// CHECK: void test1(void) { +// CHECK-NEXT: int a = 10; +// CHECK-NEXT: int * p1 = &a; +// CHECK-NEXT: foo1(p1); +// CHECK-NEXT: int * p2 = &a; +// CHECK-NEXT: foo1(p2); +// CHECK-NEXT: int * p3 = 0; +// CHECK-NEXT: foo1(p3); +// CHECK-NEXT: foo1(&a); +// CHECK-NEXT: foo1(0); +// CHECK-NEXT: } +// CHECK-EMPTY: \ No newline at end of file diff --git a/clang/test/BSC/Positive/BlockingError/Pragma/Nullability/return_nullable/return_nullable.cbs b/clang/test/BSC/Positive/BlockingError/Pragma/Nullability/return_nullable/return_nullable.cbs new file mode 100644 index 000000000000..489c5a9fe96a --- /dev/null +++ b/clang/test/BSC/Positive/BlockingError/Pragma/Nullability/return_nullable/return_nullable.cbs @@ -0,0 +1,14 @@ +// RUN: %clang -rewrite-bsc %s -o %t-rw.c +// RUN: FileCheck --input-file=%t-rw.c %s +// expected-no-diagnostics +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Ereturn-nullable" +safe int *borrow foo1(int *borrow _Nullable a) { + return a; // cannot return nullable pointer type +} +#pragma GCC diagnostic pop + +// CHECK: int * foo1(int * a) { +// CHECK-NEXT: return a; +// CHECK-NEXT: } +// CHECK-EMPTY: diff --git a/clang/test/BSC/Positive/BlockingError/Pragma/Ownership/assign_owned/assign_owned.cbs b/clang/test/BSC/Positive/BlockingError/Pragma/Ownership/assign_owned/assign_owned.cbs new file mode 100644 index 000000000000..586428073c6c --- /dev/null +++ b/clang/test/BSC/Positive/BlockingError/Pragma/Ownership/assign_owned/assign_owned.cbs @@ -0,0 +1,29 @@ +// RUN: %clang -rewrite-bsc %s -o %t-rw.c +// RUN: FileCheck --input-file=%t-rw.c %s +// expected-no-diagnostics + +safe T *owned safe_malloc(T t); +safe void test1(void) { + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Eassign-owned" + int *owned _Nullable p = nullptr; + int cond = 1; + if (cond) { + p = safe_malloc(10); + *p = 10; + } + p = nullptr; //assign to owned value: `p` + #pragma GCC diagnostic pop +} + +// CHECK: static int * safe_malloc_int(int t); +// CHECK-EMPTY: +// CHECK: void test1(void) { +// CHECK-NEXT: int * p = 0; +// CHECK-NEXT: int cond = 1; +// CHECK-NEXT: if (cond) { +// CHECK-NEXT: p = safe_malloc_int(10); +// CHECK-NEXT: *p = 10; +// CHECK-NEXT: } +// CHECK-NEXT: p = 0; +// CHECK-NEXT: } diff --git a/clang/test/BSC/Positive/BlockingError/Pragma/Ownership/cast_owned/cast_owned.cbs b/clang/test/BSC/Positive/BlockingError/Pragma/Ownership/cast_owned/cast_owned.cbs new file mode 100644 index 000000000000..3bada756a1c8 --- /dev/null +++ b/clang/test/BSC/Positive/BlockingError/Pragma/Ownership/cast_owned/cast_owned.cbs @@ -0,0 +1,29 @@ +// RUN: %clang -rewrite-bsc %s -o %t-rw.c +// RUN: FileCheck --input-file=%t-rw.c %s +// expected-no-diagnostics + +void free_owned_new(void * owned p); + +typedef struct A { + int * owned a; +} A; + +void test1(A * owned p) { + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Ecast-owned" + free_owned_new((void * owned)p); // invalid cast to `void * owned` of not all moved value: `p`, p.a is owned + #pragma GCC diagnostic pop +} + +// CHECK: struct A; +// CHECK-NEXT: typedef struct A A; +// CHECK-EMPTY: +// CHECK: struct A { +// CHECK-NEXT: int * a; +// CHECK-NEXT: }; +// CHECK-EMPTY: +// CHECK: void free_owned_new(void * p); +// CHECK-EMPTY: +// CHECK: void test1(A * p) { +// CHECK-NEXT: free_owned_new((void *)p); +// CHECK-NEXT: } diff --git a/clang/test/BSC/Positive/BlockingError/Pragma/Ownership/memory_leak/memory_leak.cbs b/clang/test/BSC/Positive/BlockingError/Pragma/Ownership/memory_leak/memory_leak.cbs new file mode 100644 index 000000000000..f921d475e100 --- /dev/null +++ b/clang/test/BSC/Positive/BlockingError/Pragma/Ownership/memory_leak/memory_leak.cbs @@ -0,0 +1,76 @@ +// RUN: %clang -rewrite-bsc %s -o %t-rw.c +// RUN: FileCheck --input-file=%t-rw.c %s +// expected-no-diagnostics + +typedef int* owned myInt; + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Echeck-memory-leak" +void test1() { + int x = 10; + int* p = &x; + myInt p1 = (myInt)p; +} // memory leak of value: `p1` +#pragma GCC diagnostic pop + +owned struct S { +public: + int a; +}; + +trait T { +}; + +impl trait T for S; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Echeck-memory-leak" +void test2(S* owned sp) { + trait T* owned t = sp; +} // field memory leak of value: `t`, t.data is leak +#pragma GCC diagnostic pop + +// CHECK: struct S; +// CHECK-NEXT: struct __Trait_T_Vtable; +// CHECK-NEXT: struct __Trait_T; +// CHECK-NEXT: struct __Trait_T_Owned; +// CHECK-NEXT: struct __Trait_T_Borrow; +// CHECK-NEXT: typedef int * myInt; +// CHECK-EMPTY: +// CHECK: struct S { +// CHECK-NEXT: int a; +// CHECK-NEXT: }; +// CHECK-EMPTY: +// CHECK: struct __Trait_T_Vtable { +// CHECK-NEXT: }; +// CHECK-EMPTY: +// CHECK: struct __Trait_T { +// CHECK-NEXT: void *data; +// CHECK-NEXT: struct __Trait_T_Vtable *vtable; +// CHECK-NEXT: }; +// CHECK-EMPTY: +// CHECK: struct __Trait_T_Owned { +// CHECK-NEXT: void * data; +// CHECK-NEXT: struct __Trait_T_Vtable *vtable; +// CHECK-NEXT: }; +// CHECK-EMPTY: +// CHECK: struct __Trait_T_Borrow { +// CHECK-NEXT: void * data; +// CHECK-NEXT: struct __Trait_T_Vtable *vtable; +// CHECK-NEXT: }; +// CHECK-EMPTY: +// CHECK: void struct_S_D( struct S this); +// CHECK-EMPTY: +// CHECK: void test1(void) { +// CHECK-NEXT: int x = 10; +// CHECK-NEXT: int *p = &x; +// CHECK-NEXT: myInt p1 = (myInt)p; +// CHECK-NEXT: } +// CHECK-EMPTY: +// CHECK: struct __Trait_T_Vtable __owned_struct_S_trait_T = {}; +// CHECK-EMPTY: +// CHECK: void test2( struct S * sp) { +// CHECK-NEXT: struct __Trait_T_Owned t = {sp, &__owned_struct_S_trait_T}; +// CHECK-NEXT: } +// CHECK-EMPTY: +// CHECK: void struct_S_D( struct S this) { +// CHECK-NEXT: } diff --git a/clang/test/BSC/Positive/BlockingError/Pragma/Ownership/use_moved_owned/use_moved_owned.cbs b/clang/test/BSC/Positive/BlockingError/Pragma/Ownership/use_moved_owned/use_moved_owned.cbs new file mode 100644 index 000000000000..104a307840e4 --- /dev/null +++ b/clang/test/BSC/Positive/BlockingError/Pragma/Ownership/use_moved_owned/use_moved_owned.cbs @@ -0,0 +1,119 @@ +// RUN: %clang -rewrite-bsc %s -o %t-rw.c +// RUN: FileCheck --input-file=%t-rw.c %s +// expected-no-diagnostics + +owned struct A { + public: + int x; + int y ; + ~A(A this){ + } +}; +int f(A a){ return 1;} + +int test1() { + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Euse-moved-owned" + A a = {1, 2}; + A b = a; + f(a); // use of moved value: `a` + return 0; + #pragma GCC diagnostic pop +} + +owned struct S { +public: + int a; + int b; +}; + +owned struct T { +public: + S s; +}; + +struct B { + T t; + int c; +}; + +void test2(struct B sb) { + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Euse-moved-owned" + S s = sb.t.s; + T t = sb.t; // use of partially moved value: `sb.t`, sb.t.s is moved + #pragma GCC diagnostic pop +} + +// CHECK: struct A; +// CHECK-NEXT: struct S; +// CHECK-NEXT: struct T; +// CHECK-NEXT: struct B; +// CHECK-NEXT: struct A { +// CHECK-NEXT: int x; +// CHECK-NEXT: int y; +// CHECK-NEXT: }; +// CHECK-EMPTY: +// CHECK: struct S { +// CHECK-NEXT: int a; +// CHECK-NEXT: int b; +// CHECK-NEXT: }; +// CHECK-EMPTY: +// CHECK: struct T { +// CHECK-NEXT: struct S s; +// CHECK-NEXT: }; +// CHECK-EMPTY: +// CHECK: struct B { +// CHECK-NEXT: struct T t; +// CHECK-NEXT: int c; +// CHECK-NEXT: }; +// CHECK-EMPTY: +// CHECK: static void struct_A_D( struct A this); +// CHECK-EMPTY: +// CHECK: void struct_S_D( struct S this); +// CHECK-EMPTY: +// CHECK: void struct_T_D( struct T this); +// CHECK-EMPTY: +// CHECK: int f( struct A a) { +// CHECK-NEXT: _Bool a_is_moved = 0; +// CHECK-NEXT: if (!a_is_moved) +// CHECK-NEXT: struct_A_D(a); +// CHECK-NEXT: return 1; +// CHECK-NEXT: } +// CHECK-EMPTY: +// CHECK: int test1(void) { +// CHECK-NEXT: struct A a = {1, 2}; +// CHECK-NEXT: _Bool a_is_moved = 0; +// CHECK-NEXT: struct A b = a; +// CHECK-NEXT: _Bool b_is_moved = 0; +// CHECK-NEXT: a_is_moved = 1; +// CHECK-NEXT: f(a); +// CHECK-NEXT: a_is_moved = 1; +// CHECK-NEXT: if (!b_is_moved) +// CHECK-NEXT: struct_A_D(b); +// CHECK-NEXT: if (!a_is_moved) +// CHECK-NEXT: struct_A_D(a); +// CHECK-NEXT: return 0; +// CHECK-NEXT: } +// CHECK-EMPTY: +// CHECK: void test2(struct B sb) { +// CHECK-NEXT: struct S s = sb.t.s; +// CHECK-NEXT: _Bool s_is_moved = 0; +// CHECK-NEXT: struct T t = sb.t; +// CHECK-NEXT: _Bool t_is_moved = 0; +// CHECK-NEXT: if (!t_is_moved) +// CHECK-NEXT: struct_T_D(t); +// CHECK-NEXT: if (!s_is_moved) +// CHECK-NEXT: struct_S_D(s); +// CHECK-NEXT: } +// CHECK-EMPTY: +// CHECK: static void struct_A_D( struct A this) { +// CHECK-NEXT: _Bool this_is_moved = 0; +// CHECK-NEXT: } +// CHECK-EMPTY: +// CHECK: void struct_S_D( struct S this) { +// CHECK-NEXT: } +// CHECK-EMPTY: +// CHECK: void struct_T_D( struct T this) { +// CHECK-NEXT: struct_S_D(this.s); +// CHECK-NEXT: } diff --git a/clang/test/BSC/Positive/BlockingError/Pragma/Ownership/use_uninit_owned/use_uninit_owned.cbs b/clang/test/BSC/Positive/BlockingError/Pragma/Ownership/use_uninit_owned/use_uninit_owned.cbs new file mode 100644 index 000000000000..97cd0bdd846d --- /dev/null +++ b/clang/test/BSC/Positive/BlockingError/Pragma/Ownership/use_uninit_owned/use_uninit_owned.cbs @@ -0,0 +1,16 @@ +// RUN: %clang -Eno-use-uninit-owned -rewrite-bsc %s -o %t-rw.c +// RUN: FileCheck --input-file=%t-rw.c %s +// expected-no-diagnostics + +int * owned test() { + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Euse-uninit-owned" + int * owned a; + return a; // use of uninitialized value: `a` + #pragma GCC diagnostic pop +} + +// CHECK: int * test(void) { +// CHECK-NEXT: int * a; +// CHECK-NEXT: return a; +// CHECK-NEXT: } diff --git a/clang/utils/TableGen/ClangDiagnosticsEmitter.cpp b/clang/utils/TableGen/ClangDiagnosticsEmitter.cpp index d6d2bb601c8f..5be8a679170d 100644 --- a/clang/utils/TableGen/ClangDiagnosticsEmitter.cpp +++ b/clang/utils/TableGen/ClangDiagnosticsEmitter.cpp @@ -1199,12 +1199,14 @@ std::string DiagnosticTextBuilder::buildForDefinition(const Record *R) { //===----------------------------------------------------------------------===// // Warning Tables (.inc file) generation. //===----------------------------------------------------------------------===// - +#if ENABLE_BSC +#else static bool isError(const Record &Diag) { const std::string &ClsName = std::string(Diag.getValueAsDef("Class")->getName()); return ClsName == "CLASS_ERROR"; } +#endif static bool isRemark(const Record &Diag) { const std::string &ClsName = @@ -1248,8 +1250,11 @@ void clang::EmitClangDiagsDefs(RecordKeeper &Records, raw_ostream &OS, for (unsigned i = 0, e = Diags.size(); i != e; ++i) { const Record &R = *Diags[i]; - // Check if this is an error that is accidentally in a warning - // group. +// Check if this is an error that is accidentally in a warning +// group. +#if ENABLE_BSC +// BSC allow adding error groups +#else if (isError(R)) { if (DefInit *Group = dyn_cast(R.getValueInit("Group"))) { const Record *GroupRec = Group->getDef(); @@ -1259,6 +1264,7 @@ void clang::EmitClangDiagsDefs(RecordKeeper &Records, raw_ostream &OS, " cannot be in a warning group [" + GroupName + "]"); } } +#endif // Check that all remarks have an associated diagnostic group. if (isRemark(R)) { -- Gitee From 211c818f0b41d9e732ad676660f821908aec29d3 Mon Sep 17 00:00:00 2001 From: liuxinyi Date: Fri, 7 Mar 2025 16:25:54 +0800 Subject: [PATCH 14/29] [FatPtr]add user manual for fat ptr --- clang/docs/BiShengCLanguageUserManual.md | 146 +++++++++++++++++++++++ 1 file changed, 146 insertions(+) diff --git a/clang/docs/BiShengCLanguageUserManual.md b/clang/docs/BiShengCLanguageUserManual.md index 7ade6b5507b5..ab5636c3a025 100644 --- a/clang/docs/BiShengCLanguageUserManual.md +++ b/clang/docs/BiShengCLanguageUserManual.md @@ -4373,6 +4373,152 @@ safe void nullptrTest(void) { ``` 对于上面这个示例,当编译选项`-nullability-check`不存在或者`-nullability-check=safeonly`时,只有在`safe`区的`error2`会被报告;当`-nullability-check=none`时,`error1`和`error2`均不会被报告;当`-nullability-check=all`时,非安全区的`error1`和安全区`error2`均会被报告。 +## 胖指针 +为了增强非安全区的内存安全检查能力,BiShengC 提供了胖指针特性,可以帮助用户在开发阶段检测 use after free 等时间类和 out of bounds 等空间类内存安全问题,一旦出现这些问题,会在程序**运行时**报错。 + +使用`fat`关键字修饰一个指针类型,表明该指针是胖指针,由于胖指针在裸指针的基础上添加了一些 metadata 信息,所以会比裸指针多占用 8 个字节的内存。胖指针支持大部分裸指针所支持的操作,如解引用、成员访问、索引等,因此用户可以很方便地使用胖指针特性对存量 C 代码进行改写。 + +胖指针可以指向一块堆内存,也可以指向栈内存和全局区,以下的代码分别展示了胖指针指向这三类内存: + +```c +#include // BiShengC 语言提供的头文件,使用胖指针特性时必须 include 该头文件 + +// 1. 堆内存场景: +void point_to_heap() { + // checked_malloc用于分配一块堆内存,并返回一个指向这块堆内存的胖指针: + int *fat p = (int *fat)checked_malloc(sizeof(int)); + // 通过 checked_malloc 分配的内存必须通过checked_free 来释放: + checked_free((void *fat)p); +} + +// 2. 栈内存场景: +void point_to_stack() { + // &fat 运算符类似于 & 运算符,用于获取地址并得到一个指向该地址的胖指针: + int local = 10; + int local_arr[3] = {1, 2, 3}; + // 胖指针可以指向普通的局部变量: + int *fat p1 = &fat local; + // 胖指针也可以指向局部数组中的元素: + int *fat p2 = &fat local_arr[0]; + // 和裸指针类似,胖指针支持解引用、索引等操作: + *p1 = 20; + p2[1] = 20; +} + +// 3. 全局区场景: +// 可以用 CHECKED 修饰数组类型的全局变量,只有 CHECKED 修饰的全局变量可以通过 &fat 来获取指向它的胖指针 +CHECKED int global_arr[3] = {1, 2, 3}; +void point_to_global_var() { + int *fat p = &fat global_arr[0]; + *p = 20; +} +``` +在编译使用了胖指针特性的 cbs 文件时,必须使用`-enable-fat-ptr`这一编译选项来使能胖指针特性,否则毕昇 C 编译器会将胖指针当作裸指针来编译,无法起到检测内存安全问题的功能。 + +### 胖指针在运行时检测内存安全问题 +胖指针特性可以检测的**时间类**内存安全问题包括: +1. Double free +2. Use after free +3. Use after return +胖指针特性可以检测的**空间类**内存安全问题包括: +1. Out of bounds + +#### Double free +```C +// double_free.cbs +#include +int main(int argc, char **argv) { + int *fat ptr = (int *fat)checked_malloc(sizeof(int)); + if (argv[1][0] == '1') + checked_free((void *fat)ptr); + checked_free((void *fat)ptr); + return 0; +} +``` +编译 double_free.cbs 并运行: +``` +clang double_free.cbs -enable-fat-ptr +./a.out 1 +``` +这一用例会触发胖指针的运行时报错机制: +``` +Error: Cannot free this pointer because the allocation may have been freed! +``` + +#### Use after free +```C +// use_after_free.cbs +#include +int main(int argc, char **argv) { + int *fat ptr = (int *fat)checked_malloc(sizeof(int)); + *ptr = 10; + if (argv[1][0] == '1') + checked_free((void *fat)ptr); + printf("%d", *ptr); + return 0; +} +``` +编译 use_after_free.cbs 并运行: +``` +clang use_after_free.cbs -enable-fat-ptr +./a.out 1 +``` +这一用例会触发胖指针的运行时报错机制: +``` +Error: Cannot use this pointer because the allocation may have been freed or reseored! +``` + +#### Use after return +```C +// use_after_return.cbs +#include + +int *fat return_fat(char **argv) { + int *fat ptr = nullptr; + int local = 10; + if (argv[1][0] == '1') { + ptr = &fat local; + } else { + ptr = (int *fat)checked_malloc(sizeof(int)); + } + return ptr; +} + +int main(int argc, char **argv) { + int *fat p = return_fat(); + *p = 10; + return 0; +} +``` +编译 use_after_return.cbs 并运行: +``` +clang use_after_return.cbs -enable-fat-ptr +./a.out 1 +``` +这一用例会触发胖指针的运行时报错机制: +``` +Error: Cannot use this pointer because the allocation may have been freed or reseored! +``` + +### 用胖指针特性改写遗留 C 代码 +用户可以很方便地使用胖指针特性对存量 C 代码进行改写,只需要: +1. 将代码中的裸指针类型`T*`修改为胖指针类型`T *fat` +2. 将代码中的取地址运算符`&`修改为`&fat` +3. 将代码中的内存管理函数`malloc`和`free`替换为`checked_malloc`和`checked_free` +4. 对于全局变量,如果想要使用`&fat`获取指向它的胖指针,在变量声明前加上`CHECKED`修饰 + +在实际使用的时候,可能会出现这样的情况,有些库是以二进制形式存在的,无法对它们重新编译,因此它们对应的头文件、函数签名、类型签名中的裸指针类型是不能修改的。**由于胖指针类型和裸指针类型之间不支持隐式转换,那么如果我们想要调用三方库中的函数,需要做类型强转,将胖指针强转为裸指针或将裸指针强转为胖指针**,例如: +```C +#include "some_lib.h" +// 假设 some_lib.h 是第三方 C 库,无法用毕昇 C 的胖指针特性改造,它里面有两个函数,函数签名分别是: +// void external_func1(int *); 它需要裸指针作为参数 +// int * external_func2(); 它的返回类型是裸指针 +void call_external_func(int *fat p) { + external_func1((int *)p); + p = (int *fat)external_func2(); +} +``` + ## 标准库 ### 安全 API -- Gitee From 0050994dc5039a572cb251f730c64d7d874e2ff1 Mon Sep 17 00:00:00 2001 From: Aperzer Date: Thu, 2 Jan 2025 16:42:08 +0800 Subject: [PATCH 15/29] [fat ptr tool] Add fat ptr modify tool. This commit mainly: 1. Add two checkers for fat ptr tool. The collecter will collect advance information, and save it in a '.cfg' file. The transformer will modify the given file with information from collector. 2. Add an entrance program `bsc-build.py`, and a fake clang compiler 'fake_clang.py'. For internal use, check README.txt in 'clang-tools-extra/clang-tidy/tools/bsc/'. All test cases have been passed. --- clang-tools-extra/clang-tidy/ClangTidy.cpp | 17 +- .../clang-tidy/bsc/BSCTidyModule.cpp | 9 + .../clang-tidy/bsc/CMakeLists.txt | 3 + .../clang-tidy/bsc/FatPtrCheck.cpp | 37 +++ .../clang-tidy/bsc/FatPtrCheck.h | 48 ++++ .../clang-tidy/bsc/FatPtrCollecterCheck.cpp | 113 ++++++++ .../clang-tidy/bsc/FatPtrCollecterCheck.h | 36 +++ .../clang-tidy/bsc/FatPtrTransformerCheck.cpp | 271 ++++++++++++++++++ .../clang-tidy/bsc/FatPtrTransformerCheck.h | 74 +++++ .../clang-tidy/tool/bsc/README.txt | 200 +++++++++++++ .../clang-tidy/tool/bsc/bsc-build.py | 221 ++++++++++++++ .../clang-tidy/tool/bsc/fake_clang.py | 93 ++++++ clang-tools-extra/docs/ReleaseNotes.rst | 15 + .../checks/bsc/fat-ptr-collecter.rst | 6 + .../checks/bsc/fat-ptr-transformer.rst | 6 + .../docs/clang-tidy/checks/bsc/fat-ptr.rst | 6 + .../docs/clang-tidy/checks/list.rst | 5 +- .../checkers/bsc/fat-ptr-collecter.cpp | 14 + .../checkers/bsc/fat-ptr-transformer.cpp | 14 + .../test/clang-tidy/checkers/bsc/fat-ptr.cpp | 14 + 20 files changed, 1194 insertions(+), 8 deletions(-) create mode 100644 clang-tools-extra/clang-tidy/bsc/FatPtrCheck.cpp create mode 100644 clang-tools-extra/clang-tidy/bsc/FatPtrCheck.h create mode 100644 clang-tools-extra/clang-tidy/bsc/FatPtrCollecterCheck.cpp create mode 100644 clang-tools-extra/clang-tidy/bsc/FatPtrCollecterCheck.h create mode 100644 clang-tools-extra/clang-tidy/bsc/FatPtrTransformerCheck.cpp create mode 100644 clang-tools-extra/clang-tidy/bsc/FatPtrTransformerCheck.h create mode 100644 clang-tools-extra/clang-tidy/tool/bsc/README.txt create mode 100644 clang-tools-extra/clang-tidy/tool/bsc/bsc-build.py create mode 100644 clang-tools-extra/clang-tidy/tool/bsc/fake_clang.py create mode 100644 clang-tools-extra/docs/clang-tidy/checks/bsc/fat-ptr-collecter.rst create mode 100644 clang-tools-extra/docs/clang-tidy/checks/bsc/fat-ptr-transformer.rst create mode 100644 clang-tools-extra/docs/clang-tidy/checks/bsc/fat-ptr.rst create mode 100644 clang-tools-extra/test/clang-tidy/checkers/bsc/fat-ptr-collecter.cpp create mode 100644 clang-tools-extra/test/clang-tidy/checkers/bsc/fat-ptr-transformer.cpp create mode 100644 clang-tools-extra/test/clang-tidy/checkers/bsc/fat-ptr.cpp diff --git a/clang-tools-extra/clang-tidy/ClangTidy.cpp b/clang-tools-extra/clang-tidy/ClangTidy.cpp index f405ace6d7fa..5d93e17ce31a 100644 --- a/clang-tools-extra/clang-tidy/ClangTidy.cpp +++ b/clang-tools-extra/clang-tidy/ClangTidy.cpp @@ -606,14 +606,17 @@ void handleErrors(llvm::ArrayRef Errors, // Situations if users want to disable clang diagnostic checker. std::string CheckName = Error.DiagnosticName; if (IsNoClangDiagnostic) { - std::string CastChecker = "bsc-explicit-cast"; - std::string AccessChecker = "bsc-access-specific-type"; - if (CheckName.find(CastChecker) != std::string::npos || - CheckName.find(AccessChecker) != std::string::npos) { - llvm::outs() << "--------- flag begin ---------\n"; + // std::string CastChecker = "bsc-explicit-cast"; + // std::string AccessChecker = "bsc-access-specific-type"; + // if (CheckName.find(CastChecker) != std::string::npos || + // CheckName.find(AccessChecker) != std::string::npos) { + // llvm::outs() << "--------- flag begin ---------\n"; + // Reporter.reportDiagnostic(Error); + // llvm::outs() << "--------- flag end ---------\n"; + // } + std::string BSCChecker = "bsc-"; + if (CheckName.find(BSCChecker) != std::string::npos) Reporter.reportDiagnostic(Error); - llvm::outs() << "--------- flag end ---------\n"; - } } else { Reporter.reportDiagnostic(Error); } diff --git a/clang-tools-extra/clang-tidy/bsc/BSCTidyModule.cpp b/clang-tools-extra/clang-tidy/bsc/BSCTidyModule.cpp index 6a40e0b72f7a..eefdba0bf4f8 100644 --- a/clang-tools-extra/clang-tidy/bsc/BSCTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/bsc/BSCTidyModule.cpp @@ -12,6 +12,9 @@ #include "AccessSpecificTypeCheck.h" #include "AddNewFieldCheck.h" #include "ExplicitCastCheck.h" +#include "FatPtrCheck.h" +#include "FatPtrCollecterCheck.h" +#include "FatPtrTransformerCheck.h" namespace clang { namespace tidy { @@ -26,6 +29,12 @@ public: "bsc-add-new-field"); CheckFactories.registerCheck( "bsc-explicit-cast"); + CheckFactories.registerCheck( + "bsc-fat-ptr"); + CheckFactories.registerCheck( + "bsc-fat-ptr-collecter"); + CheckFactories.registerCheck( + "bsc-fat-ptr-transformer"); } }; diff --git a/clang-tools-extra/clang-tidy/bsc/CMakeLists.txt b/clang-tools-extra/clang-tidy/bsc/CMakeLists.txt index 6053d867b678..7c35b3fe2efe 100644 --- a/clang-tools-extra/clang-tidy/bsc/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/bsc/CMakeLists.txt @@ -9,6 +9,9 @@ add_clang_library(clangTidyBscModule AddNewFieldCheck.cpp BSCTidyModule.cpp ExplicitCastCheck.cpp + FatPtrCheck.cpp + FatPtrCollecterCheck.cpp + FatPtrTransformerCheck.cpp LINK_LIBS clangTidy diff --git a/clang-tools-extra/clang-tidy/bsc/FatPtrCheck.cpp b/clang-tools-extra/clang-tidy/bsc/FatPtrCheck.cpp new file mode 100644 index 000000000000..5d27c18884cf --- /dev/null +++ b/clang-tools-extra/clang-tidy/bsc/FatPtrCheck.cpp @@ -0,0 +1,37 @@ +//===--- FatPtrCheck.cpp - clang-tidy -------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "FatPtrCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace bsc { + +void FatPtrCheck::registerMatchers(MatchFinder *Finder) { + // FIXME: Add matchers. + Finder->addMatcher(functionDecl().bind("x"), this); +} + +void FatPtrCheck::check(const MatchFinder::MatchResult &Result) { + // FIXME: Add callback implementation. + const auto *MatchedDecl = Result.Nodes.getNodeAs("x"); + if (!MatchedDecl->getIdentifier() || MatchedDecl->getName().startswith("awesome_")) + return; + diag(MatchedDecl->getLocation(), "function %0 is insufficiently awesome") + << MatchedDecl; + diag(MatchedDecl->getLocation(), "insert 'awesome'", DiagnosticIDs::Note) + << FixItHint::CreateInsertion(MatchedDecl->getLocation(), "awesome_"); +} + +} // namespace bsc +} // namespace tidy +} // namespace clang diff --git a/clang-tools-extra/clang-tidy/bsc/FatPtrCheck.h b/clang-tools-extra/clang-tidy/bsc/FatPtrCheck.h new file mode 100644 index 000000000000..06a38a3a2fbf --- /dev/null +++ b/clang-tools-extra/clang-tidy/bsc/FatPtrCheck.h @@ -0,0 +1,48 @@ +//===--- FatPtrCheck.h - clang-tidy -----------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BSC_FATPTRCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BSC_FATPTRCHECK_H + +#include "../ClangTidyCheck.h" + +namespace clang { +namespace tidy { +namespace bsc { + +/// FIXME: Write a short description. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/bsc/fat-ptr.html +class FatPtrCheck : public ClangTidyCheck { +public: + FatPtrCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context), + TopLevelFunctionList(Options.get("TopLevelFunctions", "TEMP_FAILURE_RETRY")), + TopLevelVarList(Options.get("TopLevelVars", "TEMP_FAILURE_RETRY")) { + StringRef(TopLevelFunctionList).split(TopLevelFunctions, ",", -1, false); + StringRef(TopLevelVarList).split(TopLevelVars, ",", -1, false); + } + void storeOptions(ClangTidyOptions::OptionMap &Opts) { + Options.store(Opts, "TopLevelFunctions", TopLevelFunctionList); + Options.store(Opts, "TopLevelVars", TopLevelVarList); + } + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; +private: + const StringRef TopLevelFunctionList; + SmallVector TopLevelFunctions; + const StringRef TopLevelVarList; + SmallVector TopLevelVars; +}; + +} // namespace bsc +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BSC_FATPTRCHECK_H diff --git a/clang-tools-extra/clang-tidy/bsc/FatPtrCollecterCheck.cpp b/clang-tools-extra/clang-tidy/bsc/FatPtrCollecterCheck.cpp new file mode 100644 index 000000000000..bc9e4ba08123 --- /dev/null +++ b/clang-tools-extra/clang-tidy/bsc/FatPtrCollecterCheck.cpp @@ -0,0 +1,113 @@ +//===--- FatPtrCollecterCheck.cpp - clang-tidy ----------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include +#include +#include "FatPtrCollecterCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/AST/BSC/DeclBSC.h" +#include "clang/Tooling/CommonOptionsParser.h" + +static llvm::cl::opt CFGPath( + "cfg-path", + llvm::cl::desc( + "Indicate the path of cfg file."), + llvm::cl::init("empty"), llvm::cl::Hidden); + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace bsc { + +static inline int SaveInCFG(const NamedDecl *ND) { + if (CFGPath == "Empty") { + std::cerr << "No cfg path received, please check input parameter `--cfg-path`" << std::endl; + return 0; + } + + std::ofstream OutFile; + OutFile.open(CFGPath, std::ios::out | std::ios::app); + if (!OutFile.is_open()) { + std::cerr << "can not open fat_ptr.cfg, fail to save toplevel items." << std::endl; + return 0; + } + + std::string Name = ND->getNameAsString(); + OutFile << "," << Name; + OutFile.close(); + return 1; +} + +// Check whether this Decl comes from Mainfile or header file. +static inline bool CheckFileId(const clang::SourceManager* SM, + FileID MainFileID, const NamedDecl *ND){ + if (MainFileID.isInvalid()) { + MainFileID = SM->getMainFileID(); + if (MainFileID.isInvalid()) + return 0; + } + + FileID LocationID = SM->getFileID(SM->getSpellingLoc(ND->getLocation())); + if (LocationID.isValid()) { + if (MainFileID == LocationID) + return 1; + } + + return 0; +} + +void FatPtrCollecterCheck::registerMatchers(MatchFinder *Finder) { + auto PT = hasUnqualifiedDesugaredType(pointerType()); + auto AT = hasUnqualifiedDesugaredType(arrayType()); + auto RT = hasUnqualifiedDesugaredType(recordType()); + + Finder->addMatcher(functionDecl(hasParent(translationUnitDecl()), hasBody(stmt())).bind("TopLevelFuncDecl"), this); + Finder->addMatcher(varDecl(hasGlobalStorage(), + anyOf(hasType(PT), hasType(AT), hasType(RT))).bind("GlobalVar"), this); +} + +void FatPtrCollecterCheck::check(const MatchFinder::MatchResult &Result) { + const auto *TopLevelFuncDecl = Result.Nodes.getNodeAs("TopLevelFuncDecl"); + const auto *GlobalVar = Result.Nodes.getNodeAs("GlobalVar"); + + if (TopLevelFuncDecl){ + const auto *BSCMD = dyn_cast_or_null(TopLevelFuncDecl); + // Exclude 'Member Function' scenarios. + if (!BSCMD) { + // Exclude 'Extern' scenarios. + if (TopLevelFuncDecl->getStorageClass() != SC_Extern) { + if (!CheckFileId(Result.SourceManager, MainFileID, TopLevelFuncDecl)) + return; + // Save top-level function-name in cfg file. + if (!SaveInCFG(TopLevelFuncDecl)) + diag(TopLevelFuncDecl->getLocation(), + "Failed to save TopLevelFuncDecl: %0", + DiagnosticIDs::Error) << TopLevelFuncDecl; + } + } + } + + if (GlobalVar) { + // Exclude 'Extern' scenarios. + if (!GlobalVar->hasExternalStorage()) { + if (!CheckFileId(Result.SourceManager, MainFileID, GlobalVar)) + return; + // Save top-level var-name in cfg file. + if (!SaveInCFG(GlobalVar)) + diag(GlobalVar->getLocation(), + "Failed to save GlobalVar: %0", + DiagnosticIDs::Error) << GlobalVar; + } + } +} + +} // namespace bsc +} // namespace tidy +} // namespace clang diff --git a/clang-tools-extra/clang-tidy/bsc/FatPtrCollecterCheck.h b/clang-tools-extra/clang-tidy/bsc/FatPtrCollecterCheck.h new file mode 100644 index 000000000000..ccf9b797395b --- /dev/null +++ b/clang-tools-extra/clang-tidy/bsc/FatPtrCollecterCheck.h @@ -0,0 +1,36 @@ +//===--- FatPtrCollecterCheck.h - clang-tidy --------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BSC_FATPTRCOLLECTERCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BSC_FATPTRCOLLECTERCHECK_H + +#include "../ClangTidyCheck.h" + +namespace clang { +namespace tidy { +namespace bsc { + +/// FIXME: Write a short description. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/bsc/fat-ptr-collecter.html +class FatPtrCollecterCheck : public ClangTidyCheck { +public: + FatPtrCollecterCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; +private: + FileID MainFileID; +}; + +} // namespace bsc +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BSC_FATPTRCOLLECTERCHECK_H diff --git a/clang-tools-extra/clang-tidy/bsc/FatPtrTransformerCheck.cpp b/clang-tools-extra/clang-tidy/bsc/FatPtrTransformerCheck.cpp new file mode 100644 index 000000000000..05cea3278f2f --- /dev/null +++ b/clang-tools-extra/clang-tidy/bsc/FatPtrTransformerCheck.cpp @@ -0,0 +1,271 @@ +//===--- FatPtrTransformerCheck.cpp - clang-tidy --------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "FatPtrTransformerCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace bsc { + +void FatPtrTransformerCheck::AddFat(SourceLocation Loc) { + diag(Loc, "Add 'fat' token here",DiagnosticIDs::Warning) + << FixItHint::CreateInsertion(Loc, "fat "); +} + +void FatPtrTransformerCheck::AddChecked(SourceLocation Loc) { + diag(Loc, "Add 'CHECKED' token here",DiagnosticIDs::Warning) + << FixItHint::CreateInsertion(Loc, "CHECKED "); +} + +void FatPtrTransformerCheck::CheckTypedefAndSetFat(const ValueDecl *D, + SourceManager *SM) { + QualType DType = D->getType(); + if (!DType.getTypePtr()->getAs()) { + AddHeader(SM); + AddFat(D->getLocation()); + } +} + +std::string FatPtrTransformerCheck::GetTypeName(const Expr *Arg) { + switch (Arg->getStmtClass()) { + case Stmt::UnaryExprOrTypeTraitExprClass: { + const auto *UEOTT = dyn_cast(Arg); + return UEOTT->getTypeOfArgument().getAsString(); + } + case Stmt::ImplicitCastExprClass: { + const auto *ImpCE = dyn_cast(Arg); + const auto *CSCE = dyn_cast(Arg); + return GetTypeName(ImpCE->getSubExpr()); + } + case Stmt::CallExprClass: { + const auto *CE = dyn_cast(Arg); + return CE->getType().getAsString(); + } + case Stmt::IntegerLiteralClass: { + const auto *IL = dyn_cast(Arg); + return IL->getType().getAsString(); + } + case Stmt::DeclRefExprClass: { + const auto *DRE = dyn_cast(Arg); + return DRE->getType().getAsString(); + } + default: { + llvm_unreachable("NYI"); + return "Unknow Type"; + } + } +} + +void FatPtrTransformerCheck::TraversalRecordDecl(RecordDecl *RD, + bool *HasArrayField) { + if (RD->isInvalidDecl()) + return; + + for(FieldDecl* DeclField : RD->fields()) { + if (const auto *GRTY = dyn_cast_or_null + (DeclField->getType().getTypePtr())) { + if (const auto *RT = dyn_cast_or_null + (GRTY->getNamedType())) { + if (FindInDeclList(HasArrayField, RT)) + return; + TraversalRecordDecl(RT->getDecl(), HasArrayField); + } + } else if (const auto *RT = dyn_cast_or_null + (DeclField->getType().getTypePtr())) { + if (FindInDeclList(HasArrayField, RT)) + return; + TraversalRecordDecl(RT->getDecl(), HasArrayField); + } + + if(*HasArrayField) { + DeclList.push_back(RD); + return; + } + + if(DeclField->getType()->isArrayType()) { + *HasArrayField = true; + DeclList.push_back(RD); + return; + } + } +} + +void FatPtrTransformerCheck::registerMatchers(MatchFinder *Finder) { + auto PT = hasUnqualifiedDesugaredType(pointerType()); + auto AT = hasUnqualifiedDesugaredType(arrayType()); + auto RT = hasUnqualifiedDesugaredType(recordType()); + + for (auto TopLevelName : TopLevels) { + Finder->addMatcher(parmVarDecl(hasParent(functionDecl( + hasName(std::string(TopLevelName)))), + hasType(PT)).bind("PtParmVarDecl"), this); + + Finder->addMatcher(functionDecl(hasParent(translationUnitDecl()), + hasName(std::string(TopLevelName)), + returns(isAnyPointer())).bind("FuncDeclRtPt"), this); + + Finder->addMatcher(callExpr(callee(functionDecl(hasName("malloc"))), + hasParent(castExpr().bind("MallocParent"))) + .bind("MallocCall"), this); + Finder->addMatcher(callExpr(callee(functionDecl(hasName("free")))).bind("FreecCall"), this); + + Finder->addMatcher(varDecl(hasGlobalStorage(), + hasName(std::string(TopLevelName)), + hasType(PT)).bind("GlobalPointerVar"), this); + Finder->addMatcher(varDecl(hasGlobalStorage(), + hasName(std::string(TopLevelName)), + allOf(hasType(AT), unless(hasType(PT)))).bind("GlobalArrayVar"), this); + Finder->addMatcher(varDecl(hasGlobalStorage(), + hasName(std::string(TopLevelName)), + hasType(RT)).bind("GlobalRecordVar"), this); + + Finder->addMatcher(varDecl(hasAncestor(functionDecl(hasName(std::string(TopLevelName)))), + hasLocalStorage(), hasType(PT)).bind("LocalPointerVar"), this); + + Finder->addMatcher(typedefDecl(hasType(PT)).bind("TypedefPT"), this); + Finder->addMatcher(unaryOperator(hasType(PT), hasOperatorName("&")).bind("UnOpAddr"), this); + Finder->addMatcher(cStyleCastExpr(hasType(PT)).bind("CCastExprPT"), this); + } +} + +void FatPtrTransformerCheck::check(const MatchFinder::MatchResult &Result) { + if (MainFileID.isInvalid()) + MainFileID = Result.SourceManager->getMainFileID(); + + const auto *PtParmVarDecl = Result.Nodes.getNodeAs("PtParmVarDecl"); + const auto *FuncDeclRtPt = Result.Nodes.getNodeAs("FuncDeclRtPt"); + + const auto *GlobalPointerVar = Result.Nodes.getNodeAs("GlobalPointerVar"); + const auto *GlobalArrayVar = Result.Nodes.getNodeAs("GlobalArrayVar"); + const auto *GlobalRecordVar = Result.Nodes.getNodeAs("GlobalRecordVar"); + const auto *LocalPointerVar = Result.Nodes.getNodeAs("LocalPointerVar"); + + const auto *TypedefPT = Result.Nodes.getNodeAs("TypedefPT"); + const auto *UnOpAddr = Result.Nodes.getNodeAs("UnOpAddr"); + const auto *CCastExprPT = Result.Nodes.getNodeAs("CCastExprPT"); + + const auto *MallocCall = Result.Nodes.getNodeAs("MallocCall"); + const auto *MallocParent = Result.Nodes.getNodeAs("MallocParent"); + + const auto *FreecCall = Result.Nodes.getNodeAs("FreecCall"); + + if (UnOpAddr) { + if (UnOpAddr->getOpcode() == UnaryOperatorKind::UO_AddrOf) { + AddHeader(Result.SourceManager); + AddFat(UnOpAddr->getEndLoc()); + } + } + + if (CCastExprPT) { + AddHeader(Result.SourceManager); + AddFat(CCastExprPT->getRParenLoc()); + } + + if (PtParmVarDecl) { + diag(PtParmVarDecl->getBeginLoc(), "Find PtParmVarDecl here", DiagnosticIDs::Warning); + } + + if (MallocCall) { + if (MallocParent) { + if (const clang::Type *CT = dyn_cast(MallocParent->getType().getTypePtr())) { + if (const PointerType *PT = CT->getAs()) { + std::string ReplaceStr = "checked_malloc<"; + ReplaceStr.append(PT->getPointeeType().getAsString()); + ReplaceStr.append(">"); + + SourceLocation BeginLoc = MallocCall->getExprLoc(); + SourceLocation EndLoc = MallocCall->getArg(0)->getExprLoc(); + SourceRange SR = SourceRange(BeginLoc, EndLoc.getLocWithOffset(-2)); + diag(BeginLoc, "Find Malloc Call here", DiagnosticIDs::Warning) + << FixItHint::CreateReplacement(SR, ReplaceStr); + AddHeader(Result.SourceManager); + } + } + } + } + + if (FreecCall) { + if (const CastExpr *Arg = dyn_cast_or_null(FreecCall->getArg(0))) { + if (const clang::Type *CT = dyn_cast(Arg->getSubExpr()->getType().getTypePtr())) { + if (const PointerType *PT = CT->getAs()) { + std::string ReplaceStr = "checked_free<"; + ReplaceStr.append(PT->getPointeeType().getAsString()); + ReplaceStr.append(">"); + + SourceLocation BeginLoc = FreecCall->getExprLoc(); + SourceLocation EndLoc = FreecCall->getArg(0)->getExprLoc(); + SourceRange SR = SourceRange(BeginLoc, EndLoc.getLocWithOffset(-2)); + diag(BeginLoc, "Find Free Call here", DiagnosticIDs::Warning) + << FixItHint::CreateReplacement(SR, ReplaceStr); + AddHeader(Result.SourceManager); + } + } + } + } + + if (GlobalArrayVar){ + if (const auto *CAT = dyn_cast_or_null + (GlobalArrayVar->getType().getTypePtr())) { + AddHeader(Result.SourceManager); + QualType VarType = CAT->getElementType(); + if (const TypedefType *TdefT = dyn_cast_or_null(VarType.getTypePtr())) { + if (!TdefT->getAs()) + AddChecked(GlobalArrayVar->getBeginLoc()); + } else if (const auto *PT = dyn_cast_or_null(VarType.getTypePtr())) { + AddFat(GlobalArrayVar->getLocation()); + } else { + AddChecked(GlobalArrayVar->getBeginLoc()); + } + } + } + + if (GlobalRecordVar){ + if (const auto *GRTY = dyn_cast_or_null + (GlobalRecordVar->getType().getTypePtr())) { + if (const auto *RT = dyn_cast_or_null + (GRTY->getNamedType())) { + bool HasArrayField = false; + if (!FindInDeclList(&HasArrayField, RT)) + TraversalRecordDecl(RT->getDecl(), &HasArrayField); + + if (HasArrayField) { + AddChecked(GlobalRecordVar->getBeginLoc()); + AddHeader(Result.SourceManager); + } + } + } + } + + if (GlobalPointerVar) + CheckTypedefAndSetFat(GlobalPointerVar, Result.SourceManager); + + if (LocalPointerVar) + CheckTypedefAndSetFat(LocalPointerVar, Result.SourceManager); + + if (FuncDeclRtPt) + CheckTypedefAndSetFat(FuncDeclRtPt, Result.SourceManager); + + if (TypedefPT) { + SourceLocation TypedefLoc = TypedefPT->getLocation(); + FileID LocationID = Result.SourceManager->getFileID(Result.SourceManager-> + getSpellingLoc(TypedefLoc)); + if (LocationID.isValid()) { + if (MainFileID == LocationID) { + AddHeader(Result.SourceManager); + AddFat(TypedefPT->getLocation()); + } + } + } +} +} // namespace bsc +} // namespace tidy +} // namespace clang diff --git a/clang-tools-extra/clang-tidy/bsc/FatPtrTransformerCheck.h b/clang-tools-extra/clang-tidy/bsc/FatPtrTransformerCheck.h new file mode 100644 index 000000000000..d4e37c9ff3c8 --- /dev/null +++ b/clang-tools-extra/clang-tidy/bsc/FatPtrTransformerCheck.h @@ -0,0 +1,74 @@ +//===--- FatPtrTransformerCheck.h - clang-tidy ------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BSC_FATPTRTRANSFORMERCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BSC_FATPTRTRANSFORMERCHECK_H + +#include "../ClangTidyCheck.h" + +namespace clang { +namespace tidy { +namespace bsc { + +/// FIXME: Write a short description. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/bsc/fat-ptr-transformer.html +class FatPtrTransformerCheck : public ClangTidyCheck { +public: + FatPtrTransformerCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context), + TopLevelList(Options.get("TopLevels", "TEMP_FAILURE_RETRY")) { + StringRef(TopLevelList).split(TopLevels, ",", -1, false); + } + void storeOptions(ClangTidyOptions::OptionMap &Opts) { + Options.store(Opts, "TopLevels", TopLevelList); + } + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; +private: + const StringRef TopLevelList; + SmallVector TopLevels; + FileID MainFileID; + bool NeedFix = false; + bool AlreadyAddHeader = false; + std::vector DeclList; + + void TraversalRecordDecl(RecordDecl *RD, bool *HasArrayField); + void AddFat(SourceLocation Loc); + void AddChecked(SourceLocation Loc); + std::string GetTypeName(const Expr *Arg); + void CheckTypedefAndSetFat(const ValueDecl *D, SourceManager *SM); + + inline bool FindInDeclList(bool *HasArrayField, + const clang::RecordType* RT) { + auto Existed = std::find(DeclList.begin(), DeclList.end(), RT->getDecl()); + if (Existed != DeclList.end()) { + *HasArrayField = true; + return true; + } + return false; + } + + inline void AddHeader(SourceManager *SM) { + if (!AlreadyAddHeader) { + if (!MainFileID.isInvalid()){ + SourceLocation StartOfFileLoc = SM->getLocForStartOfFile(MainFileID); + diag(StartOfFileLoc, "Add Header Here.", DiagnosticIDs::Warning) << + FixItHint::CreateInsertion(StartOfFileLoc, "#include \n"); + AlreadyAddHeader = true; + } + } + } +}; + +} // namespace bsc +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BSC_FATPTRTRANSFORMERCHECK_H diff --git a/clang-tools-extra/clang-tidy/tool/bsc/README.txt b/clang-tools-extra/clang-tidy/tool/bsc/README.txt new file mode 100644 index 000000000000..584b038edc6d --- /dev/null +++ b/clang-tools-extra/clang-tidy/tool/bsc/README.txt @@ -0,0 +1,200 @@ +//===----------------------------------------------------------------------===// +// BSC Tools repository +//===----------------------------------------------------------------------===// + +============================================================================ +Welcome to the repository of extra BSC Tools. This repository holds tools +that are developed as part of the BSC compiler`s one-stop retrofit tool. + +These tools can help uses to retrofit their C-code with some new features +of the BSC compiler easily, such as `fat-ptr` and 'source-to-source', and +the cost of this kind retrofit would be small. +============================================================================ + +For internal use, if you want to try it in the project source code, +not in the released version, you can try the following steps: + +1. Copy 'bsc-build.py' to $your-path/llvm-project/build/bin, and remove +the '.py' suffix. Then grant executable permission to it. + +Cmd: +''' + cp $your-path/llvm-project/clang-tools-extra/clang-tidy/tool/bsc/bsc-build.py $your-path/llvm-project/build/bin/bsc-build + chmod +x $your-path/llvm-project/build/bin/bsc-build +``` + + +2. Copy 'fake_clang.py' to $your-path/llvm-project/build/bin/fake_compiler/, +and remove the '.py' suffix. Then grant executable permission to it. + +Cmd: +``` + mkdir $your-path/llvm-project/build/bin/fake_compiler/ + cp $your-path/llvm-project/clang-tools-extra/clang-tidy/tool/bsc/fake_clang.py $your-path/llvm-project/build/bin/fake_compiler/clang + chmod +x $your-path/llvm-project/build/bin/fake_compiler/clang +``` + + +3. Run 'bsc-build' in $your-path/llvm-project/build/bin: + The options of bsc-build: + --file-path : + The path of the source code which to be retrofitted. If there are + more than one path, use ';' to separate them, without blank space. + You can use both absolute and relative paths. + e.g.: --file-path=./fat_ptr/test_fat.c + + --build-after-modify : + The origin command to build your project. Use semicolons to warp + them insidek, and it is ok to have blank space in it. If there are + more than one command, use '|' to separate them. + e.g.: ---build-after-modify='which clang | clang ./fat_ptr/test_fat.c' + +Cmd: +``` + $your-path/llvm-project/build/bin/bsc-build --file-path=$source_file_path --build-after-modify='origin_build_cmd_1 | origin_build_cmd_2' +``` + + +===================== Demos ===================== + +===----- test_fat.h -----=== +``` +int Aa = 1; + +struct S { + int arr[3]; +}; + +struct A { + struct S s; + int a; +}; + +struct B { + int b; +}; + +void test_1(); +void test_2(int *i1, struct S* s1); + +void char_test(char *c1) {} +``` + + +===----- test_fat.c -----=== +``` +#include "test_fat.h" +#include + +typedef int* intp; // typedef int* fat intp +typedef struct S* spt; // typedef struct S* fat spt + +typedef int in; // ignore +intp ip; // ignore +spt sp; // ignore + +extern int GlobalVar; // ignore +extern int *GlobalPtr; // ignore +extern void greet(); // ignore + +int p = 1; +float f = 1.5; + +const int *p0; // const int *fat p0 +int * const p1 = &p; // int * const fat p1 + +int *p2; // int *fat p2 +int *p3 = &p; // int *fat p3 = &fat p +int *p4[5]; // int *fat p4[] + +intp p5; // ignore +intp p6[5]; // ignore +spt sp7[5]; // ignore + +float *f1; // float *fat f1 +float *f2 = &f; // float *fat f2 = &fat f + +int *p7 = (int*)&f; // int *fat p7 = (int*fat )&fat f + +char *c1; // char *fat c1 +char *c2 = "c"; // char *fat c2 = "c" todo: char *fat c2 = (char *fat )"c" + +in Arr0[3]; // CHECKED +int Arr1[3]; // CHECKED +int Arr2[3] = {}; // CHECKED +int Arr3[3] = { 10, 20, 30 }; // CHECKED + +// 搞一个缓存,避免多次遍历相同类型的结构体 +struct S s1; // CHECKED +struct S s2 = {}; // CHECKED +struct S s3 = {.arr = {10, 10, 10}}; // CHECKED + +struct A a1; // CHECKED +struct A a2 = {}; // CHECKED +struct A a3 = {.a = 1}; // CHECKED + +struct B b1; // ignore +struct B b2 = {}; // ignore +struct B b3 = {.b = 1}; // ignore + +struct S* sp1 = &s1; // struct S* fat sp1 = &fat s1 +struct A* ap1 = &a1; // struct A* fat ap1 = &fat a1 + +void test_2(int *i1, struct S *s1) {} // void test_2(int *fat i1, struct S *fat s1) + +int* test_3(intp i2) {} // int*fat test_3(intp i2) + +intp test_4(int a1) { + return &a1; // return &fat a1; +} + +int test_5() { // ignore + return 32; +} + +int main() { + + typedef float* flop; // typedef float* fat flop + + static struct A a4; // CHECKED + static struct A a5 = {}; // CHECKED + static struct A a6 = {.a = 1}; // CHECKED + + int a = 1; + int *P1 = &a; // int *fat P1 = &fat a + + int B = 10; + int *P2 = &B; // int *fat P2 = &fat B + + int *P3 = (int *)malloc(sizeof(int)); // int *fat P3 = checked_malloc(sizeof(int)) + P3 = (int *)malloc(sizeof(int)); // P3 = checked_malloc(sizeof(int)) + + float *P4 = (float *)malloc(sizeof(float)); // float *fat P4 = checked_malloc(sizeof(float)) + struct S *P5 = (struct S *)malloc(sizeof(struct S)); // struct S *fat P5 = checked_malloc(sizeof(struct S)) + + int *P6 = (int *)malloc(32); // int *fat P6 = checked_malloc(32) + unsigned aa = 32; + int *P7 = (int *)malloc(aa); // int *fat P7 = checked_malloc(aa) + int *P8 = (int *)malloc(test_5()); // int *fat P8 = checked_malloc(test_5()) + + int *P9 = (int *)malloc(sizeof(int)); // int *fat P9 = (int *fat )checked_malloc(sizeof(int)) + + intp P10 = (int *)malloc(aa); // intp P10 = checked_malloc(aa) + + int *P11; // int* fat P11 + P11 = (int *)&aa; // P11 = &fat aa + spt P12; + P12 = &s1; // P12 = &fat a1 + + + free(P3); // checked_free(P3) + free((void *)P4); // checked_free((void *fat)P4) + free(P10); // checked_free(P10) + free(test_3(P11)); // checked_free(test_3(P11)) + free(test_4(aa)); // checked_free(test_4(aa)) + free(P5); // checked_free(P5) + + return 0; +} + +``` \ No newline at end of file diff --git a/clang-tools-extra/clang-tidy/tool/bsc/bsc-build.py b/clang-tools-extra/clang-tidy/tool/bsc/bsc-build.py new file mode 100644 index 000000000000..822a217f5309 --- /dev/null +++ b/clang-tools-extra/clang-tidy/tool/bsc/bsc-build.py @@ -0,0 +1,221 @@ +#!/usr/bin/env python3 + +import sys +import os +import argparse +import subprocess + +first_create_cfg = False +bsc_build_path = os.path.dirname(os.path.abspath(__file__)) +header_list = [] +h_modify_list = [] + +def excute_command(cmd: str, cmd_name: str = None): + proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, shell=True) + stdout_c, stderr_c = proc.communicate() + if cmd_name: + print(" ===== " + cmd_name + " Return Code: ", proc.returncode , " ===== ") + return proc.returncode + +def excute_cmd_list(cmd_list: list, env: dict = None): + for cmd in cmd_list: + cmd_list = [item for item in cmd.split(" ") if item.strip()] + if env: + proc_modify = subprocess.Popen( + cmd_list, + env=env + ) + proc_modify.wait() + else: + proc_modify = subprocess.Popen( + cmd_list + ) + proc_modify.wait() + pass + +def gather_top_levels(source_path: str, cfg_path: str): + global bsc_build_path + global header_list + global h_modify_list + + command_c = ( + "find " + source_path + " -type f -name '*.c' " + + " -exec " + bsc_build_path + "/clang-tidy " + + "--no-clang-diagnostic --cfg-path=" + cfg_path + + " -checks='bsc-fat-ptr-collecter' $file {} \;" + ) + excute_command(command_c, "Proc_c") + + command_h = ( + "find " + source_path + " -type f -name '*.h' " + + " -exec " + bsc_build_path + "/clang-tidy " + + "--no-clang-diagnostic --cfg-path=" + cfg_path + + " -checks='bsc-fat-ptr-collecter' $file {} \;" + ) + excute_command(command_h, "Proc_h") + + + command_h_modify = ( + "find " + source_path + " -type f -name '*.h' " + + " -exec " + bsc_build_path + "/clang-tidy " + + "--fix-errors --config-file==" + cfg_path + + " $file {} \;" + ) + h_modify_list.append(command_h_modify) + find_h = ( + "find " + source_path + " -type f -name '*.h' " + ) + proc = subprocess.Popen(find_h, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, shell=True) + stdout_c, stderr_c = proc.communicate() + header_list += stdout_c.split("\n")[:-1] + + command_cbs = ( + "find " + source_path + " -type f -name '*.cbs' " + + " -exec " + bsc_build_path + "/clang-tidy " + + "--no-clang-diagnostic --cfg-path=" + cfg_path + + " -checks='bsc-fat-ptr-collecter' $file {} \;" + ) + excute_command(command_cbs, "Proc_cbs") + + command_hbs = ( + "find " + source_path + " -type f -name '*.hbs' " + + " -exec " + bsc_build_path + "/clang-tidy " + + "--no-clang-diagnostic --cfg-path=" + cfg_path + + " -checks='bsc-fat-ptr-collecter' $file {} \;" + ) + excute_command(command_hbs, "Proc_hbs") + +def backup_source_code(source_path: str): + command_backup = ("cp -r " + source_path + " " + source_path + "_backup") + excute_command(command_backup, "Proc_backup") + +def backup_compiler_and_replace(fake_compiler: str): + default_clang = "/usr/bin/clang" + if os.path.exists(default_clang): + clang_backup = "sudo cp -a " + default_clang + " " + default_clang + "_backup" + isbackup = excute_command(clang_backup, "Proc_clang_backup") + + if isbackup==0: + clang_replace = "sudo ln -snf " + fake_compiler + "/clang " + default_clang + excute_command(clang_replace, "Proc_clang_replace") + else: + print(" ===== Failed to backup " + default_clang + " ===== ") + + pass + +def recover_compiler(): + clang_backup = "/usr/bin/clang_backup" + if os.path.exists(clang_backup): + clang_clean = "sudo rm /usr/bin/clang" + excute_command(clang_clean, "Proc_clang_clean") + + clang_recover = "sudo mv -f /usr/bin/clang_backup /usr/bin/clang" + excute_command(clang_recover, "Proc_clang_recover") + +def preparecfg(file_path: str): + global first_create_cfg + global bsc_build_path + cfg_file = bsc_build_path + "/fat_ptr.cfg" + if not first_create_cfg: + content = ( 'Checks: -*, bsc-fat-ptr-transformer\n' + 'CheckOptions:\n - key: ' + 'bsc-fat-ptr-transformer.TopLevels\n' + ' value: "0') + with open(cfg_file, 'w') as file: + file.write(content) + first_create_cfg = True + + paths = file_path.split(";") + for path in paths: + abs_source_path = os.path.abspath(path) + backup_source_code(abs_source_path) + gather_top_levels(abs_source_path, cfg_file) + + with open(cfg_file, 'a') as file: + file.write('"') + print(" =========================== Prepare fat_ptr.cfg done =========================== ") + +def main(): + parser = argparse.ArgumentParser(description='Get the path of source code files.') + parser.add_argument('--file-path', type=str, default=None, dest='file_path', + help='Specify the path of your source code, use ";" to split them if more than one.') + parser.add_argument('--fat-ptr', action='store_true', + dest='fat_ptr', help='Active fat ptr modification.') + parser.add_argument('--translate-to-c', action='store_true', + dest='rewrite', help='Rewrite the modified source code to C code.') + parser.add_argument('--build-after-modify', type=str, default=None, dest='build_after_modify', + help='Build the program by the origin building command after modifination.\n If there are' + ' more than one command, use "|" to split them. e.g.: --cmake build xxx -xxx | make xxx') + parser.add_argument('--h', action='store_true', help='show the information of the options') + args = parser.parse_args() + + if not args.file_path: + sys.stderr.write(f"Error: file path is None. Use '-file-path=xxx' to indicate it\n") + return + + preparecfg(args.file_path) + + if args.build_after_modify: + global bsc_build_path + cmds = args.build_after_modify.split("|") + + if args.fat_ptr: + global header_list + header_dict = {} + for header in header_list: + header_path = os.path.dirname(os.path.abspath(header)) + header_dict[header] = header_path + + print(" =========================== Active fat ptr modify. =========================== ") + + fakecompiler_path = bsc_build_path + "/fake_compiler" + env = os.environ.copy() + env["PATH"] = fakecompiler_path + os.pathsep + env["PATH"] + if "CC" in os.environ: + env["CC"] = fakecompiler_path + os.pathsep + env["CC"] + else: + env["CC"] = fakecompiler_path + + if "CXX" in os.environ: + env["CXX"] = fakecompiler_path + os.pathsep + env["CXX"] + else: + env["CXX"] = fakecompiler_path + + # # Modify headers + # # FIXME: We modify all header now, still need to complete the further logic, to modify the headers that only been used. + # # maybe not, it seems when we modify .c, the used .h will be modified too, need check again. + # excute_cmd_list(h_modify_list) + + if args.rewrite: + print(" =========================== Active source to source after fat-ptr modify. =========================== ") + env["REWRITE"] = "Enable" + # # FIXME: transform all file from .c/.h to .cbs/.hbs, then we can rewrite + # for file, path in header_dict.items(): + # cmd = ( + # bsc_build_path + # + "/clang -x bsc " + # + "-rewrtie-bsc " + # + file + " -o " + path + # ) + # excute_command(cmd) + + # # FIXME: complete /usr/bin/cc situation. + # backup_compiler_and_replace(fakecompiler_path) + + excute_cmd_list(cmds, env) + + # # FIXME: complete /usr/bin/cc situation. + # recover_compiler() + pass + + if args.rewrite and not args.fat_ptr: + # # FIXME: complete or delete this situation. + pass + + + # excute_cmd_list(cmds) + + + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/clang-tools-extra/clang-tidy/tool/bsc/fake_clang.py b/clang-tools-extra/clang-tidy/tool/bsc/fake_clang.py new file mode 100644 index 000000000000..4dba453f62b5 --- /dev/null +++ b/clang-tools-extra/clang-tidy/tool/bsc/fake_clang.py @@ -0,0 +1,93 @@ +#!/usr/bin/env python3 + +import sys +import os +import re +import argparse +import subprocess + + +def get_clang_tidy(): + clang_tidy_path = None + current_file = os.path.abspath(__file__) + if (os.path.islink(current_file)): + clang_tidy_path = os.path.dirname(os.readlink(current_file)) + "/../" + else: + clang_tidy_path = os.path.dirname(current_file) + "/../" + + return clang_tidy_path + +def find_matching_strings(pattern, strings): + matches = [s for s in strings if pattern.match(s)] + return matches + +def find_matching_includes(strings): + pattern = re.compile(r'\-I.*?') + matches = [s for s in strings if pattern.match(s)] + return matches + +def transform_c_file(cfile, includes, clang_tidy_path): + if (clang_tidy_path): + command_c = ( + clang_tidy_path + "clang-tidy " + "--fix-errors --config-file=" + + clang_tidy_path + "fat_ptr.cfg " + + cfile + " -header-filter=.* " + ) + if includes: + command_c += "-- " + for include in includes: + command_c = command_c + include + " " + pass + + proc_c = subprocess.Popen( + command_c, text=True, shell=True, + stdout=subprocess.PIPE, stderr=subprocess.PIPE + ) + + stdout_c, stderr_c = proc_c.communicate() + print("Proc_modify Return Code:", proc_c.returncode) + pass + +def rewrite_c_file(cfile, includes, clang_tidy_path): + if (clang_tidy_path): + cmd = ( + clang_tidy_path + "clang -x" + " bsc -rewrite-bsc " + cfile + ) + if includes: + for include in includes: + cmd = cmd + " " + include + pass + + proc_c = subprocess.Popen( + cmd, text=True, shell=True, + stdout=subprocess.PIPE, stderr=subprocess.PIPE + ) + + stdout_c, stderr_c = proc_c.communicate() + print("Proc_rewrite Return Code:", proc_c.returncode, ", " + cfile) + pass + pass + +def main(): + if len(sys.argv) > 1: + pattern_c = re.compile(r'.*?\.c$') + cfile = find_matching_strings(pattern_c, sys.argv) + + pattern_i = re.compile(r'\-I.*?') + includes = find_matching_strings(pattern_i, sys.argv) + + clang_tidy_path = get_clang_tidy() + if (cfile): + transform_c_file(cfile[0], includes, clang_tidy_path) + + env_rewrite = os.environ.get("REWRITE") + if env_rewrite and env_rewrite == "Enable": + rewrite_c_file(cfile[0], includes, clang_tidy_path) + else: + print("No command line arguments provided.") + + +if __name__ == "__main__": + main() diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index 7de001b406b8..67881817f449 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -159,6 +159,21 @@ New checks FIXME: add release notes. +- New :doc:`bsc-fat-ptr + ` check. + + FIXME: add release notes. + +- New :doc:`bsc-fat-ptr-collecter + ` check. + + FIXME: add release notes. + +- New :doc:`bsc-fat-ptr-transformer + ` check. + + FIXME: add release notes. + - New :doc:`bugprone-shared-ptr-array-mismatch ` check. Finds initializations of C++ shared pointers to non-array type that are initialized with an array. diff --git a/clang-tools-extra/docs/clang-tidy/checks/bsc/fat-ptr-collecter.rst b/clang-tools-extra/docs/clang-tidy/checks/bsc/fat-ptr-collecter.rst new file mode 100644 index 000000000000..80cdea7bd617 --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/bsc/fat-ptr-collecter.rst @@ -0,0 +1,6 @@ +.. title:: clang-tidy - bsc-fat-ptr-collecter + +bsc-fat-ptr-collecter +===================== + +FIXME: Describe what patterns does the check detect and why. Give examples. diff --git a/clang-tools-extra/docs/clang-tidy/checks/bsc/fat-ptr-transformer.rst b/clang-tools-extra/docs/clang-tidy/checks/bsc/fat-ptr-transformer.rst new file mode 100644 index 000000000000..37478d0760d3 --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/bsc/fat-ptr-transformer.rst @@ -0,0 +1,6 @@ +.. title:: clang-tidy - bsc-fat-ptr-transformer + +bsc-fat-ptr-transformer +======================= + +FIXME: Describe what patterns does the check detect and why. Give examples. diff --git a/clang-tools-extra/docs/clang-tidy/checks/bsc/fat-ptr.rst b/clang-tools-extra/docs/clang-tidy/checks/bsc/fat-ptr.rst new file mode 100644 index 000000000000..f0f92143cdbf --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/bsc/fat-ptr.rst @@ -0,0 +1,6 @@ +.. title:: clang-tidy - bsc-fat-ptr + +bsc-fat-ptr +=========== + +FIXME: Describe what patterns does the check detect and why. Give examples. diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst index df2aedc61575..e51ef1bcbfd6 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/list.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -77,9 +77,12 @@ Clang-Tidy Checks `android-cloexec-socket `_, "Yes" `android-comparison-in-temp-failure-retry `_, `boost-use-to-string `_, "Yes" - `bsc-access-specific-type `_, "Yes" + `bsc-access-specific-type `_, `bsc-add-new-field `_, "Yes" `bsc-explicit-cast `_, + `bsc-fat-ptr-collecter `_, + `bsc-fat-ptr-transformer `_, "Yes" + `bsc-fat-ptr `_, "Yes" `bugprone-argument-comment `_, "Yes" `bugprone-assert-side-effect `_, `bugprone-assignment-in-if-condition `_, diff --git a/clang-tools-extra/test/clang-tidy/checkers/bsc/fat-ptr-collecter.cpp b/clang-tools-extra/test/clang-tidy/checkers/bsc/fat-ptr-collecter.cpp new file mode 100644 index 000000000000..6c9fb682cacb --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/bsc/fat-ptr-collecter.cpp @@ -0,0 +1,14 @@ +// RUN: %check_clang_tidy %s bsc-fat-ptr-collecter %t + +// FIXME: Add something that triggers the check here. +void f(); +// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: function 'f' is insufficiently awesome [bsc-fat-ptr-collecter] + +// FIXME: Verify the applied fix. +// * Make the CHECK patterns specific enough and try to make verified lines +// unique to avoid incorrect matches. +// * Use {{}} for regular expressions. +// CHECK-FIXES: {{^}}void awesome_f();{{$}} + +// FIXME: Add something that doesn't trigger the check here. +void awesome_f2(); diff --git a/clang-tools-extra/test/clang-tidy/checkers/bsc/fat-ptr-transformer.cpp b/clang-tools-extra/test/clang-tidy/checkers/bsc/fat-ptr-transformer.cpp new file mode 100644 index 000000000000..a1212205950f --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/bsc/fat-ptr-transformer.cpp @@ -0,0 +1,14 @@ +// RUN: %check_clang_tidy %s bsc-fat-ptr-transformer %t + +// FIXME: Add something that triggers the check here. +void f(); +// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: function 'f' is insufficiently awesome [bsc-fat-ptr-transformer] + +// FIXME: Verify the applied fix. +// * Make the CHECK patterns specific enough and try to make verified lines +// unique to avoid incorrect matches. +// * Use {{}} for regular expressions. +// CHECK-FIXES: {{^}}void awesome_f();{{$}} + +// FIXME: Add something that doesn't trigger the check here. +void awesome_f2(); diff --git a/clang-tools-extra/test/clang-tidy/checkers/bsc/fat-ptr.cpp b/clang-tools-extra/test/clang-tidy/checkers/bsc/fat-ptr.cpp new file mode 100644 index 000000000000..9f34153f54d3 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/bsc/fat-ptr.cpp @@ -0,0 +1,14 @@ +// RUN: %check_clang_tidy %s bsc-fat-ptr %t + +// FIXME: Add something that triggers the check here. +void f(); +// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: function 'f' is insufficiently awesome [bsc-fat-ptr] + +// FIXME: Verify the applied fix. +// * Make the CHECK patterns specific enough and try to make verified lines +// unique to avoid incorrect matches. +// * Use {{}} for regular expressions. +// CHECK-FIXES: {{^}}void awesome_f();{{$}} + +// FIXME: Add something that doesn't trigger the check here. +void awesome_f2(); -- Gitee From 70383ec3110b2ef3fb2158ec9c8e4878ab02010b Mon Sep 17 00:00:00 2001 From: Aperzer Date: Thu, 27 Feb 2025 21:24:52 +0800 Subject: [PATCH 16/29] [fat ptr tool] Adjust some logic of checkers and tools 1. Adjust the modification of 'malloc' and 'free'; 2. Fix some bugs, mainly while building or rewriting in bsc-build; 3. Adjust '&' modify in transformer checker; 4. Delete useless code and checker, and annotate the files. --- clang-tools-extra/clang-tidy/ClangTidy.cpp | 8 - .../clang-tidy/bsc/BSCTidyModule.cpp | 3 - .../clang-tidy/bsc/CMakeLists.txt | 1 - .../clang-tidy/bsc/ExplicitCastCheck.h | 5 +- .../clang-tidy/bsc/FatPtrCheck.cpp | 37 --- .../clang-tidy/bsc/FatPtrCheck.h | 48 ---- .../clang-tidy/bsc/FatPtrCollecterCheck.cpp | 58 +++-- .../clang-tidy/bsc/FatPtrCollecterCheck.h | 1 + .../clang-tidy/bsc/FatPtrTransformerCheck.cpp | 232 +++++++++++------- .../clang-tidy/bsc/FatPtrTransformerCheck.h | 20 +- .../clang-tidy/tool/bsc/bsc-build.py | 42 ++-- .../clang-tidy/tool/bsc/fake_clang.py | 62 +++-- clang-tools-extra/docs/ReleaseNotes.rst | 5 - .../docs/clang-tidy/checks/bsc/fat-ptr.rst | 6 - .../docs/clang-tidy/checks/list.rst | 1 - .../test/clang-tidy/checkers/bsc/fat-ptr.cpp | 14 -- 16 files changed, 253 insertions(+), 290 deletions(-) delete mode 100644 clang-tools-extra/clang-tidy/bsc/FatPtrCheck.cpp delete mode 100644 clang-tools-extra/clang-tidy/bsc/FatPtrCheck.h delete mode 100644 clang-tools-extra/docs/clang-tidy/checks/bsc/fat-ptr.rst delete mode 100644 clang-tools-extra/test/clang-tidy/checkers/bsc/fat-ptr.cpp diff --git a/clang-tools-extra/clang-tidy/ClangTidy.cpp b/clang-tools-extra/clang-tidy/ClangTidy.cpp index 5d93e17ce31a..5b6111610c9f 100644 --- a/clang-tools-extra/clang-tidy/ClangTidy.cpp +++ b/clang-tools-extra/clang-tidy/ClangTidy.cpp @@ -606,14 +606,6 @@ void handleErrors(llvm::ArrayRef Errors, // Situations if users want to disable clang diagnostic checker. std::string CheckName = Error.DiagnosticName; if (IsNoClangDiagnostic) { - // std::string CastChecker = "bsc-explicit-cast"; - // std::string AccessChecker = "bsc-access-specific-type"; - // if (CheckName.find(CastChecker) != std::string::npos || - // CheckName.find(AccessChecker) != std::string::npos) { - // llvm::outs() << "--------- flag begin ---------\n"; - // Reporter.reportDiagnostic(Error); - // llvm::outs() << "--------- flag end ---------\n"; - // } std::string BSCChecker = "bsc-"; if (CheckName.find(BSCChecker) != std::string::npos) Reporter.reportDiagnostic(Error); diff --git a/clang-tools-extra/clang-tidy/bsc/BSCTidyModule.cpp b/clang-tools-extra/clang-tidy/bsc/BSCTidyModule.cpp index eefdba0bf4f8..8e2016426fef 100644 --- a/clang-tools-extra/clang-tidy/bsc/BSCTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/bsc/BSCTidyModule.cpp @@ -12,7 +12,6 @@ #include "AccessSpecificTypeCheck.h" #include "AddNewFieldCheck.h" #include "ExplicitCastCheck.h" -#include "FatPtrCheck.h" #include "FatPtrCollecterCheck.h" #include "FatPtrTransformerCheck.h" @@ -29,8 +28,6 @@ public: "bsc-add-new-field"); CheckFactories.registerCheck( "bsc-explicit-cast"); - CheckFactories.registerCheck( - "bsc-fat-ptr"); CheckFactories.registerCheck( "bsc-fat-ptr-collecter"); CheckFactories.registerCheck( diff --git a/clang-tools-extra/clang-tidy/bsc/CMakeLists.txt b/clang-tools-extra/clang-tidy/bsc/CMakeLists.txt index 7c35b3fe2efe..1e1d5c3a1ff2 100644 --- a/clang-tools-extra/clang-tidy/bsc/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/bsc/CMakeLists.txt @@ -9,7 +9,6 @@ add_clang_library(clangTidyBscModule AddNewFieldCheck.cpp BSCTidyModule.cpp ExplicitCastCheck.cpp - FatPtrCheck.cpp FatPtrCollecterCheck.cpp FatPtrTransformerCheck.cpp diff --git a/clang-tools-extra/clang-tidy/bsc/ExplicitCastCheck.h b/clang-tools-extra/clang-tidy/bsc/ExplicitCastCheck.h index 0ba11401a406..58cda5fe869c 100644 --- a/clang-tools-extra/clang-tidy/bsc/ExplicitCastCheck.h +++ b/clang-tools-extra/clang-tidy/bsc/ExplicitCastCheck.h @@ -15,10 +15,7 @@ namespace clang { namespace tidy { namespace bsc { -/// FIXME: Write a short description. -/// -/// For the user-facing documentation see: -/// http://clang.llvm.org/extra/clang-tidy/checks/bsc/explicit-cast.html +// Collect information from the specified file for bsc fat-ptr transformer. class ExplicitCastCheck : public ClangTidyCheck { public: ExplicitCastCheck(StringRef Name, ClangTidyContext *Context) diff --git a/clang-tools-extra/clang-tidy/bsc/FatPtrCheck.cpp b/clang-tools-extra/clang-tidy/bsc/FatPtrCheck.cpp deleted file mode 100644 index 5d27c18884cf..000000000000 --- a/clang-tools-extra/clang-tidy/bsc/FatPtrCheck.cpp +++ /dev/null @@ -1,37 +0,0 @@ -//===--- FatPtrCheck.cpp - clang-tidy -------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#include "FatPtrCheck.h" -#include "clang/AST/ASTContext.h" -#include "clang/ASTMatchers/ASTMatchFinder.h" - -using namespace clang::ast_matchers; - -namespace clang { -namespace tidy { -namespace bsc { - -void FatPtrCheck::registerMatchers(MatchFinder *Finder) { - // FIXME: Add matchers. - Finder->addMatcher(functionDecl().bind("x"), this); -} - -void FatPtrCheck::check(const MatchFinder::MatchResult &Result) { - // FIXME: Add callback implementation. - const auto *MatchedDecl = Result.Nodes.getNodeAs("x"); - if (!MatchedDecl->getIdentifier() || MatchedDecl->getName().startswith("awesome_")) - return; - diag(MatchedDecl->getLocation(), "function %0 is insufficiently awesome") - << MatchedDecl; - diag(MatchedDecl->getLocation(), "insert 'awesome'", DiagnosticIDs::Note) - << FixItHint::CreateInsertion(MatchedDecl->getLocation(), "awesome_"); -} - -} // namespace bsc -} // namespace tidy -} // namespace clang diff --git a/clang-tools-extra/clang-tidy/bsc/FatPtrCheck.h b/clang-tools-extra/clang-tidy/bsc/FatPtrCheck.h deleted file mode 100644 index 06a38a3a2fbf..000000000000 --- a/clang-tools-extra/clang-tidy/bsc/FatPtrCheck.h +++ /dev/null @@ -1,48 +0,0 @@ -//===--- FatPtrCheck.h - clang-tidy -----------------------------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BSC_FATPTRCHECK_H -#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BSC_FATPTRCHECK_H - -#include "../ClangTidyCheck.h" - -namespace clang { -namespace tidy { -namespace bsc { - -/// FIXME: Write a short description. -/// -/// For the user-facing documentation see: -/// http://clang.llvm.org/extra/clang-tidy/checks/bsc/fat-ptr.html -class FatPtrCheck : public ClangTidyCheck { -public: - FatPtrCheck(StringRef Name, ClangTidyContext *Context) - : ClangTidyCheck(Name, Context), - TopLevelFunctionList(Options.get("TopLevelFunctions", "TEMP_FAILURE_RETRY")), - TopLevelVarList(Options.get("TopLevelVars", "TEMP_FAILURE_RETRY")) { - StringRef(TopLevelFunctionList).split(TopLevelFunctions, ",", -1, false); - StringRef(TopLevelVarList).split(TopLevelVars, ",", -1, false); - } - void storeOptions(ClangTidyOptions::OptionMap &Opts) { - Options.store(Opts, "TopLevelFunctions", TopLevelFunctionList); - Options.store(Opts, "TopLevelVars", TopLevelVarList); - } - void registerMatchers(ast_matchers::MatchFinder *Finder) override; - void check(const ast_matchers::MatchFinder::MatchResult &Result) override; -private: - const StringRef TopLevelFunctionList; - SmallVector TopLevelFunctions; - const StringRef TopLevelVarList; - SmallVector TopLevelVars; -}; - -} // namespace bsc -} // namespace tidy -} // namespace clang - -#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BSC_FATPTRCHECK_H diff --git a/clang-tools-extra/clang-tidy/bsc/FatPtrCollecterCheck.cpp b/clang-tools-extra/clang-tidy/bsc/FatPtrCollecterCheck.cpp index bc9e4ba08123..ac7691a4aa17 100644 --- a/clang-tools-extra/clang-tidy/bsc/FatPtrCollecterCheck.cpp +++ b/clang-tools-extra/clang-tidy/bsc/FatPtrCollecterCheck.cpp @@ -6,19 +6,17 @@ // //===----------------------------------------------------------------------===// -#include -#include #include "FatPtrCollecterCheck.h" #include "clang/AST/ASTContext.h" -#include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/AST/BSC/DeclBSC.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/Tooling/CommonOptionsParser.h" +#include +#include -static llvm::cl::opt CFGPath( - "cfg-path", - llvm::cl::desc( - "Indicate the path of cfg file."), - llvm::cl::init("empty"), llvm::cl::Hidden); +static llvm::cl::opt + CFGPath("cfg-path", llvm::cl::desc("Indicate the path of cfg file."), + llvm::cl::init("empty"), llvm::cl::Hidden); using namespace clang::ast_matchers; @@ -26,16 +24,20 @@ namespace clang { namespace tidy { namespace bsc { +// Get the name of Decl, and save it into the '.cfg' file. static inline int SaveInCFG(const NamedDecl *ND) { if (CFGPath == "Empty") { - std::cerr << "No cfg path received, please check input parameter `--cfg-path`" << std::endl; + std::cerr + << "No cfg path received, please check input parameter `--cfg-path`" + << std::endl; return 0; } std::ofstream OutFile; OutFile.open(CFGPath, std::ios::out | std::ios::app); - if (!OutFile.is_open()) { - std::cerr << "can not open fat_ptr.cfg, fail to save toplevel items." << std::endl; + if (!OutFile.is_open()) { + std::cerr << "can not open fat_ptr.cfg, fail to save toplevel items." + << std::endl; return 0; } @@ -46,14 +48,14 @@ static inline int SaveInCFG(const NamedDecl *ND) { } // Check whether this Decl comes from Mainfile or header file. -static inline bool CheckFileId(const clang::SourceManager* SM, - FileID MainFileID, const NamedDecl *ND){ +static inline bool CheckFileId(const clang::SourceManager *SM, + FileID MainFileID, const NamedDecl *ND) { if (MainFileID.isInvalid()) { MainFileID = SM->getMainFileID(); if (MainFileID.isInvalid()) return 0; } - + FileID LocationID = SM->getFileID(SM->getSpellingLoc(ND->getLocation())); if (LocationID.isValid()) { if (MainFileID == LocationID) @@ -68,28 +70,34 @@ void FatPtrCollecterCheck::registerMatchers(MatchFinder *Finder) { auto AT = hasUnqualifiedDesugaredType(arrayType()); auto RT = hasUnqualifiedDesugaredType(recordType()); - Finder->addMatcher(functionDecl(hasParent(translationUnitDecl()), hasBody(stmt())).bind("TopLevelFuncDecl"), this); - Finder->addMatcher(varDecl(hasGlobalStorage(), - anyOf(hasType(PT), hasType(AT), hasType(RT))).bind("GlobalVar"), this); + Finder->addMatcher( + functionDecl(hasParent(translationUnitDecl()), hasBody(stmt())) + .bind("TopLevelFuncDecl"), + this); + Finder->addMatcher( + varDecl(hasGlobalStorage(), anyOf(hasType(PT), hasType(AT), hasType(RT))) + .bind("GlobalVar"), + this); } void FatPtrCollecterCheck::check(const MatchFinder::MatchResult &Result) { - const auto *TopLevelFuncDecl = Result.Nodes.getNodeAs("TopLevelFuncDecl"); + const auto *TopLevelFuncDecl = + Result.Nodes.getNodeAs("TopLevelFuncDecl"); const auto *GlobalVar = Result.Nodes.getNodeAs("GlobalVar"); - if (TopLevelFuncDecl){ + if (TopLevelFuncDecl) { const auto *BSCMD = dyn_cast_or_null(TopLevelFuncDecl); // Exclude 'Member Function' scenarios. if (!BSCMD) { // Exclude 'Extern' scenarios. if (TopLevelFuncDecl->getStorageClass() != SC_Extern) { - if (!CheckFileId(Result.SourceManager, MainFileID, TopLevelFuncDecl)) + if (!CheckFileId(Result.SourceManager, MainFileID, TopLevelFuncDecl)) return; // Save top-level function-name in cfg file. if (!SaveInCFG(TopLevelFuncDecl)) diag(TopLevelFuncDecl->getLocation(), - "Failed to save TopLevelFuncDecl: %0", - DiagnosticIDs::Error) << TopLevelFuncDecl; + "Failed to save TopLevelFuncDecl: %0", DiagnosticIDs::Error) + << TopLevelFuncDecl; } } } @@ -101,9 +109,9 @@ void FatPtrCollecterCheck::check(const MatchFinder::MatchResult &Result) { return; // Save top-level var-name in cfg file. if (!SaveInCFG(GlobalVar)) - diag(GlobalVar->getLocation(), - "Failed to save GlobalVar: %0", - DiagnosticIDs::Error) << GlobalVar; + diag(GlobalVar->getLocation(), "Failed to save GlobalVar: %0", + DiagnosticIDs::Error) + << GlobalVar; } } } diff --git a/clang-tools-extra/clang-tidy/bsc/FatPtrCollecterCheck.h b/clang-tools-extra/clang-tidy/bsc/FatPtrCollecterCheck.h index ccf9b797395b..65b55aec5e62 100644 --- a/clang-tools-extra/clang-tidy/bsc/FatPtrCollecterCheck.h +++ b/clang-tools-extra/clang-tidy/bsc/FatPtrCollecterCheck.h @@ -25,6 +25,7 @@ public: : ClangTidyCheck(Name, Context) {} void registerMatchers(ast_matchers::MatchFinder *Finder) override; void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + private: FileID MainFileID; }; diff --git a/clang-tools-extra/clang-tidy/bsc/FatPtrTransformerCheck.cpp b/clang-tools-extra/clang-tidy/bsc/FatPtrTransformerCheck.cpp index 05cea3278f2f..a989e7e46882 100644 --- a/clang-tools-extra/clang-tidy/bsc/FatPtrTransformerCheck.cpp +++ b/clang-tools-extra/clang-tidy/bsc/FatPtrTransformerCheck.cpp @@ -17,17 +17,23 @@ namespace tidy { namespace bsc { void FatPtrTransformerCheck::AddFat(SourceLocation Loc) { - diag(Loc, "Add 'fat' token here",DiagnosticIDs::Warning) + diag(Loc, "Add 'fat' token here", DiagnosticIDs::Warning) << FixItHint::CreateInsertion(Loc, "fat "); } void FatPtrTransformerCheck::AddChecked(SourceLocation Loc) { - diag(Loc, "Add 'CHECKED' token here",DiagnosticIDs::Warning) + diag(Loc, "Add 'CHECKED' token here", DiagnosticIDs::Warning) << FixItHint::CreateInsertion(Loc, "CHECKED "); } +// When we use a typedef pointer type to declare a var, we won`t +// add `fat` token before it. +// e.g.: +// typedef int* ip; // add `fat` here -> typedef int* fat ip; +// ip p1 = &p; // not here +// So we will pass the vars which are declared by these kinds. void FatPtrTransformerCheck::CheckTypedefAndSetFat(const ValueDecl *D, - SourceManager *SM) { + SourceManager *SM) { QualType DType = D->getType(); if (!DType.getTypePtr()->getAs()) { AddHeader(SM); @@ -65,33 +71,37 @@ std::string FatPtrTransformerCheck::GetTypeName(const Expr *Arg) { } } +// Traverse a RecordDecl, to find out wheather the RecordDecl itself or +// it`s field (if the field is a record type) has an array-type field. +// When find a RecordDecl with array-type field, we will also store it +// into a buffer, so that we don`t need to traverse it next time. void FatPtrTransformerCheck::TraversalRecordDecl(RecordDecl *RD, bool *HasArrayField) { if (RD->isInvalidDecl()) return; - - for(FieldDecl* DeclField : RD->fields()) { - if (const auto *GRTY = dyn_cast_or_null - (DeclField->getType().getTypePtr())) { - if (const auto *RT = dyn_cast_or_null - (GRTY->getNamedType())) { + + for (FieldDecl *DeclField : RD->fields()) { + if (const auto *GRTY = dyn_cast_or_null( + DeclField->getType().getTypePtr())) { + if (const auto *RT = + dyn_cast_or_null(GRTY->getNamedType())) { if (FindInDeclList(HasArrayField, RT)) return; TraversalRecordDecl(RT->getDecl(), HasArrayField); } - } else if (const auto *RT = dyn_cast_or_null - (DeclField->getType().getTypePtr())) { + } else if (const auto *RT = dyn_cast_or_null( + DeclField->getType().getTypePtr())) { if (FindInDeclList(HasArrayField, RT)) - return; + return; TraversalRecordDecl(RT->getDecl(), HasArrayField); } - - if(*HasArrayField) { + + if (*HasArrayField) { DeclList.push_back(RD); return; } - - if(DeclField->getType()->isArrayType()) { + + if (DeclField->getType()->isArrayType()) { *HasArrayField = true; DeclList.push_back(RD); return; @@ -105,34 +115,48 @@ void FatPtrTransformerCheck::registerMatchers(MatchFinder *Finder) { auto RT = hasUnqualifiedDesugaredType(recordType()); for (auto TopLevelName : TopLevels) { - Finder->addMatcher(parmVarDecl(hasParent(functionDecl( - hasName(std::string(TopLevelName)))), - hasType(PT)).bind("PtParmVarDecl"), this); - Finder->addMatcher(functionDecl(hasParent(translationUnitDecl()), hasName(std::string(TopLevelName)), - returns(isAnyPointer())).bind("FuncDeclRtPt"), this); + returns(isAnyPointer())) + .bind("FuncDeclRtPt"), + this); Finder->addMatcher(callExpr(callee(functionDecl(hasName("malloc"))), - hasParent(castExpr().bind("MallocParent"))) - .bind("MallocCall"), this); - Finder->addMatcher(callExpr(callee(functionDecl(hasName("free")))).bind("FreecCall"), this); - - Finder->addMatcher(varDecl(hasGlobalStorage(), - hasName(std::string(TopLevelName)), - hasType(PT)).bind("GlobalPointerVar"), this); - Finder->addMatcher(varDecl(hasGlobalStorage(), - hasName(std::string(TopLevelName)), - allOf(hasType(AT), unless(hasType(PT)))).bind("GlobalArrayVar"), this); - Finder->addMatcher(varDecl(hasGlobalStorage(), - hasName(std::string(TopLevelName)), - hasType(RT)).bind("GlobalRecordVar"), this); - - Finder->addMatcher(varDecl(hasAncestor(functionDecl(hasName(std::string(TopLevelName)))), - hasLocalStorage(), hasType(PT)).bind("LocalPointerVar"), this); + hasParent(castExpr().bind("MallocParentCast"))) + .bind("MallocCallCast"), + this); + Finder->addMatcher( + callExpr(callee(functionDecl(hasName("malloc"))), hasParent(varDecl())) + .bind("MallocCallVar"), + this); + Finder->addMatcher( + callExpr(callee(functionDecl(hasName("free")))).bind("FreecCall"), + this); + + Finder->addMatcher(varDecl(hasGlobalStorage(), + hasName(std::string(TopLevelName)), hasType(PT)) + .bind("GlobalPointerVar"), + this); + Finder->addMatcher(varDecl(hasGlobalStorage(), + hasName(std::string(TopLevelName)), + allOf(hasType(AT), unless(hasType(PT)))) + .bind("GlobalArrayVar"), + this); + Finder->addMatcher(varDecl(hasGlobalStorage(), + hasName(std::string(TopLevelName)), hasType(RT)) + .bind("GlobalRecordVar"), + this); + + Finder->addMatcher( + varDecl(hasAncestor(functionDecl(hasName(std::string(TopLevelName)))), + hasLocalStorage(), hasType(PT)) + .bind("LocalPointerVar"), + this); Finder->addMatcher(typedefDecl(hasType(PT)).bind("TypedefPT"), this); - Finder->addMatcher(unaryOperator(hasType(PT), hasOperatorName("&")).bind("UnOpAddr"), this); + Finder->addMatcher( + unaryOperator(hasType(PT), hasOperatorName("&")).bind("UnOpAddr"), + this); Finder->addMatcher(cStyleCastExpr(hasType(PT)).bind("CCastExprPT"), this); } } @@ -141,86 +165,121 @@ void FatPtrTransformerCheck::check(const MatchFinder::MatchResult &Result) { if (MainFileID.isInvalid()) MainFileID = Result.SourceManager->getMainFileID(); - const auto *PtParmVarDecl = Result.Nodes.getNodeAs("PtParmVarDecl"); - const auto *FuncDeclRtPt = Result.Nodes.getNodeAs("FuncDeclRtPt"); + const auto *FuncDeclRtPt = + Result.Nodes.getNodeAs("FuncDeclRtPt"); - const auto *GlobalPointerVar = Result.Nodes.getNodeAs("GlobalPointerVar"); - const auto *GlobalArrayVar = Result.Nodes.getNodeAs("GlobalArrayVar"); - const auto *GlobalRecordVar = Result.Nodes.getNodeAs("GlobalRecordVar"); - const auto *LocalPointerVar = Result.Nodes.getNodeAs("LocalPointerVar"); + const auto *GlobalPointerVar = + Result.Nodes.getNodeAs("GlobalPointerVar"); + const auto *GlobalArrayVar = + Result.Nodes.getNodeAs("GlobalArrayVar"); + const auto *GlobalRecordVar = + Result.Nodes.getNodeAs("GlobalRecordVar"); + const auto *LocalPointerVar = + Result.Nodes.getNodeAs("LocalPointerVar"); const auto *TypedefPT = Result.Nodes.getNodeAs("TypedefPT"); const auto *UnOpAddr = Result.Nodes.getNodeAs("UnOpAddr"); - const auto *CCastExprPT = Result.Nodes.getNodeAs("CCastExprPT"); + const auto *CCastExprPT = + Result.Nodes.getNodeAs("CCastExprPT"); - const auto *MallocCall = Result.Nodes.getNodeAs("MallocCall"); - const auto *MallocParent = Result.Nodes.getNodeAs("MallocParent"); + const auto *MallocCallCast = + Result.Nodes.getNodeAs("MallocCallCast"); + const auto *MallocParentCast = + Result.Nodes.getNodeAs("MallocParentCast"); + + const auto *MallocCallVar = Result.Nodes.getNodeAs("MallocCallVar"); const auto *FreecCall = Result.Nodes.getNodeAs("FreecCall"); - + + // Find AddrOf symbol '&'. if (UnOpAddr) { if (UnOpAddr->getOpcode() == UnaryOperatorKind::UO_AddrOf) { AddHeader(Result.SourceManager); - AddFat(UnOpAddr->getEndLoc()); + AddFat(UnOpAddr->getBeginLoc().getLocWithOffset(1)); } } + // Find explicitly cast. if (CCastExprPT) { AddHeader(Result.SourceManager); AddFat(CCastExprPT->getRParenLoc()); } - if (PtParmVarDecl) { - diag(PtParmVarDecl->getBeginLoc(), "Find PtParmVarDecl here", DiagnosticIDs::Warning); - } - - if (MallocCall) { - if (MallocParent) { - if (const clang::Type *CT = dyn_cast(MallocParent->getType().getTypePtr())) { + // Find 'malloc()' call with 'non-void' type. + if (MallocCallCast) { + if (MallocParentCast) { + if (const clang::Type *CT = + dyn_cast(MallocParentCast->getType().getTypePtr())) { if (const PointerType *PT = CT->getAs()) { - std::string ReplaceStr = "checked_malloc<"; - ReplaceStr.append(PT->getPointeeType().getAsString()); - ReplaceStr.append(">"); - - SourceLocation BeginLoc = MallocCall->getExprLoc(); - SourceLocation EndLoc = MallocCall->getArg(0)->getExprLoc(); + std::string ReplaceStr; + if (MallocParentCast->getStmtClass() == Stmt::CStyleCastExprClass) { + ReplaceStr = "checked_malloc"; + } else { + ReplaceStr = "("; + ReplaceStr.append(PT->getPointeeType().getAsString()); + ReplaceStr.append(" *fat)checked_malloc"); + } + + SourceLocation BeginLoc = MallocCallCast->getExprLoc(); + SourceLocation EndLoc = MallocCallCast->getArg(0)->getExprLoc(); SourceRange SR = SourceRange(BeginLoc, EndLoc.getLocWithOffset(-2)); + diag(BeginLoc, "Find Malloc Call here", DiagnosticIDs::Warning) - << FixItHint::CreateReplacement(SR, ReplaceStr); + << FixItHint::CreateReplacement(SR, ReplaceStr); AddHeader(Result.SourceManager); } } } } + // Find 'malloc()' call with 'void *' type. + if (MallocCallVar) { + std::string ReplaceStr = "checked_malloc"; + + SourceLocation BeginLoc = MallocCallVar->getExprLoc(); + SourceLocation EndLoc = MallocCallVar->getArg(0)->getExprLoc(); + SourceRange SR = SourceRange(BeginLoc, EndLoc.getLocWithOffset(-2)); + + diag(BeginLoc, "Find Malloc Call here", DiagnosticIDs::Warning) + << FixItHint::CreateReplacement(SR, ReplaceStr); + AddHeader(Result.SourceManager); + } + + // Find 'free()' call. if (FreecCall) { - if (const CastExpr *Arg = dyn_cast_or_null(FreecCall->getArg(0))) { - if (const clang::Type *CT = dyn_cast(Arg->getSubExpr()->getType().getTypePtr())) { + if (const CastExpr *Arg = + dyn_cast_or_null(FreecCall->getArg(0))) { + if (const clang::Type *CT = dyn_cast( + Arg->getSubExpr()->getType().getTypePtr())) { if (const PointerType *PT = CT->getAs()) { - std::string ReplaceStr = "checked_free<"; - ReplaceStr.append(PT->getPointeeType().getAsString()); - ReplaceStr.append(">"); + std::string ReplaceStr = "checked_free("; + if (Arg->getStmtClass() != Stmt::CStyleCastExprClass) { + ReplaceStr.append("(void *fat)"); + } SourceLocation BeginLoc = FreecCall->getExprLoc(); SourceLocation EndLoc = FreecCall->getArg(0)->getExprLoc(); - SourceRange SR = SourceRange(BeginLoc, EndLoc.getLocWithOffset(-2)); + SourceRange SR = SourceRange(BeginLoc, EndLoc.getLocWithOffset(-1)); diag(BeginLoc, "Find Free Call here", DiagnosticIDs::Warning) - << FixItHint::CreateReplacement(SR, ReplaceStr); + << FixItHint::CreateReplacement(SR, ReplaceStr); AddHeader(Result.SourceManager); } } } } - if (GlobalArrayVar){ - if (const auto *CAT = dyn_cast_or_null - (GlobalArrayVar->getType().getTypePtr())) { + // Find array-type vars with global declaration. + if (GlobalArrayVar) { + if (const auto *CAT = dyn_cast_or_null( + GlobalArrayVar->getType().getTypePtr())) { AddHeader(Result.SourceManager); QualType VarType = CAT->getElementType(); - if (const TypedefType *TdefT = dyn_cast_or_null(VarType.getTypePtr())) { + if (const TypedefType *TdefT = + dyn_cast_or_null(VarType.getTypePtr())) { if (!TdefT->getAs()) AddChecked(GlobalArrayVar->getBeginLoc()); - } else if (const auto *PT = dyn_cast_or_null(VarType.getTypePtr())) { + } else if (const auto *PT = + dyn_cast_or_null(VarType.getTypePtr())) { AddFat(GlobalArrayVar->getLocation()); } else { AddChecked(GlobalArrayVar->getBeginLoc()); @@ -228,11 +287,12 @@ void FatPtrTransformerCheck::check(const MatchFinder::MatchResult &Result) { } } - if (GlobalRecordVar){ - if (const auto *GRTY = dyn_cast_or_null - (GlobalRecordVar->getType().getTypePtr())) { - if (const auto *RT = dyn_cast_or_null - (GRTY->getNamedType())) { + // Find record-type vars with global declaration. + if (GlobalRecordVar) { + if (const auto *GRTY = dyn_cast_or_null( + GlobalRecordVar->getType().getTypePtr())) { + if (const auto *RT = + dyn_cast_or_null(GRTY->getNamedType())) { bool HasArrayField = false; if (!FindInDeclList(&HasArrayField, RT)) TraversalRecordDecl(RT->getDecl(), &HasArrayField); @@ -245,19 +305,23 @@ void FatPtrTransformerCheck::check(const MatchFinder::MatchResult &Result) { } } + // Find pointer-type vars with global declaration. if (GlobalPointerVar) CheckTypedefAndSetFat(GlobalPointerVar, Result.SourceManager); - if (LocalPointerVar) + // Find pointer-type vars with local declaration. + if (LocalPointerVar) CheckTypedefAndSetFat(LocalPointerVar, Result.SourceManager); + // Find pointer-type vars with local declaration. if (FuncDeclRtPt) CheckTypedefAndSetFat(FuncDeclRtPt, Result.SourceManager); + // Find typedef with pointer type. if (TypedefPT) { - SourceLocation TypedefLoc = TypedefPT->getLocation(); - FileID LocationID = Result.SourceManager->getFileID(Result.SourceManager-> - getSpellingLoc(TypedefLoc)); + SourceLocation TypedefLoc = TypedefPT->getLocation(); + FileID LocationID = Result.SourceManager->getFileID( + Result.SourceManager->getSpellingLoc(TypedefLoc)); if (LocationID.isValid()) { if (MainFileID == LocationID) { AddHeader(Result.SourceManager); diff --git a/clang-tools-extra/clang-tidy/bsc/FatPtrTransformerCheck.h b/clang-tools-extra/clang-tidy/bsc/FatPtrTransformerCheck.h index d4e37c9ff3c8..6341149fbae1 100644 --- a/clang-tools-extra/clang-tidy/bsc/FatPtrTransformerCheck.h +++ b/clang-tools-extra/clang-tidy/bsc/FatPtrTransformerCheck.h @@ -15,14 +15,11 @@ namespace clang { namespace tidy { namespace bsc { -/// FIXME: Write a short description. -/// -/// For the user-facing documentation see: -/// http://clang.llvm.org/extra/clang-tidy/checks/bsc/fat-ptr-transformer.html +// Transform the specified file with bsc fat-ptr. class FatPtrTransformerCheck : public ClangTidyCheck { public: FatPtrTransformerCheck(StringRef Name, ClangTidyContext *Context) - : ClangTidyCheck(Name, Context), + : ClangTidyCheck(Name, Context), TopLevelList(Options.get("TopLevels", "TEMP_FAILURE_RETRY")) { StringRef(TopLevelList).split(TopLevels, ",", -1, false); } @@ -31,6 +28,7 @@ public: } void registerMatchers(ast_matchers::MatchFinder *Finder) override; void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + private: const StringRef TopLevelList; SmallVector TopLevels; @@ -38,15 +36,14 @@ private: bool NeedFix = false; bool AlreadyAddHeader = false; std::vector DeclList; - + void TraversalRecordDecl(RecordDecl *RD, bool *HasArrayField); void AddFat(SourceLocation Loc); void AddChecked(SourceLocation Loc); std::string GetTypeName(const Expr *Arg); void CheckTypedefAndSetFat(const ValueDecl *D, SourceManager *SM); - inline bool FindInDeclList(bool *HasArrayField, - const clang::RecordType* RT) { + inline bool FindInDeclList(bool *HasArrayField, const clang::RecordType *RT) { auto Existed = std::find(DeclList.begin(), DeclList.end(), RT->getDecl()); if (Existed != DeclList.end()) { *HasArrayField = true; @@ -57,10 +54,11 @@ private: inline void AddHeader(SourceManager *SM) { if (!AlreadyAddHeader) { - if (!MainFileID.isInvalid()){ + if (!MainFileID.isInvalid()) { SourceLocation StartOfFileLoc = SM->getLocForStartOfFile(MainFileID); - diag(StartOfFileLoc, "Add Header Here.", DiagnosticIDs::Warning) << - FixItHint::CreateInsertion(StartOfFileLoc, "#include \n"); + diag(StartOfFileLoc, "Add Header Here.", DiagnosticIDs::Warning) + << FixItHint::CreateInsertion(StartOfFileLoc, + "#include \n"); AlreadyAddHeader = true; } } diff --git a/clang-tools-extra/clang-tidy/tool/bsc/bsc-build.py b/clang-tools-extra/clang-tidy/tool/bsc/bsc-build.py index 822a217f5309..33c01d985386 100644 --- a/clang-tools-extra/clang-tidy/tool/bsc/bsc-build.py +++ b/clang-tools-extra/clang-tidy/tool/bsc/bsc-build.py @@ -5,11 +5,14 @@ import os import argparse import subprocess +# Make sure it`s the first time to create '.cfg' file. first_create_cfg = False +# Get the path of this python scrpit. bsc_build_path = os.path.dirname(os.path.abspath(__file__)) +# store all header files from source path. header_list = [] -h_modify_list = [] +# Run shell command. def excute_command(cmd: str, cmd_name: str = None): proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, shell=True) stdout_c, stderr_c = proc.communicate() @@ -17,6 +20,7 @@ def excute_command(cmd: str, cmd_name: str = None): print(" ===== " + cmd_name + " Return Code: ", proc.returncode , " ===== ") return proc.returncode +# Run shell command in a list way, and set new env vars if env is not None. def excute_cmd_list(cmd_list: list, env: dict = None): for cmd in cmd_list: cmd_list = [item for item in cmd.split(" ") if item.strip()] @@ -33,10 +37,10 @@ def excute_cmd_list(cmd_list: list, env: dict = None): proc_modify.wait() pass +# Gather information of top-level vars/funcs from source code files. def gather_top_levels(source_path: str, cfg_path: str): global bsc_build_path global header_list - global h_modify_list command_c = ( "find " + source_path + " -type f -name '*.c' " @@ -54,14 +58,8 @@ def gather_top_levels(source_path: str, cfg_path: str): ) excute_command(command_h, "Proc_h") - - command_h_modify = ( - "find " + source_path + " -type f -name '*.h' " - + " -exec " + bsc_build_path + "/clang-tidy " - + "--fix-errors --config-file==" + cfg_path - + " $file {} \;" - ) - h_modify_list.append(command_h_modify) + # # Find all header files and save into a list. + # # FIXME: Try to use SqlLite to save the information into a DB. find_h = ( "find " + source_path + " -type f -name '*.h' " ) @@ -85,10 +83,14 @@ def gather_top_levels(source_path: str, cfg_path: str): ) excute_command(command_hbs, "Proc_hbs") +# Backup sourece codes. def backup_source_code(source_path: str): command_backup = ("cp -r " + source_path + " " + source_path + "_backup") excute_command(command_backup, "Proc_backup") +# Backup system`s default compilers, like '/usr/bin/clang'. Then replace +# them with our fake compilers, by change the direction of the softlink. +# # FIXME: Add situation of other compilers, like 'gcc' or 'cc'. def backup_compiler_and_replace(fake_compiler: str): default_clang = "/usr/bin/clang" if os.path.exists(default_clang): @@ -100,9 +102,10 @@ def backup_compiler_and_replace(fake_compiler: str): excute_command(clang_replace, "Proc_clang_replace") else: print(" ===== Failed to backup " + default_clang + " ===== ") - pass +# Recover system`s default compilers with backup. +# # FIXME: Add situation of other compilers, like 'gcc' or 'cc'. def recover_compiler(): clang_backup = "/usr/bin/clang_backup" if os.path.exists(clang_backup): @@ -112,10 +115,12 @@ def recover_compiler(): clang_recover = "sudo mv -f /usr/bin/clang_backup /usr/bin/clang" excute_command(clang_recover, "Proc_clang_recover") +# Prepare the '.cfg' file, and gather information for it. def preparecfg(file_path: str): global first_create_cfg global bsc_build_path cfg_file = bsc_build_path + "/fat_ptr.cfg" + # Create file. if not first_create_cfg: content = ( 'Checks: -*, bsc-fat-ptr-transformer\n' 'CheckOptions:\n - key: ' @@ -125,6 +130,7 @@ def preparecfg(file_path: str): file.write(content) first_create_cfg = True + # Backup source codes, and gather information. paths = file_path.split(";") for path in paths: abs_source_path = os.path.abspath(path) @@ -180,16 +186,11 @@ def main(): env["CXX"] = fakecompiler_path + os.pathsep + env["CXX"] else: env["CXX"] = fakecompiler_path - - # # Modify headers - # # FIXME: We modify all header now, still need to complete the further logic, to modify the headers that only been used. - # # maybe not, it seems when we modify .c, the used .h will be modified too, need check again. - # excute_cmd_list(h_modify_list) if args.rewrite: print(" =========================== Active source to source after fat-ptr modify. =========================== ") env["REWRITE"] = "Enable" - # # FIXME: transform all file from .c/.h to .cbs/.hbs, then we can rewrite + # # FIXME: transform all file from .c/.h to .cbs/.hbs, then we can rewrite properly. # for file, path in header_dict.items(): # cmd = ( # bsc_build_path @@ -199,12 +200,12 @@ def main(): # ) # excute_command(cmd) - # # FIXME: complete /usr/bin/cc situation. + # # FIXME: complete softlink like '/usr/bin/cc'. # backup_compiler_and_replace(fakecompiler_path) excute_cmd_list(cmds, env) - # # FIXME: complete /usr/bin/cc situation. + # # FIXME: complete softlink like '/usr/bin/cc'. # recover_compiler() pass @@ -212,7 +213,8 @@ def main(): # # FIXME: complete or delete this situation. pass - + # # Build the origin command with default compilers. + # # //FIXME: Finish this part after fixing the problems of rewrite. # excute_cmd_list(cmds) diff --git a/clang-tools-extra/clang-tidy/tool/bsc/fake_clang.py b/clang-tools-extra/clang-tidy/tool/bsc/fake_clang.py index 4dba453f62b5..b62486ef6dec 100644 --- a/clang-tools-extra/clang-tidy/tool/bsc/fake_clang.py +++ b/clang-tools-extra/clang-tidy/tool/bsc/fake_clang.py @@ -6,27 +6,33 @@ import re import argparse import subprocess - +# Get the path of our clang-tidy. def get_clang_tidy(): - clang_tidy_path = None + clang_tidy_path = "" current_file = os.path.abspath(__file__) if (os.path.islink(current_file)): clang_tidy_path = os.path.dirname(os.readlink(current_file)) + "/../" else: clang_tidy_path = os.path.dirname(current_file) + "/../" - - return clang_tidy_path -def find_matching_strings(pattern, strings): - matches = [s for s in strings if pattern.match(s)] - return matches + return clang_tidy_path -def find_matching_includes(strings): - pattern = re.compile(r'\-I.*?') +# Find the certain string from the command. +def find_matching_strings(pattern: str, strings: list): matches = [s for s in strings if pattern.match(s)] return matches -def transform_c_file(cfile, includes, clang_tidy_path): +# Add the certain options to the new command. +def add_options_to_cmd(cmd: str, options: list): + if options: + for opt in options: + cmd = " " + cmd + opt + return cmd + +# Modify the certain C-file. Add '-I' or '-D' +# options into the new command if exsits. +def transform_c_file(cfile: str, includes: list, + macros: list, clang_tidy_path: str): if (clang_tidy_path): command_c = ( clang_tidy_path + "clang-tidy " @@ -34,31 +40,31 @@ def transform_c_file(cfile, includes, clang_tidy_path): + clang_tidy_path + "fat_ptr.cfg " + cfile + " -header-filter=.* " ) - if includes: + if includes or macros: command_c += "-- " - for include in includes: - command_c = command_c + include + " " - pass + command_c = add_options_to_cmd(command_c, includes) + command_c = add_options_to_cmd(command_c, macros) proc_c = subprocess.Popen( command_c, text=True, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE ) - + stdout_c, stderr_c = proc_c.communicate() print("Proc_modify Return Code:", proc_c.returncode) pass -def rewrite_c_file(cfile, includes, clang_tidy_path): +# Rewrite this C-file if the rewrite option is on. +def rewrite_c_file(cfile: str, includes: list, + macros: list, clang_tidy_path: str): if (clang_tidy_path): cmd = ( clang_tidy_path + "clang -x" " bsc -rewrite-bsc " + cfile ) - if includes: - for include in includes: - cmd = cmd + " " + include - pass + if includes or macros: + cmd = add_options_to_cmd(cmd, includes) + cmd = add_options_to_cmd(cmd, macros) proc_c = subprocess.Popen( cmd, text=True, shell=True, @@ -72,22 +78,32 @@ def rewrite_c_file(cfile, includes, clang_tidy_path): def main(): if len(sys.argv) > 1: + # Find C-file from the building command. pattern_c = re.compile(r'.*?\.c$') cfile = find_matching_strings(pattern_c, sys.argv) + # Find include option from the building command. pattern_i = re.compile(r'\-I.*?') includes = find_matching_strings(pattern_i, sys.argv) + # Find macro option from the building command. + pattern_d = re.compile(r'\-D.*?') + macros = find_matching_strings(pattern_d, sys.argv) + clang_tidy_path = get_clang_tidy() if (cfile): - transform_c_file(cfile[0], includes, clang_tidy_path) + # Modify C-file. + transform_c_file(cfile[0], includes, + macros, clang_tidy_path) + # Rewrite C-file. env_rewrite = os.environ.get("REWRITE") if env_rewrite and env_rewrite == "Enable": - rewrite_c_file(cfile[0], includes, clang_tidy_path) + rewrite_c_file(cfile[0], includes, + macros, clang_tidy_path) else: print("No command line arguments provided.") if __name__ == "__main__": - main() + main() \ No newline at end of file diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index 67881817f449..9100baa7bef0 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -159,11 +159,6 @@ New checks FIXME: add release notes. -- New :doc:`bsc-fat-ptr - ` check. - - FIXME: add release notes. - - New :doc:`bsc-fat-ptr-collecter ` check. diff --git a/clang-tools-extra/docs/clang-tidy/checks/bsc/fat-ptr.rst b/clang-tools-extra/docs/clang-tidy/checks/bsc/fat-ptr.rst deleted file mode 100644 index f0f92143cdbf..000000000000 --- a/clang-tools-extra/docs/clang-tidy/checks/bsc/fat-ptr.rst +++ /dev/null @@ -1,6 +0,0 @@ -.. title:: clang-tidy - bsc-fat-ptr - -bsc-fat-ptr -=========== - -FIXME: Describe what patterns does the check detect and why. Give examples. diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst index e51ef1bcbfd6..b3c200d3176a 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/list.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -82,7 +82,6 @@ Clang-Tidy Checks `bsc-explicit-cast `_, `bsc-fat-ptr-collecter `_, `bsc-fat-ptr-transformer `_, "Yes" - `bsc-fat-ptr `_, "Yes" `bugprone-argument-comment `_, "Yes" `bugprone-assert-side-effect `_, `bugprone-assignment-in-if-condition `_, diff --git a/clang-tools-extra/test/clang-tidy/checkers/bsc/fat-ptr.cpp b/clang-tools-extra/test/clang-tidy/checkers/bsc/fat-ptr.cpp deleted file mode 100644 index 9f34153f54d3..000000000000 --- a/clang-tools-extra/test/clang-tidy/checkers/bsc/fat-ptr.cpp +++ /dev/null @@ -1,14 +0,0 @@ -// RUN: %check_clang_tidy %s bsc-fat-ptr %t - -// FIXME: Add something that triggers the check here. -void f(); -// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: function 'f' is insufficiently awesome [bsc-fat-ptr] - -// FIXME: Verify the applied fix. -// * Make the CHECK patterns specific enough and try to make verified lines -// unique to avoid incorrect matches. -// * Use {{}} for regular expressions. -// CHECK-FIXES: {{^}}void awesome_f();{{$}} - -// FIXME: Add something that doesn't trigger the check here. -void awesome_f2(); -- Gitee From 7f5afd3e82ed3fe686b74110f033f36bcf4fdfac Mon Sep 17 00:00:00 2001 From: xiaoyu Date: Sat, 8 Mar 2025 11:57:05 +0800 Subject: [PATCH 17/29] update user manual for fat ptr, including illustrative demos, and comparison with ASan --- clang/docs/BiShengCLanguageUserManual.md | 128 +++++++++++++++++++++-- 1 file changed, 120 insertions(+), 8 deletions(-) diff --git a/clang/docs/BiShengCLanguageUserManual.md b/clang/docs/BiShengCLanguageUserManual.md index ab5636c3a025..33b6f5967e23 100644 --- a/clang/docs/BiShengCLanguageUserManual.md +++ b/clang/docs/BiShengCLanguageUserManual.md @@ -4420,8 +4420,11 @@ void point_to_global_var() { 1. Double free 2. Use after free 3. Use after return + 胖指针特性可以检测的**空间类**内存安全问题包括: -1. Out of bounds +1. Buffer overflow +2. Out of bounds + #### Double free ```C @@ -4437,8 +4440,8 @@ int main(int argc, char **argv) { ``` 编译 double_free.cbs 并运行: ``` -clang double_free.cbs -enable-fat-ptr -./a.out 1 +$ clang double_free.cbs -enable-fat-ptr +$ ./a.out 1 ``` 这一用例会触发胖指针的运行时报错机制: ``` @@ -4460,8 +4463,8 @@ int main(int argc, char **argv) { ``` 编译 use_after_free.cbs 并运行: ``` -clang use_after_free.cbs -enable-fat-ptr -./a.out 1 +$ clang use_after_free.cbs -enable-fat-ptr +$ ./a.out 1 ``` 这一用例会触发胖指针的运行时报错机制: ``` @@ -4492,14 +4495,89 @@ int main(int argc, char **argv) { ``` 编译 use_after_return.cbs 并运行: ``` -clang use_after_return.cbs -enable-fat-ptr -./a.out 1 +$ clang use_after_return.cbs -enable-fat-ptr +$ ./a.out 1 ``` 这一用例会触发胖指针的运行时报错机制: ``` Error: Cannot use this pointer because the allocation may have been freed or reseored! ``` +#### Buffer overflow +```C +// buffer_overflow.cbs +#include +#define BUFSIZE 8 + +char *fat fat_strcpy(char *fat dest, const char *src) { + while (*src != '\0') { + *dest = *src; + dest = dest + 1; + src = src + 1; + } + return dest; +} + +int main(int argc, char **argv) { + int secret = 1; // 重要数据 + char buffer[BUFSIZE]; // 小缓冲区 + printf("Before overflow: secret = %d\n", secret); + char *fat p = &fat buffer[0]; + // 复制超长字符串,导致缓冲区溢出 + fat_strcpy(p, argv[1]); + printf("After overflow: secret = %d\n", secret); + return 0; +} +``` +当不使能胖指针,仍按照裸指针编译并运行时,可以观察到`secret`被篡改了: +``` +$ clang buffer_overflow.cbs +$ ./a.out lonstring +``` +打印内容如下 +``` +Before overflow: secret = 1 +After overflow: secret = 26478 // 不同目标平台,具体值可能变化 +``` +当入参长度更大一些时,可能由于溢出覆盖返回地址,导致程序崩溃。 + +若开启胖指针编译选项,再次编译运行,这一用例会触发胖指针的运行时报错机制: +``` +$ clang buffer_overflow.cbs -enable-fat-ptr +$ ./a.out lonstring +``` +此时,运行时报错信息如下: +``` +Error: Pointer offset exceeds the allocation size! +Error at: buffer_overflow.cbs:7 in fat_strcpy +``` + +#### Out of bounds +```C +// out_of_bounds.cbs +#include + +int main() { + int arr[5]; + int *fat p = &fat arr[0]; + for (int i = 0; i <= 5; i++) { + *p = i; + p = p + 1; + } + return 0; +} +``` +编译 out_of_bounds.cbs 并运行: +``` +$ clang out_of_bounds.cbs -enable-fat-ptr +$ ./a.out +``` +这一用例会触发胖指针的运行时报错机制: +``` +Error: Pointer offset exceeds the allocation size! +Error at: out_of_bounds.cbs:8 in main +``` + ### 用胖指针特性改写遗留 C 代码 用户可以很方便地使用胖指针特性对存量 C 代码进行改写,只需要: 1. 将代码中的裸指针类型`T*`修改为胖指针类型`T *fat` @@ -4507,6 +4585,30 @@ Error: Cannot use this pointer because the allocation may have been freed or res 3. 将代码中的内存管理函数`malloc`和`free`替换为`checked_malloc`和`checked_free` 4. 对于全局变量,如果想要使用`&fat`获取指向它的胖指针,在变量声明前加上`CHECKED`修饰 +如下是一个包含如上改写方法的简单示例: + +```C +#include + +CHECKED int arr[3]; + +typedef struct node Node; + +struct node { + int val; + Node *fat next; +}; + +int main() { + Node *fat node = (Node *fat)checked_malloc(sizeof(Node)); + node->val = 1; + int *fat p = &fat arr[0]; + *p = 10; + checked_free((void *fat)node); + return 0; +} +``` + 在实际使用的时候,可能会出现这样的情况,有些库是以二进制形式存在的,无法对它们重新编译,因此它们对应的头文件、函数签名、类型签名中的裸指针类型是不能修改的。**由于胖指针类型和裸指针类型之间不支持隐式转换,那么如果我们想要调用三方库中的函数,需要做类型强转,将胖指针强转为裸指针或将裸指针强转为胖指针**,例如: ```C #include "some_lib.h" @@ -4518,6 +4620,16 @@ void call_external_func(int *fat p) { p = (int *fat)external_func2(); } ``` +### 胖指针与ASan对比 +AddressSanitizer(ASan) 是一种在业界广泛使用的内存安全问题检测工具,相比之下,胖指针特性在以下几个维度更优: + +- 1、ASan 漏报概率较大,绕过检测的场景较多;胖指针检测能力更强,尤其是在被胖指针全面覆盖的区域; + +- 2、ASan 运行性能劣化严重,内存占用极大,容易触发程序 OOM;胖指针对内存额外消耗不超过1倍,运行时长也控制在平均1倍以内; + +- 3、ASan 由于存在虚拟地址空间,受到系统`ulimit`限制,也不支持可执行文件的静态链接;胖指针不存在此类限制。 + +**注意:** ASan 和胖指针都涉及对原生 malloc 的特异性改造,不建议编译时同时开启。 ## 标准库 @@ -5958,7 +6070,7 @@ int main() { | assign-owned | "assign to owned value: \`%0\`"
"assign to part of owned value: \`%0\`"
"assign to subfield owned value: \`%0\`, %1 owned"
还包括assign-moved-owned、assign-uninit-owned 错误类型标识可屏蔽的错误日志 | | cast-moved-owned | "invalid cast to `void * owned` of moved value: \`%0\`" | | cast-owned | "invalid cast to `void * owned` of owned value: \`%0\`"
"invalid cast to `void * owned` of uninit value: \`%0\`"
"invalid cast to `void * owned` of not all moved value: \`%0\`, %1 owned"
"invalid cast to `void * owned` of moved value:\`%0\`" | -| check-memory-leak | "field memory leak of value: `%0`, %1 leak"
"memory leak of value: `%0` | +| check-memory-leak | "field memory leak of value: \`%0\`, %1 leak"
"memory leak of value: \`%0\` | | bsc-ownership | 可屏蔽所有 owned 数据流分析过程的报错,
包括 use-owned、assign-owned、cast-owned、check-memory-leak 错误类型标识可屏蔽的错误日志。 | | assign-borrowed | "cannot assign to \`%0\` because it is borrowed" | | move-borrowed | "cannot move out of \`%0\` because it is borrowed" | -- Gitee From de79706584a1a31c91007a23eb55d29c25c03648 Mon Sep 17 00:00:00 2001 From: zhaoxuhui Date: Fri, 21 Mar 2025 16:16:12 +0800 Subject: [PATCH 18/29] Revert "update user manual for fat ptr, including illustrative demos, and comparison with ASan" This reverts commit 7f5afd3e82ed3fe686b74110f033f36bcf4fdfac. --- clang/docs/BiShengCLanguageUserManual.md | 128 ++--------------------- 1 file changed, 8 insertions(+), 120 deletions(-) diff --git a/clang/docs/BiShengCLanguageUserManual.md b/clang/docs/BiShengCLanguageUserManual.md index 33b6f5967e23..ab5636c3a025 100644 --- a/clang/docs/BiShengCLanguageUserManual.md +++ b/clang/docs/BiShengCLanguageUserManual.md @@ -4420,11 +4420,8 @@ void point_to_global_var() { 1. Double free 2. Use after free 3. Use after return - 胖指针特性可以检测的**空间类**内存安全问题包括: -1. Buffer overflow -2. Out of bounds - +1. Out of bounds #### Double free ```C @@ -4440,8 +4437,8 @@ int main(int argc, char **argv) { ``` 编译 double_free.cbs 并运行: ``` -$ clang double_free.cbs -enable-fat-ptr -$ ./a.out 1 +clang double_free.cbs -enable-fat-ptr +./a.out 1 ``` 这一用例会触发胖指针的运行时报错机制: ``` @@ -4463,8 +4460,8 @@ int main(int argc, char **argv) { ``` 编译 use_after_free.cbs 并运行: ``` -$ clang use_after_free.cbs -enable-fat-ptr -$ ./a.out 1 +clang use_after_free.cbs -enable-fat-ptr +./a.out 1 ``` 这一用例会触发胖指针的运行时报错机制: ``` @@ -4495,89 +4492,14 @@ int main(int argc, char **argv) { ``` 编译 use_after_return.cbs 并运行: ``` -$ clang use_after_return.cbs -enable-fat-ptr -$ ./a.out 1 +clang use_after_return.cbs -enable-fat-ptr +./a.out 1 ``` 这一用例会触发胖指针的运行时报错机制: ``` Error: Cannot use this pointer because the allocation may have been freed or reseored! ``` -#### Buffer overflow -```C -// buffer_overflow.cbs -#include -#define BUFSIZE 8 - -char *fat fat_strcpy(char *fat dest, const char *src) { - while (*src != '\0') { - *dest = *src; - dest = dest + 1; - src = src + 1; - } - return dest; -} - -int main(int argc, char **argv) { - int secret = 1; // 重要数据 - char buffer[BUFSIZE]; // 小缓冲区 - printf("Before overflow: secret = %d\n", secret); - char *fat p = &fat buffer[0]; - // 复制超长字符串,导致缓冲区溢出 - fat_strcpy(p, argv[1]); - printf("After overflow: secret = %d\n", secret); - return 0; -} -``` -当不使能胖指针,仍按照裸指针编译并运行时,可以观察到`secret`被篡改了: -``` -$ clang buffer_overflow.cbs -$ ./a.out lonstring -``` -打印内容如下 -``` -Before overflow: secret = 1 -After overflow: secret = 26478 // 不同目标平台,具体值可能变化 -``` -当入参长度更大一些时,可能由于溢出覆盖返回地址,导致程序崩溃。 - -若开启胖指针编译选项,再次编译运行,这一用例会触发胖指针的运行时报错机制: -``` -$ clang buffer_overflow.cbs -enable-fat-ptr -$ ./a.out lonstring -``` -此时,运行时报错信息如下: -``` -Error: Pointer offset exceeds the allocation size! -Error at: buffer_overflow.cbs:7 in fat_strcpy -``` - -#### Out of bounds -```C -// out_of_bounds.cbs -#include - -int main() { - int arr[5]; - int *fat p = &fat arr[0]; - for (int i = 0; i <= 5; i++) { - *p = i; - p = p + 1; - } - return 0; -} -``` -编译 out_of_bounds.cbs 并运行: -``` -$ clang out_of_bounds.cbs -enable-fat-ptr -$ ./a.out -``` -这一用例会触发胖指针的运行时报错机制: -``` -Error: Pointer offset exceeds the allocation size! -Error at: out_of_bounds.cbs:8 in main -``` - ### 用胖指针特性改写遗留 C 代码 用户可以很方便地使用胖指针特性对存量 C 代码进行改写,只需要: 1. 将代码中的裸指针类型`T*`修改为胖指针类型`T *fat` @@ -4585,30 +4507,6 @@ Error at: out_of_bounds.cbs:8 in main 3. 将代码中的内存管理函数`malloc`和`free`替换为`checked_malloc`和`checked_free` 4. 对于全局变量,如果想要使用`&fat`获取指向它的胖指针,在变量声明前加上`CHECKED`修饰 -如下是一个包含如上改写方法的简单示例: - -```C -#include - -CHECKED int arr[3]; - -typedef struct node Node; - -struct node { - int val; - Node *fat next; -}; - -int main() { - Node *fat node = (Node *fat)checked_malloc(sizeof(Node)); - node->val = 1; - int *fat p = &fat arr[0]; - *p = 10; - checked_free((void *fat)node); - return 0; -} -``` - 在实际使用的时候,可能会出现这样的情况,有些库是以二进制形式存在的,无法对它们重新编译,因此它们对应的头文件、函数签名、类型签名中的裸指针类型是不能修改的。**由于胖指针类型和裸指针类型之间不支持隐式转换,那么如果我们想要调用三方库中的函数,需要做类型强转,将胖指针强转为裸指针或将裸指针强转为胖指针**,例如: ```C #include "some_lib.h" @@ -4620,16 +4518,6 @@ void call_external_func(int *fat p) { p = (int *fat)external_func2(); } ``` -### 胖指针与ASan对比 -AddressSanitizer(ASan) 是一种在业界广泛使用的内存安全问题检测工具,相比之下,胖指针特性在以下几个维度更优: - -- 1、ASan 漏报概率较大,绕过检测的场景较多;胖指针检测能力更强,尤其是在被胖指针全面覆盖的区域; - -- 2、ASan 运行性能劣化严重,内存占用极大,容易触发程序 OOM;胖指针对内存额外消耗不超过1倍,运行时长也控制在平均1倍以内; - -- 3、ASan 由于存在虚拟地址空间,受到系统`ulimit`限制,也不支持可执行文件的静态链接;胖指针不存在此类限制。 - -**注意:** ASan 和胖指针都涉及对原生 malloc 的特异性改造,不建议编译时同时开启。 ## 标准库 @@ -6070,7 +5958,7 @@ int main() { | assign-owned | "assign to owned value: \`%0\`"
"assign to part of owned value: \`%0\`"
"assign to subfield owned value: \`%0\`, %1 owned"
还包括assign-moved-owned、assign-uninit-owned 错误类型标识可屏蔽的错误日志 | | cast-moved-owned | "invalid cast to `void * owned` of moved value: \`%0\`" | | cast-owned | "invalid cast to `void * owned` of owned value: \`%0\`"
"invalid cast to `void * owned` of uninit value: \`%0\`"
"invalid cast to `void * owned` of not all moved value: \`%0\`, %1 owned"
"invalid cast to `void * owned` of moved value:\`%0\`" | -| check-memory-leak | "field memory leak of value: \`%0\`, %1 leak"
"memory leak of value: \`%0\` | +| check-memory-leak | "field memory leak of value: `%0`, %1 leak"
"memory leak of value: `%0` | | bsc-ownership | 可屏蔽所有 owned 数据流分析过程的报错,
包括 use-owned、assign-owned、cast-owned、check-memory-leak 错误类型标识可屏蔽的错误日志。 | | assign-borrowed | "cannot assign to \`%0\` because it is borrowed" | | move-borrowed | "cannot move out of \`%0\` because it is borrowed" | -- Gitee From f873a603f87da9997483696c156ed82416785f85 Mon Sep 17 00:00:00 2001 From: zhaoxuhui Date: Fri, 21 Mar 2025 16:16:34 +0800 Subject: [PATCH 19/29] Revert "[fat ptr tool] Adjust some logic of checkers and tools" This reverts commit 70383ec3110b2ef3fb2158ec9c8e4878ab02010b. --- clang-tools-extra/clang-tidy/ClangTidy.cpp | 8 + .../clang-tidy/bsc/BSCTidyModule.cpp | 3 + .../clang-tidy/bsc/CMakeLists.txt | 1 + .../clang-tidy/bsc/ExplicitCastCheck.h | 5 +- .../clang-tidy/bsc/FatPtrCheck.cpp | 37 +++ .../clang-tidy/bsc/FatPtrCheck.h | 48 ++++ .../clang-tidy/bsc/FatPtrCollecterCheck.cpp | 58 ++--- .../clang-tidy/bsc/FatPtrCollecterCheck.h | 1 - .../clang-tidy/bsc/FatPtrTransformerCheck.cpp | 232 +++++++----------- .../clang-tidy/bsc/FatPtrTransformerCheck.h | 20 +- .../clang-tidy/tool/bsc/bsc-build.py | 42 ++-- .../clang-tidy/tool/bsc/fake_clang.py | 62 ++--- clang-tools-extra/docs/ReleaseNotes.rst | 5 + .../docs/clang-tidy/checks/bsc/fat-ptr.rst | 6 + .../docs/clang-tidy/checks/list.rst | 1 + .../test/clang-tidy/checkers/bsc/fat-ptr.cpp | 14 ++ 16 files changed, 290 insertions(+), 253 deletions(-) create mode 100644 clang-tools-extra/clang-tidy/bsc/FatPtrCheck.cpp create mode 100644 clang-tools-extra/clang-tidy/bsc/FatPtrCheck.h create mode 100644 clang-tools-extra/docs/clang-tidy/checks/bsc/fat-ptr.rst create mode 100644 clang-tools-extra/test/clang-tidy/checkers/bsc/fat-ptr.cpp diff --git a/clang-tools-extra/clang-tidy/ClangTidy.cpp b/clang-tools-extra/clang-tidy/ClangTidy.cpp index 5b6111610c9f..5d93e17ce31a 100644 --- a/clang-tools-extra/clang-tidy/ClangTidy.cpp +++ b/clang-tools-extra/clang-tidy/ClangTidy.cpp @@ -606,6 +606,14 @@ void handleErrors(llvm::ArrayRef Errors, // Situations if users want to disable clang diagnostic checker. std::string CheckName = Error.DiagnosticName; if (IsNoClangDiagnostic) { + // std::string CastChecker = "bsc-explicit-cast"; + // std::string AccessChecker = "bsc-access-specific-type"; + // if (CheckName.find(CastChecker) != std::string::npos || + // CheckName.find(AccessChecker) != std::string::npos) { + // llvm::outs() << "--------- flag begin ---------\n"; + // Reporter.reportDiagnostic(Error); + // llvm::outs() << "--------- flag end ---------\n"; + // } std::string BSCChecker = "bsc-"; if (CheckName.find(BSCChecker) != std::string::npos) Reporter.reportDiagnostic(Error); diff --git a/clang-tools-extra/clang-tidy/bsc/BSCTidyModule.cpp b/clang-tools-extra/clang-tidy/bsc/BSCTidyModule.cpp index 8e2016426fef..eefdba0bf4f8 100644 --- a/clang-tools-extra/clang-tidy/bsc/BSCTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/bsc/BSCTidyModule.cpp @@ -12,6 +12,7 @@ #include "AccessSpecificTypeCheck.h" #include "AddNewFieldCheck.h" #include "ExplicitCastCheck.h" +#include "FatPtrCheck.h" #include "FatPtrCollecterCheck.h" #include "FatPtrTransformerCheck.h" @@ -28,6 +29,8 @@ public: "bsc-add-new-field"); CheckFactories.registerCheck( "bsc-explicit-cast"); + CheckFactories.registerCheck( + "bsc-fat-ptr"); CheckFactories.registerCheck( "bsc-fat-ptr-collecter"); CheckFactories.registerCheck( diff --git a/clang-tools-extra/clang-tidy/bsc/CMakeLists.txt b/clang-tools-extra/clang-tidy/bsc/CMakeLists.txt index 1e1d5c3a1ff2..7c35b3fe2efe 100644 --- a/clang-tools-extra/clang-tidy/bsc/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/bsc/CMakeLists.txt @@ -9,6 +9,7 @@ add_clang_library(clangTidyBscModule AddNewFieldCheck.cpp BSCTidyModule.cpp ExplicitCastCheck.cpp + FatPtrCheck.cpp FatPtrCollecterCheck.cpp FatPtrTransformerCheck.cpp diff --git a/clang-tools-extra/clang-tidy/bsc/ExplicitCastCheck.h b/clang-tools-extra/clang-tidy/bsc/ExplicitCastCheck.h index 58cda5fe869c..0ba11401a406 100644 --- a/clang-tools-extra/clang-tidy/bsc/ExplicitCastCheck.h +++ b/clang-tools-extra/clang-tidy/bsc/ExplicitCastCheck.h @@ -15,7 +15,10 @@ namespace clang { namespace tidy { namespace bsc { -// Collect information from the specified file for bsc fat-ptr transformer. +/// FIXME: Write a short description. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/bsc/explicit-cast.html class ExplicitCastCheck : public ClangTidyCheck { public: ExplicitCastCheck(StringRef Name, ClangTidyContext *Context) diff --git a/clang-tools-extra/clang-tidy/bsc/FatPtrCheck.cpp b/clang-tools-extra/clang-tidy/bsc/FatPtrCheck.cpp new file mode 100644 index 000000000000..5d27c18884cf --- /dev/null +++ b/clang-tools-extra/clang-tidy/bsc/FatPtrCheck.cpp @@ -0,0 +1,37 @@ +//===--- FatPtrCheck.cpp - clang-tidy -------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "FatPtrCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace bsc { + +void FatPtrCheck::registerMatchers(MatchFinder *Finder) { + // FIXME: Add matchers. + Finder->addMatcher(functionDecl().bind("x"), this); +} + +void FatPtrCheck::check(const MatchFinder::MatchResult &Result) { + // FIXME: Add callback implementation. + const auto *MatchedDecl = Result.Nodes.getNodeAs("x"); + if (!MatchedDecl->getIdentifier() || MatchedDecl->getName().startswith("awesome_")) + return; + diag(MatchedDecl->getLocation(), "function %0 is insufficiently awesome") + << MatchedDecl; + diag(MatchedDecl->getLocation(), "insert 'awesome'", DiagnosticIDs::Note) + << FixItHint::CreateInsertion(MatchedDecl->getLocation(), "awesome_"); +} + +} // namespace bsc +} // namespace tidy +} // namespace clang diff --git a/clang-tools-extra/clang-tidy/bsc/FatPtrCheck.h b/clang-tools-extra/clang-tidy/bsc/FatPtrCheck.h new file mode 100644 index 000000000000..06a38a3a2fbf --- /dev/null +++ b/clang-tools-extra/clang-tidy/bsc/FatPtrCheck.h @@ -0,0 +1,48 @@ +//===--- FatPtrCheck.h - clang-tidy -----------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BSC_FATPTRCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BSC_FATPTRCHECK_H + +#include "../ClangTidyCheck.h" + +namespace clang { +namespace tidy { +namespace bsc { + +/// FIXME: Write a short description. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/bsc/fat-ptr.html +class FatPtrCheck : public ClangTidyCheck { +public: + FatPtrCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context), + TopLevelFunctionList(Options.get("TopLevelFunctions", "TEMP_FAILURE_RETRY")), + TopLevelVarList(Options.get("TopLevelVars", "TEMP_FAILURE_RETRY")) { + StringRef(TopLevelFunctionList).split(TopLevelFunctions, ",", -1, false); + StringRef(TopLevelVarList).split(TopLevelVars, ",", -1, false); + } + void storeOptions(ClangTidyOptions::OptionMap &Opts) { + Options.store(Opts, "TopLevelFunctions", TopLevelFunctionList); + Options.store(Opts, "TopLevelVars", TopLevelVarList); + } + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; +private: + const StringRef TopLevelFunctionList; + SmallVector TopLevelFunctions; + const StringRef TopLevelVarList; + SmallVector TopLevelVars; +}; + +} // namespace bsc +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BSC_FATPTRCHECK_H diff --git a/clang-tools-extra/clang-tidy/bsc/FatPtrCollecterCheck.cpp b/clang-tools-extra/clang-tidy/bsc/FatPtrCollecterCheck.cpp index ac7691a4aa17..bc9e4ba08123 100644 --- a/clang-tools-extra/clang-tidy/bsc/FatPtrCollecterCheck.cpp +++ b/clang-tools-extra/clang-tidy/bsc/FatPtrCollecterCheck.cpp @@ -6,17 +6,19 @@ // //===----------------------------------------------------------------------===// +#include +#include #include "FatPtrCollecterCheck.h" #include "clang/AST/ASTContext.h" -#include "clang/AST/BSC/DeclBSC.h" #include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/AST/BSC/DeclBSC.h" #include "clang/Tooling/CommonOptionsParser.h" -#include -#include -static llvm::cl::opt - CFGPath("cfg-path", llvm::cl::desc("Indicate the path of cfg file."), - llvm::cl::init("empty"), llvm::cl::Hidden); +static llvm::cl::opt CFGPath( + "cfg-path", + llvm::cl::desc( + "Indicate the path of cfg file."), + llvm::cl::init("empty"), llvm::cl::Hidden); using namespace clang::ast_matchers; @@ -24,20 +26,16 @@ namespace clang { namespace tidy { namespace bsc { -// Get the name of Decl, and save it into the '.cfg' file. static inline int SaveInCFG(const NamedDecl *ND) { if (CFGPath == "Empty") { - std::cerr - << "No cfg path received, please check input parameter `--cfg-path`" - << std::endl; + std::cerr << "No cfg path received, please check input parameter `--cfg-path`" << std::endl; return 0; } std::ofstream OutFile; OutFile.open(CFGPath, std::ios::out | std::ios::app); - if (!OutFile.is_open()) { - std::cerr << "can not open fat_ptr.cfg, fail to save toplevel items." - << std::endl; + if (!OutFile.is_open()) { + std::cerr << "can not open fat_ptr.cfg, fail to save toplevel items." << std::endl; return 0; } @@ -48,14 +46,14 @@ static inline int SaveInCFG(const NamedDecl *ND) { } // Check whether this Decl comes from Mainfile or header file. -static inline bool CheckFileId(const clang::SourceManager *SM, - FileID MainFileID, const NamedDecl *ND) { +static inline bool CheckFileId(const clang::SourceManager* SM, + FileID MainFileID, const NamedDecl *ND){ if (MainFileID.isInvalid()) { MainFileID = SM->getMainFileID(); if (MainFileID.isInvalid()) return 0; } - + FileID LocationID = SM->getFileID(SM->getSpellingLoc(ND->getLocation())); if (LocationID.isValid()) { if (MainFileID == LocationID) @@ -70,34 +68,28 @@ void FatPtrCollecterCheck::registerMatchers(MatchFinder *Finder) { auto AT = hasUnqualifiedDesugaredType(arrayType()); auto RT = hasUnqualifiedDesugaredType(recordType()); - Finder->addMatcher( - functionDecl(hasParent(translationUnitDecl()), hasBody(stmt())) - .bind("TopLevelFuncDecl"), - this); - Finder->addMatcher( - varDecl(hasGlobalStorage(), anyOf(hasType(PT), hasType(AT), hasType(RT))) - .bind("GlobalVar"), - this); + Finder->addMatcher(functionDecl(hasParent(translationUnitDecl()), hasBody(stmt())).bind("TopLevelFuncDecl"), this); + Finder->addMatcher(varDecl(hasGlobalStorage(), + anyOf(hasType(PT), hasType(AT), hasType(RT))).bind("GlobalVar"), this); } void FatPtrCollecterCheck::check(const MatchFinder::MatchResult &Result) { - const auto *TopLevelFuncDecl = - Result.Nodes.getNodeAs("TopLevelFuncDecl"); + const auto *TopLevelFuncDecl = Result.Nodes.getNodeAs("TopLevelFuncDecl"); const auto *GlobalVar = Result.Nodes.getNodeAs("GlobalVar"); - if (TopLevelFuncDecl) { + if (TopLevelFuncDecl){ const auto *BSCMD = dyn_cast_or_null(TopLevelFuncDecl); // Exclude 'Member Function' scenarios. if (!BSCMD) { // Exclude 'Extern' scenarios. if (TopLevelFuncDecl->getStorageClass() != SC_Extern) { - if (!CheckFileId(Result.SourceManager, MainFileID, TopLevelFuncDecl)) + if (!CheckFileId(Result.SourceManager, MainFileID, TopLevelFuncDecl)) return; // Save top-level function-name in cfg file. if (!SaveInCFG(TopLevelFuncDecl)) diag(TopLevelFuncDecl->getLocation(), - "Failed to save TopLevelFuncDecl: %0", DiagnosticIDs::Error) - << TopLevelFuncDecl; + "Failed to save TopLevelFuncDecl: %0", + DiagnosticIDs::Error) << TopLevelFuncDecl; } } } @@ -109,9 +101,9 @@ void FatPtrCollecterCheck::check(const MatchFinder::MatchResult &Result) { return; // Save top-level var-name in cfg file. if (!SaveInCFG(GlobalVar)) - diag(GlobalVar->getLocation(), "Failed to save GlobalVar: %0", - DiagnosticIDs::Error) - << GlobalVar; + diag(GlobalVar->getLocation(), + "Failed to save GlobalVar: %0", + DiagnosticIDs::Error) << GlobalVar; } } } diff --git a/clang-tools-extra/clang-tidy/bsc/FatPtrCollecterCheck.h b/clang-tools-extra/clang-tidy/bsc/FatPtrCollecterCheck.h index 65b55aec5e62..ccf9b797395b 100644 --- a/clang-tools-extra/clang-tidy/bsc/FatPtrCollecterCheck.h +++ b/clang-tools-extra/clang-tidy/bsc/FatPtrCollecterCheck.h @@ -25,7 +25,6 @@ public: : ClangTidyCheck(Name, Context) {} void registerMatchers(ast_matchers::MatchFinder *Finder) override; void check(const ast_matchers::MatchFinder::MatchResult &Result) override; - private: FileID MainFileID; }; diff --git a/clang-tools-extra/clang-tidy/bsc/FatPtrTransformerCheck.cpp b/clang-tools-extra/clang-tidy/bsc/FatPtrTransformerCheck.cpp index a989e7e46882..05cea3278f2f 100644 --- a/clang-tools-extra/clang-tidy/bsc/FatPtrTransformerCheck.cpp +++ b/clang-tools-extra/clang-tidy/bsc/FatPtrTransformerCheck.cpp @@ -17,23 +17,17 @@ namespace tidy { namespace bsc { void FatPtrTransformerCheck::AddFat(SourceLocation Loc) { - diag(Loc, "Add 'fat' token here", DiagnosticIDs::Warning) + diag(Loc, "Add 'fat' token here",DiagnosticIDs::Warning) << FixItHint::CreateInsertion(Loc, "fat "); } void FatPtrTransformerCheck::AddChecked(SourceLocation Loc) { - diag(Loc, "Add 'CHECKED' token here", DiagnosticIDs::Warning) + diag(Loc, "Add 'CHECKED' token here",DiagnosticIDs::Warning) << FixItHint::CreateInsertion(Loc, "CHECKED "); } -// When we use a typedef pointer type to declare a var, we won`t -// add `fat` token before it. -// e.g.: -// typedef int* ip; // add `fat` here -> typedef int* fat ip; -// ip p1 = &p; // not here -// So we will pass the vars which are declared by these kinds. void FatPtrTransformerCheck::CheckTypedefAndSetFat(const ValueDecl *D, - SourceManager *SM) { + SourceManager *SM) { QualType DType = D->getType(); if (!DType.getTypePtr()->getAs()) { AddHeader(SM); @@ -71,37 +65,33 @@ std::string FatPtrTransformerCheck::GetTypeName(const Expr *Arg) { } } -// Traverse a RecordDecl, to find out wheather the RecordDecl itself or -// it`s field (if the field is a record type) has an array-type field. -// When find a RecordDecl with array-type field, we will also store it -// into a buffer, so that we don`t need to traverse it next time. void FatPtrTransformerCheck::TraversalRecordDecl(RecordDecl *RD, bool *HasArrayField) { if (RD->isInvalidDecl()) return; - - for (FieldDecl *DeclField : RD->fields()) { - if (const auto *GRTY = dyn_cast_or_null( - DeclField->getType().getTypePtr())) { - if (const auto *RT = - dyn_cast_or_null(GRTY->getNamedType())) { + + for(FieldDecl* DeclField : RD->fields()) { + if (const auto *GRTY = dyn_cast_or_null + (DeclField->getType().getTypePtr())) { + if (const auto *RT = dyn_cast_or_null + (GRTY->getNamedType())) { if (FindInDeclList(HasArrayField, RT)) return; TraversalRecordDecl(RT->getDecl(), HasArrayField); } - } else if (const auto *RT = dyn_cast_or_null( - DeclField->getType().getTypePtr())) { + } else if (const auto *RT = dyn_cast_or_null + (DeclField->getType().getTypePtr())) { if (FindInDeclList(HasArrayField, RT)) - return; + return; TraversalRecordDecl(RT->getDecl(), HasArrayField); } - - if (*HasArrayField) { + + if(*HasArrayField) { DeclList.push_back(RD); return; } - - if (DeclField->getType()->isArrayType()) { + + if(DeclField->getType()->isArrayType()) { *HasArrayField = true; DeclList.push_back(RD); return; @@ -115,48 +105,34 @@ void FatPtrTransformerCheck::registerMatchers(MatchFinder *Finder) { auto RT = hasUnqualifiedDesugaredType(recordType()); for (auto TopLevelName : TopLevels) { + Finder->addMatcher(parmVarDecl(hasParent(functionDecl( + hasName(std::string(TopLevelName)))), + hasType(PT)).bind("PtParmVarDecl"), this); + Finder->addMatcher(functionDecl(hasParent(translationUnitDecl()), hasName(std::string(TopLevelName)), - returns(isAnyPointer())) - .bind("FuncDeclRtPt"), - this); + returns(isAnyPointer())).bind("FuncDeclRtPt"), this); Finder->addMatcher(callExpr(callee(functionDecl(hasName("malloc"))), - hasParent(castExpr().bind("MallocParentCast"))) - .bind("MallocCallCast"), - this); - Finder->addMatcher( - callExpr(callee(functionDecl(hasName("malloc"))), hasParent(varDecl())) - .bind("MallocCallVar"), - this); - Finder->addMatcher( - callExpr(callee(functionDecl(hasName("free")))).bind("FreecCall"), - this); - - Finder->addMatcher(varDecl(hasGlobalStorage(), - hasName(std::string(TopLevelName)), hasType(PT)) - .bind("GlobalPointerVar"), - this); - Finder->addMatcher(varDecl(hasGlobalStorage(), - hasName(std::string(TopLevelName)), - allOf(hasType(AT), unless(hasType(PT)))) - .bind("GlobalArrayVar"), - this); - Finder->addMatcher(varDecl(hasGlobalStorage(), - hasName(std::string(TopLevelName)), hasType(RT)) - .bind("GlobalRecordVar"), - this); - - Finder->addMatcher( - varDecl(hasAncestor(functionDecl(hasName(std::string(TopLevelName)))), - hasLocalStorage(), hasType(PT)) - .bind("LocalPointerVar"), - this); + hasParent(castExpr().bind("MallocParent"))) + .bind("MallocCall"), this); + Finder->addMatcher(callExpr(callee(functionDecl(hasName("free")))).bind("FreecCall"), this); + + Finder->addMatcher(varDecl(hasGlobalStorage(), + hasName(std::string(TopLevelName)), + hasType(PT)).bind("GlobalPointerVar"), this); + Finder->addMatcher(varDecl(hasGlobalStorage(), + hasName(std::string(TopLevelName)), + allOf(hasType(AT), unless(hasType(PT)))).bind("GlobalArrayVar"), this); + Finder->addMatcher(varDecl(hasGlobalStorage(), + hasName(std::string(TopLevelName)), + hasType(RT)).bind("GlobalRecordVar"), this); + + Finder->addMatcher(varDecl(hasAncestor(functionDecl(hasName(std::string(TopLevelName)))), + hasLocalStorage(), hasType(PT)).bind("LocalPointerVar"), this); Finder->addMatcher(typedefDecl(hasType(PT)).bind("TypedefPT"), this); - Finder->addMatcher( - unaryOperator(hasType(PT), hasOperatorName("&")).bind("UnOpAddr"), - this); + Finder->addMatcher(unaryOperator(hasType(PT), hasOperatorName("&")).bind("UnOpAddr"), this); Finder->addMatcher(cStyleCastExpr(hasType(PT)).bind("CCastExprPT"), this); } } @@ -165,121 +141,86 @@ void FatPtrTransformerCheck::check(const MatchFinder::MatchResult &Result) { if (MainFileID.isInvalid()) MainFileID = Result.SourceManager->getMainFileID(); - const auto *FuncDeclRtPt = - Result.Nodes.getNodeAs("FuncDeclRtPt"); + const auto *PtParmVarDecl = Result.Nodes.getNodeAs("PtParmVarDecl"); + const auto *FuncDeclRtPt = Result.Nodes.getNodeAs("FuncDeclRtPt"); - const auto *GlobalPointerVar = - Result.Nodes.getNodeAs("GlobalPointerVar"); - const auto *GlobalArrayVar = - Result.Nodes.getNodeAs("GlobalArrayVar"); - const auto *GlobalRecordVar = - Result.Nodes.getNodeAs("GlobalRecordVar"); - const auto *LocalPointerVar = - Result.Nodes.getNodeAs("LocalPointerVar"); + const auto *GlobalPointerVar = Result.Nodes.getNodeAs("GlobalPointerVar"); + const auto *GlobalArrayVar = Result.Nodes.getNodeAs("GlobalArrayVar"); + const auto *GlobalRecordVar = Result.Nodes.getNodeAs("GlobalRecordVar"); + const auto *LocalPointerVar = Result.Nodes.getNodeAs("LocalPointerVar"); const auto *TypedefPT = Result.Nodes.getNodeAs("TypedefPT"); const auto *UnOpAddr = Result.Nodes.getNodeAs("UnOpAddr"); - const auto *CCastExprPT = - Result.Nodes.getNodeAs("CCastExprPT"); + const auto *CCastExprPT = Result.Nodes.getNodeAs("CCastExprPT"); - const auto *MallocCallCast = - Result.Nodes.getNodeAs("MallocCallCast"); - const auto *MallocParentCast = - Result.Nodes.getNodeAs("MallocParentCast"); - - const auto *MallocCallVar = Result.Nodes.getNodeAs("MallocCallVar"); + const auto *MallocCall = Result.Nodes.getNodeAs("MallocCall"); + const auto *MallocParent = Result.Nodes.getNodeAs("MallocParent"); const auto *FreecCall = Result.Nodes.getNodeAs("FreecCall"); - - // Find AddrOf symbol '&'. + if (UnOpAddr) { if (UnOpAddr->getOpcode() == UnaryOperatorKind::UO_AddrOf) { AddHeader(Result.SourceManager); - AddFat(UnOpAddr->getBeginLoc().getLocWithOffset(1)); + AddFat(UnOpAddr->getEndLoc()); } } - // Find explicitly cast. if (CCastExprPT) { AddHeader(Result.SourceManager); AddFat(CCastExprPT->getRParenLoc()); } - // Find 'malloc()' call with 'non-void' type. - if (MallocCallCast) { - if (MallocParentCast) { - if (const clang::Type *CT = - dyn_cast(MallocParentCast->getType().getTypePtr())) { + if (PtParmVarDecl) { + diag(PtParmVarDecl->getBeginLoc(), "Find PtParmVarDecl here", DiagnosticIDs::Warning); + } + + if (MallocCall) { + if (MallocParent) { + if (const clang::Type *CT = dyn_cast(MallocParent->getType().getTypePtr())) { if (const PointerType *PT = CT->getAs()) { - std::string ReplaceStr; - if (MallocParentCast->getStmtClass() == Stmt::CStyleCastExprClass) { - ReplaceStr = "checked_malloc"; - } else { - ReplaceStr = "("; - ReplaceStr.append(PT->getPointeeType().getAsString()); - ReplaceStr.append(" *fat)checked_malloc"); - } - - SourceLocation BeginLoc = MallocCallCast->getExprLoc(); - SourceLocation EndLoc = MallocCallCast->getArg(0)->getExprLoc(); - SourceRange SR = SourceRange(BeginLoc, EndLoc.getLocWithOffset(-2)); + std::string ReplaceStr = "checked_malloc<"; + ReplaceStr.append(PT->getPointeeType().getAsString()); + ReplaceStr.append(">"); + SourceLocation BeginLoc = MallocCall->getExprLoc(); + SourceLocation EndLoc = MallocCall->getArg(0)->getExprLoc(); + SourceRange SR = SourceRange(BeginLoc, EndLoc.getLocWithOffset(-2)); diag(BeginLoc, "Find Malloc Call here", DiagnosticIDs::Warning) - << FixItHint::CreateReplacement(SR, ReplaceStr); + << FixItHint::CreateReplacement(SR, ReplaceStr); AddHeader(Result.SourceManager); } } } } - // Find 'malloc()' call with 'void *' type. - if (MallocCallVar) { - std::string ReplaceStr = "checked_malloc"; - - SourceLocation BeginLoc = MallocCallVar->getExprLoc(); - SourceLocation EndLoc = MallocCallVar->getArg(0)->getExprLoc(); - SourceRange SR = SourceRange(BeginLoc, EndLoc.getLocWithOffset(-2)); - - diag(BeginLoc, "Find Malloc Call here", DiagnosticIDs::Warning) - << FixItHint::CreateReplacement(SR, ReplaceStr); - AddHeader(Result.SourceManager); - } - - // Find 'free()' call. if (FreecCall) { - if (const CastExpr *Arg = - dyn_cast_or_null(FreecCall->getArg(0))) { - if (const clang::Type *CT = dyn_cast( - Arg->getSubExpr()->getType().getTypePtr())) { + if (const CastExpr *Arg = dyn_cast_or_null(FreecCall->getArg(0))) { + if (const clang::Type *CT = dyn_cast(Arg->getSubExpr()->getType().getTypePtr())) { if (const PointerType *PT = CT->getAs()) { - std::string ReplaceStr = "checked_free("; - if (Arg->getStmtClass() != Stmt::CStyleCastExprClass) { - ReplaceStr.append("(void *fat)"); - } + std::string ReplaceStr = "checked_free<"; + ReplaceStr.append(PT->getPointeeType().getAsString()); + ReplaceStr.append(">"); SourceLocation BeginLoc = FreecCall->getExprLoc(); SourceLocation EndLoc = FreecCall->getArg(0)->getExprLoc(); - SourceRange SR = SourceRange(BeginLoc, EndLoc.getLocWithOffset(-1)); + SourceRange SR = SourceRange(BeginLoc, EndLoc.getLocWithOffset(-2)); diag(BeginLoc, "Find Free Call here", DiagnosticIDs::Warning) - << FixItHint::CreateReplacement(SR, ReplaceStr); + << FixItHint::CreateReplacement(SR, ReplaceStr); AddHeader(Result.SourceManager); } } } } - // Find array-type vars with global declaration. - if (GlobalArrayVar) { - if (const auto *CAT = dyn_cast_or_null( - GlobalArrayVar->getType().getTypePtr())) { + if (GlobalArrayVar){ + if (const auto *CAT = dyn_cast_or_null + (GlobalArrayVar->getType().getTypePtr())) { AddHeader(Result.SourceManager); QualType VarType = CAT->getElementType(); - if (const TypedefType *TdefT = - dyn_cast_or_null(VarType.getTypePtr())) { + if (const TypedefType *TdefT = dyn_cast_or_null(VarType.getTypePtr())) { if (!TdefT->getAs()) AddChecked(GlobalArrayVar->getBeginLoc()); - } else if (const auto *PT = - dyn_cast_or_null(VarType.getTypePtr())) { + } else if (const auto *PT = dyn_cast_or_null(VarType.getTypePtr())) { AddFat(GlobalArrayVar->getLocation()); } else { AddChecked(GlobalArrayVar->getBeginLoc()); @@ -287,12 +228,11 @@ void FatPtrTransformerCheck::check(const MatchFinder::MatchResult &Result) { } } - // Find record-type vars with global declaration. - if (GlobalRecordVar) { - if (const auto *GRTY = dyn_cast_or_null( - GlobalRecordVar->getType().getTypePtr())) { - if (const auto *RT = - dyn_cast_or_null(GRTY->getNamedType())) { + if (GlobalRecordVar){ + if (const auto *GRTY = dyn_cast_or_null + (GlobalRecordVar->getType().getTypePtr())) { + if (const auto *RT = dyn_cast_or_null + (GRTY->getNamedType())) { bool HasArrayField = false; if (!FindInDeclList(&HasArrayField, RT)) TraversalRecordDecl(RT->getDecl(), &HasArrayField); @@ -305,23 +245,19 @@ void FatPtrTransformerCheck::check(const MatchFinder::MatchResult &Result) { } } - // Find pointer-type vars with global declaration. if (GlobalPointerVar) CheckTypedefAndSetFat(GlobalPointerVar, Result.SourceManager); - // Find pointer-type vars with local declaration. - if (LocalPointerVar) + if (LocalPointerVar) CheckTypedefAndSetFat(LocalPointerVar, Result.SourceManager); - // Find pointer-type vars with local declaration. if (FuncDeclRtPt) CheckTypedefAndSetFat(FuncDeclRtPt, Result.SourceManager); - // Find typedef with pointer type. if (TypedefPT) { - SourceLocation TypedefLoc = TypedefPT->getLocation(); - FileID LocationID = Result.SourceManager->getFileID( - Result.SourceManager->getSpellingLoc(TypedefLoc)); + SourceLocation TypedefLoc = TypedefPT->getLocation(); + FileID LocationID = Result.SourceManager->getFileID(Result.SourceManager-> + getSpellingLoc(TypedefLoc)); if (LocationID.isValid()) { if (MainFileID == LocationID) { AddHeader(Result.SourceManager); diff --git a/clang-tools-extra/clang-tidy/bsc/FatPtrTransformerCheck.h b/clang-tools-extra/clang-tidy/bsc/FatPtrTransformerCheck.h index 6341149fbae1..d4e37c9ff3c8 100644 --- a/clang-tools-extra/clang-tidy/bsc/FatPtrTransformerCheck.h +++ b/clang-tools-extra/clang-tidy/bsc/FatPtrTransformerCheck.h @@ -15,11 +15,14 @@ namespace clang { namespace tidy { namespace bsc { -// Transform the specified file with bsc fat-ptr. +/// FIXME: Write a short description. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/bsc/fat-ptr-transformer.html class FatPtrTransformerCheck : public ClangTidyCheck { public: FatPtrTransformerCheck(StringRef Name, ClangTidyContext *Context) - : ClangTidyCheck(Name, Context), + : ClangTidyCheck(Name, Context), TopLevelList(Options.get("TopLevels", "TEMP_FAILURE_RETRY")) { StringRef(TopLevelList).split(TopLevels, ",", -1, false); } @@ -28,7 +31,6 @@ public: } void registerMatchers(ast_matchers::MatchFinder *Finder) override; void check(const ast_matchers::MatchFinder::MatchResult &Result) override; - private: const StringRef TopLevelList; SmallVector TopLevels; @@ -36,14 +38,15 @@ private: bool NeedFix = false; bool AlreadyAddHeader = false; std::vector DeclList; - + void TraversalRecordDecl(RecordDecl *RD, bool *HasArrayField); void AddFat(SourceLocation Loc); void AddChecked(SourceLocation Loc); std::string GetTypeName(const Expr *Arg); void CheckTypedefAndSetFat(const ValueDecl *D, SourceManager *SM); - inline bool FindInDeclList(bool *HasArrayField, const clang::RecordType *RT) { + inline bool FindInDeclList(bool *HasArrayField, + const clang::RecordType* RT) { auto Existed = std::find(DeclList.begin(), DeclList.end(), RT->getDecl()); if (Existed != DeclList.end()) { *HasArrayField = true; @@ -54,11 +57,10 @@ private: inline void AddHeader(SourceManager *SM) { if (!AlreadyAddHeader) { - if (!MainFileID.isInvalid()) { + if (!MainFileID.isInvalid()){ SourceLocation StartOfFileLoc = SM->getLocForStartOfFile(MainFileID); - diag(StartOfFileLoc, "Add Header Here.", DiagnosticIDs::Warning) - << FixItHint::CreateInsertion(StartOfFileLoc, - "#include \n"); + diag(StartOfFileLoc, "Add Header Here.", DiagnosticIDs::Warning) << + FixItHint::CreateInsertion(StartOfFileLoc, "#include \n"); AlreadyAddHeader = true; } } diff --git a/clang-tools-extra/clang-tidy/tool/bsc/bsc-build.py b/clang-tools-extra/clang-tidy/tool/bsc/bsc-build.py index 33c01d985386..822a217f5309 100644 --- a/clang-tools-extra/clang-tidy/tool/bsc/bsc-build.py +++ b/clang-tools-extra/clang-tidy/tool/bsc/bsc-build.py @@ -5,14 +5,11 @@ import os import argparse import subprocess -# Make sure it`s the first time to create '.cfg' file. first_create_cfg = False -# Get the path of this python scrpit. bsc_build_path = os.path.dirname(os.path.abspath(__file__)) -# store all header files from source path. header_list = [] +h_modify_list = [] -# Run shell command. def excute_command(cmd: str, cmd_name: str = None): proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, shell=True) stdout_c, stderr_c = proc.communicate() @@ -20,7 +17,6 @@ def excute_command(cmd: str, cmd_name: str = None): print(" ===== " + cmd_name + " Return Code: ", proc.returncode , " ===== ") return proc.returncode -# Run shell command in a list way, and set new env vars if env is not None. def excute_cmd_list(cmd_list: list, env: dict = None): for cmd in cmd_list: cmd_list = [item for item in cmd.split(" ") if item.strip()] @@ -37,10 +33,10 @@ def excute_cmd_list(cmd_list: list, env: dict = None): proc_modify.wait() pass -# Gather information of top-level vars/funcs from source code files. def gather_top_levels(source_path: str, cfg_path: str): global bsc_build_path global header_list + global h_modify_list command_c = ( "find " + source_path + " -type f -name '*.c' " @@ -58,8 +54,14 @@ def gather_top_levels(source_path: str, cfg_path: str): ) excute_command(command_h, "Proc_h") - # # Find all header files and save into a list. - # # FIXME: Try to use SqlLite to save the information into a DB. + + command_h_modify = ( + "find " + source_path + " -type f -name '*.h' " + + " -exec " + bsc_build_path + "/clang-tidy " + + "--fix-errors --config-file==" + cfg_path + + " $file {} \;" + ) + h_modify_list.append(command_h_modify) find_h = ( "find " + source_path + " -type f -name '*.h' " ) @@ -83,14 +85,10 @@ def gather_top_levels(source_path: str, cfg_path: str): ) excute_command(command_hbs, "Proc_hbs") -# Backup sourece codes. def backup_source_code(source_path: str): command_backup = ("cp -r " + source_path + " " + source_path + "_backup") excute_command(command_backup, "Proc_backup") -# Backup system`s default compilers, like '/usr/bin/clang'. Then replace -# them with our fake compilers, by change the direction of the softlink. -# # FIXME: Add situation of other compilers, like 'gcc' or 'cc'. def backup_compiler_and_replace(fake_compiler: str): default_clang = "/usr/bin/clang" if os.path.exists(default_clang): @@ -102,10 +100,9 @@ def backup_compiler_and_replace(fake_compiler: str): excute_command(clang_replace, "Proc_clang_replace") else: print(" ===== Failed to backup " + default_clang + " ===== ") + pass -# Recover system`s default compilers with backup. -# # FIXME: Add situation of other compilers, like 'gcc' or 'cc'. def recover_compiler(): clang_backup = "/usr/bin/clang_backup" if os.path.exists(clang_backup): @@ -115,12 +112,10 @@ def recover_compiler(): clang_recover = "sudo mv -f /usr/bin/clang_backup /usr/bin/clang" excute_command(clang_recover, "Proc_clang_recover") -# Prepare the '.cfg' file, and gather information for it. def preparecfg(file_path: str): global first_create_cfg global bsc_build_path cfg_file = bsc_build_path + "/fat_ptr.cfg" - # Create file. if not first_create_cfg: content = ( 'Checks: -*, bsc-fat-ptr-transformer\n' 'CheckOptions:\n - key: ' @@ -130,7 +125,6 @@ def preparecfg(file_path: str): file.write(content) first_create_cfg = True - # Backup source codes, and gather information. paths = file_path.split(";") for path in paths: abs_source_path = os.path.abspath(path) @@ -186,11 +180,16 @@ def main(): env["CXX"] = fakecompiler_path + os.pathsep + env["CXX"] else: env["CXX"] = fakecompiler_path + + # # Modify headers + # # FIXME: We modify all header now, still need to complete the further logic, to modify the headers that only been used. + # # maybe not, it seems when we modify .c, the used .h will be modified too, need check again. + # excute_cmd_list(h_modify_list) if args.rewrite: print(" =========================== Active source to source after fat-ptr modify. =========================== ") env["REWRITE"] = "Enable" - # # FIXME: transform all file from .c/.h to .cbs/.hbs, then we can rewrite properly. + # # FIXME: transform all file from .c/.h to .cbs/.hbs, then we can rewrite # for file, path in header_dict.items(): # cmd = ( # bsc_build_path @@ -200,12 +199,12 @@ def main(): # ) # excute_command(cmd) - # # FIXME: complete softlink like '/usr/bin/cc'. + # # FIXME: complete /usr/bin/cc situation. # backup_compiler_and_replace(fakecompiler_path) excute_cmd_list(cmds, env) - # # FIXME: complete softlink like '/usr/bin/cc'. + # # FIXME: complete /usr/bin/cc situation. # recover_compiler() pass @@ -213,8 +212,7 @@ def main(): # # FIXME: complete or delete this situation. pass - # # Build the origin command with default compilers. - # # //FIXME: Finish this part after fixing the problems of rewrite. + # excute_cmd_list(cmds) diff --git a/clang-tools-extra/clang-tidy/tool/bsc/fake_clang.py b/clang-tools-extra/clang-tidy/tool/bsc/fake_clang.py index b62486ef6dec..4dba453f62b5 100644 --- a/clang-tools-extra/clang-tidy/tool/bsc/fake_clang.py +++ b/clang-tools-extra/clang-tidy/tool/bsc/fake_clang.py @@ -6,33 +6,27 @@ import re import argparse import subprocess -# Get the path of our clang-tidy. + def get_clang_tidy(): - clang_tidy_path = "" + clang_tidy_path = None current_file = os.path.abspath(__file__) if (os.path.islink(current_file)): clang_tidy_path = os.path.dirname(os.readlink(current_file)) + "/../" else: clang_tidy_path = os.path.dirname(current_file) + "/../" - + return clang_tidy_path -# Find the certain string from the command. -def find_matching_strings(pattern: str, strings: list): +def find_matching_strings(pattern, strings): matches = [s for s in strings if pattern.match(s)] return matches -# Add the certain options to the new command. -def add_options_to_cmd(cmd: str, options: list): - if options: - for opt in options: - cmd = " " + cmd + opt - return cmd - -# Modify the certain C-file. Add '-I' or '-D' -# options into the new command if exsits. -def transform_c_file(cfile: str, includes: list, - macros: list, clang_tidy_path: str): +def find_matching_includes(strings): + pattern = re.compile(r'\-I.*?') + matches = [s for s in strings if pattern.match(s)] + return matches + +def transform_c_file(cfile, includes, clang_tidy_path): if (clang_tidy_path): command_c = ( clang_tidy_path + "clang-tidy " @@ -40,31 +34,31 @@ def transform_c_file(cfile: str, includes: list, + clang_tidy_path + "fat_ptr.cfg " + cfile + " -header-filter=.* " ) - if includes or macros: + if includes: command_c += "-- " - command_c = add_options_to_cmd(command_c, includes) - command_c = add_options_to_cmd(command_c, macros) + for include in includes: + command_c = command_c + include + " " + pass proc_c = subprocess.Popen( command_c, text=True, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE ) - + stdout_c, stderr_c = proc_c.communicate() print("Proc_modify Return Code:", proc_c.returncode) pass -# Rewrite this C-file if the rewrite option is on. -def rewrite_c_file(cfile: str, includes: list, - macros: list, clang_tidy_path: str): +def rewrite_c_file(cfile, includes, clang_tidy_path): if (clang_tidy_path): cmd = ( clang_tidy_path + "clang -x" " bsc -rewrite-bsc " + cfile ) - if includes or macros: - cmd = add_options_to_cmd(cmd, includes) - cmd = add_options_to_cmd(cmd, macros) + if includes: + for include in includes: + cmd = cmd + " " + include + pass proc_c = subprocess.Popen( cmd, text=True, shell=True, @@ -78,32 +72,22 @@ def rewrite_c_file(cfile: str, includes: list, def main(): if len(sys.argv) > 1: - # Find C-file from the building command. pattern_c = re.compile(r'.*?\.c$') cfile = find_matching_strings(pattern_c, sys.argv) - # Find include option from the building command. pattern_i = re.compile(r'\-I.*?') includes = find_matching_strings(pattern_i, sys.argv) - # Find macro option from the building command. - pattern_d = re.compile(r'\-D.*?') - macros = find_matching_strings(pattern_d, sys.argv) - clang_tidy_path = get_clang_tidy() if (cfile): - # Modify C-file. - transform_c_file(cfile[0], includes, - macros, clang_tidy_path) + transform_c_file(cfile[0], includes, clang_tidy_path) - # Rewrite C-file. env_rewrite = os.environ.get("REWRITE") if env_rewrite and env_rewrite == "Enable": - rewrite_c_file(cfile[0], includes, - macros, clang_tidy_path) + rewrite_c_file(cfile[0], includes, clang_tidy_path) else: print("No command line arguments provided.") if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index 9100baa7bef0..67881817f449 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -159,6 +159,11 @@ New checks FIXME: add release notes. +- New :doc:`bsc-fat-ptr + ` check. + + FIXME: add release notes. + - New :doc:`bsc-fat-ptr-collecter ` check. diff --git a/clang-tools-extra/docs/clang-tidy/checks/bsc/fat-ptr.rst b/clang-tools-extra/docs/clang-tidy/checks/bsc/fat-ptr.rst new file mode 100644 index 000000000000..f0f92143cdbf --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/bsc/fat-ptr.rst @@ -0,0 +1,6 @@ +.. title:: clang-tidy - bsc-fat-ptr + +bsc-fat-ptr +=========== + +FIXME: Describe what patterns does the check detect and why. Give examples. diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst index b3c200d3176a..e51ef1bcbfd6 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/list.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -82,6 +82,7 @@ Clang-Tidy Checks `bsc-explicit-cast `_, `bsc-fat-ptr-collecter `_, `bsc-fat-ptr-transformer `_, "Yes" + `bsc-fat-ptr `_, "Yes" `bugprone-argument-comment `_, "Yes" `bugprone-assert-side-effect `_, `bugprone-assignment-in-if-condition `_, diff --git a/clang-tools-extra/test/clang-tidy/checkers/bsc/fat-ptr.cpp b/clang-tools-extra/test/clang-tidy/checkers/bsc/fat-ptr.cpp new file mode 100644 index 000000000000..9f34153f54d3 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/bsc/fat-ptr.cpp @@ -0,0 +1,14 @@ +// RUN: %check_clang_tidy %s bsc-fat-ptr %t + +// FIXME: Add something that triggers the check here. +void f(); +// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: function 'f' is insufficiently awesome [bsc-fat-ptr] + +// FIXME: Verify the applied fix. +// * Make the CHECK patterns specific enough and try to make verified lines +// unique to avoid incorrect matches. +// * Use {{}} for regular expressions. +// CHECK-FIXES: {{^}}void awesome_f();{{$}} + +// FIXME: Add something that doesn't trigger the check here. +void awesome_f2(); -- Gitee From a4b6cf61964a9fffe6ccdaf2474fd6d82e07a685 Mon Sep 17 00:00:00 2001 From: zhaoxuhui Date: Fri, 21 Mar 2025 16:16:48 +0800 Subject: [PATCH 20/29] Revert "[fat ptr tool] Add fat ptr modify tool." This reverts commit 0050994dc5039a572cb251f730c64d7d874e2ff1. --- clang-tools-extra/clang-tidy/ClangTidy.cpp | 17 +- .../clang-tidy/bsc/BSCTidyModule.cpp | 9 - .../clang-tidy/bsc/CMakeLists.txt | 3 - .../clang-tidy/bsc/FatPtrCheck.cpp | 37 --- .../clang-tidy/bsc/FatPtrCheck.h | 48 ---- .../clang-tidy/bsc/FatPtrCollecterCheck.cpp | 113 -------- .../clang-tidy/bsc/FatPtrCollecterCheck.h | 36 --- .../clang-tidy/bsc/FatPtrTransformerCheck.cpp | 271 ------------------ .../clang-tidy/bsc/FatPtrTransformerCheck.h | 74 ----- .../clang-tidy/tool/bsc/README.txt | 200 ------------- .../clang-tidy/tool/bsc/bsc-build.py | 221 -------------- .../clang-tidy/tool/bsc/fake_clang.py | 93 ------ clang-tools-extra/docs/ReleaseNotes.rst | 15 - .../checks/bsc/fat-ptr-collecter.rst | 6 - .../checks/bsc/fat-ptr-transformer.rst | 6 - .../docs/clang-tidy/checks/bsc/fat-ptr.rst | 6 - .../docs/clang-tidy/checks/list.rst | 5 +- .../checkers/bsc/fat-ptr-collecter.cpp | 14 - .../checkers/bsc/fat-ptr-transformer.cpp | 14 - .../test/clang-tidy/checkers/bsc/fat-ptr.cpp | 14 - 20 files changed, 8 insertions(+), 1194 deletions(-) delete mode 100644 clang-tools-extra/clang-tidy/bsc/FatPtrCheck.cpp delete mode 100644 clang-tools-extra/clang-tidy/bsc/FatPtrCheck.h delete mode 100644 clang-tools-extra/clang-tidy/bsc/FatPtrCollecterCheck.cpp delete mode 100644 clang-tools-extra/clang-tidy/bsc/FatPtrCollecterCheck.h delete mode 100644 clang-tools-extra/clang-tidy/bsc/FatPtrTransformerCheck.cpp delete mode 100644 clang-tools-extra/clang-tidy/bsc/FatPtrTransformerCheck.h delete mode 100644 clang-tools-extra/clang-tidy/tool/bsc/README.txt delete mode 100644 clang-tools-extra/clang-tidy/tool/bsc/bsc-build.py delete mode 100644 clang-tools-extra/clang-tidy/tool/bsc/fake_clang.py delete mode 100644 clang-tools-extra/docs/clang-tidy/checks/bsc/fat-ptr-collecter.rst delete mode 100644 clang-tools-extra/docs/clang-tidy/checks/bsc/fat-ptr-transformer.rst delete mode 100644 clang-tools-extra/docs/clang-tidy/checks/bsc/fat-ptr.rst delete mode 100644 clang-tools-extra/test/clang-tidy/checkers/bsc/fat-ptr-collecter.cpp delete mode 100644 clang-tools-extra/test/clang-tidy/checkers/bsc/fat-ptr-transformer.cpp delete mode 100644 clang-tools-extra/test/clang-tidy/checkers/bsc/fat-ptr.cpp diff --git a/clang-tools-extra/clang-tidy/ClangTidy.cpp b/clang-tools-extra/clang-tidy/ClangTidy.cpp index 5d93e17ce31a..f405ace6d7fa 100644 --- a/clang-tools-extra/clang-tidy/ClangTidy.cpp +++ b/clang-tools-extra/clang-tidy/ClangTidy.cpp @@ -606,17 +606,14 @@ void handleErrors(llvm::ArrayRef Errors, // Situations if users want to disable clang diagnostic checker. std::string CheckName = Error.DiagnosticName; if (IsNoClangDiagnostic) { - // std::string CastChecker = "bsc-explicit-cast"; - // std::string AccessChecker = "bsc-access-specific-type"; - // if (CheckName.find(CastChecker) != std::string::npos || - // CheckName.find(AccessChecker) != std::string::npos) { - // llvm::outs() << "--------- flag begin ---------\n"; - // Reporter.reportDiagnostic(Error); - // llvm::outs() << "--------- flag end ---------\n"; - // } - std::string BSCChecker = "bsc-"; - if (CheckName.find(BSCChecker) != std::string::npos) + std::string CastChecker = "bsc-explicit-cast"; + std::string AccessChecker = "bsc-access-specific-type"; + if (CheckName.find(CastChecker) != std::string::npos || + CheckName.find(AccessChecker) != std::string::npos) { + llvm::outs() << "--------- flag begin ---------\n"; Reporter.reportDiagnostic(Error); + llvm::outs() << "--------- flag end ---------\n"; + } } else { Reporter.reportDiagnostic(Error); } diff --git a/clang-tools-extra/clang-tidy/bsc/BSCTidyModule.cpp b/clang-tools-extra/clang-tidy/bsc/BSCTidyModule.cpp index eefdba0bf4f8..6a40e0b72f7a 100644 --- a/clang-tools-extra/clang-tidy/bsc/BSCTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/bsc/BSCTidyModule.cpp @@ -12,9 +12,6 @@ #include "AccessSpecificTypeCheck.h" #include "AddNewFieldCheck.h" #include "ExplicitCastCheck.h" -#include "FatPtrCheck.h" -#include "FatPtrCollecterCheck.h" -#include "FatPtrTransformerCheck.h" namespace clang { namespace tidy { @@ -29,12 +26,6 @@ public: "bsc-add-new-field"); CheckFactories.registerCheck( "bsc-explicit-cast"); - CheckFactories.registerCheck( - "bsc-fat-ptr"); - CheckFactories.registerCheck( - "bsc-fat-ptr-collecter"); - CheckFactories.registerCheck( - "bsc-fat-ptr-transformer"); } }; diff --git a/clang-tools-extra/clang-tidy/bsc/CMakeLists.txt b/clang-tools-extra/clang-tidy/bsc/CMakeLists.txt index 7c35b3fe2efe..6053d867b678 100644 --- a/clang-tools-extra/clang-tidy/bsc/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/bsc/CMakeLists.txt @@ -9,9 +9,6 @@ add_clang_library(clangTidyBscModule AddNewFieldCheck.cpp BSCTidyModule.cpp ExplicitCastCheck.cpp - FatPtrCheck.cpp - FatPtrCollecterCheck.cpp - FatPtrTransformerCheck.cpp LINK_LIBS clangTidy diff --git a/clang-tools-extra/clang-tidy/bsc/FatPtrCheck.cpp b/clang-tools-extra/clang-tidy/bsc/FatPtrCheck.cpp deleted file mode 100644 index 5d27c18884cf..000000000000 --- a/clang-tools-extra/clang-tidy/bsc/FatPtrCheck.cpp +++ /dev/null @@ -1,37 +0,0 @@ -//===--- FatPtrCheck.cpp - clang-tidy -------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#include "FatPtrCheck.h" -#include "clang/AST/ASTContext.h" -#include "clang/ASTMatchers/ASTMatchFinder.h" - -using namespace clang::ast_matchers; - -namespace clang { -namespace tidy { -namespace bsc { - -void FatPtrCheck::registerMatchers(MatchFinder *Finder) { - // FIXME: Add matchers. - Finder->addMatcher(functionDecl().bind("x"), this); -} - -void FatPtrCheck::check(const MatchFinder::MatchResult &Result) { - // FIXME: Add callback implementation. - const auto *MatchedDecl = Result.Nodes.getNodeAs("x"); - if (!MatchedDecl->getIdentifier() || MatchedDecl->getName().startswith("awesome_")) - return; - diag(MatchedDecl->getLocation(), "function %0 is insufficiently awesome") - << MatchedDecl; - diag(MatchedDecl->getLocation(), "insert 'awesome'", DiagnosticIDs::Note) - << FixItHint::CreateInsertion(MatchedDecl->getLocation(), "awesome_"); -} - -} // namespace bsc -} // namespace tidy -} // namespace clang diff --git a/clang-tools-extra/clang-tidy/bsc/FatPtrCheck.h b/clang-tools-extra/clang-tidy/bsc/FatPtrCheck.h deleted file mode 100644 index 06a38a3a2fbf..000000000000 --- a/clang-tools-extra/clang-tidy/bsc/FatPtrCheck.h +++ /dev/null @@ -1,48 +0,0 @@ -//===--- FatPtrCheck.h - clang-tidy -----------------------------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BSC_FATPTRCHECK_H -#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BSC_FATPTRCHECK_H - -#include "../ClangTidyCheck.h" - -namespace clang { -namespace tidy { -namespace bsc { - -/// FIXME: Write a short description. -/// -/// For the user-facing documentation see: -/// http://clang.llvm.org/extra/clang-tidy/checks/bsc/fat-ptr.html -class FatPtrCheck : public ClangTidyCheck { -public: - FatPtrCheck(StringRef Name, ClangTidyContext *Context) - : ClangTidyCheck(Name, Context), - TopLevelFunctionList(Options.get("TopLevelFunctions", "TEMP_FAILURE_RETRY")), - TopLevelVarList(Options.get("TopLevelVars", "TEMP_FAILURE_RETRY")) { - StringRef(TopLevelFunctionList).split(TopLevelFunctions, ",", -1, false); - StringRef(TopLevelVarList).split(TopLevelVars, ",", -1, false); - } - void storeOptions(ClangTidyOptions::OptionMap &Opts) { - Options.store(Opts, "TopLevelFunctions", TopLevelFunctionList); - Options.store(Opts, "TopLevelVars", TopLevelVarList); - } - void registerMatchers(ast_matchers::MatchFinder *Finder) override; - void check(const ast_matchers::MatchFinder::MatchResult &Result) override; -private: - const StringRef TopLevelFunctionList; - SmallVector TopLevelFunctions; - const StringRef TopLevelVarList; - SmallVector TopLevelVars; -}; - -} // namespace bsc -} // namespace tidy -} // namespace clang - -#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BSC_FATPTRCHECK_H diff --git a/clang-tools-extra/clang-tidy/bsc/FatPtrCollecterCheck.cpp b/clang-tools-extra/clang-tidy/bsc/FatPtrCollecterCheck.cpp deleted file mode 100644 index bc9e4ba08123..000000000000 --- a/clang-tools-extra/clang-tidy/bsc/FatPtrCollecterCheck.cpp +++ /dev/null @@ -1,113 +0,0 @@ -//===--- FatPtrCollecterCheck.cpp - clang-tidy ----------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#include -#include -#include "FatPtrCollecterCheck.h" -#include "clang/AST/ASTContext.h" -#include "clang/ASTMatchers/ASTMatchFinder.h" -#include "clang/AST/BSC/DeclBSC.h" -#include "clang/Tooling/CommonOptionsParser.h" - -static llvm::cl::opt CFGPath( - "cfg-path", - llvm::cl::desc( - "Indicate the path of cfg file."), - llvm::cl::init("empty"), llvm::cl::Hidden); - -using namespace clang::ast_matchers; - -namespace clang { -namespace tidy { -namespace bsc { - -static inline int SaveInCFG(const NamedDecl *ND) { - if (CFGPath == "Empty") { - std::cerr << "No cfg path received, please check input parameter `--cfg-path`" << std::endl; - return 0; - } - - std::ofstream OutFile; - OutFile.open(CFGPath, std::ios::out | std::ios::app); - if (!OutFile.is_open()) { - std::cerr << "can not open fat_ptr.cfg, fail to save toplevel items." << std::endl; - return 0; - } - - std::string Name = ND->getNameAsString(); - OutFile << "," << Name; - OutFile.close(); - return 1; -} - -// Check whether this Decl comes from Mainfile or header file. -static inline bool CheckFileId(const clang::SourceManager* SM, - FileID MainFileID, const NamedDecl *ND){ - if (MainFileID.isInvalid()) { - MainFileID = SM->getMainFileID(); - if (MainFileID.isInvalid()) - return 0; - } - - FileID LocationID = SM->getFileID(SM->getSpellingLoc(ND->getLocation())); - if (LocationID.isValid()) { - if (MainFileID == LocationID) - return 1; - } - - return 0; -} - -void FatPtrCollecterCheck::registerMatchers(MatchFinder *Finder) { - auto PT = hasUnqualifiedDesugaredType(pointerType()); - auto AT = hasUnqualifiedDesugaredType(arrayType()); - auto RT = hasUnqualifiedDesugaredType(recordType()); - - Finder->addMatcher(functionDecl(hasParent(translationUnitDecl()), hasBody(stmt())).bind("TopLevelFuncDecl"), this); - Finder->addMatcher(varDecl(hasGlobalStorage(), - anyOf(hasType(PT), hasType(AT), hasType(RT))).bind("GlobalVar"), this); -} - -void FatPtrCollecterCheck::check(const MatchFinder::MatchResult &Result) { - const auto *TopLevelFuncDecl = Result.Nodes.getNodeAs("TopLevelFuncDecl"); - const auto *GlobalVar = Result.Nodes.getNodeAs("GlobalVar"); - - if (TopLevelFuncDecl){ - const auto *BSCMD = dyn_cast_or_null(TopLevelFuncDecl); - // Exclude 'Member Function' scenarios. - if (!BSCMD) { - // Exclude 'Extern' scenarios. - if (TopLevelFuncDecl->getStorageClass() != SC_Extern) { - if (!CheckFileId(Result.SourceManager, MainFileID, TopLevelFuncDecl)) - return; - // Save top-level function-name in cfg file. - if (!SaveInCFG(TopLevelFuncDecl)) - diag(TopLevelFuncDecl->getLocation(), - "Failed to save TopLevelFuncDecl: %0", - DiagnosticIDs::Error) << TopLevelFuncDecl; - } - } - } - - if (GlobalVar) { - // Exclude 'Extern' scenarios. - if (!GlobalVar->hasExternalStorage()) { - if (!CheckFileId(Result.SourceManager, MainFileID, GlobalVar)) - return; - // Save top-level var-name in cfg file. - if (!SaveInCFG(GlobalVar)) - diag(GlobalVar->getLocation(), - "Failed to save GlobalVar: %0", - DiagnosticIDs::Error) << GlobalVar; - } - } -} - -} // namespace bsc -} // namespace tidy -} // namespace clang diff --git a/clang-tools-extra/clang-tidy/bsc/FatPtrCollecterCheck.h b/clang-tools-extra/clang-tidy/bsc/FatPtrCollecterCheck.h deleted file mode 100644 index ccf9b797395b..000000000000 --- a/clang-tools-extra/clang-tidy/bsc/FatPtrCollecterCheck.h +++ /dev/null @@ -1,36 +0,0 @@ -//===--- FatPtrCollecterCheck.h - clang-tidy --------------------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BSC_FATPTRCOLLECTERCHECK_H -#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BSC_FATPTRCOLLECTERCHECK_H - -#include "../ClangTidyCheck.h" - -namespace clang { -namespace tidy { -namespace bsc { - -/// FIXME: Write a short description. -/// -/// For the user-facing documentation see: -/// http://clang.llvm.org/extra/clang-tidy/checks/bsc/fat-ptr-collecter.html -class FatPtrCollecterCheck : public ClangTidyCheck { -public: - FatPtrCollecterCheck(StringRef Name, ClangTidyContext *Context) - : ClangTidyCheck(Name, Context) {} - void registerMatchers(ast_matchers::MatchFinder *Finder) override; - void check(const ast_matchers::MatchFinder::MatchResult &Result) override; -private: - FileID MainFileID; -}; - -} // namespace bsc -} // namespace tidy -} // namespace clang - -#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BSC_FATPTRCOLLECTERCHECK_H diff --git a/clang-tools-extra/clang-tidy/bsc/FatPtrTransformerCheck.cpp b/clang-tools-extra/clang-tidy/bsc/FatPtrTransformerCheck.cpp deleted file mode 100644 index 05cea3278f2f..000000000000 --- a/clang-tools-extra/clang-tidy/bsc/FatPtrTransformerCheck.cpp +++ /dev/null @@ -1,271 +0,0 @@ -//===--- FatPtrTransformerCheck.cpp - clang-tidy --------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#include "FatPtrTransformerCheck.h" -#include "clang/AST/ASTContext.h" -#include "clang/ASTMatchers/ASTMatchFinder.h" - -using namespace clang::ast_matchers; - -namespace clang { -namespace tidy { -namespace bsc { - -void FatPtrTransformerCheck::AddFat(SourceLocation Loc) { - diag(Loc, "Add 'fat' token here",DiagnosticIDs::Warning) - << FixItHint::CreateInsertion(Loc, "fat "); -} - -void FatPtrTransformerCheck::AddChecked(SourceLocation Loc) { - diag(Loc, "Add 'CHECKED' token here",DiagnosticIDs::Warning) - << FixItHint::CreateInsertion(Loc, "CHECKED "); -} - -void FatPtrTransformerCheck::CheckTypedefAndSetFat(const ValueDecl *D, - SourceManager *SM) { - QualType DType = D->getType(); - if (!DType.getTypePtr()->getAs()) { - AddHeader(SM); - AddFat(D->getLocation()); - } -} - -std::string FatPtrTransformerCheck::GetTypeName(const Expr *Arg) { - switch (Arg->getStmtClass()) { - case Stmt::UnaryExprOrTypeTraitExprClass: { - const auto *UEOTT = dyn_cast(Arg); - return UEOTT->getTypeOfArgument().getAsString(); - } - case Stmt::ImplicitCastExprClass: { - const auto *ImpCE = dyn_cast(Arg); - const auto *CSCE = dyn_cast(Arg); - return GetTypeName(ImpCE->getSubExpr()); - } - case Stmt::CallExprClass: { - const auto *CE = dyn_cast(Arg); - return CE->getType().getAsString(); - } - case Stmt::IntegerLiteralClass: { - const auto *IL = dyn_cast(Arg); - return IL->getType().getAsString(); - } - case Stmt::DeclRefExprClass: { - const auto *DRE = dyn_cast(Arg); - return DRE->getType().getAsString(); - } - default: { - llvm_unreachable("NYI"); - return "Unknow Type"; - } - } -} - -void FatPtrTransformerCheck::TraversalRecordDecl(RecordDecl *RD, - bool *HasArrayField) { - if (RD->isInvalidDecl()) - return; - - for(FieldDecl* DeclField : RD->fields()) { - if (const auto *GRTY = dyn_cast_or_null - (DeclField->getType().getTypePtr())) { - if (const auto *RT = dyn_cast_or_null - (GRTY->getNamedType())) { - if (FindInDeclList(HasArrayField, RT)) - return; - TraversalRecordDecl(RT->getDecl(), HasArrayField); - } - } else if (const auto *RT = dyn_cast_or_null - (DeclField->getType().getTypePtr())) { - if (FindInDeclList(HasArrayField, RT)) - return; - TraversalRecordDecl(RT->getDecl(), HasArrayField); - } - - if(*HasArrayField) { - DeclList.push_back(RD); - return; - } - - if(DeclField->getType()->isArrayType()) { - *HasArrayField = true; - DeclList.push_back(RD); - return; - } - } -} - -void FatPtrTransformerCheck::registerMatchers(MatchFinder *Finder) { - auto PT = hasUnqualifiedDesugaredType(pointerType()); - auto AT = hasUnqualifiedDesugaredType(arrayType()); - auto RT = hasUnqualifiedDesugaredType(recordType()); - - for (auto TopLevelName : TopLevels) { - Finder->addMatcher(parmVarDecl(hasParent(functionDecl( - hasName(std::string(TopLevelName)))), - hasType(PT)).bind("PtParmVarDecl"), this); - - Finder->addMatcher(functionDecl(hasParent(translationUnitDecl()), - hasName(std::string(TopLevelName)), - returns(isAnyPointer())).bind("FuncDeclRtPt"), this); - - Finder->addMatcher(callExpr(callee(functionDecl(hasName("malloc"))), - hasParent(castExpr().bind("MallocParent"))) - .bind("MallocCall"), this); - Finder->addMatcher(callExpr(callee(functionDecl(hasName("free")))).bind("FreecCall"), this); - - Finder->addMatcher(varDecl(hasGlobalStorage(), - hasName(std::string(TopLevelName)), - hasType(PT)).bind("GlobalPointerVar"), this); - Finder->addMatcher(varDecl(hasGlobalStorage(), - hasName(std::string(TopLevelName)), - allOf(hasType(AT), unless(hasType(PT)))).bind("GlobalArrayVar"), this); - Finder->addMatcher(varDecl(hasGlobalStorage(), - hasName(std::string(TopLevelName)), - hasType(RT)).bind("GlobalRecordVar"), this); - - Finder->addMatcher(varDecl(hasAncestor(functionDecl(hasName(std::string(TopLevelName)))), - hasLocalStorage(), hasType(PT)).bind("LocalPointerVar"), this); - - Finder->addMatcher(typedefDecl(hasType(PT)).bind("TypedefPT"), this); - Finder->addMatcher(unaryOperator(hasType(PT), hasOperatorName("&")).bind("UnOpAddr"), this); - Finder->addMatcher(cStyleCastExpr(hasType(PT)).bind("CCastExprPT"), this); - } -} - -void FatPtrTransformerCheck::check(const MatchFinder::MatchResult &Result) { - if (MainFileID.isInvalid()) - MainFileID = Result.SourceManager->getMainFileID(); - - const auto *PtParmVarDecl = Result.Nodes.getNodeAs("PtParmVarDecl"); - const auto *FuncDeclRtPt = Result.Nodes.getNodeAs("FuncDeclRtPt"); - - const auto *GlobalPointerVar = Result.Nodes.getNodeAs("GlobalPointerVar"); - const auto *GlobalArrayVar = Result.Nodes.getNodeAs("GlobalArrayVar"); - const auto *GlobalRecordVar = Result.Nodes.getNodeAs("GlobalRecordVar"); - const auto *LocalPointerVar = Result.Nodes.getNodeAs("LocalPointerVar"); - - const auto *TypedefPT = Result.Nodes.getNodeAs("TypedefPT"); - const auto *UnOpAddr = Result.Nodes.getNodeAs("UnOpAddr"); - const auto *CCastExprPT = Result.Nodes.getNodeAs("CCastExprPT"); - - const auto *MallocCall = Result.Nodes.getNodeAs("MallocCall"); - const auto *MallocParent = Result.Nodes.getNodeAs("MallocParent"); - - const auto *FreecCall = Result.Nodes.getNodeAs("FreecCall"); - - if (UnOpAddr) { - if (UnOpAddr->getOpcode() == UnaryOperatorKind::UO_AddrOf) { - AddHeader(Result.SourceManager); - AddFat(UnOpAddr->getEndLoc()); - } - } - - if (CCastExprPT) { - AddHeader(Result.SourceManager); - AddFat(CCastExprPT->getRParenLoc()); - } - - if (PtParmVarDecl) { - diag(PtParmVarDecl->getBeginLoc(), "Find PtParmVarDecl here", DiagnosticIDs::Warning); - } - - if (MallocCall) { - if (MallocParent) { - if (const clang::Type *CT = dyn_cast(MallocParent->getType().getTypePtr())) { - if (const PointerType *PT = CT->getAs()) { - std::string ReplaceStr = "checked_malloc<"; - ReplaceStr.append(PT->getPointeeType().getAsString()); - ReplaceStr.append(">"); - - SourceLocation BeginLoc = MallocCall->getExprLoc(); - SourceLocation EndLoc = MallocCall->getArg(0)->getExprLoc(); - SourceRange SR = SourceRange(BeginLoc, EndLoc.getLocWithOffset(-2)); - diag(BeginLoc, "Find Malloc Call here", DiagnosticIDs::Warning) - << FixItHint::CreateReplacement(SR, ReplaceStr); - AddHeader(Result.SourceManager); - } - } - } - } - - if (FreecCall) { - if (const CastExpr *Arg = dyn_cast_or_null(FreecCall->getArg(0))) { - if (const clang::Type *CT = dyn_cast(Arg->getSubExpr()->getType().getTypePtr())) { - if (const PointerType *PT = CT->getAs()) { - std::string ReplaceStr = "checked_free<"; - ReplaceStr.append(PT->getPointeeType().getAsString()); - ReplaceStr.append(">"); - - SourceLocation BeginLoc = FreecCall->getExprLoc(); - SourceLocation EndLoc = FreecCall->getArg(0)->getExprLoc(); - SourceRange SR = SourceRange(BeginLoc, EndLoc.getLocWithOffset(-2)); - diag(BeginLoc, "Find Free Call here", DiagnosticIDs::Warning) - << FixItHint::CreateReplacement(SR, ReplaceStr); - AddHeader(Result.SourceManager); - } - } - } - } - - if (GlobalArrayVar){ - if (const auto *CAT = dyn_cast_or_null - (GlobalArrayVar->getType().getTypePtr())) { - AddHeader(Result.SourceManager); - QualType VarType = CAT->getElementType(); - if (const TypedefType *TdefT = dyn_cast_or_null(VarType.getTypePtr())) { - if (!TdefT->getAs()) - AddChecked(GlobalArrayVar->getBeginLoc()); - } else if (const auto *PT = dyn_cast_or_null(VarType.getTypePtr())) { - AddFat(GlobalArrayVar->getLocation()); - } else { - AddChecked(GlobalArrayVar->getBeginLoc()); - } - } - } - - if (GlobalRecordVar){ - if (const auto *GRTY = dyn_cast_or_null - (GlobalRecordVar->getType().getTypePtr())) { - if (const auto *RT = dyn_cast_or_null - (GRTY->getNamedType())) { - bool HasArrayField = false; - if (!FindInDeclList(&HasArrayField, RT)) - TraversalRecordDecl(RT->getDecl(), &HasArrayField); - - if (HasArrayField) { - AddChecked(GlobalRecordVar->getBeginLoc()); - AddHeader(Result.SourceManager); - } - } - } - } - - if (GlobalPointerVar) - CheckTypedefAndSetFat(GlobalPointerVar, Result.SourceManager); - - if (LocalPointerVar) - CheckTypedefAndSetFat(LocalPointerVar, Result.SourceManager); - - if (FuncDeclRtPt) - CheckTypedefAndSetFat(FuncDeclRtPt, Result.SourceManager); - - if (TypedefPT) { - SourceLocation TypedefLoc = TypedefPT->getLocation(); - FileID LocationID = Result.SourceManager->getFileID(Result.SourceManager-> - getSpellingLoc(TypedefLoc)); - if (LocationID.isValid()) { - if (MainFileID == LocationID) { - AddHeader(Result.SourceManager); - AddFat(TypedefPT->getLocation()); - } - } - } -} -} // namespace bsc -} // namespace tidy -} // namespace clang diff --git a/clang-tools-extra/clang-tidy/bsc/FatPtrTransformerCheck.h b/clang-tools-extra/clang-tidy/bsc/FatPtrTransformerCheck.h deleted file mode 100644 index d4e37c9ff3c8..000000000000 --- a/clang-tools-extra/clang-tidy/bsc/FatPtrTransformerCheck.h +++ /dev/null @@ -1,74 +0,0 @@ -//===--- FatPtrTransformerCheck.h - clang-tidy ------------------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BSC_FATPTRTRANSFORMERCHECK_H -#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BSC_FATPTRTRANSFORMERCHECK_H - -#include "../ClangTidyCheck.h" - -namespace clang { -namespace tidy { -namespace bsc { - -/// FIXME: Write a short description. -/// -/// For the user-facing documentation see: -/// http://clang.llvm.org/extra/clang-tidy/checks/bsc/fat-ptr-transformer.html -class FatPtrTransformerCheck : public ClangTidyCheck { -public: - FatPtrTransformerCheck(StringRef Name, ClangTidyContext *Context) - : ClangTidyCheck(Name, Context), - TopLevelList(Options.get("TopLevels", "TEMP_FAILURE_RETRY")) { - StringRef(TopLevelList).split(TopLevels, ",", -1, false); - } - void storeOptions(ClangTidyOptions::OptionMap &Opts) { - Options.store(Opts, "TopLevels", TopLevelList); - } - void registerMatchers(ast_matchers::MatchFinder *Finder) override; - void check(const ast_matchers::MatchFinder::MatchResult &Result) override; -private: - const StringRef TopLevelList; - SmallVector TopLevels; - FileID MainFileID; - bool NeedFix = false; - bool AlreadyAddHeader = false; - std::vector DeclList; - - void TraversalRecordDecl(RecordDecl *RD, bool *HasArrayField); - void AddFat(SourceLocation Loc); - void AddChecked(SourceLocation Loc); - std::string GetTypeName(const Expr *Arg); - void CheckTypedefAndSetFat(const ValueDecl *D, SourceManager *SM); - - inline bool FindInDeclList(bool *HasArrayField, - const clang::RecordType* RT) { - auto Existed = std::find(DeclList.begin(), DeclList.end(), RT->getDecl()); - if (Existed != DeclList.end()) { - *HasArrayField = true; - return true; - } - return false; - } - - inline void AddHeader(SourceManager *SM) { - if (!AlreadyAddHeader) { - if (!MainFileID.isInvalid()){ - SourceLocation StartOfFileLoc = SM->getLocForStartOfFile(MainFileID); - diag(StartOfFileLoc, "Add Header Here.", DiagnosticIDs::Warning) << - FixItHint::CreateInsertion(StartOfFileLoc, "#include \n"); - AlreadyAddHeader = true; - } - } - } -}; - -} // namespace bsc -} // namespace tidy -} // namespace clang - -#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BSC_FATPTRTRANSFORMERCHECK_H diff --git a/clang-tools-extra/clang-tidy/tool/bsc/README.txt b/clang-tools-extra/clang-tidy/tool/bsc/README.txt deleted file mode 100644 index 584b038edc6d..000000000000 --- a/clang-tools-extra/clang-tidy/tool/bsc/README.txt +++ /dev/null @@ -1,200 +0,0 @@ -//===----------------------------------------------------------------------===// -// BSC Tools repository -//===----------------------------------------------------------------------===// - -============================================================================ -Welcome to the repository of extra BSC Tools. This repository holds tools -that are developed as part of the BSC compiler`s one-stop retrofit tool. - -These tools can help uses to retrofit their C-code with some new features -of the BSC compiler easily, such as `fat-ptr` and 'source-to-source', and -the cost of this kind retrofit would be small. -============================================================================ - -For internal use, if you want to try it in the project source code, -not in the released version, you can try the following steps: - -1. Copy 'bsc-build.py' to $your-path/llvm-project/build/bin, and remove -the '.py' suffix. Then grant executable permission to it. - -Cmd: -''' - cp $your-path/llvm-project/clang-tools-extra/clang-tidy/tool/bsc/bsc-build.py $your-path/llvm-project/build/bin/bsc-build - chmod +x $your-path/llvm-project/build/bin/bsc-build -``` - - -2. Copy 'fake_clang.py' to $your-path/llvm-project/build/bin/fake_compiler/, -and remove the '.py' suffix. Then grant executable permission to it. - -Cmd: -``` - mkdir $your-path/llvm-project/build/bin/fake_compiler/ - cp $your-path/llvm-project/clang-tools-extra/clang-tidy/tool/bsc/fake_clang.py $your-path/llvm-project/build/bin/fake_compiler/clang - chmod +x $your-path/llvm-project/build/bin/fake_compiler/clang -``` - - -3. Run 'bsc-build' in $your-path/llvm-project/build/bin: - The options of bsc-build: - --file-path : - The path of the source code which to be retrofitted. If there are - more than one path, use ';' to separate them, without blank space. - You can use both absolute and relative paths. - e.g.: --file-path=./fat_ptr/test_fat.c - - --build-after-modify : - The origin command to build your project. Use semicolons to warp - them insidek, and it is ok to have blank space in it. If there are - more than one command, use '|' to separate them. - e.g.: ---build-after-modify='which clang | clang ./fat_ptr/test_fat.c' - -Cmd: -``` - $your-path/llvm-project/build/bin/bsc-build --file-path=$source_file_path --build-after-modify='origin_build_cmd_1 | origin_build_cmd_2' -``` - - -===================== Demos ===================== - -===----- test_fat.h -----=== -``` -int Aa = 1; - -struct S { - int arr[3]; -}; - -struct A { - struct S s; - int a; -}; - -struct B { - int b; -}; - -void test_1(); -void test_2(int *i1, struct S* s1); - -void char_test(char *c1) {} -``` - - -===----- test_fat.c -----=== -``` -#include "test_fat.h" -#include - -typedef int* intp; // typedef int* fat intp -typedef struct S* spt; // typedef struct S* fat spt - -typedef int in; // ignore -intp ip; // ignore -spt sp; // ignore - -extern int GlobalVar; // ignore -extern int *GlobalPtr; // ignore -extern void greet(); // ignore - -int p = 1; -float f = 1.5; - -const int *p0; // const int *fat p0 -int * const p1 = &p; // int * const fat p1 - -int *p2; // int *fat p2 -int *p3 = &p; // int *fat p3 = &fat p -int *p4[5]; // int *fat p4[] - -intp p5; // ignore -intp p6[5]; // ignore -spt sp7[5]; // ignore - -float *f1; // float *fat f1 -float *f2 = &f; // float *fat f2 = &fat f - -int *p7 = (int*)&f; // int *fat p7 = (int*fat )&fat f - -char *c1; // char *fat c1 -char *c2 = "c"; // char *fat c2 = "c" todo: char *fat c2 = (char *fat )"c" - -in Arr0[3]; // CHECKED -int Arr1[3]; // CHECKED -int Arr2[3] = {}; // CHECKED -int Arr3[3] = { 10, 20, 30 }; // CHECKED - -// 搞一个缓存,避免多次遍历相同类型的结构体 -struct S s1; // CHECKED -struct S s2 = {}; // CHECKED -struct S s3 = {.arr = {10, 10, 10}}; // CHECKED - -struct A a1; // CHECKED -struct A a2 = {}; // CHECKED -struct A a3 = {.a = 1}; // CHECKED - -struct B b1; // ignore -struct B b2 = {}; // ignore -struct B b3 = {.b = 1}; // ignore - -struct S* sp1 = &s1; // struct S* fat sp1 = &fat s1 -struct A* ap1 = &a1; // struct A* fat ap1 = &fat a1 - -void test_2(int *i1, struct S *s1) {} // void test_2(int *fat i1, struct S *fat s1) - -int* test_3(intp i2) {} // int*fat test_3(intp i2) - -intp test_4(int a1) { - return &a1; // return &fat a1; -} - -int test_5() { // ignore - return 32; -} - -int main() { - - typedef float* flop; // typedef float* fat flop - - static struct A a4; // CHECKED - static struct A a5 = {}; // CHECKED - static struct A a6 = {.a = 1}; // CHECKED - - int a = 1; - int *P1 = &a; // int *fat P1 = &fat a - - int B = 10; - int *P2 = &B; // int *fat P2 = &fat B - - int *P3 = (int *)malloc(sizeof(int)); // int *fat P3 = checked_malloc(sizeof(int)) - P3 = (int *)malloc(sizeof(int)); // P3 = checked_malloc(sizeof(int)) - - float *P4 = (float *)malloc(sizeof(float)); // float *fat P4 = checked_malloc(sizeof(float)) - struct S *P5 = (struct S *)malloc(sizeof(struct S)); // struct S *fat P5 = checked_malloc(sizeof(struct S)) - - int *P6 = (int *)malloc(32); // int *fat P6 = checked_malloc(32) - unsigned aa = 32; - int *P7 = (int *)malloc(aa); // int *fat P7 = checked_malloc(aa) - int *P8 = (int *)malloc(test_5()); // int *fat P8 = checked_malloc(test_5()) - - int *P9 = (int *)malloc(sizeof(int)); // int *fat P9 = (int *fat )checked_malloc(sizeof(int)) - - intp P10 = (int *)malloc(aa); // intp P10 = checked_malloc(aa) - - int *P11; // int* fat P11 - P11 = (int *)&aa; // P11 = &fat aa - spt P12; - P12 = &s1; // P12 = &fat a1 - - - free(P3); // checked_free(P3) - free((void *)P4); // checked_free((void *fat)P4) - free(P10); // checked_free(P10) - free(test_3(P11)); // checked_free(test_3(P11)) - free(test_4(aa)); // checked_free(test_4(aa)) - free(P5); // checked_free(P5) - - return 0; -} - -``` \ No newline at end of file diff --git a/clang-tools-extra/clang-tidy/tool/bsc/bsc-build.py b/clang-tools-extra/clang-tidy/tool/bsc/bsc-build.py deleted file mode 100644 index 822a217f5309..000000000000 --- a/clang-tools-extra/clang-tidy/tool/bsc/bsc-build.py +++ /dev/null @@ -1,221 +0,0 @@ -#!/usr/bin/env python3 - -import sys -import os -import argparse -import subprocess - -first_create_cfg = False -bsc_build_path = os.path.dirname(os.path.abspath(__file__)) -header_list = [] -h_modify_list = [] - -def excute_command(cmd: str, cmd_name: str = None): - proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, shell=True) - stdout_c, stderr_c = proc.communicate() - if cmd_name: - print(" ===== " + cmd_name + " Return Code: ", proc.returncode , " ===== ") - return proc.returncode - -def excute_cmd_list(cmd_list: list, env: dict = None): - for cmd in cmd_list: - cmd_list = [item for item in cmd.split(" ") if item.strip()] - if env: - proc_modify = subprocess.Popen( - cmd_list, - env=env - ) - proc_modify.wait() - else: - proc_modify = subprocess.Popen( - cmd_list - ) - proc_modify.wait() - pass - -def gather_top_levels(source_path: str, cfg_path: str): - global bsc_build_path - global header_list - global h_modify_list - - command_c = ( - "find " + source_path + " -type f -name '*.c' " - + " -exec " + bsc_build_path + "/clang-tidy " - + "--no-clang-diagnostic --cfg-path=" + cfg_path - + " -checks='bsc-fat-ptr-collecter' $file {} \;" - ) - excute_command(command_c, "Proc_c") - - command_h = ( - "find " + source_path + " -type f -name '*.h' " - + " -exec " + bsc_build_path + "/clang-tidy " - + "--no-clang-diagnostic --cfg-path=" + cfg_path - + " -checks='bsc-fat-ptr-collecter' $file {} \;" - ) - excute_command(command_h, "Proc_h") - - - command_h_modify = ( - "find " + source_path + " -type f -name '*.h' " - + " -exec " + bsc_build_path + "/clang-tidy " - + "--fix-errors --config-file==" + cfg_path - + " $file {} \;" - ) - h_modify_list.append(command_h_modify) - find_h = ( - "find " + source_path + " -type f -name '*.h' " - ) - proc = subprocess.Popen(find_h, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, shell=True) - stdout_c, stderr_c = proc.communicate() - header_list += stdout_c.split("\n")[:-1] - - command_cbs = ( - "find " + source_path + " -type f -name '*.cbs' " - + " -exec " + bsc_build_path + "/clang-tidy " - + "--no-clang-diagnostic --cfg-path=" + cfg_path - + " -checks='bsc-fat-ptr-collecter' $file {} \;" - ) - excute_command(command_cbs, "Proc_cbs") - - command_hbs = ( - "find " + source_path + " -type f -name '*.hbs' " - + " -exec " + bsc_build_path + "/clang-tidy " - + "--no-clang-diagnostic --cfg-path=" + cfg_path - + " -checks='bsc-fat-ptr-collecter' $file {} \;" - ) - excute_command(command_hbs, "Proc_hbs") - -def backup_source_code(source_path: str): - command_backup = ("cp -r " + source_path + " " + source_path + "_backup") - excute_command(command_backup, "Proc_backup") - -def backup_compiler_and_replace(fake_compiler: str): - default_clang = "/usr/bin/clang" - if os.path.exists(default_clang): - clang_backup = "sudo cp -a " + default_clang + " " + default_clang + "_backup" - isbackup = excute_command(clang_backup, "Proc_clang_backup") - - if isbackup==0: - clang_replace = "sudo ln -snf " + fake_compiler + "/clang " + default_clang - excute_command(clang_replace, "Proc_clang_replace") - else: - print(" ===== Failed to backup " + default_clang + " ===== ") - - pass - -def recover_compiler(): - clang_backup = "/usr/bin/clang_backup" - if os.path.exists(clang_backup): - clang_clean = "sudo rm /usr/bin/clang" - excute_command(clang_clean, "Proc_clang_clean") - - clang_recover = "sudo mv -f /usr/bin/clang_backup /usr/bin/clang" - excute_command(clang_recover, "Proc_clang_recover") - -def preparecfg(file_path: str): - global first_create_cfg - global bsc_build_path - cfg_file = bsc_build_path + "/fat_ptr.cfg" - if not first_create_cfg: - content = ( 'Checks: -*, bsc-fat-ptr-transformer\n' - 'CheckOptions:\n - key: ' - 'bsc-fat-ptr-transformer.TopLevels\n' - ' value: "0') - with open(cfg_file, 'w') as file: - file.write(content) - first_create_cfg = True - - paths = file_path.split(";") - for path in paths: - abs_source_path = os.path.abspath(path) - backup_source_code(abs_source_path) - gather_top_levels(abs_source_path, cfg_file) - - with open(cfg_file, 'a') as file: - file.write('"') - print(" =========================== Prepare fat_ptr.cfg done =========================== ") - -def main(): - parser = argparse.ArgumentParser(description='Get the path of source code files.') - parser.add_argument('--file-path', type=str, default=None, dest='file_path', - help='Specify the path of your source code, use ";" to split them if more than one.') - parser.add_argument('--fat-ptr', action='store_true', - dest='fat_ptr', help='Active fat ptr modification.') - parser.add_argument('--translate-to-c', action='store_true', - dest='rewrite', help='Rewrite the modified source code to C code.') - parser.add_argument('--build-after-modify', type=str, default=None, dest='build_after_modify', - help='Build the program by the origin building command after modifination.\n If there are' - ' more than one command, use "|" to split them. e.g.: --cmake build xxx -xxx | make xxx') - parser.add_argument('--h', action='store_true', help='show the information of the options') - args = parser.parse_args() - - if not args.file_path: - sys.stderr.write(f"Error: file path is None. Use '-file-path=xxx' to indicate it\n") - return - - preparecfg(args.file_path) - - if args.build_after_modify: - global bsc_build_path - cmds = args.build_after_modify.split("|") - - if args.fat_ptr: - global header_list - header_dict = {} - for header in header_list: - header_path = os.path.dirname(os.path.abspath(header)) - header_dict[header] = header_path - - print(" =========================== Active fat ptr modify. =========================== ") - - fakecompiler_path = bsc_build_path + "/fake_compiler" - env = os.environ.copy() - env["PATH"] = fakecompiler_path + os.pathsep + env["PATH"] - if "CC" in os.environ: - env["CC"] = fakecompiler_path + os.pathsep + env["CC"] - else: - env["CC"] = fakecompiler_path - - if "CXX" in os.environ: - env["CXX"] = fakecompiler_path + os.pathsep + env["CXX"] - else: - env["CXX"] = fakecompiler_path - - # # Modify headers - # # FIXME: We modify all header now, still need to complete the further logic, to modify the headers that only been used. - # # maybe not, it seems when we modify .c, the used .h will be modified too, need check again. - # excute_cmd_list(h_modify_list) - - if args.rewrite: - print(" =========================== Active source to source after fat-ptr modify. =========================== ") - env["REWRITE"] = "Enable" - # # FIXME: transform all file from .c/.h to .cbs/.hbs, then we can rewrite - # for file, path in header_dict.items(): - # cmd = ( - # bsc_build_path - # + "/clang -x bsc " - # + "-rewrtie-bsc " - # + file + " -o " + path - # ) - # excute_command(cmd) - - # # FIXME: complete /usr/bin/cc situation. - # backup_compiler_and_replace(fakecompiler_path) - - excute_cmd_list(cmds, env) - - # # FIXME: complete /usr/bin/cc situation. - # recover_compiler() - pass - - if args.rewrite and not args.fat_ptr: - # # FIXME: complete or delete this situation. - pass - - - # excute_cmd_list(cmds) - - - -if __name__ == '__main__': - main() \ No newline at end of file diff --git a/clang-tools-extra/clang-tidy/tool/bsc/fake_clang.py b/clang-tools-extra/clang-tidy/tool/bsc/fake_clang.py deleted file mode 100644 index 4dba453f62b5..000000000000 --- a/clang-tools-extra/clang-tidy/tool/bsc/fake_clang.py +++ /dev/null @@ -1,93 +0,0 @@ -#!/usr/bin/env python3 - -import sys -import os -import re -import argparse -import subprocess - - -def get_clang_tidy(): - clang_tidy_path = None - current_file = os.path.abspath(__file__) - if (os.path.islink(current_file)): - clang_tidy_path = os.path.dirname(os.readlink(current_file)) + "/../" - else: - clang_tidy_path = os.path.dirname(current_file) + "/../" - - return clang_tidy_path - -def find_matching_strings(pattern, strings): - matches = [s for s in strings if pattern.match(s)] - return matches - -def find_matching_includes(strings): - pattern = re.compile(r'\-I.*?') - matches = [s for s in strings if pattern.match(s)] - return matches - -def transform_c_file(cfile, includes, clang_tidy_path): - if (clang_tidy_path): - command_c = ( - clang_tidy_path + "clang-tidy " - "--fix-errors --config-file=" - + clang_tidy_path + "fat_ptr.cfg " - + cfile + " -header-filter=.* " - ) - if includes: - command_c += "-- " - for include in includes: - command_c = command_c + include + " " - pass - - proc_c = subprocess.Popen( - command_c, text=True, shell=True, - stdout=subprocess.PIPE, stderr=subprocess.PIPE - ) - - stdout_c, stderr_c = proc_c.communicate() - print("Proc_modify Return Code:", proc_c.returncode) - pass - -def rewrite_c_file(cfile, includes, clang_tidy_path): - if (clang_tidy_path): - cmd = ( - clang_tidy_path + "clang -x" - " bsc -rewrite-bsc " + cfile - ) - if includes: - for include in includes: - cmd = cmd + " " + include - pass - - proc_c = subprocess.Popen( - cmd, text=True, shell=True, - stdout=subprocess.PIPE, stderr=subprocess.PIPE - ) - - stdout_c, stderr_c = proc_c.communicate() - print("Proc_rewrite Return Code:", proc_c.returncode, ", " + cfile) - pass - pass - -def main(): - if len(sys.argv) > 1: - pattern_c = re.compile(r'.*?\.c$') - cfile = find_matching_strings(pattern_c, sys.argv) - - pattern_i = re.compile(r'\-I.*?') - includes = find_matching_strings(pattern_i, sys.argv) - - clang_tidy_path = get_clang_tidy() - if (cfile): - transform_c_file(cfile[0], includes, clang_tidy_path) - - env_rewrite = os.environ.get("REWRITE") - if env_rewrite and env_rewrite == "Enable": - rewrite_c_file(cfile[0], includes, clang_tidy_path) - else: - print("No command line arguments provided.") - - -if __name__ == "__main__": - main() diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index 67881817f449..7de001b406b8 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -159,21 +159,6 @@ New checks FIXME: add release notes. -- New :doc:`bsc-fat-ptr - ` check. - - FIXME: add release notes. - -- New :doc:`bsc-fat-ptr-collecter - ` check. - - FIXME: add release notes. - -- New :doc:`bsc-fat-ptr-transformer - ` check. - - FIXME: add release notes. - - New :doc:`bugprone-shared-ptr-array-mismatch ` check. Finds initializations of C++ shared pointers to non-array type that are initialized with an array. diff --git a/clang-tools-extra/docs/clang-tidy/checks/bsc/fat-ptr-collecter.rst b/clang-tools-extra/docs/clang-tidy/checks/bsc/fat-ptr-collecter.rst deleted file mode 100644 index 80cdea7bd617..000000000000 --- a/clang-tools-extra/docs/clang-tidy/checks/bsc/fat-ptr-collecter.rst +++ /dev/null @@ -1,6 +0,0 @@ -.. title:: clang-tidy - bsc-fat-ptr-collecter - -bsc-fat-ptr-collecter -===================== - -FIXME: Describe what patterns does the check detect and why. Give examples. diff --git a/clang-tools-extra/docs/clang-tidy/checks/bsc/fat-ptr-transformer.rst b/clang-tools-extra/docs/clang-tidy/checks/bsc/fat-ptr-transformer.rst deleted file mode 100644 index 37478d0760d3..000000000000 --- a/clang-tools-extra/docs/clang-tidy/checks/bsc/fat-ptr-transformer.rst +++ /dev/null @@ -1,6 +0,0 @@ -.. title:: clang-tidy - bsc-fat-ptr-transformer - -bsc-fat-ptr-transformer -======================= - -FIXME: Describe what patterns does the check detect and why. Give examples. diff --git a/clang-tools-extra/docs/clang-tidy/checks/bsc/fat-ptr.rst b/clang-tools-extra/docs/clang-tidy/checks/bsc/fat-ptr.rst deleted file mode 100644 index f0f92143cdbf..000000000000 --- a/clang-tools-extra/docs/clang-tidy/checks/bsc/fat-ptr.rst +++ /dev/null @@ -1,6 +0,0 @@ -.. title:: clang-tidy - bsc-fat-ptr - -bsc-fat-ptr -=========== - -FIXME: Describe what patterns does the check detect and why. Give examples. diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst index e51ef1bcbfd6..df2aedc61575 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/list.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -77,12 +77,9 @@ Clang-Tidy Checks `android-cloexec-socket `_, "Yes" `android-comparison-in-temp-failure-retry `_, `boost-use-to-string `_, "Yes" - `bsc-access-specific-type `_, + `bsc-access-specific-type `_, "Yes" `bsc-add-new-field `_, "Yes" `bsc-explicit-cast `_, - `bsc-fat-ptr-collecter `_, - `bsc-fat-ptr-transformer `_, "Yes" - `bsc-fat-ptr `_, "Yes" `bugprone-argument-comment `_, "Yes" `bugprone-assert-side-effect `_, `bugprone-assignment-in-if-condition `_, diff --git a/clang-tools-extra/test/clang-tidy/checkers/bsc/fat-ptr-collecter.cpp b/clang-tools-extra/test/clang-tidy/checkers/bsc/fat-ptr-collecter.cpp deleted file mode 100644 index 6c9fb682cacb..000000000000 --- a/clang-tools-extra/test/clang-tidy/checkers/bsc/fat-ptr-collecter.cpp +++ /dev/null @@ -1,14 +0,0 @@ -// RUN: %check_clang_tidy %s bsc-fat-ptr-collecter %t - -// FIXME: Add something that triggers the check here. -void f(); -// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: function 'f' is insufficiently awesome [bsc-fat-ptr-collecter] - -// FIXME: Verify the applied fix. -// * Make the CHECK patterns specific enough and try to make verified lines -// unique to avoid incorrect matches. -// * Use {{}} for regular expressions. -// CHECK-FIXES: {{^}}void awesome_f();{{$}} - -// FIXME: Add something that doesn't trigger the check here. -void awesome_f2(); diff --git a/clang-tools-extra/test/clang-tidy/checkers/bsc/fat-ptr-transformer.cpp b/clang-tools-extra/test/clang-tidy/checkers/bsc/fat-ptr-transformer.cpp deleted file mode 100644 index a1212205950f..000000000000 --- a/clang-tools-extra/test/clang-tidy/checkers/bsc/fat-ptr-transformer.cpp +++ /dev/null @@ -1,14 +0,0 @@ -// RUN: %check_clang_tidy %s bsc-fat-ptr-transformer %t - -// FIXME: Add something that triggers the check here. -void f(); -// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: function 'f' is insufficiently awesome [bsc-fat-ptr-transformer] - -// FIXME: Verify the applied fix. -// * Make the CHECK patterns specific enough and try to make verified lines -// unique to avoid incorrect matches. -// * Use {{}} for regular expressions. -// CHECK-FIXES: {{^}}void awesome_f();{{$}} - -// FIXME: Add something that doesn't trigger the check here. -void awesome_f2(); diff --git a/clang-tools-extra/test/clang-tidy/checkers/bsc/fat-ptr.cpp b/clang-tools-extra/test/clang-tidy/checkers/bsc/fat-ptr.cpp deleted file mode 100644 index 9f34153f54d3..000000000000 --- a/clang-tools-extra/test/clang-tidy/checkers/bsc/fat-ptr.cpp +++ /dev/null @@ -1,14 +0,0 @@ -// RUN: %check_clang_tidy %s bsc-fat-ptr %t - -// FIXME: Add something that triggers the check here. -void f(); -// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: function 'f' is insufficiently awesome [bsc-fat-ptr] - -// FIXME: Verify the applied fix. -// * Make the CHECK patterns specific enough and try to make verified lines -// unique to avoid incorrect matches. -// * Use {{}} for regular expressions. -// CHECK-FIXES: {{^}}void awesome_f();{{$}} - -// FIXME: Add something that doesn't trigger the check here. -void awesome_f2(); -- Gitee From cff2a27623c4c0955cfb6089ba701bce417b5556 Mon Sep 17 00:00:00 2001 From: zhaoxuhui Date: Fri, 21 Mar 2025 16:17:02 +0800 Subject: [PATCH 21/29] Revert "[FatPtr]add user manual for fat ptr" This reverts commit 211c818f0b41d9e732ad676660f821908aec29d3. --- clang/docs/BiShengCLanguageUserManual.md | 146 ----------------------- 1 file changed, 146 deletions(-) diff --git a/clang/docs/BiShengCLanguageUserManual.md b/clang/docs/BiShengCLanguageUserManual.md index ab5636c3a025..7ade6b5507b5 100644 --- a/clang/docs/BiShengCLanguageUserManual.md +++ b/clang/docs/BiShengCLanguageUserManual.md @@ -4373,152 +4373,6 @@ safe void nullptrTest(void) { ``` 对于上面这个示例,当编译选项`-nullability-check`不存在或者`-nullability-check=safeonly`时,只有在`safe`区的`error2`会被报告;当`-nullability-check=none`时,`error1`和`error2`均不会被报告;当`-nullability-check=all`时,非安全区的`error1`和安全区`error2`均会被报告。 -## 胖指针 -为了增强非安全区的内存安全检查能力,BiShengC 提供了胖指针特性,可以帮助用户在开发阶段检测 use after free 等时间类和 out of bounds 等空间类内存安全问题,一旦出现这些问题,会在程序**运行时**报错。 - -使用`fat`关键字修饰一个指针类型,表明该指针是胖指针,由于胖指针在裸指针的基础上添加了一些 metadata 信息,所以会比裸指针多占用 8 个字节的内存。胖指针支持大部分裸指针所支持的操作,如解引用、成员访问、索引等,因此用户可以很方便地使用胖指针特性对存量 C 代码进行改写。 - -胖指针可以指向一块堆内存,也可以指向栈内存和全局区,以下的代码分别展示了胖指针指向这三类内存: - -```c -#include // BiShengC 语言提供的头文件,使用胖指针特性时必须 include 该头文件 - -// 1. 堆内存场景: -void point_to_heap() { - // checked_malloc用于分配一块堆内存,并返回一个指向这块堆内存的胖指针: - int *fat p = (int *fat)checked_malloc(sizeof(int)); - // 通过 checked_malloc 分配的内存必须通过checked_free 来释放: - checked_free((void *fat)p); -} - -// 2. 栈内存场景: -void point_to_stack() { - // &fat 运算符类似于 & 运算符,用于获取地址并得到一个指向该地址的胖指针: - int local = 10; - int local_arr[3] = {1, 2, 3}; - // 胖指针可以指向普通的局部变量: - int *fat p1 = &fat local; - // 胖指针也可以指向局部数组中的元素: - int *fat p2 = &fat local_arr[0]; - // 和裸指针类似,胖指针支持解引用、索引等操作: - *p1 = 20; - p2[1] = 20; -} - -// 3. 全局区场景: -// 可以用 CHECKED 修饰数组类型的全局变量,只有 CHECKED 修饰的全局变量可以通过 &fat 来获取指向它的胖指针 -CHECKED int global_arr[3] = {1, 2, 3}; -void point_to_global_var() { - int *fat p = &fat global_arr[0]; - *p = 20; -} -``` -在编译使用了胖指针特性的 cbs 文件时,必须使用`-enable-fat-ptr`这一编译选项来使能胖指针特性,否则毕昇 C 编译器会将胖指针当作裸指针来编译,无法起到检测内存安全问题的功能。 - -### 胖指针在运行时检测内存安全问题 -胖指针特性可以检测的**时间类**内存安全问题包括: -1. Double free -2. Use after free -3. Use after return -胖指针特性可以检测的**空间类**内存安全问题包括: -1. Out of bounds - -#### Double free -```C -// double_free.cbs -#include -int main(int argc, char **argv) { - int *fat ptr = (int *fat)checked_malloc(sizeof(int)); - if (argv[1][0] == '1') - checked_free((void *fat)ptr); - checked_free((void *fat)ptr); - return 0; -} -``` -编译 double_free.cbs 并运行: -``` -clang double_free.cbs -enable-fat-ptr -./a.out 1 -``` -这一用例会触发胖指针的运行时报错机制: -``` -Error: Cannot free this pointer because the allocation may have been freed! -``` - -#### Use after free -```C -// use_after_free.cbs -#include -int main(int argc, char **argv) { - int *fat ptr = (int *fat)checked_malloc(sizeof(int)); - *ptr = 10; - if (argv[1][0] == '1') - checked_free((void *fat)ptr); - printf("%d", *ptr); - return 0; -} -``` -编译 use_after_free.cbs 并运行: -``` -clang use_after_free.cbs -enable-fat-ptr -./a.out 1 -``` -这一用例会触发胖指针的运行时报错机制: -``` -Error: Cannot use this pointer because the allocation may have been freed or reseored! -``` - -#### Use after return -```C -// use_after_return.cbs -#include - -int *fat return_fat(char **argv) { - int *fat ptr = nullptr; - int local = 10; - if (argv[1][0] == '1') { - ptr = &fat local; - } else { - ptr = (int *fat)checked_malloc(sizeof(int)); - } - return ptr; -} - -int main(int argc, char **argv) { - int *fat p = return_fat(); - *p = 10; - return 0; -} -``` -编译 use_after_return.cbs 并运行: -``` -clang use_after_return.cbs -enable-fat-ptr -./a.out 1 -``` -这一用例会触发胖指针的运行时报错机制: -``` -Error: Cannot use this pointer because the allocation may have been freed or reseored! -``` - -### 用胖指针特性改写遗留 C 代码 -用户可以很方便地使用胖指针特性对存量 C 代码进行改写,只需要: -1. 将代码中的裸指针类型`T*`修改为胖指针类型`T *fat` -2. 将代码中的取地址运算符`&`修改为`&fat` -3. 将代码中的内存管理函数`malloc`和`free`替换为`checked_malloc`和`checked_free` -4. 对于全局变量,如果想要使用`&fat`获取指向它的胖指针,在变量声明前加上`CHECKED`修饰 - -在实际使用的时候,可能会出现这样的情况,有些库是以二进制形式存在的,无法对它们重新编译,因此它们对应的头文件、函数签名、类型签名中的裸指针类型是不能修改的。**由于胖指针类型和裸指针类型之间不支持隐式转换,那么如果我们想要调用三方库中的函数,需要做类型强转,将胖指针强转为裸指针或将裸指针强转为胖指针**,例如: -```C -#include "some_lib.h" -// 假设 some_lib.h 是第三方 C 库,无法用毕昇 C 的胖指针特性改造,它里面有两个函数,函数签名分别是: -// void external_func1(int *); 它需要裸指针作为参数 -// int * external_func2(); 它的返回类型是裸指针 -void call_external_func(int *fat p) { - external_func1((int *)p); - p = (int *fat)external_func2(); -} -``` - ## 标准库 ### 安全 API -- Gitee From 7ec7526df29fe13a2701476c61d0ac3093c68603 Mon Sep 17 00:00:00 2001 From: zhaoxuhui Date: Fri, 21 Mar 2025 16:19:15 +0800 Subject: [PATCH 22/29] Revert "[FatPtr]support rewrite to C for fat ptr" This reverts commit 330dfc2b6114a011b7b6217c92321fae92c11557. --- clang/include/clang/AST/ASTContext.h | 2 +- clang/lib/AST/ASTContext.cpp | 2 +- clang/lib/AST/DeclPrinter.cpp | 6 - clang/lib/AST/StmtPrinter.cpp | 32 +-- clang/lib/AST/TypePrinter.cpp | 6 - clang/lib/Frontend/Rewrite/RewriteBSC.cpp | 47 ---- clang/lib/Headers/bsc_include/bsc_fat_ptr.hbs | 15 ++ clang/lib/Sema/BSC/SemaBSCFatPtr.cpp | 205 +++++++++--------- clang/lib/Sema/SemaExpr.cpp | 3 +- .../global_array/array_out_of_bounds1.cbs | 7 +- .../global_array/array_out_of_bounds2.cbs | 7 +- .../heap/array_out_of_bounds1.cbs | 8 +- .../heap/array_out_of_bounds10.cbs | 7 +- .../heap/array_out_of_bounds11.cbs | 7 +- .../heap/array_out_of_bounds12.cbs | 7 +- .../heap/array_out_of_bounds13.cbs | 7 +- .../heap/array_out_of_bounds14.cbs | 7 +- .../heap/array_out_of_bounds15.cbs | 7 +- .../heap/array_out_of_bounds16.cbs | 7 +- .../heap/array_out_of_bounds17.cbs | 7 +- .../heap/array_out_of_bounds18.cbs | 7 +- .../heap/array_out_of_bounds19.cbs | 7 +- .../heap/array_out_of_bounds2.cbs | 7 +- .../heap/array_out_of_bounds20.cbs | 7 +- .../heap/array_out_of_bounds21.cbs | 7 +- .../heap/array_out_of_bounds22.cbs | 7 +- .../heap/array_out_of_bounds23.cbs | 7 +- .../heap/array_out_of_bounds24.cbs | 7 +- .../heap/array_out_of_bounds3.cbs | 7 +- .../heap/array_out_of_bounds4.cbs | 7 +- .../heap/array_out_of_bounds5.cbs | 7 +- .../heap/array_out_of_bounds6.cbs | 7 +- .../heap/array_out_of_bounds7.cbs | 7 +- .../heap/array_out_of_bounds8.cbs | 7 +- .../heap/array_out_of_bounds9.cbs | 7 +- .../heap/buffer_overflow.cbs | 7 +- .../heap/double_free1.cbs | 7 +- .../heap/double_free2.cbs | 7 +- .../heap/double_free3.cbs | 7 +- .../heap/double_free4.cbs | 7 +- .../heap/use_after_free1.cbs | 7 +- .../heap/use_after_free2.cbs | 7 +- .../heap/use_after_free3.cbs | 7 +- .../heap/use_after_free4.cbs | 7 +- .../heap/use_after_free5.cbs | 7 +- .../heap/use_after_free6.cbs | 7 +- .../heap/use_after_free7.cbs | 7 +- .../heap/use_after_free8.cbs | 7 +- .../stack/array_out_of_bounds1.cbs | 7 +- .../stack/array_out_of_bounds2.cbs | 7 +- .../stack/array_out_of_bounds3.cbs | 7 +- .../stack/array_out_of_bounds4.cbs | 4 - .../stack/buffer_overflow.cbs | 7 +- .../stack/return_local_address.cbs | 7 +- .../test/BSC/Positive/FatPtr/global_array.cbs | 3 - clang/test/BSC/Positive/FatPtr/heap.cbs | 3 - .../Positive/FatPtr/multi_level_pointer.cbs | 3 - .../FatPtr/nullptr_cast/nullptr_cast.cbs | 3 - clang/test/BSC/Positive/FatPtr/stack.cbs | 3 - .../BSC/Positive/FatPtr/string_literal.cbs | 5 +- 60 files changed, 165 insertions(+), 486 deletions(-) diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index 47306d4d1add..855103b0e9a4 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -2566,7 +2566,7 @@ public: return getCanonicalType(T1) == getCanonicalType(T2); } #if ENABLE_BSC - bool hasSameFatPtrType(QualType LHSType, QualType RHSType) const; + bool hasSameFatPtrType(QualType LHSType, QualType RHSType); #endif /// Return this type as a completely-unqualified array type, /// capturing the qualifiers in \p Quals. diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 1395799d197f..d730b1638a11 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -3248,7 +3248,7 @@ bool ASTContext::hasSameFunctionTypeIgnoringPtrSizes(QualType T, QualType U) { } #if ENABLE_BSC -bool ASTContext::hasSameFatPtrType(QualType LHSType, QualType RHSType) const { +bool ASTContext::hasSameFatPtrType(QualType LHSType, QualType RHSType) { if (LHSType.hasFat() != RHSType.hasFat()) return false; if (LHSType->isFunctionProtoType() && RHSType->isFunctionProtoType()) { diff --git a/clang/lib/AST/DeclPrinter.cpp b/clang/lib/AST/DeclPrinter.cpp index ee62eb74de5f..22075632e80d 100644 --- a/clang/lib/AST/DeclPrinter.cpp +++ b/clang/lib/AST/DeclPrinter.cpp @@ -1233,12 +1233,6 @@ void DeclPrinter::VisitCXXRecordDecl(CXXRecordDecl *D) { if (Policy.RewriteBSC) { std::string BSCRecordNameStr = S->getNameAsString(); BSCRecordNameStr += MangleBSCContext::getBSCTemplateArgsName(Args, Policy); - if (S->getNameAsString() == "_FatPtr" && - S->getTemplateArgs().size() == 1) { - size_t constStartPos = BSCRecordNameStr.find("_const"); - if (constStartPos != std::string::npos) - BSCRecordNameStr.erase(constStartPos, 6); - } Out << ' ' << BSCRecordNameStr; } else { #endif diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp index 6fcdaaac38c1..570d7bee40f4 100644 --- a/clang/lib/AST/StmtPrinter.cpp +++ b/clang/lib/AST/StmtPrinter.cpp @@ -1661,30 +1661,6 @@ void StmtPrinter::VisitOMPIteratorExpr(OMPIteratorExpr *Node) { } void StmtPrinter::PrintCallArgs(CallExpr *Call) { -#if ENABLE_BSC - if (Policy.RewriteBSC && Context->getLangOpts().EnableFatPtr) { - if (auto FD = dyn_cast(Call->getCalleeDecl())) { - if (FD->getNameAsString() == "_check_version") { - for (unsigned i = 0; i < 3; ++i) { - if (i) - OS << ", "; - PrintExpr(Call->getArg(i)); - } - OS << ", __FILE__, __LINE__, __func__"; - return; - } else if (FD->getNameAsString() == "_check_offset" || - FD->getNameAsString() == "_check_version_and_offset") { - for (unsigned i = 0; i < 4; ++i) { - if (i) - OS << ", "; - PrintExpr(Call->getArg(i)); - } - OS << ", __FILE__, __LINE__, __func__"; - return; - } - } - } -#endif for (unsigned i = 0, e = Call->getNumArgs(); i != e; ++i) { if (isa(Call->getArg(i))) { // Don't print any defaulted arguments @@ -1788,13 +1764,7 @@ void StmtPrinter::VisitExtVectorElementExpr(ExtVectorElementExpr *Node) { void StmtPrinter::VisitCStyleCastExpr(CStyleCastExpr *Node) { OS << '('; -#if ENABLE_BSC - if (Policy.RewriteBSC && Context->getLangOpts().EnableFatPtr && - Node->getType().hasFat()) - Node->getType().print(OS, Policy); - else -#endif - Node->getTypeAsWritten().print(OS, Policy); + Node->getTypeAsWritten().print(OS, Policy); OS << ')'; PrintExpr(Node->getSubExpr()); } diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp index bbd0aa26dae6..8279e434ad67 100644 --- a/clang/lib/AST/TypePrinter.cpp +++ b/clang/lib/AST/TypePrinter.cpp @@ -1486,12 +1486,6 @@ void TypePrinter::printTag(TagDecl *D, raw_ostream &OS) { if (Policy.RewriteBSC) { Args = Spec->getTemplateArgs().asArray(); std::string BSCRecordNameStr = MangleBSCContext::getBSCTemplateArgsName(Args, Policy); - if (Spec->getNameAsString() == "_FatPtr" && - Spec->getTemplateArgs().size() == 1) { - size_t constStartPos = BSCRecordNameStr.find("_const"); - if (constStartPos != std::string::npos) - BSCRecordNameStr.erase(constStartPos, 6); - } OS << BSCRecordNameStr; } else { #endif diff --git a/clang/lib/Frontend/Rewrite/RewriteBSC.cpp b/clang/lib/Frontend/Rewrite/RewriteBSC.cpp index 9513f424dcab..464a26f320f2 100644 --- a/clang/lib/Frontend/Rewrite/RewriteBSC.cpp +++ b/clang/lib/Frontend/Rewrite/RewriteBSC.cpp @@ -213,7 +213,6 @@ private: std::set &RewritedFunctionDeclarationSet); void RewriteNonGenericFuncAndVar(std::vector &DeclList); void RewriteInstantFunctionDef(std::vector &DeclList); - void RewriteFatPtrRecord(ClassTemplateDecl *CTD); void InsertText(SourceLocation Loc, StringRef Str, bool InsertAfter = true) { // If insertion succeeded or warning disabled return with no warning. @@ -553,10 +552,6 @@ void RewriteBSC::RewriteDecls() { // For decls with bsc feature, we use pretty printer. // For decls without bsc feature, we use original string text. if (DeclsWithoutBSCFeature.find(D) == DeclsWithoutBSCFeature.end()) { - if (Context->getLangOpts().EnableFatPtr) - if (auto CTSD = dyn_cast(D)) - if (CTSD->getNameAsString() == "_FatPtr") - continue; D->print(Buf, Policy); Buf << ";\n\n"; } else { @@ -619,11 +614,6 @@ void RewriteBSC::RewriteRecordDeclaration(std::vector &DeclList) { break; } } - if (Context->getLangOpts().EnableFatPtr && - CTD->getNameAsString() == "_FatPtr") { - RewriteFatPtrRecord(CTD); - break; - } for (ClassTemplateSpecializationDecl *CTSD : CTD->specializations()) { // Because we only want to print the declaration of the generic struct, // we set it to incomplete before printing, and then restore it after @@ -934,41 +924,4 @@ void RewriteBSC::RewriteInstantFunctionDef(std::vector &DeclList) { } } -void RewriteBSC::RewriteFatPtrRecord(ClassTemplateDecl *CTD) { - llvm::DenseMap SimpleFatPtr; - llvm::DenseMap NestedFatPtr; - for (ClassTemplateSpecializationDecl *CTSD : CTD->specializations()) { - QualType ArgType = CTSD->getTemplateArgs()[0].getAsType(); - if (ArgType.isFatPtrRecordType()) - NestedFatPtr[ArgType.getFatPtrPointeeType()] = CTSD; - else - SimpleFatPtr[ArgType] = CTSD; - } - for (auto ArgTypeAndCTSD : SimpleFatPtr) { - QualType ArgType = ArgTypeAndCTSD.first; - QualType ArgTypeCopy = ArgType; - if (ArgType.isLocalConstQualified()) { - ArgType.removeLocalConst(); - if (SimpleFatPtr.count(ArgType)) - SimpleFatPtr.erase(ArgTypeCopy); - } - } - for (auto ArgTypeAndCTSD : NestedFatPtr) { - QualType ArgType = ArgTypeAndCTSD.first; - QualType ArgTypeCopy = ArgType; - if (ArgType.isLocalConstQualified()) { - ArgType.removeLocalConst(); - if (NestedFatPtr.count(ArgType)) - NestedFatPtr.erase(ArgTypeCopy); - } - } - for (auto ArgTypeAndCTSD : SimpleFatPtr) { - ArgTypeAndCTSD.second->print(Buf, Policy); - Buf << ";\n"; - } - for (auto ArgTypeAndCTSD : NestedFatPtr) { - ArgTypeAndCTSD.second->print(Buf, Policy); - Buf << ";\n"; - } -} #endif \ No newline at end of file diff --git a/clang/lib/Headers/bsc_include/bsc_fat_ptr.hbs b/clang/lib/Headers/bsc_include/bsc_fat_ptr.hbs index 9cf3488305d6..e70f88cbcf20 100644 --- a/clang/lib/Headers/bsc_include/bsc_fat_ptr.hbs +++ b/clang/lib/Headers/bsc_include/bsc_fat_ptr.hbs @@ -162,4 +162,19 @@ static __always_inline void _check_version_and_offset( } } +T** cast_two_level_fat_to_raw (T *fat *fat ptr) { + _FatPtr<_FatPtr> ptr_copy = ptr; + if (ptr_copy.key_version == 0) + _report_error(CannotCast, 0, 0, 0); + const uint8_t *head = (const uint8_t *)ptr_copy.raw_ptr - ptr_copy.offset - 8; + const struct _AllocationUnit *phead = (const struct _AllocationUnit *)head; + size_t fat_ptr_size = sizeof(void *) + 8; + if (phead->size < fat_ptr_size) + _report_error(CannotCast, 0, 0, 0); + int element_num = phead->size / fat_ptr_size; + T **raw = (T **)malloc(element_num * sizeof(T *)); + for (int i = 0; i < element_num; i++) + raw[i] = (T *)ptr_copy.raw_ptr[i]; + return raw; +} #endif /* BSC_FAT_PTR_HBS */ \ No newline at end of file diff --git a/clang/lib/Sema/BSC/SemaBSCFatPtr.cpp b/clang/lib/Sema/BSC/SemaBSCFatPtr.cpp index 7a752ee200ab..c04f2ec8753d 100644 --- a/clang/lib/Sema/BSC/SemaBSCFatPtr.cpp +++ b/clang/lib/Sema/BSC/SemaBSCFatPtr.cpp @@ -254,9 +254,10 @@ StmtResult FatPtrPreprocessor::TransformCompoundStmt(CompoundStmt *CS, } CurrStmts.push_back(BaseTransform::TransformStmt(S).get()); } - Sema::CompoundScopeRAII CompoundScope(SemaRef); - StmtResult NewCS = BaseTransform::RebuildCompoundStmt( - CS->getLBracLoc(), CurrStmts, CS->getRBracLoc(), false); + CompoundStmt *NewCS = CompoundStmt::Create( + SemaRef.Context, CurrStmts, FPOptionsOverride(), CS->getLBracLoc(), + CS->getRBracLoc(), CS->getSafeSpecifier(), CS->getSafeSpecifierLoc(), + CS->getCompSafeZoneSpecifier()); CurrStmts = PrevStmts; return NewCS; } @@ -338,7 +339,7 @@ ExprResult FatPtrPreprocessor::TransformUnaryOperator(UnaryOperator *UO) { Expr *SubE = UO->getSubExpr(); QualType SubQT = SubE->getType(); if ((UO->isPrefix() || UO->isPostfix()) && SubQT.isFatPtrType()) { - SubE = BaseTransform::TransformExpr(SubE).get(); + SubE = SubE->IgnoreParens(); bool IsPostfix = UO->isPostfix(); bool IsInc = UO->isIncrementOp(); // Build the first expr of CommaExpr: @@ -371,9 +372,6 @@ ExprResult FatPtrPreprocessor::TransformUnaryOperator(UnaryOperator *UO) { UO->setSubExpr(isa(SubE->IgnoreParens()) && SubQT.isFatPtrType() ? ReplaceWithTemporaryVarDecl(SubE->IgnoreParens()) : BaseTransform::TransformExpr(SubE).get()); - if ((UO->getOpcode() == UO_Deref && UO->getType().isFatPtrType())) { - return new (SemaRef.Context) ParenExpr(SLoc, UO->getEndLoc(), UO); - } return UO; } @@ -440,10 +438,8 @@ Stmt *FatPtrPreprocessor::BuildCompoundStmtForIfOrLoopStmtBody(Stmt *Body) { if (!isa(Body)) { Statements.push_back(Body); } - Sema::CompoundScopeRAII CompoundScope(SemaRef); - return BaseTransform::RebuildCompoundStmt(Body->getBeginLoc(), Statements, - Body->getEndLoc(), false) - .get(); + return CompoundStmt::Create(SemaRef.Context, Statements, FPOptionsOverride(), + Body->getBeginLoc(), Body->getEndLoc()); } // If return type of a CallExpr is fat ptr type, build a temporary @@ -464,8 +460,9 @@ ImplicitCastExpr *FatPtrPreprocessor::ReplaceWithTemporaryVarDecl(Expr *E) { VD->markUsed(SemaRef.Context); DeclStmt *DS = new (SemaRef.Context) DeclStmt(DeclGroupRef(VD), SLoc, SLoc); CurrStmts.push_back(DS); - DeclRefExpr *DRE = - SemaRef.BuildDeclRefExpr(VD, CE->getType(), VK_LValue, SLoc); + DeclRefExpr *DRE = DeclRefExpr::Create( + SemaRef.Context, NestedNameSpecifierLoc(), SourceLocation(), VD, false, + SLoc, CE->getType(), VK_LValue); return ImplicitCastExpr::Create(SemaRef.Context, QT, CK_LValueToRValue, DRE, nullptr, VK_PRValue, FPOptionsOverride()); } @@ -728,10 +725,11 @@ TransformFatPtr::HandleLocalAllocationUnit(CompoundStmt *CS, if (IsTopLevelCompoundStmt && Finder.LocalNoArrayVarAddressIsFatTaken) Statements.push_back( BuildLockVersionAssignmentForLocalNoArrayVars(CS->getEndLoc())); - Sema::CompoundScopeRAII CompoundScope(SemaRef); - StmtResult NewCS = BaseTransform::RebuildCompoundStmt( - CS->getLBracLoc(), Statements, CS->getRBracLoc(), false); - return NewCS.getAs(); + auto NewCS = CompoundStmt::Create( + SemaRef.Context, Statements, FPOptionsOverride(), CS->getLBracLoc(), + CS->getRBracLoc(), CS->getSafeSpecifier(), CS->getSafeSpecifierLoc(), + CS->getCompSafeZoneSpecifier()); + return NewCS; } // Build allocation unit header for all local vars whose @@ -777,7 +775,7 @@ TransformFatPtr::BuildAllocationUnitHeaderForLocalNoArrayVars( // int arr[3]; // }; // struct _arr_AllocationUnit _arr = -// { _new_version_number(), sizeof(int[3]), {1, 2, 3} }; +// { _new_version_number(), 12, {1, 2, 3} }; // @endcode std::pair TransformFatPtr::BuildAllocationUnitForLocalArrayVar(VarDecl *VD) { @@ -820,13 +818,11 @@ TransformFatPtr::BuildAllocationUnitForLocalArrayVar(VarDecl *VD) { // Build first init expr: _ner_version_number() llvm::SmallVector Args; Expr *LockVersionInit = BuildFatPtrCall(SLoc, "_new_version_number", Args); - // Build second init expr: sizeof(array type) - Expr *SizeInit = SemaRef - .CreateUnaryExprOrTypeTraitExpr( - SemaRef.Context.getTrivialTypeSourceInfo( - DesugarFatPtrType(SLoc, VD->getType()), SLoc), - SLoc, UETT_SizeOf, SourceRange(SLoc)) - .get(); + // Build second init expr by array size + auto ArraySize = + SemaRef.Context.getTypeSize(DesugarFatPtrType(SLoc, VD->getType())) / 8; + Expr *SizeInit = IntegerLiteral::Create( + SemaRef.Context, llvm::APInt(32, ArraySize), UInt32Ty, SLoc); // Build InitListExpr SmallVector Inits{LockVersionInit, SizeInit}; if (VD->getInit()) @@ -885,9 +881,11 @@ StmtResult TransformFatPtr::TransformCompoundStmt(CompoundStmt *CS, for (auto S : TempStatements) { DesugaredStatements[CS].push_back(S); } - Sema::CompoundScopeRAII CompoundScope(SemaRef); - return BaseTransform::RebuildCompoundStmt( - CS->getLBracLoc(), DesugaredStatements[CS], CS->getRBracLoc(), false); + auto NewCS = CompoundStmt::Create( + SemaRef.Context, DesugaredStatements[CS], FPOptionsOverride(), + CS->getLBracLoc(), CS->getRBracLoc(), CS->getSafeSpecifier(), + CS->getSafeSpecifierLoc(), CS->getCompSafeZoneSpecifier()); + return NewCS; } // If there is fat ptr deref operation in CondExpr of IfStmt or @@ -1083,10 +1081,10 @@ ExprResult TransformFatPtr::TransformDeclRefExpr(DeclRefExpr *DRE) { it++; FieldDecl *ArrayFD = *it; return SemaRef.BuildMemberExpr( - AllocationUnitVDRef, false, SLoc, NestedNameSpecifierLoc(), - SourceLocation(), ArrayFD, - DeclAccessPair::make(AllocationUnitRD, ArrayFD->getAccess()), false, - DeclarationNameInfo(), ArrayFD->getType(), VK_LValue, OK_Ordinary); + AllocationUnitVDRef, false, SLoc, NestedNameSpecifierLoc(), SLoc, + ArrayFD, DeclAccessPair::make(AllocationUnitRD, ArrayFD->getAccess()), + false, DeclarationNameInfo(), ArrayFD->getType(), VK_LValue, + OK_Ordinary); } } return DRE; @@ -1112,19 +1110,20 @@ ExprResult TransformFatPtr::TransformImplicitCastExpr(ImplicitCastExpr *ICE) { } ExprResult TransformFatPtr::TransformParenExpr(ParenExpr *PE) { + SourceLocation SLoc = PE->getBeginLoc(); if (DesugarToMemberPtr && PreprocessedSelfIncOrDecExpr.count(PE)) { return HandlePEFromPreprocessedSelfIncOrDecExpr(PE); } - SourceLocation SLoc = PE->getBeginLoc(); - QualType QT = DesugarFatPtrType(SLoc, PE->getType()); - PE->setType(QT); - - Expr *MemberRawPtr = nullptr; - if (QT.isFatPtrRecordType() && DesugarToMemberPtr) - MemberRawPtr = BuildMemberRawPtrAndInsertCheckCallForFatPtr(PE); - + QualType QT = PE->getType(); + if (DesugarToMemberPtr && QT.isFatPtrType()) { + QT = DesugarFatPtrType(SLoc, QT.getFatPtrPointeeType()); + PE->setType(SemaRef.Context.getPointerType(QT)); + } else { + QT = DesugarFatPtrType(SLoc, QT); + PE->setType(QT); + } PE->setSubExpr(BaseTransform::TransformExpr(PE->getSubExpr()).get()); - return MemberRawPtr ? MemberRawPtr : PE; + return PE; } ExprResult @@ -1323,20 +1322,15 @@ TransformFatPtr::TransformArraySubscriptExpr(ArraySubscriptExpr *ASE) { // Build offset arg expr, for example: // for p[i] where p[i] is int type, // offset should be `i * sizeof(int) + sizeof(int)` + QualType Int32Ty = SemaRef.Context.getIntTypeForBitwidth(32, true); Expr *OffsetExprLHS = - SemaRef - .CreateUnaryExprOrTypeTraitExpr( - SemaRef.Context.getTrivialTypeSourceInfo(QT, SLoc), SLoc, - UETT_SizeOf, SourceRange(SLoc)) - .get(); + IntegerLiteral::Create(SemaRef.Context, + llvm::APInt(32, SemaRef.Context.getTypeSize(QT) / 8), Int32Ty, SLoc); OffsetExprLHS = SemaRef.CreateBuiltinBinOp(SLoc, BO_Mul, ASE->getRHS(), OffsetExprLHS).get(); Expr *OffsetExprRHS = - SemaRef - .CreateUnaryExprOrTypeTraitExpr( - SemaRef.Context.getTrivialTypeSourceInfo(QT, SLoc), SLoc, - UETT_SizeOf, SourceRange(SLoc)) - .get(); + IntegerLiteral::Create(SemaRef.Context, + llvm::APInt(32, SemaRef.Context.getTypeSize(QT) / 8), Int32Ty, SLoc); Expr *OffsetExpr = SemaRef.CreateBuiltinBinOp(SLoc, BO_Add, OffsetExprLHS, OffsetExprRHS).get(); InsertCheckOffsetCall.second = OffsetExpr; @@ -1401,16 +1395,20 @@ ExprResult TransformFatPtr::TransformCStyleCastExpr(CStyleCastExpr *CSCE) { ExprResult TransformFatPtr::TransformUnaryExprOrTypeTraitExpr( UnaryExprOrTypeTraitExpr *UETT) { SourceLocation SLoc = UETT->getBeginLoc(); + QualType QT = DesugarFatPtrType(SLoc, UETT->getType()); + UETT->setType(QT); // Recalculate size of fat ptr which is larger than raw ptr: // `sizeof(int *)` equals to 8, but `sizeof(int *fat)` equals to 16 if (UETT->isArgumentType()) { - // handle sizeof(type) - UETT->setArgument(SemaRef.Context.getTrivialTypeSourceInfo( - DesugarFatPtrType(SLoc, UETT->getArgumentType()), SLoc)); - } else { - // handle sizeof(expr) - UETT->setArgument( - BaseTransform::TransformExpr(UETT->getArgumentExpr()).get()); + QualType OldQT = UETT->getArgumentType(); + if (OldQT.isFatPtrType()) { + QualType NewQT = DesugarFatPtrType(SLoc, OldQT); + TypeSourceInfo *NewTInfo = + SemaRef.Context.getTrivialTypeSourceInfo(NewQT, SLoc); + return BaseTransform::RebuildUnaryExprOrTypeTrait( + NewTInfo, UETT->getOperatorLoc(), UETT->getKind(), + UETT->getSourceRange()); + } } return UETT; } @@ -1535,26 +1533,29 @@ ExprResult TransformFatPtr::HandleBOFatPtrAddOrSubInteger(BinaryOperator *BO) { SourceLocation SLoc = BO->getBeginLoc(); QualType QT = BO->getType(); BinaryOperator::Opcode Op = BO->getOpcode(); + DesugarToMemberPtr = true; Expr *LHS = BaseTransform::TransformExpr(BO->getLHS()).get(); Expr *RHS = BaseTransform::TransformExpr(BO->getRHS()).get(); - Expr *FirstInitLHS = BuildMemberForFatPtr(LHS, "raw_ptr"); Expr *FirstInitE = - SemaRef.CreateBuiltinBinOp(SLoc, Op, FirstInitLHS, RHS).get(); - Expr *SecondInitE = BuildMemberForFatPtr(LHS, "key_version"); - Expr *ThirdInitLHS = BuildMemberForFatPtr(LHS, "offset"); - QualType PointeeQT = LHS->getType().getFatPtrPointeeType(); - Expr *ThirdInitRHSRHS = - SemaRef - .CreateUnaryExprOrTypeTraitExpr( - SemaRef.Context.getTrivialTypeSourceInfo(PointeeQT, SLoc), SLoc, - UETT_SizeOf, SourceRange(SLoc)) - .get(); - Expr *ThirdInitRHS = - SemaRef.CreateBuiltinBinOp(SLoc, BO_Mul, RHS, ThirdInitRHSRHS).get(); - Expr *ThirdInitE = - SemaRef.CreateBuiltinBinOp(SLoc, Op, ThirdInitLHS, ThirdInitRHS).get(); - return BuildFatPtrCompoundLiteralExpr(FirstInitE, SecondInitE, ThirdInitE, - QT); + SemaRef.CreateBuiltinBinOp(SLoc, Op, LHS, RHS).get(); + if (auto MemberFatPtr = dyn_cast(LHS->IgnoreParenImpCasts())) { + Expr *FatPtrBase = MemberFatPtr->getBase(); + Expr *SecondInitE = BuildMemberForFatPtr(FatPtrBase, "key_version"); + Expr *ThirdInitLHS = BuildMemberForFatPtr(FatPtrBase, "offset"); + QualType PointeeQT = FatPtrBase->getType().getFatPtrPointeeType(); + QualType Int32Ty = SemaRef.Context.getIntTypeForBitwidth(32, true); + Expr *ThirdInitRHSRHS = IntegerLiteral::Create( + SemaRef.Context, + llvm::APInt(32, SemaRef.Context.getTypeSize(PointeeQT) / 8), Int32Ty, + SLoc); + Expr *ThirdInitRHS = + SemaRef.CreateBuiltinBinOp(SLoc, BO_Mul, RHS, ThirdInitRHSRHS).get(); + Expr *ThirdInitE = + SemaRef.CreateBuiltinBinOp(SLoc, Op, ThirdInitLHS, ThirdInitRHS).get(); + return BuildFatPtrCompoundLiteralExpr(FirstInitE, SecondInitE, ThirdInitE, + QT); + } + return BO; } ExprResult TransformFatPtr::TransformUnaryOperator(UnaryOperator *UO) { @@ -1618,19 +1619,17 @@ ExprResult TransformFatPtr::TransformUnaryOperator(UnaryOperator *UO) { } ExprResult TransformFatPtr::HandleUODerefFatPtr(UnaryOperator *UO) { - SourceLocation SLoc = UO->getBeginLoc(); Expr *SubE = UO->getSubExpr(); DesugarToMemberPtr = true; InsertCheckVersionCall = true; InsertCheckOffsetCall.first = true; // Build offset arg expr, for example: // for *p, offset should be `sizeof(int)` - Expr *OffsetExpr = - SemaRef - .CreateUnaryExprOrTypeTraitExpr( - SemaRef.Context.getTrivialTypeSourceInfo(UO->getType(), SLoc), - SLoc, UETT_SizeOf, SourceRange(SLoc)) - .get(); + QualType Int32Ty = SemaRef.Context.getIntTypeForBitwidth(32, true); + Expr *OffsetExpr = IntegerLiteral::Create( + SemaRef.Context, + llvm::APInt(32, SemaRef.Context.getTypeSize(UO->getType()) / 8), Int32Ty, + UO->getBeginLoc()); InsertCheckOffsetCall.second = OffsetExpr; SubE = BaseTransform::TransformExpr(SubE).get(); if (auto ICE = dyn_cast(SubE)) { @@ -1666,7 +1665,7 @@ ExprResult TransformFatPtr::HandleUOAddrFatOnExprWithFatPtrBase( // Build second init expr: p.key_version Expr *SecondInitE = BuildMemberForFatPtr(FatPtrBaseE, "key_version"); // Build third init expr: - // `(int32_t)((uint8_t *)&p.ptr->a - (uint8_t *)p.ptr) + p.offset` + // `(int32_t)((uint8_t *)&p.ptr->a - (uint8_t *)p.ptr + p.offset` QualType UInt8PointerTy = SemaRef.Context.getPointerType( SemaRef.Context.getIntTypeForBitwidth(8, false)); Expr *ThirdInitLHSLHS = @@ -1743,8 +1742,6 @@ TransformFatPtr::HandleUOAddrFatOnLocalNoArrayVar(UnaryOperator *UO) { SemaRef .CreateBuiltinBinOp(SLoc, BO_Sub, ThirdInitLHSLHS,ThirdInitLHSRHS) .get(); - ThirdInitLHS = - new (SemaRef.Context) ParenExpr(SLoc, UO->getEndLoc(), ThirdInitLHS); QualType Int32Ty = SemaRef.Context.getIntTypeForBitwidth(32, true); ThirdInitLHS = SemaRef @@ -1801,12 +1798,10 @@ ExprResult TransformFatPtr::HandleUOAddrFatOnArrayVar( Expr *AllocationUnitVDRef = SemaRef.BuildDeclRefExpr( AllocationUnitVD, AllocationUnitVD->getType(), VK_LValue, SLoc); Expr *SecondInitE = SemaRef.BuildMemberExpr( - AllocationUnitVDRef, false, SLoc, NestedNameSpecifierLoc(), - SourceLocation(), LockVersionFD, + AllocationUnitVDRef, false, SLoc, NestedNameSpecifierLoc(), SLoc, LockVersionFD, DeclAccessPair::make(AllocationUnitRD, LockVersionFD->getAccess()), false, DeclarationNameInfo(), LockVersionFD->getType(), VK_LValue, OK_Ordinary); - // Build third init expr: - // `(int32_t)((uint8_t *)&_arr.arr[5] - (uint8_t *)&_arr.lock_version) - 8` + // Build third init expr: 5 * size(int); QualType UInt8PointerTy = SemaRef.Context.getPointerType( SemaRef.Context.getIntTypeForBitwidth(8, false)); Expr *ThirdInitLHSLHS = @@ -1825,8 +1820,6 @@ ExprResult TransformFatPtr::HandleUOAddrFatOnArrayVar( .get(); Expr *ThirdInitLHS = SemaRef.CreateBuiltinBinOp(SLoc, BO_Sub, ThirdInitLHSLHS, ThirdInitLHSRHS).get(); - ThirdInitLHS = - new (SemaRef.Context) ParenExpr(SLoc, UO->getEndLoc(), ThirdInitLHS); QualType Int32Ty = SemaRef.Context.getIntTypeForBitwidth(32, true); ThirdInitLHS = SemaRef @@ -1877,12 +1870,11 @@ ExprResult TransformFatPtr::TransformMemberExpr(MemberExpr *ME) { } Expr *OffsetExprLHS = SemaRef.BuildBuiltinOffsetOf(SLoc, PointeeTInfo, Comps, SLoc).get(); - Expr *OffsetExprRHS = - SemaRef - .CreateUnaryExprOrTypeTraitExpr( - SemaRef.Context.getTrivialTypeSourceInfo(ElementType, SLoc), - SLoc, UETT_SizeOf, SourceRange(SLoc)) - .get(); + QualType Int32Ty = SemaRef.Context.getIntTypeForBitwidth(32, true); + Expr *OffsetExprRHS = IntegerLiteral::Create( + SemaRef.Context, + llvm::APInt(32, SemaRef.Context.getTypeSize(ElementType) / 8), Int32Ty, + SLoc); InsertCheckOffsetCall.second = SemaRef.CreateBuiltinBinOp(SLoc, BO_Add, OffsetExprLHS, OffsetExprRHS).get(); } @@ -2194,7 +2186,9 @@ TransformFatPtr::IsAccessingArrayMemberThroughFatPtr(Expr *E) { void TransformFatPtr::VisitExprToBuildOffsetOfComponent( Expr *E, SourceLocation SLoc, SmallVector &Comps) { - if (auto ASE = dyn_cast(E)) { + if (E->getType().isFatPtrType()) { + return; + } else if (auto ASE = dyn_cast(E)) { VisitExprToBuildOffsetOfComponent(ASE->getBase(), SLoc, Comps); Comps.push_back(Sema::OffsetOfComponent()); Comps.back().isBrackets = true; @@ -2359,6 +2353,9 @@ bool Sema::CheckFatQualTypeAssignment(QualType LHSType, QualType RHSType) { // T *fat -> const T *fat is OK if (Context.hasSameFatPtrType(LHSPointeeType, RHSPointeeType)) return true; + // T *fat -> const void *fat is OK + if (LHSPointeeType->isVoidType()) + return true; return false; } @@ -2472,14 +2469,10 @@ Sema::BuildAllocationUnitForGlobalArrayVar(VarDecl *VD) { // Build first init expr : 1 Expr *LockVersionInit = IntegerLiteral::Create(Context, llvm::APInt(32, 1), UInt32Ty, SLoc); - // Build second init expr: sizeof(array type) - Expr *SizeInit = CreateUnaryExprOrTypeTraitExpr( - Context.getTrivialTypeSourceInfo(VD->getType(), SLoc), - SLoc, UETT_SizeOf, SourceRange(SLoc)) - .get(); - SizeInit = - ImplicitCastExpr::Create(Context, UInt32Ty, CK_IntegralCast, SizeInit, - nullptr, VK_PRValue, FPOptionsOverride()); + // Build second init expr : array size + Expr *SizeInit = IntegerLiteral::Create( + Context, llvm::APInt(32, Context.getTypeSize(VD->getType()) / 8), + UInt32Ty, SLoc); // Build InitListExpr SmallVector Inits{LockVersionInit, SizeInit}; if (VD->getInit()) diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index fc83d60569f9..88048413a43f 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -9253,7 +9253,8 @@ QualType Sema::CheckConditionalOperands(ExprResult &Cond, ExprResult &LHS, return LHSTy; if (LHS.get()->isNullExpr(Context) && RHSTy.isFatPtrType()) return RHSTy; - if (LHSTy.isFatPtrType() && RHSTy.isFatPtrType()) + if (LHSTy.isFatPtrType() && RHSTy.isFatPtrType() && + Context.hasSameFatPtrType(LHSTy, RHSTy)) return LHSTy; } #endif diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/global_array/array_out_of_bounds1.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/global_array/array_out_of_bounds1.cbs index 808528c68ccb..bcd766bdb6e4 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/global_array/array_out_of_bounds1.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/global_array/array_out_of_bounds1.cbs @@ -1,8 +1,5 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s -// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c -// RUN: %clang %t-rw.c -o %t-rw.output -// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include @@ -13,6 +10,4 @@ int main() { return 0; } // CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/global_array/array_out_of_bounds1.cbs:[[@LINE-4]] in main - -// CHECK_REWRITE: Error: Pointer offset exceeds the allocation size! \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/global_array/array_out_of_bounds1.cbs:[[@LINE-4]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/global_array/array_out_of_bounds2.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/global_array/array_out_of_bounds2.cbs index 87932c20abc4..5b260692ffc0 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/global_array/array_out_of_bounds2.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/global_array/array_out_of_bounds2.cbs @@ -1,8 +1,5 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s -// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c -// RUN: %clang %t-rw.c -o %t-rw.output -// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include @@ -15,6 +12,4 @@ int main() { return 0; } // CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/global_array/array_out_of_bounds2.cbs:[[@LINE-4]] in main - -// CHECK_REWRITE: Error: Pointer offset exceeds the allocation size! \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/global_array/array_out_of_bounds2.cbs:[[@LINE-4]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds1.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds1.cbs index 5a33310bb120..0fbfd3e63612 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds1.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds1.cbs @@ -1,8 +1,6 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s -// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c -// RUN: %clang %t-rw.c -o %t-rw.output -// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE + #include int main() { @@ -11,6 +9,4 @@ int main() { return 0; } // CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds1.cbs:[[@LINE-4]] in main - -// CHECK_REWRITE: Error: Pointer offset exceeds the allocation size! \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds1.cbs:[[@LINE-4]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds10.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds10.cbs index d17365dc3916..77f601353803 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds10.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds10.cbs @@ -1,8 +1,5 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s -// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c -// RUN: %clang %t-rw.c -o %t-rw.output -// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include @@ -16,6 +13,4 @@ int main() { return 0; } // CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds10.cbs:[[@LINE-5]] in main - -// CHECK_REWRITE: Error: Pointer offset exceeds the allocation size! \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds10.cbs:[[@LINE-5]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds11.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds11.cbs index 6ccbfaf5f9dd..be9b109ef756 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds11.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds11.cbs @@ -1,8 +1,5 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s -// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c -// RUN: %clang %t-rw.c -o %t-rw.output -// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include @@ -17,6 +14,4 @@ int main() { return 0; } // CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds11.cbs:[[@LINE-5]] in main - -// CHECK_REWRITE: Error: Pointer offset exceeds the allocation size! \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds11.cbs:[[@LINE-5]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds12.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds12.cbs index adcf493632ca..d56eea2a6605 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds12.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds12.cbs @@ -1,8 +1,5 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s -// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c -// RUN: %clang %t-rw.c -o %t-rw.output -// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include @@ -15,6 +12,4 @@ int main() { return 0; } // CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds12.cbs:[[@LINE-4]] in main - -// CHECK_REWRITE: Error: Pointer offset exceeds the allocation size! \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds12.cbs:[[@LINE-4]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds13.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds13.cbs index 678efe61c9e4..9bf14e9103f5 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds13.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds13.cbs @@ -1,8 +1,5 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s -// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c -// RUN: %clang %t-rw.c -o %t-rw.output -// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include @@ -16,6 +13,4 @@ int main() { } } // CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds13.cbs:[[@LINE-5]] in main - -// CHECK_REWRITE: Error: Pointer offset exceeds the allocation size! \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds13.cbs:[[@LINE-5]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds14.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds14.cbs index 0596724fb1a6..3bcc0dd4bad0 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds14.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds14.cbs @@ -1,8 +1,5 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s -// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c -// RUN: %clang %t-rw.c -o %t-rw.output -// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include @@ -13,6 +10,4 @@ int main() { return 0; } // CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds14.cbs:[[@LINE-4]] in main - -// CHECK_REWRITE: Error: Pointer offset exceeds the allocation size! \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds14.cbs:[[@LINE-4]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds15.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds15.cbs index ff9a588c37a0..73a2921356e5 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds15.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds15.cbs @@ -1,8 +1,5 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s -// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c -// RUN: %clang %t-rw.c -o %t-rw.output -// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include @@ -15,6 +12,4 @@ int main() { return 0; } // CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds15.cbs:[[@LINE-4]] in main - -// CHECK_REWRITE: Error: Pointer offset exceeds the allocation size! \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds15.cbs:[[@LINE-4]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds16.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds16.cbs index c73e2c7e3b94..a2dced01ec47 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds16.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds16.cbs @@ -1,8 +1,5 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s -// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c -// RUN: %clang %t-rw.c -o %t-rw.output -// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include @@ -14,6 +11,4 @@ int main() { return 0; } // CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds16.cbs:[[@LINE-4]] in main - -// CHECK_REWRITE: Error: Pointer offset exceeds the allocation size! \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds16.cbs:[[@LINE-4]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds17.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds17.cbs index ffff7ed5b15d..ec5abdf668aa 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds17.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds17.cbs @@ -1,8 +1,5 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s -// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c -// RUN: %clang %t-rw.c -o %t-rw.output -// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include @@ -18,6 +15,4 @@ int main() { foo(p1); } // CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds17.cbs:[[@LINE-10]] in bar - -// CHECK_REWRITE: Error: Pointer offset exceeds the allocation size! \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds17.cbs:[[@LINE-10]] in bar \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds18.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds18.cbs index 0c5de151e3da..3d46121aa394 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds18.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds18.cbs @@ -1,8 +1,5 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s -// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c -// RUN: %clang %t-rw.c -o %t-rw.output -// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include @@ -18,6 +15,4 @@ int main() { } } // CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds18.cbs:[[@LINE-5]] in main - -// CHECK_REWRITE: Error: Pointer offset exceeds the allocation size! \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds18.cbs:[[@LINE-5]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds19.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds19.cbs index 3fe6c1756968..7a1b384742c3 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds19.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds19.cbs @@ -1,8 +1,5 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s -// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c -// RUN: %clang %t-rw.c -o %t-rw.output -// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include @@ -19,6 +16,4 @@ int main() { } } // CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds19.cbs:[[@LINE-7]] in main - -// CHECK_REWRITE: Error: Pointer offset exceeds the allocation size! \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds19.cbs:[[@LINE-7]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds2.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds2.cbs index 250a016ccd9a..3f9fe90d1241 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds2.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds2.cbs @@ -1,8 +1,5 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s -// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c -// RUN: %clang %t-rw.c -o %t-rw.output -// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include struct S { int *fat p; }; @@ -12,6 +9,4 @@ int main() { return 0; } // CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds2.cbs:[[@LINE-4]] in main - -// CHECK_REWRITE: Error: Pointer offset exceeds the allocation size! \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds2.cbs:[[@LINE-4]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds20.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds20.cbs index b2b85fc016b0..ca43167ee674 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds20.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds20.cbs @@ -1,8 +1,5 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s -// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c -// RUN: %clang %t-rw.c -o %t-rw.output -// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include int *fat return_fat() { @@ -14,6 +11,4 @@ int main() { *p = 10; } // CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds20.cbs:[[@LINE-3]] in main - -// CHECK_REWRITE: Error: Pointer offset exceeds the allocation size! \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds20.cbs:[[@LINE-3]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds21.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds21.cbs index dab1a0677c53..1f153854bb56 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds21.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds21.cbs @@ -1,8 +1,5 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s -// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c -// RUN: %clang %t-rw.c -o %t-rw.output -// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include @@ -12,6 +9,4 @@ int main() { *p1 = 10; } // CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds21.cbs:[[@LINE-3]] in main - -// CHECK_REWRITE: Error: Pointer offset exceeds the allocation size! \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds21.cbs:[[@LINE-3]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds22.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds22.cbs index 06026faef3f1..d67859854fb7 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds22.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds22.cbs @@ -1,8 +1,5 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s -// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c -// RUN: %clang %t-rw.c -o %t-rw.output -// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include @@ -16,6 +13,4 @@ int main() { return 0; } // CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds22.cbs:[[@LINE-5]] in main - -// CHECK_REWRITE: Error: Pointer offset exceeds the allocation size! \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds22.cbs:[[@LINE-5]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds23.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds23.cbs index 5121f47f5c43..a9e49d3a7c23 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds23.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds23.cbs @@ -1,8 +1,5 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s -// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c -// RUN: %clang %t-rw.c -o %t-rw.output -// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include @@ -16,6 +13,4 @@ int main() { return 0; } // CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds23.cbs:[[@LINE-5]] in main - -// CHECK_REWRITE: Error: Pointer offset exceeds the allocation size! \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds23.cbs:[[@LINE-5]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds24.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds24.cbs index 139155ec5d30..1849f3f1dd55 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds24.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds24.cbs @@ -1,8 +1,5 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s -// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c -// RUN: %clang %t-rw.c -o %t-rw.output -// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include @@ -16,6 +13,4 @@ int main() { return 0; } // CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds24.cbs:[[@LINE-4]] in main - -// CHECK_REWRITE: Error: Pointer offset exceeds the allocation size! \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds24.cbs:[[@LINE-4]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds3.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds3.cbs index 7257cf7677e1..ffd3256569c8 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds3.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds3.cbs @@ -1,8 +1,5 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s -// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c -// RUN: %clang %t-rw.c -o %t-rw.output -// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include @@ -13,6 +10,4 @@ int main() { return 0; } // CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds3.cbs:[[@LINE-4]] in main - -// CHECK_REWRITE: Error: Pointer offset exceeds the allocation size! \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds3.cbs:[[@LINE-4]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds4.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds4.cbs index 9a04b5bb800f..50a853dd1568 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds4.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds4.cbs @@ -1,8 +1,5 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s -// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c -// RUN: %clang %t-rw.c -o %t-rw.output -// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include @@ -14,6 +11,4 @@ int main() { return 0; } // CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds4.cbs:[[@LINE-4]] in main - -// CHECK_REWRITE: Error: Pointer offset exceeds the allocation size! \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds4.cbs:[[@LINE-4]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds5.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds5.cbs index f5bae62d9392..5065a33b6391 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds5.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds5.cbs @@ -1,8 +1,5 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s -// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c -// RUN: %clang %t-rw.c -o %t-rw.output -// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include @@ -14,6 +11,4 @@ int main() { return 0; } // CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds5.cbs:[[@LINE-4]] in main - -// CHECK_REWRITE: Error: Pointer offset exceeds the allocation size! \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds5.cbs:[[@LINE-4]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds6.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds6.cbs index c4108deaa7e4..6b2be24bedba 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds6.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds6.cbs @@ -1,8 +1,5 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s -// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c -// RUN: %clang %t-rw.c -o %t-rw.output -// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include @@ -12,6 +9,4 @@ int main() { int a = p->arr[3]; } // CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds6.cbs:[[@LINE-3]] in main - -// CHECK_REWRITE: Error: Pointer offset exceeds the allocation size! \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds6.cbs:[[@LINE-3]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds7.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds7.cbs index 918ef42f5ef8..6c41e1f26999 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds7.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds7.cbs @@ -1,8 +1,5 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s -// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c -// RUN: %clang %t-rw.c -o %t-rw.output -// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include @@ -17,6 +14,4 @@ int main() { return 0; } // CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds7.cbs:[[@LINE-6]] in main - -// CHECK_REWRITE: Error: Pointer offset exceeds the allocation size! \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds7.cbs:[[@LINE-6]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds8.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds8.cbs index 8bd93d146702..7f02135b5b61 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds8.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds8.cbs @@ -1,8 +1,5 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s -// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c -// RUN: %clang %t-rw.c -o %t-rw.output -// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include @@ -17,6 +14,4 @@ int main() { return 0; } // CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds8.cbs:[[@LINE-4]] in main - -// CHECK_REWRITE: Error: Pointer offset exceeds the allocation size! \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds8.cbs:[[@LINE-4]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds9.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds9.cbs index 29682bed1f40..e39e5e37957c 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds9.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds9.cbs @@ -1,8 +1,5 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s -// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c -// RUN: %clang %t-rw.c -o %t-rw.output -// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include @@ -15,6 +12,4 @@ int main() { return 0; } // CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds9.cbs:[[@LINE-5]] in main - -// CHECK_REWRITE: Error: Pointer offset exceeds the allocation size! \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds9.cbs:[[@LINE-5]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/buffer_overflow.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/buffer_overflow.cbs index e84c92934dd0..8a11f4223d8a 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/buffer_overflow.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/buffer_overflow.cbs @@ -1,8 +1,5 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output longstring 2>&1 | FileCheck %s -// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c -// RUN: %clang %t-rw.c -o %t-rw.output -// RUN: not %t-rw.output longstring 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include #define BUFSIZE 8 @@ -22,6 +19,4 @@ int main(int argc, char **argv) { return 0; } // CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/buffer_overflow.cbs:[[@LINE-13]] in fat_strcpy - -// CHECK_REWRITE: Error: Pointer offset exceeds the allocation size! \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/buffer_overflow.cbs:[[@LINE-13]] in fat_strcpy \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free1.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free1.cbs index 990b0694b5b5..76ce095d0468 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free1.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free1.cbs @@ -1,8 +1,5 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s -// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c -// RUN: %clang %t-rw.c -o %t-rw.output -// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include @@ -13,6 +10,4 @@ int main() { return 0; } -// CHECK: Error: Cannot free this pointer because the allocation may have been freed! - -// CHECK_REWRITE: Error: Cannot free this pointer because the allocation may have been freed! \ No newline at end of file +// CHECK: Error: Cannot free this pointer because the allocation may have been freed! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free2.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free2.cbs index 10a74377018f..0011f1b6ca6c 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free2.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free2.cbs @@ -1,8 +1,5 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s -// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c -// RUN: %clang %t-rw.c -o %t-rw.output -// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include @@ -14,6 +11,4 @@ int main() { return 0; } -// CHECK: Error: Cannot free this pointer because the allocation may have been freed! - -// CHECK_REWRITE: Error: Cannot free this pointer because the allocation may have been freed! \ No newline at end of file +// CHECK: Error: Cannot free this pointer because the allocation may have been freed! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free3.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free3.cbs index ec5603d9cab6..621c6515254b 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free3.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free3.cbs @@ -1,8 +1,5 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s -// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c -// RUN: %clang %t-rw.c -o %t-rw.output -// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include @@ -16,6 +13,4 @@ int main() { return 0; } -// CHECK: Error: Cannot free this pointer because the allocation may have been freed! - -// CHECK_REWRITE: Error: Cannot free this pointer because the allocation may have been freed! \ No newline at end of file +// CHECK: Error: Cannot free this pointer because the allocation may have been freed! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free4.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free4.cbs index 4fead9d6cb42..3872ebea1bc9 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free4.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free4.cbs @@ -1,8 +1,5 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s -// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c -// RUN: %clang %t-rw.c -o %t-rw.output -// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include @@ -18,6 +15,4 @@ int main() { return 0; } -// CHECK: Error: Cannot free this pointer because the allocation may have been freed! - -// CHECK_REWRITE: Error: Cannot free this pointer because the allocation may have been freed! \ No newline at end of file +// CHECK: Error: Cannot free this pointer because the allocation may have been freed! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free1.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free1.cbs index fa819d279390..da6ab753e4e1 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free1.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free1.cbs @@ -1,8 +1,5 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s -// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c -// RUN: %clang %t-rw.c -o %t-rw.output -// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include @@ -15,6 +12,4 @@ int main() { } // CHECK: Error: Cannot use this pointer because the allocation may have been freed or reseored! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free1.cbs:[[@LINE-5]] in main - -// CHECK_REWRITE: Error: Cannot use this pointer because the allocation may have been freed or reseored! \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free1.cbs:[[@LINE-5]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free2.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free2.cbs index 81a61e4a3b99..02d21d1a8651 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free2.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free2.cbs @@ -1,8 +1,5 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s -// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c -// RUN: %clang %t-rw.c -o %t-rw.output -// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include @@ -16,6 +13,4 @@ int main() { } // CHECK: Error: Cannot use this pointer because the allocation may have been freed or reseored! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free2.cbs:[[@LINE-5]] in main - -// CHECK_REWRITE: Error: Cannot use this pointer because the allocation may have been freed or reseored! \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free2.cbs:[[@LINE-5]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free3.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free3.cbs index 9b0b9e4a1e1a..fdc25eaec623 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free3.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free3.cbs @@ -1,8 +1,5 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s -// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c -// RUN: %clang %t-rw.c -o %t-rw.output -// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include @@ -18,6 +15,4 @@ int main() { return 0; } // CHECK: Error: Cannot use this pointer because the allocation may have been freed or reseored! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free3.cbs:[[@LINE-4]] in main - -// CHECK_REWRITE: Error: Cannot use this pointer because the allocation may have been freed or reseored! \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free3.cbs:[[@LINE-4]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free4.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free4.cbs index 0f05fd96231e..4a0f2ee93bf7 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free4.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free4.cbs @@ -1,8 +1,5 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s -// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c -// RUN: %clang %t-rw.c -o %t-rw.output -// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include @@ -16,6 +13,4 @@ int main() { return 0; } // CHECK: Error: Cannot use this pointer because the allocation may have been freed or reseored! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free4.cbs:[[@LINE-4]] in main - -// CHECK_REWRITE: Error: Cannot use this pointer because the allocation may have been freed or reseored! \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free4.cbs:[[@LINE-4]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free5.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free5.cbs index 8de15ede94da..3568698813ac 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free5.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free5.cbs @@ -1,8 +1,5 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s -// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c -// RUN: %clang %t-rw.c -o %t-rw.output -// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include #define BUFSIZE 32 @@ -30,6 +27,4 @@ int main() { return 0; } // CHECK: Error: Cannot use this pointer because the allocation may have been freed or reseored! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free5.cbs:[[@LINE-20]] in fat_strncpy - -// CHECK_REWRITE: Error: Cannot use this pointer because the allocation may have been freed or reseored! \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free5.cbs:[[@LINE-20]] in fat_strncpy \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free6.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free6.cbs index e5bfcd027159..4292d301d94b 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free6.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free6.cbs @@ -1,8 +1,5 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s -// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c -// RUN: %clang %t-rw.c -o %t-rw.output -// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include @@ -15,6 +12,4 @@ int main() { return 0; } // CHECK: Error: Cannot use this pointer because the allocation may have been freed or reseored! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free6.cbs:[[@LINE-4]] in main - -// CHECK_REWRITE: Error: Cannot use this pointer because the allocation may have been freed or reseored! \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free6.cbs:[[@LINE-4]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free7.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free7.cbs index 6a4fe7029e6c..9b6f5b78119c 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free7.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free7.cbs @@ -1,8 +1,5 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s -// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c -// RUN: %clang %t-rw.c -o %t-rw.output -// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include @@ -15,6 +12,4 @@ int main() { return 0; } // CHECK: Error: Cannot use this pointer because the allocation may have been freed or reseored! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free7.cbs:[[@LINE-6]] in main - -// CHECK_REWRITE: Error: Cannot use this pointer because the allocation may have been freed or reseored! \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free7.cbs:[[@LINE-6]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free8.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free8.cbs index 0ea01d4e2dfb..28b7648db77d 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free8.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free8.cbs @@ -1,8 +1,5 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s -// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c -// RUN: %clang %t-rw.c -o %t-rw.output -// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include @@ -17,6 +14,4 @@ int main() { return 0; } // CHECK: Error: Cannot use this pointer because the allocation may have been freed or reseored! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free8.cbs:[[@LINE-6]] in main - -// CHECK_REWRITE: Error: Cannot use this pointer because the allocation may have been freed or reseored! \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free8.cbs:[[@LINE-6]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds1.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds1.cbs index 95626b015062..e99f7725e1b5 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds1.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds1.cbs @@ -1,8 +1,5 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s -// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c -// RUN: %clang %t-rw.c -o %t-rw.output -// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include @@ -13,6 +10,4 @@ int main() { return 0; } // CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds1.cbs:[[@LINE-4]] in main - -// CHECK_REWRITE: Error: Pointer offset exceeds the allocation size! \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds1.cbs:[[@LINE-4]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds2.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds2.cbs index 402aa037db0f..2b1425733499 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds2.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds2.cbs @@ -1,8 +1,5 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s -// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c -// RUN: %clang %t-rw.c -o %t-rw.output -// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include @@ -14,6 +11,4 @@ int main() { return 0; } // CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds2.cbs:[[@LINE-4]] in main - -// CHECK_REWRITE: Error: Pointer offset exceeds the allocation size! \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds2.cbs:[[@LINE-4]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds3.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds3.cbs index dea1a8b0a909..52a72be91eeb 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds3.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds3.cbs @@ -1,8 +1,5 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s -// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c -// RUN: %clang %t-rw.c -o %t-rw.output -// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include @@ -14,6 +11,4 @@ int main() { return 0; } // CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds3.cbs:[[@LINE-4]] in main - -// CHECK_REWRITE: Error: Pointer offset exceeds the allocation size! \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds3.cbs:[[@LINE-4]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds4.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds4.cbs index 8f3465267eb0..2c6abd37814b 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds4.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds4.cbs @@ -1,8 +1,5 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s -// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c -// RUN: %clang %t-rw.c -o %t-rw.output -// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include @@ -15,4 +12,3 @@ int main() { // CHECK: Error: Pointer offset exceeds the allocation size! // CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds4.cbs:[[@LINE-4]] in main -// CHECK_REWRITE: Error: Pointer offset exceeds the allocation size! diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/buffer_overflow.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/buffer_overflow.cbs index 582df755ea21..610d45c170c3 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/buffer_overflow.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/buffer_overflow.cbs @@ -1,8 +1,5 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output longstring 2>&1 | FileCheck %s -// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c -// RUN: %clang %t-rw.c -o %t-rw.output -// RUN: not %t-rw.output longstring 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include #define BUFSIZE 8 @@ -23,6 +20,4 @@ int main(int argc, char **argv) { return 0; } // CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/buffer_overflow.cbs:[[@LINE-14]] in fat_strcpy - -// CHECK_REWRITE: Error: Pointer offset exceeds the allocation size! \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/buffer_overflow.cbs:[[@LINE-14]] in fat_strcpy \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/return_local_address.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/return_local_address.cbs index 7f36b4c1932a..c2d89d83e759 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/return_local_address.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/return_local_address.cbs @@ -1,8 +1,5 @@ // RUN: %clang -enable-fat-ptr %s -o %t.output // RUN: not %t.output 2>&1 | FileCheck %s -// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c -// RUN: %clang %t-rw.c -o %t-rw.output -// RUN: not %t-rw.output 2>&1 | FileCheck %s --check-prefix=CHECK_REWRITE #include @@ -18,6 +15,4 @@ int main() { return 0; } // CHECK: Error: Cannot use this pointer because the allocation may have been freed or reseored! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/return_local_address.cbs:[[@LINE-4]] in main - -// CHECK_REWRITE: Error: Cannot use this pointer because the allocation may have been freed or reseored! \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/return_local_address.cbs:[[@LINE-4]] in main \ No newline at end of file diff --git a/clang/test/BSC/Positive/FatPtr/global_array.cbs b/clang/test/BSC/Positive/FatPtr/global_array.cbs index 9c77c2999d8a..7f4893eab5f0 100644 --- a/clang/test/BSC/Positive/FatPtr/global_array.cbs +++ b/clang/test/BSC/Positive/FatPtr/global_array.cbs @@ -1,8 +1,5 @@ // RUN: %clang %s -enable-fat-ptr -o %t.output // RUN: %t.output -// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c -// RUN: %clang %t-rw.c -o %t-rw.output -// RUN: %t-rw.output // expected-no-diagnostics #include diff --git a/clang/test/BSC/Positive/FatPtr/heap.cbs b/clang/test/BSC/Positive/FatPtr/heap.cbs index 3a02ff54c677..4d904d01d50a 100644 --- a/clang/test/BSC/Positive/FatPtr/heap.cbs +++ b/clang/test/BSC/Positive/FatPtr/heap.cbs @@ -1,8 +1,5 @@ // RUN: %clang %s -enable-fat-ptr -o %t.output // RUN: %t.output -// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c -// RUN: %clang %t-rw.c -o %t-rw.output -// RUN: %t-rw.output // expected-no-diagnostics #include diff --git a/clang/test/BSC/Positive/FatPtr/multi_level_pointer.cbs b/clang/test/BSC/Positive/FatPtr/multi_level_pointer.cbs index 182dfc7a432c..b0cfd7a418fd 100644 --- a/clang/test/BSC/Positive/FatPtr/multi_level_pointer.cbs +++ b/clang/test/BSC/Positive/FatPtr/multi_level_pointer.cbs @@ -1,8 +1,5 @@ // RUN: %clang %s -enable-fat-ptr -o %t.output // RUN: %t.output | FileCheck %s -// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c -// RUN: %clang %t-rw.c -o %t-rw.output -// RUN: %t-rw.output | FileCheck %s // expected-no-diagnostics #include diff --git a/clang/test/BSC/Positive/FatPtr/nullptr_cast/nullptr_cast.cbs b/clang/test/BSC/Positive/FatPtr/nullptr_cast/nullptr_cast.cbs index f835dc7a158a..9a47ca5dac2b 100644 --- a/clang/test/BSC/Positive/FatPtr/nullptr_cast/nullptr_cast.cbs +++ b/clang/test/BSC/Positive/FatPtr/nullptr_cast/nullptr_cast.cbs @@ -1,8 +1,5 @@ // RUN: %clang %s -enable-fat-ptr -o %t.output // RUN: %t.output -// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c -// RUN: %clang %t-rw.c -o %t-rw.output -// RUN: %t-rw.output // expected-no-diagnostics #include diff --git a/clang/test/BSC/Positive/FatPtr/stack.cbs b/clang/test/BSC/Positive/FatPtr/stack.cbs index b7a13ea08243..22b307601f5b 100644 --- a/clang/test/BSC/Positive/FatPtr/stack.cbs +++ b/clang/test/BSC/Positive/FatPtr/stack.cbs @@ -1,8 +1,5 @@ // RUN: %clang %s -enable-fat-ptr -o %t.output // RUN: %t.output -// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c -// RUN: %clang %t-rw.c -o %t-rw.output -// RUN: %t-rw.output // expected-no-diagnostics #include diff --git a/clang/test/BSC/Positive/FatPtr/string_literal.cbs b/clang/test/BSC/Positive/FatPtr/string_literal.cbs index 237200bff930..7460656d2b6d 100644 --- a/clang/test/BSC/Positive/FatPtr/string_literal.cbs +++ b/clang/test/BSC/Positive/FatPtr/string_literal.cbs @@ -1,8 +1,5 @@ // RUN: %clang %s -enable-fat-ptr -o %t.output // RUN: %t.output | FileCheck %s -// RUN: %clang %s -enable-fat-ptr -rewrite-bsc -o %t-rw.c -// RUN: %clang %t-rw.c -o %t-rw.output -// RUN: %t-rw.output | FileCheck %s // expected-no-diagnostics #include @@ -18,7 +15,7 @@ char *fat bar(char *fat p) { int main() { char *fat p = (char *fat)"1: Hello world!\n"; printf("%s", (char *)p); - foo((const char *fat)"2: Hello world!\n"); + foo((char *fat)"2: Hello world!\n"); char *fat ss[] = { (char *fat)"Hello", (char *fat)"world"}; printf("3: %s %s!\n", (char *)ss[0], (char *)ss[1]); printf("4: %s %s!\n", (char *)bar(ss[0]), (char *)bar(ss[1])); -- Gitee From 94561141e9ae9d4b56c1b371b9fe5a9fc48b881e Mon Sep 17 00:00:00 2001 From: zhaoxuhui Date: Fri, 21 Mar 2025 16:19:28 +0800 Subject: [PATCH 23/29] Revert "[FatPtr] Adjust the redundant check analysis results to be stored in the ASTContext, which optimizes the memory usage of Expr" This reverts commit 4f659828d67d50f0e8ccde7e32e6b78f43a9397d. --- clang/include/clang/AST/ASTContext.h | 1 - clang/include/clang/AST/Expr.h | 7 +++ .../Analysis/Analyses/BSC/BSCFatPtrCheck.h | 4 +- clang/lib/AST/BSC/TypeBSC.cpp | 2 +- clang/lib/Analysis/BSC/BSCFatPtrCheck.cpp | 33 ++++++------ clang/lib/Analysis/ThreadSafetyCommon.cpp | 6 +-- clang/lib/CodeGen/CGCall.cpp | 2 - clang/lib/Sema/BSC/SemaBSCFatPtr.cpp | 21 +++----- clang/lib/Sema/SemaExpr.cpp | 9 ++-- clang/lib/Sema/SemaStmt.cpp | 1 + .../FatPtr/redundant_check/func_call.cbs | 52 ------------------- .../FatPtr/redundant_check/if_stmt.cbs | 46 ---------------- .../FatPtr/redundant_check/member_visit.cbs | 52 ------------------- 13 files changed, 41 insertions(+), 195 deletions(-) delete mode 100644 clang/test/BSC/Positive/FatPtr/redundant_check/func_call.cbs delete mode 100644 clang/test/BSC/Positive/FatPtr/redundant_check/if_stmt.cbs delete mode 100644 clang/test/BSC/Positive/FatPtr/redundant_check/member_visit.cbs diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index 855103b0e9a4..64d6e5ec52a9 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -497,7 +497,6 @@ public: mutable llvm::DenseMap> BSCDesugaredMap; llvm::SmallVector InstantiationVec; - mutable llvm::DenseMap FatPtrCheckStatusMap; #endif diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h index 867dede1da1b..98615e310b26 100644 --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -151,6 +151,13 @@ public: bool IsDesugaredCastExpr = false; // Add BSC Member func desugar flag. bool IsDesugaredBSCMethodCall = false; + // Stores the check status of a fat pointer used in the expression. + // Possible values: + // Unchecked: 0 - No checks have been performed. + // KeyCheckedOnly: 1 - Only the key was checked. + // OffsetCheckedOnly: 2 - Only the offset was checked. + // BothChecked: 3 - Both the key and the offset were checked. + uint8_t FatPtrCheckedStatus = 0; // Default: Unchecked SourceLocation getExtendedTypeBeginLoc() { return EBLoc; } void setExtendedTypeBeginLoc(SourceLocation L) { EBLoc = L; } diff --git a/clang/include/clang/Analysis/Analyses/BSC/BSCFatPtrCheck.h b/clang/include/clang/Analysis/Analyses/BSC/BSCFatPtrCheck.h index f353e43e2e7c..e9df8841a989 100644 --- a/clang/include/clang/Analysis/Analyses/BSC/BSCFatPtrCheck.h +++ b/clang/include/clang/Analysis/Analyses/BSC/BSCFatPtrCheck.h @@ -1,4 +1,4 @@ -//===- BSCFatPtrCheck.h - FatPtr Check Reduntant Analysis for Source CFGs -*- BSC ---*// +//===- BSCNullabilityCheck.h - Nullability Check for Source CFGs -*- BSC ---*// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,7 +6,7 @@ // //===----------------------------------------------------------------------===// // -// This file implements BSC Fat Pointer Check Reduntant Analysis for source-level CFGs. +// This file implements BSC Pointer Nullability Check for source-level CFGs. // //===----------------------------------------------------------------------===// diff --git a/clang/lib/AST/BSC/TypeBSC.cpp b/clang/lib/AST/BSC/TypeBSC.cpp index 8b2d257d503c..375c94e78cd3 100644 --- a/clang/lib/AST/BSC/TypeBSC.cpp +++ b/clang/lib/AST/BSC/TypeBSC.cpp @@ -68,7 +68,7 @@ bool Type::hasBorrowFields() const { // T *fat, T *fat *, T *fat[n], struct S which has fat fields... bool QualType::hasFat() const { - const Type * Ty = getTypePtr(); + auto Ty = getTypePtr(); // getCanonicalType from FunctionProtoType will lose fat qualifier // of return type or parameter types, from ArrayType will lose fat // qualifier of element type, so we handle these two kinds type first. diff --git a/clang/lib/Analysis/BSC/BSCFatPtrCheck.cpp b/clang/lib/Analysis/BSC/BSCFatPtrCheck.cpp index e4a6e6d0e742..92e119b81d1e 100644 --- a/clang/lib/Analysis/BSC/BSCFatPtrCheck.cpp +++ b/clang/lib/Analysis/BSC/BSCFatPtrCheck.cpp @@ -257,7 +257,7 @@ void TransferFunctions::VisitUnaryOperator(UnaryOperator *UO) { FP = {VD, ""}; if (VD->getType().isFatPtrType() && CurrStatus.count(FP)) { // Label the check kind in the sema phase, for unary expression: *p; - Ctx.FatPtrCheckStatusMap[DRE] = static_cast(CurrStatus[FP]); + DRE->FatPtrCheckedStatus = static_cast(CurrStatus[FP]); // update the redundant check status CurrStatus[FP] = FatPtrCheckStatus::BothChecked; } @@ -268,7 +268,7 @@ void TransferFunctions::VisitUnaryOperator(UnaryOperator *UO) { if (FD->getType().isFatPtrType()) { VisitMEForFieldPath(ME, FP); if (CurrStatus.count(FP)) { - Ctx.FatPtrCheckStatusMap[ME] = static_cast(CurrStatus[FP]); + ME->FatPtrCheckedStatus = static_cast(CurrStatus[FP]); CurrStatus[FP] = FatPtrCheckStatus::BothChecked; } } @@ -311,7 +311,7 @@ void TransferFunctions::VisitMemberExpr(MemberExpr *ME) { FP = {VD, ""}; if (VD->getType().isFatPtrType() && CurrStatus.count(FP)) { // Label the check kind in the sema phase, for member expression: p -> a; - Ctx.FatPtrCheckStatusMap[DRE] = static_cast(CurrStatus[FP]); + DRE->FatPtrCheckedStatus = static_cast(CurrStatus[FP]); // update the redundant check status CurrStatus[FP] = FatPtrCheckStatus::BothChecked; } @@ -322,7 +322,7 @@ void TransferFunctions::VisitMemberExpr(MemberExpr *ME) { if (FD->getType().isFatPtrType()) { VisitMEForFieldPath(SubME, FP); if (CurrStatus.count(FP)) { - Ctx.FatPtrCheckStatusMap[SubME] = static_cast(CurrStatus[FP]); + SubME->FatPtrCheckedStatus = static_cast(CurrStatus[FP]); CurrStatus[FP] = FatPtrCheckStatus::BothChecked; } } @@ -344,22 +344,21 @@ void TransferFunctions::VisitMemberExpr(MemberExpr *ME) { bool TransferFunctions::mayFreeCallExpr(CallExpr *CE) { // Check if it is an direct call, for indirect call, we assuems it may free if (FunctionDecl *CalleeDecl = CE->getDirectCallee()) { - // Check if the function is in the whitelist - if (MayFreeFnWL.find(CalleeDecl->getNameAsString()) != MayFreeFnWL.end()) { - return false; - } // Add the CalleeDecl to the call graph CG.addToCallGraph(CalleeDecl); CallGraphNode *Node = CG.getNode(CalleeDecl); - // Current callee has no other function call in call graph if (!Node || Node->empty()) { - // If there is no function definition, it is considered mayfree - return !CalleeDecl->hasBody(); + // Callee Function definition is in the current module, and has no other function call + if (CalleeDecl->hasBody()) { + return false; + } + // Check if the function is in the whitelist + return MayFreeFnWL.find(CalleeDecl->getNameAsString()) == MayFreeFnWL.end(); } else { // Check if the CalleeDecl calls any function that may free heap objects bool mayFree = false; - for (CallGraphNode::iterator It = Node->begin(), end = Node->end(); - It != end; ++It) { + for (CallGraphNode::iterator It = Node->begin(); It != Node->end(); + ++It) { CallGraphNode *CalleeNode = It->Callee; if (!CalleeNode) continue; @@ -430,7 +429,7 @@ void TransferFunctions::VisitCStyleCastExpr(CStyleCastExpr *CSCE) { FP = {VD, ""}; if (VD->getType().isFatPtrType() && CurrStatus.count(FP)) { // Label the check kind in the sema phase, for CStyleCast expression: (int *)p; - Ctx.FatPtrCheckStatusMap[DRE] = static_cast(CurrStatus[FP]); + DRE->FatPtrCheckedStatus = static_cast(CurrStatus[FP]); // update the redundant check status CurrStatus[FP] = FatPtrCheckStatus::BothChecked; } @@ -441,7 +440,7 @@ void TransferFunctions::VisitCStyleCastExpr(CStyleCastExpr *CSCE) { if (FD->getType().isFatPtrType()) { VisitMEForFieldPath(ME, FP); if (CurrStatus.count(FP)) { - Ctx.FatPtrCheckStatusMap[ME] = static_cast(CurrStatus[FP]); + ME->FatPtrCheckedStatus = static_cast(CurrStatus[FP]); CurrStatus[FP] = FatPtrCheckStatus::BothChecked; } } @@ -456,7 +455,7 @@ void TransferFunctions::VisitArraySubscriptExpr(ArraySubscriptExpr *ASE) { FP = {VD, ""}; if (VD->getType().isFatPtrType() && CurrStatus.count(FP)) { // the offset check is not redundant, must be remained for array subscript expression: p[i]; - Ctx.FatPtrCheckStatusMap[DRE] = + DRE->FatPtrCheckedStatus = static_cast(CurrStatus[FP]) & static_cast(FatPtrCheckStatus::KeyCheckedOnly); // update the redundant check status to KeyCheckedOnly @@ -469,7 +468,7 @@ void TransferFunctions::VisitArraySubscriptExpr(ArraySubscriptExpr *ASE) { if (FD->getType().isFatPtrType()) { VisitMEForFieldPath(ME, FP); if (CurrStatus.count(FP)) { - Ctx.FatPtrCheckStatusMap[ME] = + ME->FatPtrCheckedStatus = static_cast(CurrStatus[FP]) & static_cast(FatPtrCheckStatus::KeyCheckedOnly); CurrStatus[FP] = FatPtrCheckStatus::BothChecked; diff --git a/clang/lib/Analysis/ThreadSafetyCommon.cpp b/clang/lib/Analysis/ThreadSafetyCommon.cpp index 732901769dde..89b71e7a37e4 100644 --- a/clang/lib/Analysis/ThreadSafetyCommon.cpp +++ b/clang/lib/Analysis/ThreadSafetyCommon.cpp @@ -462,7 +462,7 @@ til::SExpr *SExprBuilder::translateUnaryOperator(const UnaryOperator *UO, return new (Arena) til::Undefined(UO); case UO_AddrOf: -#if ENABLE_BSC + #if ENABLE_BSC case UO_AddrMut: case UO_AddrConst: case UO_AddrFat: @@ -484,10 +484,10 @@ til::SExpr *SExprBuilder::translateUnaryOperator(const UnaryOperator *UO, // We treat these as no-ops case UO_Deref: case UO_Plus: -#if ENABLE_BSC + #if ENABLE_BSC case UO_AddrMutDeref: case UO_AddrConstDeref: -#endif + #endif return translate(UO->getSubExpr(), Ctx); case UO_Minus: diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp index 366de5b41d51..4ff156690c82 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -4185,7 +4185,6 @@ void CodeGenFunction::EmitCallArgs( CallExpr::const_arg_iterator Arg = ArgRange.begin(); for (QualType Ty : ArgTypes) { assert(Arg != ArgRange.end() && "Running over edge of argument list!"); -#if ENABLE_BSC if (Ty.getCanonicalType()->isRecordType()) if (RecordDecl *RD = Ty.getCanonicalType()->getAs()->getDecl()) @@ -4193,7 +4192,6 @@ void CodeGenFunction::EmitCallArgs( ++Arg; continue; } -#endif assert( (isGenericMethod || Ty->isVariablyModifiedType() || Ty.getNonReferenceType()->isObjCRetainableType() || diff --git a/clang/lib/Sema/BSC/SemaBSCFatPtr.cpp b/clang/lib/Sema/BSC/SemaBSCFatPtr.cpp index c04f2ec8753d..21c3ed3c4eb0 100644 --- a/clang/lib/Sema/BSC/SemaBSCFatPtr.cpp +++ b/clang/lib/Sema/BSC/SemaBSCFatPtr.cpp @@ -2089,21 +2089,16 @@ MemberExpr *TransformFatPtr::BuildMemberForFatPtr(Expr *FatPtrBaseE, MemberExpr *TransformFatPtr::BuildMemberRawPtrAndInsertCheckCallForFatPtr( Expr *FatPtrBaseE) { SourceLocation SLoc = FatPtrBaseE->getBeginLoc(); - // Determine whether to remove redundant checks based on static analysis results. - // If the the fat ptr has been checked before, it can be safely removed. - if ((InsertCheckVersionCall || InsertCheckOffsetCall.first) && - SemaRef.Context.FatPtrCheckStatusMap.count(FatPtrBaseE)) { - uint8_t FatPtrCheckedStatus = SemaRef.Context.FatPtrCheckStatusMap[FatPtrBaseE]; - if (FatPtrCheckedStatus == 1 || FatPtrCheckedStatus == 3) { - InsertCheckVersionCall = false; - } - if (FatPtrCheckedStatus == 2 || FatPtrCheckedStatus == 3) { - InsertCheckOffsetCall.first = false; - InsertCheckOffsetCall.second = nullptr; + if ((InsertCheckVersionCall || InsertCheckOffsetCall.first) && FatPtrBaseE->FatPtrCheckedStatus) { + if (FatPtrBaseE->FatPtrCheckedStatus == 1 || FatPtrBaseE->FatPtrCheckedStatus == 3) { + InsertCheckVersionCall = false; + } + if (FatPtrBaseE->FatPtrCheckedStatus == 2 || FatPtrBaseE->FatPtrCheckedStatus == 3) { + InsertCheckOffsetCall.first = false; + InsertCheckOffsetCall.second = nullptr; + } } - } if (InsertCheckOffsetCall.first && InsertCheckVersionCall) { - // Build CallExpr to check version and offset. llvm::SmallVector Args{InsertCheckOffsetCall.second, FatPtrBaseE}; Expr *CE = BuildFatPtrCall(SLoc, "_check_version_and_offset", Args); CheckCallExpr.push(std::make_pair(FatPtrBaseE, CE)); diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 88048413a43f..1a65e55cd7b8 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -10033,13 +10033,12 @@ Sema::CheckAssignmentConstraints(QualType LHSType, ExprResult &RHS, QualType RHSType = RHS.get()->getType(); QualType OrigLHSType = LHSType; -#if ENABLE_BSC if (getLangOpts().BSC && getLangOpts().EnableFatPtr && Context.hasSameFatPtrType(LHSType, RHSType)) { Kind = CK_NoOp; return Compatible; } -#endif + // Get canonical types. We're not formatting these types, just comparing // them. LHSType = Context.getCanonicalType(LHSType).getUnqualifiedType(); @@ -18010,10 +18009,8 @@ bool Sema::DiagnoseAssignmentResult(AssignConvertType ConvTy, isInvalid = true; break; case Incompatible: -#if ENABLE_BSC if (getLangOpts().BSC && IsFatPtrEqualExpr(*this, DstType, SrcType)) return false; -#endif if (maybeDiagnoseAssignmentToFunction(*this, DstType, SrcExpr)) { if (Complained) *Complained = true; @@ -18026,7 +18023,7 @@ bool Sema::DiagnoseAssignmentResult(AssignConvertType ConvTy, isInvalid = true; MayHaveFunctionDiff = true; break; -#if ENABLE_BSC + #if ENABLE_BSC case IncompatibleOwnedPointer: case IncompatibleBorrowPointer: case IncompatibleFatPointer: @@ -18061,7 +18058,7 @@ bool Sema::DiagnoseAssignmentResult(AssignConvertType ConvTy, AssignmentAction ActionForDiag = Action; if (Action == AA_Passing_CFAudited) ActionForDiag = AA_Passing; -#if ENABLE_BSC + #if ENABLE_BSC SecondType = CompleteTraitType(SecondType); #endif diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp index 64a83ad41060..1ff622209128 100644 --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -1752,6 +1752,7 @@ Sema::ActOnDoStmt(SourceLocation DoLoc, Stmt *Body, ExprResult CondResult = CheckBooleanCondition(DoLoc, Cond); if (CondResult.isInvalid()) return StmtError(); + Cond = CondResult.get(); CondResult = ActOnFinishFullExpr(Cond, DoLoc, /*DiscardedValue*/ false); diff --git a/clang/test/BSC/Positive/FatPtr/redundant_check/func_call.cbs b/clang/test/BSC/Positive/FatPtr/redundant_check/func_call.cbs deleted file mode 100644 index 929d2a344982..000000000000 --- a/clang/test/BSC/Positive/FatPtr/redundant_check/func_call.cbs +++ /dev/null @@ -1,52 +0,0 @@ -// RUN: %clang %s -enable-fat-ptr -c -Xclang -ast-dump \ -// RUN: | FileCheck --strict-whitespace %s -// RUN: %clang %s -enable-fat-ptr -o %test.output - -#include - -void use_mayfree(int *fat p){ - checked_free((void *fat)p); -} - -void use_notfree(int *fat p){ - *p = 0; -} - -void test_call_mayfree() { - int *fat p = (int *fat)checked_malloc(sizeof(int)); - *p = 1; - use_mayfree(p); - *p = 2; -} - -void test_call_notfree() { - int *fat p = (int *fat)checked_malloc(sizeof(int)); - *p = 1; - use_notfree(p); - *p = 2; -} - -int main() { - test_call_mayfree(); - test_call_notfree(); -} - -// CHECK: FunctionDecl 0x{{[^ ]*}} <{{.*}}> line:{{.*}} used test_call_mayfree 'void (void)' - -// CHECK: DeclRefExpr 0x{{[^ ]*}} <{{.*}}> 'void ({{.*}})' lvalue Function 0x{{[^ ]*}} '_check_version_and_offset' 'void ({{.*}})' -// CHECK: BinaryOperator 0x{{[^ ]*}} <{{.*}}> 'int' '=' -// CHECK-NEXT: UnaryOperator 0x{{[^ ]*}} <{{.*}}> 'int' lvalue prefix '*' cannot overflow - -// CHECK: DeclRefExpr 0x{{[^ ]*}} <{{.*}}> 'void ({{.*}})' lvalue Function 0x{{[^ ]*}} '_check_version_and_offset' 'void ({{.*}})' -// CHECK: BinaryOperator 0x{{[^ ]*}} <{{.*}}> 'int' '=' -// CHECK-NEXT: UnaryOperator 0x{{[^ ]*}} <{{.*}}> 'int' lvalue prefix '*' cannot overflow - -// CHECK: FunctionDecl 0x{{[^ ]*}} <{{.*}}> line:{{.*}} used test_call_notfree 'void (void)' - -// CHECK: DeclRefExpr 0x{{[^ ]*}} <{{.*}}> 'void ({{.*}})' lvalue Function 0x{{[^ ]*}} '_check_version_and_offset' 'void ({{.*}})' -// CHECK: BinaryOperator 0x{{[^ ]*}} <{{.*}}> 'int' '=' -// CHECK-NEXT: UnaryOperator 0x{{[^ ]*}} <{{.*}}> 'int' lvalue prefix '*' cannot overflow - -// CHECK: DeclRefExpr 0x{{[^ ]*}} <{{.*}}> 'void ({{.*}})' lvalue Function 0x{{[^ ]*}} '_check_offset' 'void ({{.*}})' -// CHECK: BinaryOperator 0x{{[^ ]*}} <{{.*}}> 'int' '=' -// CHECK-NEXT: UnaryOperator 0x{{[^ ]*}} <{{.*}}> 'int' lvalue prefix '*' cannot overflow \ No newline at end of file diff --git a/clang/test/BSC/Positive/FatPtr/redundant_check/if_stmt.cbs b/clang/test/BSC/Positive/FatPtr/redundant_check/if_stmt.cbs deleted file mode 100644 index 4d9a6383aa67..000000000000 --- a/clang/test/BSC/Positive/FatPtr/redundant_check/if_stmt.cbs +++ /dev/null @@ -1,46 +0,0 @@ -// RUN: %clang %s -enable-fat-ptr -c -Xclang -ast-dump \ -// RUN: | FileCheck --strict-whitespace %s -// RUN: %clang %s -enable-fat-ptr -o %test.output - -#include - -int *fat unsure_func() { - int *fat p = (int *fat)checked_malloc(sizeof(int)); - // do some thing unsure - return p; -} -int *fat get_ptr(){ - return unsure_func(); -} - -void test_if_stmt() { - int *fat p = (int *fat)checked_malloc(sizeof(int)); - *p = 1; - if (p) { - p = get_ptr(); - } else { - *p = 2; - } - *p = 3; -} - -int main() { - test_if_stmt(); -} - -// CHECK: FunctionDecl 0x{{[^ ]*}} <{{.*}}> line:{{.*}} used test_if_stmt 'void (void)' - -// CHECK: DeclRefExpr 0x{{[^ ]*}} <{{.*}}> 'void ({{.*}})' lvalue Function 0x{{[^ ]*}} '_check_version_and_offset' 'void ({{.*}})' -// CHECK: BinaryOperator 0x{{[^ ]*}} <{{.*}}> 'int' '=' -// CHECK-NEXT: UnaryOperator 0x{{[^ ]*}} <{{.*}}> 'int' lvalue prefix '*' cannot overflow - -// CHECK: IfStmt 0x{{[^ ]*}} <{{.*}}> has_else -// CHECK: CompoundStmt 0x{{[^ ]*}} <{{.*}}> -// CHECK: CompoundStmt 0x{{[^ ]*}} <{{.*}}> -// CHECK-NOT: DeclRefExpr 0x{{[^ ]*}} <{{.*}}> 'void ({{.*}})' lvalue Function 0x{{[^ ]*}} '_check_version_and_offset' 'void ({{.*}})' -// CHECK: BinaryOperator 0x{{[^ ]*}} <{{.*}}> 'int' '=' -// CHECK-NEXT: UnaryOperator 0x{{[^ ]*}} <{{.*}}> 'int' lvalue prefix '*' cannot overflow - -// CHECK: DeclRefExpr 0x{{[^ ]*}} <{{.*}}> 'void ({{.*}})' lvalue Function 0x{{[^ ]*}} '_check_version_and_offset' 'void ({{.*}})' -// CHECK: BinaryOperator 0x{{[^ ]*}} <{{.*}}> 'int' '=' -// CHECK-NEXT: UnaryOperator 0x{{[^ ]*}} <{{.*}}> 'int' lvalue prefix '*' cannot overflow diff --git a/clang/test/BSC/Positive/FatPtr/redundant_check/member_visit.cbs b/clang/test/BSC/Positive/FatPtr/redundant_check/member_visit.cbs deleted file mode 100644 index 0ac958d9d6ac..000000000000 --- a/clang/test/BSC/Positive/FatPtr/redundant_check/member_visit.cbs +++ /dev/null @@ -1,52 +0,0 @@ -// RUN: %clang %s -enable-fat-ptr -c -Xclang -ast-dump \ -// RUN: | FileCheck --strict-whitespace %s -// RUN: %clang %s -enable-fat-ptr -o %test.output - - -#include - -typedef struct node node_fat; - -struct node { - int val; - double *fat weight; - node_fat *fat next; -}; - - - -void new_node(int n) { - node_fat *fat p = (node_fat *fat)checked_malloc(sizeof(node_fat)); - p->val = n; - p->next = nullptr; - p->weight = (double *fat)checked_malloc(sizeof(double)); - double a = *p->weight; -} - -int main() { - new_node(1); - return 0; -} - -// CHECK: FunctionDecl 0x{{[^ ]*}} <{{.*}}> line:{{.*}} used new_node 'void (int)' - -// CHECK: VarDecl 0x{{[^ ]*}} <{{.*}}> col:{{.*}} used p '_FatPtr':'struct _FatPtr' cinit - -// CHECK: DeclRefExpr 0x{{[^ ]*}} <{{.*}}> 'void ({{.*}})' lvalue Function 0x{{[^ ]*}} '_check_version_and_offset' 'void ({{.*}})' -// CHECK: BinaryOperator 0x{{[^ ]*}} <{{.*}}> 'int' '=' -// CHECK-NEXT: MemberExpr 0x{{[^ ]*}} <{{.*}}> 'int' lvalue ->val 0x{{[^ ]*}} - -// CHECK-NOT: DeclRefExpr 0x{{[^ ]*}} <{{.*}}> 'void ({{.*}})' lvalue Function 0x{{[^ ]*}} '_check_version_and_offset' 'void ({{.*}})' -// CHECK: BinaryOperator 0x{{[^ ]*}} <{{.*}}> '_FatPtr':'struct _FatPtr' '=' -// CHECK-NEXT: MemberExpr 0x{{[^ ]*}} <{{.*}}> '_FatPtr':'struct _FatPtr' lvalue ->next 0x{{[^ ]*}} - -// CHECK-NOT: DeclRefExpr 0x{{[^ ]*}} <{{.*}}> 'void ({{.*}})' lvalue Function 0x{{[^ ]*}} '_check_version_and_offset' 'void ({{.*}})' -// CHECK: BinaryOperator 0x{{[^ ]*}} <{{.*}}> '_FatPtr':'struct _FatPtr' '=' -// CHECK-NEXT: MemberExpr 0x{{[^ ]*}} <{{.*}}> '_FatPtr':'struct _FatPtr' lvalue ->weight 0x{{[^ ]*}} - -// CHECK: DeclRefExpr 0x{{[^ ]*}} <{{.*}}> 'void ({{.*}})' lvalue Function 0x{{[^ ]*}} '_check_version_and_offset' 'void ({{.*}})' -// CHECK: MemberExpr 0x{{[^ ]*}} <{{.*}}> '_FatPtr':'struct _FatPtr' lvalue ->weight 0x{{[^ ]*}} - -// CHECK: VarDecl 0x{{[^ ]*}} <{{.*}}> col:{{.*}} a 'double' cinit -// CHECK: UnaryOperator 0x{{[^ ]*}} <{{.*}}> 'double' lvalue prefix '*' cannot overflow -// CHECK: MemberExpr 0x{{[^ ]*}} <{{.*}}> '_FatPtr':'struct _FatPtr' lvalue ->weight 0x{{[^ ]*}} \ No newline at end of file -- Gitee From 7d5eeb085dbec1e135322d4340f0326c68f6f4d2 Mon Sep 17 00:00:00 2001 From: zhaoxuhui Date: Fri, 21 Mar 2025 16:27:44 +0800 Subject: [PATCH 24/29] Revert "modify bsc_fat_ptr.hbs and some bugfix" This reverts commit 6980f46f51645c069d27c17e26793c219c48403f. --- clang/include/clang/Sema/Sema.h | 1 + clang/lib/AST/DeclPrinter.cpp | 7 +- clang/lib/AST/Expr.cpp | 7 - clang/lib/AST/StmtPrinter.cpp | 10 +- clang/lib/Headers/bsc_include/bsc_fat_ptr.hbs | 27 +- clang/lib/Sema/BSC/SemaBSCFatPtr.cpp | 453 ++++++++---------- clang/lib/Sema/BSC/SemaDeclBSC.cpp | 13 +- clang/lib/Sema/SemaExpr.cpp | 23 +- clang/lib/Sema/SemaStmt.cpp | 6 + .../FatPtr/self_inc_or_dec_of_fat_ptr.cbs | 22 + .../heap/array_out_of_bounds1.cbs | 2 +- .../heap/array_out_of_bounds10.cbs | 2 +- .../heap/array_out_of_bounds11.cbs | 4 +- .../heap/array_out_of_bounds12.cbs | 2 +- .../heap/array_out_of_bounds13.cbs | 2 +- .../heap/array_out_of_bounds14.cbs | 2 +- .../heap/array_out_of_bounds15.cbs | 4 +- .../heap/array_out_of_bounds16.cbs | 2 +- .../heap/array_out_of_bounds17.cbs | 2 +- .../heap/array_out_of_bounds18.cbs | 6 +- .../heap/array_out_of_bounds19.cbs | 4 +- .../heap/array_out_of_bounds2.cbs | 2 +- .../heap/array_out_of_bounds20.cbs | 14 - .../heap/array_out_of_bounds21.cbs | 12 - .../heap/array_out_of_bounds22.cbs | 16 - .../heap/array_out_of_bounds23.cbs | 16 - .../heap/array_out_of_bounds24.cbs | 16 - .../heap/array_out_of_bounds3.cbs | 2 +- .../heap/array_out_of_bounds4.cbs | 2 +- .../heap/array_out_of_bounds5.cbs | 2 +- .../heap/array_out_of_bounds6.cbs | 2 +- .../heap/array_out_of_bounds7.cbs | 2 +- .../heap/array_out_of_bounds8.cbs | 2 +- .../heap/array_out_of_bounds9.cbs | 2 +- .../heap/buffer_overflow.cbs | 2 +- .../heap/double_free1.cbs | 6 +- .../heap/double_free2.cbs | 6 +- .../heap/double_free3.cbs | 8 +- .../heap/double_free4.cbs | 6 +- .../heap/use_after_free1.cbs | 4 +- .../heap/use_after_free2.cbs | 4 +- .../heap/use_after_free3.cbs | 4 +- .../heap/use_after_free4.cbs | 6 +- .../heap/use_after_free5.cbs | 8 +- .../heap/use_after_free6.cbs | 4 +- .../heap/use_after_free7.cbs | 4 +- .../heap/use_after_free8.cbs | 4 +- clang/test/BSC/Positive/FatPtr/heap.cbs | 77 +-- .../Positive/FatPtr/multi_level_pointer.cbs | 14 +- .../perform_anaysis/deref_loop/deref_loop.cbs | 39 ++ .../perform_anaysis/olden/bisort/bitonic.c | 303 ++++++++++++ .../olden/bisort_bsc/bitonic.cbs | 302 ++++++++++++ .../perform_anaysis/olden/treeadd/node.c | 233 +++++++++ .../perform_anaysis/olden/treeadd/tree.h | 25 + .../olden/treeadd_bsc/node.cbs | 193 ++++++++ .../olden/treeadd_bsc/tree.hbs | 29 ++ clang/test/BSC/Positive/FatPtr/stack.cbs | 10 - 57 files changed, 1457 insertions(+), 525 deletions(-) create mode 100644 clang/test/BSC/Negative/FatPtr/self_inc_or_dec_of_fat_ptr.cbs delete mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds20.cbs delete mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds21.cbs delete mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds22.cbs delete mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds23.cbs delete mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds24.cbs create mode 100644 clang/test/BSC/Positive/FatPtr/perform_anaysis/deref_loop/deref_loop.cbs create mode 100644 clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/bisort/bitonic.c create mode 100644 clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/bisort_bsc/bitonic.cbs create mode 100644 clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd/node.c create mode 100644 clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd/tree.h create mode 100644 clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd_bsc/node.cbs create mode 100644 clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd_bsc/tree.hbs diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 441767b1a5e3..efac59d36178 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -3227,6 +3227,7 @@ public: bool CheckFatQualTypeAssignment(QualType LHSType, QualType RHSType); bool CheckFatFunctionPointerType(QualType LHSType, Expr *RHSExpr); bool HasDiffFatTypeAtBothFunction(QualType LHSType, QualType RHSType); + bool CheckSelfIncOrDecOfFatPtr(Expr *E); void DesugarFunctionDeclWithFatPtr(FunctionDecl *FD); void DesugarRecordDeclWithFatPtr(RecordDecl *RD); void DesugarGlobalVarDeclWithFatPtr(VarDecl *VD); diff --git a/clang/lib/AST/DeclPrinter.cpp b/clang/lib/AST/DeclPrinter.cpp index 22075632e80d..c7cd98eebf87 100644 --- a/clang/lib/AST/DeclPrinter.cpp +++ b/clang/lib/AST/DeclPrinter.cpp @@ -1057,12 +1057,7 @@ void DeclPrinter::VisitVarDecl(VarDecl *D) { QualType T = D->getTypeSourceInfo() ? D->getTypeSourceInfo()->getType() : D->getASTContext().getUnqualifiedObjCPointerType(D->getType()); -#if ENABLE_BSC - if (Policy.RewriteBSC && Context.getLangOpts().EnableFatPtr && - T.isFatPtrType()) { - T = D->getType(); - } -#endif + if (!Policy.SuppressSpecifiers) { StorageClass SC = D->getStorageClass(); if (SC != SC_None) diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index 1cd4e8ceb469..025a70a754fa 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -2002,16 +2002,9 @@ ImplicitCastExpr *ImplicitCastExpr::Create(const ASTContext &C, QualType T, PathSize, FPO.requiresTrailingStorage())); // Per C++ [conv.lval]p3, lvalue-to-rvalue conversions on class and // std::nullptr_t have special semantics not captured by CK_LValueToRValue. -#if ENABLE_BSC - assert((Kind != CK_LValueToRValue || - (!(T->isNullPtrType() || T->getAsCXXRecordDecl()) || - T.isFatPtrType())) && - "invalid type for lvalue-to-rvalue conversion"); -#else assert((Kind != CK_LValueToRValue || !(T->isNullPtrType() || T->getAsCXXRecordDecl())) && "invalid type for lvalue-to-rvalue conversion"); -#endif ImplicitCastExpr *E = new (Buffer) ImplicitCastExpr(T, Kind, Operand, PathSize, FPO, VK); if (PathSize) diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp index 570d7bee40f4..444b2e161204 100644 --- a/clang/lib/AST/StmtPrinter.cpp +++ b/clang/lib/AST/StmtPrinter.cpp @@ -1853,14 +1853,8 @@ void StmtPrinter::VisitConvertVectorExpr(ConvertVectorExpr *Node) { void StmtPrinter::VisitInitListExpr(InitListExpr* Node) { if (Node->getSyntacticForm()) { -#if ENABLE_BSC - if (!(Policy.RewriteBSC && Context->getLangOpts().EnableFatPtr)) { -#endif - Visit(Node->getSyntacticForm()); - return; -#if ENABLE_BSC - } -#endif + Visit(Node->getSyntacticForm()); + return; } OS << "{"; diff --git a/clang/lib/Headers/bsc_include/bsc_fat_ptr.hbs b/clang/lib/Headers/bsc_include/bsc_fat_ptr.hbs index e70f88cbcf20..5b1c32a1cb87 100644 --- a/clang/lib/Headers/bsc_include/bsc_fat_ptr.hbs +++ b/clang/lib/Headers/bsc_include/bsc_fat_ptr.hbs @@ -36,8 +36,7 @@ enum _FatPtrErrorKind { FreeNotHeadPointer, DoubleFree, UseAfterFree, - OutOfBoundsAccess, - CannotCast + OutOfBoundsAccess }; static void _report_error(enum _FatPtrErrorKind kind, char *file_name, @@ -59,9 +58,6 @@ static void _report_error(enum _FatPtrErrorKind kind, char *file_name, case OutOfBoundsAccess: msg = "Pointer offset exceeds the allocation size"; break; - case CannotCast: - msg = "Current two-level fat pointer cannot be cast to two-level raw pointer"; - break; } fprintf(stderr, "Error: %s!\n", msg); if (kind == UseAfterFree || kind == OutOfBoundsAccess) @@ -84,7 +80,7 @@ static uint32_t _new_version_number() { return atomic_load(&uuid); } -static _FatPtr checked_malloc(size_t payload_bytes) { +_FatPtr checked_malloc(size_t payload_bytes) { if (payload_bytes == 0) _report_error(AllocateZero, 0, 0, 0); size_t all_size = payload_bytes + 8; @@ -93,11 +89,11 @@ static _FatPtr checked_malloc(size_t payload_bytes) { uint32_t ver = _new_version_number(); *p = ver; *(p + 1) = payload_bytes; - _FatPtr ptr = { .raw_ptr = (void*)(p + 2), .key_version = ver, .offset = 0 }; + _FatPtr ptr = { .raw_ptr = (T*)(p + 2), .key_version = ver, .offset = 0 }; return ptr; } -static void checked_free(_FatPtr ptr) { +void checked_free(_FatPtr ptr) { if (ptr.raw_ptr == nullptr) return; if (ptr.key_version == 0) { @@ -162,19 +158,4 @@ static __always_inline void _check_version_and_offset( } } -T** cast_two_level_fat_to_raw (T *fat *fat ptr) { - _FatPtr<_FatPtr> ptr_copy = ptr; - if (ptr_copy.key_version == 0) - _report_error(CannotCast, 0, 0, 0); - const uint8_t *head = (const uint8_t *)ptr_copy.raw_ptr - ptr_copy.offset - 8; - const struct _AllocationUnit *phead = (const struct _AllocationUnit *)head; - size_t fat_ptr_size = sizeof(void *) + 8; - if (phead->size < fat_ptr_size) - _report_error(CannotCast, 0, 0, 0); - int element_num = phead->size / fat_ptr_size; - T **raw = (T **)malloc(element_num * sizeof(T *)); - for (int i = 0; i < element_num; i++) - raw[i] = (T *)ptr_copy.raw_ptr[i]; - return raw; -} #endif /* BSC_FAT_PTR_HBS */ \ No newline at end of file diff --git a/clang/lib/Sema/BSC/SemaBSCFatPtr.cpp b/clang/lib/Sema/BSC/SemaBSCFatPtr.cpp index 21c3ed3c4eb0..563545220405 100644 --- a/clang/lib/Sema/BSC/SemaBSCFatPtr.cpp +++ b/clang/lib/Sema/BSC/SemaBSCFatPtr.cpp @@ -184,29 +184,30 @@ public: // 1) while (*p > 0) foo(); -> while (*p > 0) { foo(); } // 2) if (*p > 0 ) foo(); else foo(); // ->if (*p > 0) { foo(); } else { foo(); } -// 4. for SelfIncExpr or SelfDecExpr of FatPtr, we build a -// CommaExpr to replace it, for example: -// 1) int a = *p++; -> int a = *(p = p + 1, p - 1); -// 2) int a = *++p; -> int a = *(p = p + 1, p); -// 3) int a = *p--; -> int a = *(p = p - 1, p + 1); -// 4) int a = *--p; -> int a = *(p = p - 1, p); -// 5. for CompoundAssignOperator of FatPtr, build an assignment -// BinaryOperator to replace it, for example -// `p += 1` should be preprocessed to `p = p + 1` +// 4. for SelfIncExpr or SelfDecExpr of FatPtr, we split it +// into two expr. For example: +// 1) int a = *p++; -> int a = *p; p = p + 1; +// 2) int a = *++p; -> p = p + 1; int a = *p; // It should be noted that in this preprocess stage, we don't // desugar fat ptr pointer to _FatPtr struct, but only do some // stmt replacement. class FatPtrPreprocessor : public TreeTransform { typedef TreeTransform BaseTransform; - FunctionDecl *FD; - unsigned TempVarCounter = 0; - llvm::SmallVector CurrStmts; -public: - llvm::SmallSet PreprocessedSelfIncOrDecExpr; + // whether need to create a temporary variable to replace + // the current CallExpr. + bool NeedToReplace = false; + + // The first element of pair records if current SelfIncExpr or + // SelfDecExpr is postfix or not: + // 1) true means `p++` or `p--`, then the second element of pair + // will be inserted behind this SelfIncExpr or SelfDecExpr; + // 2) false means `++p` or `--p`, then the second element of pair + // will be inserted before this SelfIncExpr or SelfDecExpr. + std::pair IncOrDecFatPtrExpr = std::make_pair(false, nullptr); - FatPtrPreprocessor(Sema &SemaRef, FunctionDecl *Fd) - : BaseTransform(SemaRef), FD(Fd) {} +public: + FatPtrPreprocessor(Sema &SemaRef) : BaseTransform(SemaRef) {} StmtResult TransformCompoundStmt(CompoundStmt *CS); StmtResult TransformCompoundStmt(CompoundStmt *CS, bool IsStmtExpr); StmtResult TransformIfStmt(IfStmt *IS); @@ -218,15 +219,13 @@ public: StmtResult TransformDefaultStmt(DefaultStmt *DS); StmtResult TransformDeclStmt(DeclStmt *DS); ExprResult TransformInitListExpr(InitListExpr *ILE); + ExprResult TransformConditionalOperator(ConditionalOperator *CO); ExprResult TransformUnaryOperator(UnaryOperator *UO); ExprResult TransformBinaryOperator(BinaryOperator *BO); - ExprResult TransformCStyleCastExpr(CStyleCastExpr *CSCE); - ExprResult TransformCompoundAssignOperator(CompoundAssignOperator *CAO); ExprResult TransformImplicitCastExpr(ImplicitCastExpr *ICE); private: Stmt *BuildCompoundStmtForIfOrLoopStmtBody(Stmt *Body); - ImplicitCastExpr *ReplaceWithTemporaryVarDecl(Expr *E); }; StmtResult FatPtrPreprocessor::TransformCompoundStmt(CompoundStmt *CS) { @@ -237,8 +236,7 @@ StmtResult FatPtrPreprocessor::TransformCompoundStmt(CompoundStmt *CS, bool IsStmtExpr) { if (CS == nullptr) return CS; - llvm::SmallVector PrevStmts = CurrStmts; - CurrStmts = llvm::SmallVector(); + llvm::SmallVector Stmts; for (Stmt *S : CS->body()) { if (auto DS = dyn_cast(S)) { if (!DS->isSingleDecl()) { @@ -247,18 +245,41 @@ StmtResult FatPtrPreprocessor::TransformCompoundStmt(CompoundStmt *CS, for (auto D : DS->decls()) { DeclStmt *NewDS = new (SemaRef.Context) DeclStmt(DeclGroupRef(D), D->getLocation(), D->getLocation()); - CurrStmts.push_back(BaseTransform::TransformStmt(NewDS).get()); + Stmt *NewS = BaseTransform::TransformStmt(NewDS).get(); + if (IncOrDecFatPtrExpr.second) { + if (IncOrDecFatPtrExpr.first) { + Stmts.push_back(NewS); + Stmts.push_back(IncOrDecFatPtrExpr.second); + } else { + Stmts.push_back(IncOrDecFatPtrExpr.second); + Stmts.push_back(NewS); + } + IncOrDecFatPtrExpr.second = nullptr; + } else { + Stmts.push_back(NewS); + } } continue; } } - CurrStmts.push_back(BaseTransform::TransformStmt(S).get()); + Stmt *NewS = BaseTransform::TransformStmt(S).get(); + if (IncOrDecFatPtrExpr.second) { + if (IncOrDecFatPtrExpr.first) { + Stmts.push_back(NewS); + Stmts.push_back(IncOrDecFatPtrExpr.second); + } else { + Stmts.push_back(IncOrDecFatPtrExpr.second); + Stmts.push_back(NewS); + } + IncOrDecFatPtrExpr.second = nullptr; + } else { + Stmts.push_back(NewS); + } } CompoundStmt *NewCS = CompoundStmt::Create( - SemaRef.Context, CurrStmts, FPOptionsOverride(), CS->getLBracLoc(), + SemaRef.Context, Stmts, FPOptionsOverride(), CS->getLBracLoc(), CS->getRBracLoc(), CS->getSafeSpecifier(), CS->getSafeSpecifierLoc(), CS->getCompSafeZoneSpecifier()); - CurrStmts = PrevStmts; return NewCS; } @@ -321,7 +342,8 @@ StmtResult FatPtrPreprocessor::TransformDefaultStmt(DefaultStmt *DS) { StmtResult FatPtrPreprocessor::TransformDeclStmt(DeclStmt *DS) { assert(DS->isSingleDecl() && "DeclStmt can only contain a Decl here"); - if (VarDecl *VD = dyn_cast(DS->getSingleDecl())) { + Decl *D = DS->getSingleDecl(); + if (VarDecl *VD = dyn_cast(D)) { if (VD->getInit()) VD->setInit(BaseTransform::TransformExpr(VD->getInit()).get()); } @@ -334,95 +356,48 @@ ExprResult FatPtrPreprocessor::TransformInitListExpr(InitListExpr *ILE) { return ILE; } +ExprResult +FatPtrPreprocessor::TransformConditionalOperator(ConditionalOperator *CO) { + Expr *CondExpr = BaseTransform::TransformExpr(CO->getCond()).get(); + Expr *LHS = BaseTransform::TransformExpr(CO->getLHS()).get(); + Expr *RHS = BaseTransform::TransformExpr(CO->getRHS()).get(); + return BaseTransform::RebuildConditionalOperator( + CondExpr, CO->getQuestionLoc(), LHS, CO->getColonLoc(), RHS); +} + ExprResult FatPtrPreprocessor::TransformUnaryOperator(UnaryOperator *UO) { + // 1) int a = *p++; -> int a = *p; p = p + 1; + // 2) int a = *++p; -> p = p + 1; int a = *p; SourceLocation SLoc = UO->getBeginLoc(); Expr *SubE = UO->getSubExpr(); - QualType SubQT = SubE->getType(); - if ((UO->isPrefix() || UO->isPostfix()) && SubQT.isFatPtrType()) { - SubE = SubE->IgnoreParens(); - bool IsPostfix = UO->isPostfix(); - bool IsInc = UO->isIncrementOp(); - // Build the first expr of CommaExpr: - // 1) p = p + 1 for p++/++p; 2) p = p - 1 for p--/--p; - Expr *One = IntegerLiteral::Create(SemaRef.Context, llvm::APInt(32, 1), - SemaRef.Context.IntTy, SLoc); - Expr *FirstRHS = - SemaRef.CreateBuiltinBinOp(SLoc, IsInc ? BO_Add : BO_Sub, SubE, One) + if ((UO->isPrefix() || UO->isPostfix()) && SubE->getType().isFatPtrType()) { + // Build p = p + 1 for p++/++p, build p = p - 1 for p--/--p + Expr *InitRHS = IntegerLiteral::Create(SemaRef.Context, llvm::APInt(32, 1), + SemaRef.Context.IntTy, SLoc); + Expr *InitE = + SemaRef + .CreateBuiltinBinOp(SLoc, UO->isIncrementOp() ? BO_Add : BO_Sub, + SubE, InitRHS) .get(); - Expr *FirstE = - SemaRef.CreateBuiltinBinOp(SLoc, BO_Assign, SubE, FirstRHS).get(); - // Build the second expr of CommaExpr: - // 1) p for ++p/--p; 2) p - 1 for p++; 3) p + 1 for p-- - Expr *SecondE = ImplicitCastExpr::Create(SemaRef.Context, SubQT, - CK_LValueToRValue, SubE, nullptr, - VK_PRValue, FPOptionsOverride()); - if (IsPostfix) - SecondE = - SemaRef - .CreateBuiltinBinOp(SLoc, IsInc ? BO_Sub : BO_Add, SecondE, One) - .get(); - // Build CommaExpr and wrap it in a ParenExpr. - Expr *CommaE = - SemaRef.CreateBuiltinBinOp(SLoc, BO_Comma, FirstE, SecondE).get(); - Expr *ParenE = - new (SemaRef.Context) ParenExpr(SLoc, UO->getEndLoc(), CommaE); - PreprocessedSelfIncOrDecExpr.insert(ParenE); - return ParenE; - } - UO->setSubExpr(isa(SubE->IgnoreParens()) && SubQT.isFatPtrType() - ? ReplaceWithTemporaryVarDecl(SubE->IgnoreParens()) - : BaseTransform::TransformExpr(SubE).get()); + IncOrDecFatPtrExpr.first = UO->isPostfix(); + IncOrDecFatPtrExpr.second = + SemaRef.CreateBuiltinBinOp(SLoc, BO_Assign, SubE, InitE).get(); + return ImplicitCastExpr::Create(SemaRef.Context, SubE->getType(), + CK_LValueToRValue, SubE, nullptr, + VK_PRValue, FPOptionsOverride()); + } + UO->setSubExpr(BaseTransform::TransformExpr(SubE).get()); return UO; } ExprResult FatPtrPreprocessor::TransformBinaryOperator(BinaryOperator *BO) { Expr *LHS = BO->getLHS(); Expr *RHS = BO->getRHS(); - if (BO->isAdditiveOp()) { - BO->setLHS(isa(LHS->IgnoreParens()) && - LHS->getType().isFatPtrType() - ? ReplaceWithTemporaryVarDecl(LHS->IgnoreParens()) - : BaseTransform::TransformExpr(LHS).get()); - BO->setRHS(isa(RHS->IgnoreParens()) && - RHS->getType().isFatPtrType() - ? ReplaceWithTemporaryVarDecl(RHS->IgnoreParens()) - : BaseTransform::TransformExpr(RHS).get()); - } else { - BO->setLHS(BaseTransform::TransformExpr(LHS).get()); - BO->setRHS(BaseTransform::TransformExpr(RHS).get()); - } + BO->setLHS(BaseTransform::TransformExpr(LHS).get()); + BO->setRHS(BaseTransform::TransformExpr(RHS).get()); return BO; } -ExprResult FatPtrPreprocessor::TransformCompoundAssignOperator( - CompoundAssignOperator *CAO) { - BinaryOperator::Opcode Op = CAO->getOpcode(); - if (CAO->getType().isFatPtrType() && - (Op == BO_AddAssign || Op == BO_SubAssign)) { - Expr *LHS = CAO->getLHS(); - Expr *RHS = CAO->getRHS(); - SourceLocation SLoc = CAO->getBeginLoc(); - Expr *NewLHS = LHS; - Expr *NewRHS = SemaRef - .CreateBuiltinBinOp( - SLoc, Op == BO_AddAssign ? BO_Add : BO_Sub, LHS, RHS) - .get(); - Expr *New = - SemaRef.CreateBuiltinBinOp(SLoc, BO_Assign, NewLHS, NewRHS).get(); - return BaseTransform::TransformExpr(New); - } - return CAO; -} - -ExprResult FatPtrPreprocessor::TransformCStyleCastExpr(CStyleCastExpr *CSCE) { - Expr *SubE = CSCE->getSubExpr(); - CSCE->setSubExpr(isa(SubE->IgnoreParens()) && - SubE->getType().isFatPtrType() - ? ReplaceWithTemporaryVarDecl(SubE->IgnoreParens()) - : BaseTransform::TransformExpr(SubE).get()); - return CSCE; -} - ExprResult FatPtrPreprocessor::TransformImplicitCastExpr(ImplicitCastExpr *ICE) { ICE->setSubExpr(BaseTransform::TransformExpr(ICE->getSubExpr()).get()); @@ -432,41 +407,28 @@ FatPtrPreprocessor::TransformImplicitCastExpr(ImplicitCastExpr *ICE) { // If body of IfStmt/WhileStmt/DoStmt/ForStmt is not CompoundStmt, // we build CompoundStmt and add original body into it. Stmt *FatPtrPreprocessor::BuildCompoundStmtForIfOrLoopStmtBody(Stmt *Body) { - if (isa(Body)) + if (isa(Body) && !IncOrDecFatPtrExpr.second) return Body; llvm::SmallVector Statements; - if (!isa(Body)) { + if (auto CS = dyn_cast(Body)) { + for (Stmt *S : CS->body()) + Statements.push_back(S); + } else if (!isa(Body)) { Statements.push_back(Body); } + + // IncExpr of ForStmt maybe SelfIncExpr or SelfDecExpr of fat ptr, + // we add it in the end of ForStmt body, for example: + // for (;;p++) {} -> for (;;p) { p = p + 1; } + if (IncOrDecFatPtrExpr.second) { + Statements.push_back(IncOrDecFatPtrExpr.second); + IncOrDecFatPtrExpr.second = nullptr; + } + return CompoundStmt::Create(SemaRef.Context, Statements, FPOptionsOverride(), Body->getBeginLoc(), Body->getEndLoc()); } -// If return type of a CallExpr is fat ptr type, build a temporary -// VarDecl and return the corresponding DeclRefExpr. -ImplicitCastExpr *FatPtrPreprocessor::ReplaceWithTemporaryVarDecl(Expr *E) { - assert(isa(E) && E->getType().isFatPtrType() && - "Only replace call expr whose return type is fat ptr"); - auto CE = dyn_cast(E); - for (unsigned i = 0; i < CE->getNumArgs(); i++) - CE->setArg(i, BaseTransform::TransformExpr(CE->getArg(i)).get()); - SourceLocation SLoc = CE->getBeginLoc(); - QualType QT = CE->getType(); - std::string Name = "_FatPtr_temp_" + std::to_string(TempVarCounter++); - VarDecl *VD = - VarDecl::Create(SemaRef.Context, FD, SLoc, SLoc, - &SemaRef.Context.Idents.get(Name), QT, nullptr, SC_None); - VD->setInit(CE); - VD->markUsed(SemaRef.Context); - DeclStmt *DS = new (SemaRef.Context) DeclStmt(DeclGroupRef(VD), SLoc, SLoc); - CurrStmts.push_back(DS); - DeclRefExpr *DRE = DeclRefExpr::Create( - SemaRef.Context, NestedNameSpecifierLoc(), SourceLocation(), VD, false, - SLoc, CE->getType(), VK_LValue); - return ImplicitCastExpr::Create(SemaRef.Context, QT, CK_LValueToRValue, DRE, - nullptr, VK_PRValue, FPOptionsOverride()); -} - // Desugar fat ptr for FunctionDecl, VarDecl, RecordDecl and TypedefNameDecl class TransformFatPtr : public TreeTransform { typedef TreeTransform BaseTransform; @@ -516,7 +478,6 @@ class TransformFatPtr : public TreeTransform { // such as `p->arr[3]` or `p->a.b.arr[3]` // we should check offset to avoid out of bounds. ArraySubscriptExpr *CurrentASEToAccessMemberThroughFatPtr = nullptr; - llvm::SmallSet PreprocessedSelfIncOrDecExpr; public: TransformFatPtr(Sema &SemaRef) : BaseTransform(SemaRef) {} @@ -551,6 +512,7 @@ public: StmtResult TransformReturnStmt(ReturnStmt *RS); ExprResult TransformDeclRefExpr(DeclRefExpr *DRE); ExprResult TransformImplicitCastExpr(ImplicitCastExpr *ICE); + ExprResult TransformParenExpr(ParenExpr *PE); ExprResult TransformCompoundLiteralExpr(CompoundLiteralExpr *CLE); ExprResult TransformInitListExpr(InitListExpr *ILE); ExprResult TransformCallExpr(CallExpr *CE); @@ -559,9 +521,7 @@ public: ExprResult TransformUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *UETT); ExprResult TransformMemberExpr(MemberExpr *ME); ExprResult TransformConditionalOperator(ConditionalOperator *CO); - - ExprResult TransformParenExpr(ParenExpr *PE); - ExprResult HandlePEFromPreprocessedSelfIncOrDecExpr(ParenExpr *PE); + ExprResult TransformCompoundAssignOperator(CompoundAssignOperator *CAO); ExprResult TransformBinaryOperator(BinaryOperator *BO); ExprResult HandleBOFatPtrAddOrSubInteger(BinaryOperator *BO); @@ -588,8 +548,7 @@ private: QualType DesugarFatPtrType(SourceLocation SLoc, QualType T); MemberExpr *BuildMemberForFatPtr(Expr *FatPtrBaseE, std::string FieldName); Expr *BuildFatPtrCall(SourceLocation SLoc, std::string FDName, - llvm::SmallVector &Args, - bool ArgsAreReady = false); + llvm::SmallVector &Args); MemberExpr *BuildMemberRawPtrAndInsertCheckCallForFatPtr(Expr *FatPtrBaseE); Expr *BuildFatPtrCStyleCastExpr(SourceLocation SLoc, QualType ToType, Expr *SubExpr); Expr *BuildFatPtrCompoundLiteralExprForRawPtr(Expr *RawPtrE, QualType QT); @@ -622,9 +581,9 @@ void TransformFatPtr::TransformFunctionDecl() { return; // First traversal: do preprocess - FatPtrPreprocessor Preprocessor(SemaRef, FD); + FatPtrPreprocessor Preprocessor(SemaRef); FD->setBody(Preprocessor.TransformStmt(FD->getBody()).get()); - PreprocessedSelfIncOrDecExpr = Preprocessor.PreprocessedSelfIncOrDecExpr; + // Second traversal: // find local vars whose address is taken by `&fat` expr. Finder.VisitStmt(FD->getBody()); @@ -1111,9 +1070,6 @@ ExprResult TransformFatPtr::TransformImplicitCastExpr(ImplicitCastExpr *ICE) { ExprResult TransformFatPtr::TransformParenExpr(ParenExpr *PE) { SourceLocation SLoc = PE->getBeginLoc(); - if (DesugarToMemberPtr && PreprocessedSelfIncOrDecExpr.count(PE)) { - return HandlePEFromPreprocessedSelfIncOrDecExpr(PE); - } QualType QT = PE->getType(); if (DesugarToMemberPtr && QT.isFatPtrType()) { QT = DesugarFatPtrType(SLoc, QT.getFatPtrPointeeType()); @@ -1126,61 +1082,6 @@ ExprResult TransformFatPtr::TransformParenExpr(ParenExpr *PE) { return PE; } -ExprResult -TransformFatPtr::HandlePEFromPreprocessedSelfIncOrDecExpr(ParenExpr *PE) { - SourceLocation SLoc = PE->getBeginLoc(); - PE->setType(DesugarFatPtrType(SLoc, PE->getType())); - - assert(isa(PE->getSubExpr())); - auto BO = dyn_cast(PE->getSubExpr()); - assert(BO->getOpcode() == BO_Comma); - DesugarToMemberPtr = false; - if (!InsertCheckVersionCall && !InsertCheckOffsetCall.first) { - BO->setLHS(BaseTransform::TransformExpr(BO->getLHS()).get()); - BO->setRHS(BaseTransform::TransformExpr(BO->getRHS()).get()); - PE->setSubExpr(BO); - } else { - llvm::SmallVector Args{InsertCheckOffsetCall.second}; - InsertCheckOffsetCall.first = false; - InsertCheckVersionCall = false; - Expr *FirstE = BaseTransform::TransformExpr(BO->getLHS()).get(); - Expr *ThirdE = BaseTransform::TransformExpr(BO->getRHS()).get(); - Expr *SecondE; - if (auto CLE = dyn_cast(ThirdE)) { - ThirdE = SemaRef.DefaultFunctionArrayLvalueConversion(CLE).get(); - // *p++ has been preprocessed to *(p = p + 1, p - 1), - // here we will desugar it to - // @code - // *(p = (_FatPtr) {p.ptr + 1, p.key_version, p.offset + 4}, - // _check_version_and_offset(4, p.ptr - 1, - // p.key_version, p.offset - 4), - // (_FatPtr) {p.ptr - 1, p.key_version, p.offset - 4}).ptr - // @endcode - assert(isa(CLE->getInitializer())); - auto ILE = dyn_cast(CLE->getInitializer()); - for (unsigned i = 0; i < ILE->getNumInits(); i++) - Args.push_back(ILE->getInit(i)); - SecondE = BuildFatPtrCall(SLoc, "_check_version_and_offset", Args, true); - } else if (auto ICE = dyn_cast(ThirdE)) { - // `*++p` has been preprocessed to `*(p = p + 1, p)`, - // here we desugar it to - // @code - // *(p = (_FatPtr) {p.ptr + 1, p.key_version, p.offset + 4}, - // _check_version_and_offset(4, p.ptr, p.key_version, p.offset), - // p).ptr - // @endcode - Args.push_back(ICE->getSubExpr()); - SecondE = BuildFatPtrCall(SLoc, "_check_version_and_offset", Args); - } - Expr *FirstSecondE = - SemaRef.CreateBuiltinBinOp(SLoc, BO_Comma, FirstE, SecondE).get(); - Expr *FirstSecondThirdE = - SemaRef.CreateBuiltinBinOp(SLoc, BO_Comma, FirstSecondE, ThirdE).get(); - PE->setSubExpr(FirstSecondThirdE); - } - return BuildMemberForFatPtr(PE, "raw_ptr"); -} - StmtResult TransformFatPtr::TransformDeclStmt(DeclStmt *DS) { assert(DS->isSingleDecl() && "DeclStmt can only contain a Decl here"); Decl *D = DS->getSingleDecl(); @@ -1233,19 +1134,16 @@ ExprResult TransformFatPtr::TransformCallExpr(CallExpr *CE) { MemberRawPtr = BuildMemberRawPtrAndInsertCheckCallForFatPtr(CE); Decl *CalleeD = CE->getCalleeDecl(); - if (auto CalleeFD = dyn_cast(CalleeD)) { - // Desugar callee function type - QualType CalleeFDType = CalleeFD->getType(); - if (CalleeFDType.hasFat()) { - if (auto CalleeICE = dyn_cast(CE->getCallee())) { - CalleeFDType = DesugarFatPtrType(SLoc, CalleeFD->getType()); - CalleeICE->setType(SemaRef.Context.getPointerType(CalleeFDType)); - if (auto CalleeDRE = dyn_cast(CalleeICE->getSubExpr())) { - CalleeDRE->setType(CalleeFDType); - } + if (CalleeD == FD) { + // For self-call Expr, desugar the callee function type + if (auto CalleeICE = dyn_cast(CE->getCallee())) { + CalleeICE->setType(SemaRef.Context.getPointerType(FD->getType())); + if (auto CalleeDRE = dyn_cast(CalleeICE->getSubExpr())) { + CalleeDRE->setType(FD->getType()); } } - // Desugar argument expr + } + if (auto CalleeFD = dyn_cast(CalleeD)) { for (unsigned i = 0; i < CE->getNumArgs(); i++) { Expr *ArgE = CE->getArg(i); if (i < CalleeFD->getNumParams() && CalleeFD->getParamDecl(i) && @@ -1349,11 +1247,10 @@ TransformFatPtr::TransformArraySubscriptExpr(ArraySubscriptExpr *ASE) { ExprResult TransformFatPtr::TransformCStyleCastExpr(CStyleCastExpr *CSCE) { SourceLocation SLoc = CSCE->getBeginLoc(); - QualType QT = DesugarFatPtrType(SLoc, CSCE->getType()); - CSCE->setType(QT); + QualType QT = CSCE->getType(); Expr *SubE = CSCE->getSubExpr(); - QualType SubQT = SubE->getType(); - if (QT->isPointerType() && SubQT.isFatPtrType()) { + QualType SubQT = SubE->getType(); + if (QT->isPointerType() && !QT.isFatPtrType() && SubQT.isFatPtrType()) { // desuagr `(int*)fat_p` to `(int*)fat_p.raw_ptr` CSCE->setCastKind(CK_NoOp); DesugarToMemberPtr = true; @@ -1364,19 +1261,14 @@ ExprResult TransformFatPtr::TransformCStyleCastExpr(CStyleCastExpr *CSCE) { IntegerLiteral::Create(SemaRef.Context, llvm::APInt(32, 1), SemaRef.Context.getIntTypeForBitwidth(32, true), SLoc); InsertCheckOffsetCall.second = OffsetExpr; - } else if (QT.isFatPtrType() && SubQT->isPointerType() && - !SubQT.isFatPtrType()) { + } else if (QT.isFatPtrType() && SubQT->isPointerType() && !SubQT.isFatPtrType()) { + QT = DesugarFatPtrType(SLoc, QT); // desugar `(int *fat)raw_p` to `(_FatPtr) { raw_p, 0, 0 }` Expr *SubRawPtrE = BaseTransform::TransformExpr(SubE).get(); Expr *FatPtrE = BuildFatPtrCompoundLiteralExprForRawPtr(SubRawPtrE, QT); return ImplicitCastExpr::Create(SemaRef.Context, QT, CK_NoOp, FatPtrE, nullptr, VK_PRValue, FPOptionsOverride()); } else if (QT.isFatPtrType() && SubQT.isFatPtrType()) { - // desuagr `(float *fat)fat_int` to - // @code - // (_FatPtr) { fat_int.raw_ptr, fat_int.key_version, - // fat_int.offset } - //@endcode if (DesugarToMemberPtr) { QualType ToPointerTy = SemaRef.Context.getPointerType(QT.getFatPtrPointeeType()); @@ -1385,8 +1277,8 @@ ExprResult TransformFatPtr::TransformCStyleCastExpr(CStyleCastExpr *CSCE) { CK_NoOp, Res, nullptr, VK_PRValue, FPOptionsOverride()); return Res; - } - return BuildFatPtrCStyleCastExpr(SLoc, QT, SubE); + } + return BaseTransform::TransformExpr(SubE).get(); } CSCE->setSubExpr(BaseTransform::TransformExpr(SubE).get()); return CSCE; @@ -1441,6 +1333,48 @@ TransformFatPtr::TransformConditionalOperator(ConditionalOperator *CO) { CondExpr, CO->getQuestionLoc(), LHS, CO->getColonLoc(), RHS); } +ExprResult +TransformFatPtr::TransformCompoundAssignOperator(CompoundAssignOperator *CAO) { + SourceLocation SLoc = CAO->getBeginLoc(); + QualType QT = DesugarFatPtrType(SLoc, CAO->getType()); + CAO->setType(QT); + BinaryOperator::Opcode Op = CAO->getOpcode(); + CAO->setLHS(BaseTransform::TransformExpr(CAO->getLHS()).get()); + CAO->setRHS(BaseTransform::TransformExpr(CAO->getRHS()).get()); + if (QT.isFatPtrType() && (Op == BO_AddAssign || Op == BO_SubAssign)) { + // `p += 1` should be desugared to + // `p = (_FatPtr) { p.raw_ptr + 1, p.key_version, p.offset + 1 * + // sizeof(int) }` + Expr *LHS = CAO->getLHS(); + Expr *RHS = CAO->getRHS(); + bool OpIsAddAssign = Op == BO_AddAssign ? true : false; + Expr *FirstInitE = + SemaRef + .CreateBuiltinBinOp(SLoc, OpIsAddAssign ? BO_Add : BO_Sub, + BuildMemberForFatPtr(LHS, "raw_ptr"), RHS) + .get(); + Expr *SecondInitE = BuildMemberForFatPtr(LHS, "key_version"); + Expr *ThirdInitLHS = BuildMemberForFatPtr(LHS, "offset"); + QualType PointeeQT = LHS->getType().getFatPtrPointeeType(); + QualType Int32Ty = SemaRef.Context.getIntTypeForBitwidth(32, true); + Expr *ThirdInitRHSRHS = IntegerLiteral::Create( + SemaRef.Context, + llvm::APInt(32, SemaRef.Context.getTypeSize(PointeeQT) / 8), Int32Ty, + SLoc); + Expr *ThirdInitRHS = + SemaRef.CreateBuiltinBinOp(SLoc, BO_Mul, RHS, ThirdInitRHSRHS).get(); + Expr *ThirdInitE = + SemaRef + .CreateBuiltinBinOp(SLoc, OpIsAddAssign ? BO_Add : BO_Sub, + ThirdInitLHS, ThirdInitRHS) + .get(); + RHS = + BuildFatPtrCompoundLiteralExpr(FirstInitE, SecondInitE, ThirdInitE, QT); + return SemaRef.CreateBuiltinBinOp(SLoc, BO_Assign, LHS, RHS); + } + return CAO; +} + // BinaryOperator includes: // reassign fat ptr : p1 = p2, p1 = nullptr // comparison between fat ptr : p1 != p2, p == nullptr, p1 < p2 @@ -1684,8 +1618,6 @@ ExprResult TransformFatPtr::HandleUOAddrFatOnExprWithFatPtrBase( SemaRef .CreateBuiltinBinOp(SLoc, BO_Sub, ThirdInitLHSLHS, ThirdInitLHSRHS) .get(); - ThirdInitLHS = - new (SemaRef.Context) ParenExpr(SLoc, UO->getEndLoc(), ThirdInitLHS); QualType Int32Ty = SemaRef.Context.getIntTypeForBitwidth(32, true); ThirdInitLHS = SemaRef @@ -1879,21 +1811,6 @@ ExprResult TransformFatPtr::TransformMemberExpr(MemberExpr *ME) { SemaRef.CreateBuiltinBinOp(SLoc, BO_Add, OffsetExprLHS, OffsetExprRHS).get(); } ME->setBase(BaseTransform::TransformExpr(ME->getBase()).get()); - // The type of base expr maybe desugared to another record type, - // so we should bind MemberExpr to the according FieldDecl of - // desugared record type - if (auto RT = - dyn_cast(ME->getBase()->getType().getCanonicalType())) { - if (auto RD = dyn_cast(RT->getDecl())) { - if (const FieldDecl *FD = dyn_cast(ME->getMemberDecl())) { - RecordDecl::field_iterator it = RD->field_begin(); - for (unsigned i = 0; i < FD->getFieldIndex(); i++) - it++; - FieldDecl *NewFD = *it; - ME->setMemberDecl(NewFD); - } - } - } return MemberRawPtr ? MemberRawPtr : ME; } @@ -1904,12 +1821,9 @@ ExprResult TransformFatPtr::TransformMemberExpr(MemberExpr *ME) { // 4. `int *fat *fat` will be desugared to `_FatPtr<_FatPtr>`; QualType TransformFatPtr::DesugarFatPtrType(SourceLocation SLoc, QualType T) { - if (T->isFunctionPointerType() || T->isFunctionProtoType()) { - const FunctionProtoType *FuncType = T->isFunctionPointerType() - ? T->getAs() - ->getPointeeType() - ->getAs() - : T->getAs(); + if (T->isFunctionPointerType()) { + const FunctionProtoType *FuncType = + T->getAs()->getPointeeType()->getAs(); if (!FuncType->hasFatRetOrParams()) return T; // Desugar fat qualified return type. @@ -1919,11 +1833,8 @@ TransformFatPtr::DesugarFatPtrType(SourceLocation SLoc, QualType T) { for (auto ParamTy : FuncType->getParamTypes()) { ParamTys.push_back(DesugarFatPtrType(SLoc, ParamTy)); } - QualType DesugaredFuncTy = SemaRef.Context.getFunctionType( - ReturnTy, ParamTys, FuncType->getExtProtoInfo()); - return T->isFunctionPointerType() - ? SemaRef.Context.getPointerType(DesugaredFuncTy) - : DesugaredFuncTy; + return SemaRef.Context.getPointerType(SemaRef.Context.getFunctionType( + ReturnTy, ParamTys, FuncType->getExtProtoInfo())); } if (auto AT = dyn_cast(T)) { QualType ElementT = DesugarFatPtrType(SLoc, AT->getElementType()); @@ -2007,8 +1918,7 @@ Expr *TransformFatPtr::BuildFatPtrCStyleCastExpr(SourceLocation SLoc, QualType T // Build CallExpr for _check_version, _check_offset or _new_version_number. Expr *TransformFatPtr::BuildFatPtrCall(SourceLocation SLoc, std::string FDName, - llvm::SmallVector &Args, - bool ArgsAreReady) { + llvm::SmallVector &Args) { if (!FatPtrFD[FDName]) { DeclContext::lookup_result Decls = SemaRef.Context.getTranslationUnitDecl()->lookup( @@ -2021,12 +1931,10 @@ Expr *TransformFatPtr::BuildFatPtrCall(SourceLocation SLoc, std::string FDName, // 1. split the first _FatPtr argument into three fields argument // 2. add file name, line number and function name to report error if (FDName != "_new_version_number") { - if (!ArgsAreReady) { - Expr *FatPtrE = Args.pop_back_val(); - Args.push_back(BuildMemberForFatPtr(FatPtrE, "raw_ptr")); - Args.push_back(BuildMemberForFatPtr(FatPtrE, "key_version")); - Args.push_back(BuildMemberForFatPtr(FatPtrE, "offset")); - } + Expr *FatPtrE = Args.pop_back_val(); + Args.push_back(BuildMemberForFatPtr(FatPtrE, "raw_ptr")); + Args.push_back(BuildMemberForFatPtr(FatPtrE, "key_version")); + Args.push_back(BuildMemberForFatPtr(FatPtrE, "offset")); // Build argE for file name. StringRef FileName = SemaRef.Context.getSourceManager().getBufferName(SLoc); @@ -2351,11 +2259,7 @@ bool Sema::CheckFatQualTypeAssignment(QualType LHSType, QualType RHSType) { // T *fat -> const void *fat is OK if (LHSPointeeType->isVoidType()) return true; - return false; - } - - if (LHSPointeeType.hasFat() || RHSPointeeType.hasFat()) { - return CheckFatQualTypeAssignment(LHSPointeeType, RHSPointeeType); + // T1 *fat -> const T2 *fat is ERROR } return false; } @@ -2373,9 +2277,7 @@ bool Sema::CheckFatQualTypeAssignment(QualType LHSType, QualType RHSType) { LHSCanType->getAs()->getPointeeType(); QualType RHSPointeeType = RHSCanType->getAs()->getPointeeType(); - if (LHSPointeeType.hasFat() || RHSPointeeType.hasFat()) { - return CheckFatQualTypeAssignment(LHSPointeeType, RHSPointeeType); - } + return CheckFatQualTypeAssignment(LHSPointeeType, RHSPointeeType); } return true; } @@ -2411,6 +2313,31 @@ bool Sema::CheckFatFunctionPointerType(QualType LHSType, Expr *RHSExpr) { return true; } +// In some context, it is hard to desugar SelfIncExpr or +// SelfDecExpr of fat ptr, so we report error when: +// 1) the CondExpr of IfStmt/WhileStmt/DoStmt/ForStmt/ +// SwitchStmt/ConditionalOperator has SelfIncExpr or +// SelfDecExpr of fat ptr, such as `while (*p++ > 0)`, +// `*p++ > 0 ? : *p : *q;` +// 2) the LHS or RHS of ConditionalOperator has SelfIncExpr +// or SelfDecExpr of fat ptr, such as `*p ? : p++ : q++;` +bool Sema::CheckSelfIncOrDecOfFatPtr(Expr *E) { + if (auto UO = dyn_cast(E)) { + if ((UO->isPrefix() || UO->isPostfix()) && + UO->getSubExpr()->getType().isFatPtrType()) { + Diag(UO->getBeginLoc(), diag::err_no_inc_or_dec_of_fat_ptr); + return false; + } + } + for (auto C : E->children()) { + if (auto ChildE = dyn_cast_or_null(C)) { + if (!CheckSelfIncOrDecOfFatPtr(ChildE)) + return false; + } + } + return true; +} + // Build allocation unit RecordDecl for every global array var // which has attribute fat_ptr_checked, for example: // @code diff --git a/clang/lib/Sema/BSC/SemaDeclBSC.cpp b/clang/lib/Sema/BSC/SemaDeclBSC.cpp index fe82b6f447c6..7e4cb9653242 100644 --- a/clang/lib/Sema/BSC/SemaDeclBSC.cpp +++ b/clang/lib/Sema/BSC/SemaDeclBSC.cpp @@ -165,8 +165,7 @@ bool Sema::HasSafeZoneInFunction(const FunctionDecl* FnDecl) { /// --> | BorrowCheck | --> FuncDecl --> CodeGen /// |__________________| /// ==================================================================== -void Sema::BSCDataflowAnalysis(const FunctionDecl *FD, - bool EnableOwnershipCheck, +void Sema::BSCDataflowAnalysis(const Decl *D, bool EnableOwnershipCheck, bool EnableNullabilityCheck) { AnalysisDeclContext AC(/* AnalysisDeclContextManager */ nullptr, FD); @@ -180,6 +179,8 @@ void Sema::BSCDataflowAnalysis(const FunctionDecl *FD, AC.getCFGBuildOptions().BSCMode = true; AC.getCFGBuildOptions().setAllAlwaysAdd(); + const FunctionDecl *FD = dyn_cast_or_null(D); + // If D does not use memory safety features like "owned, borrow, &mut, &const", // we should not do borrow checking. bool RequireBorrowCheck = LangOpts.BSC ? FindSafeFeatures(FD) : false; @@ -219,11 +220,9 @@ void Sema::BSCDataflowAnalysis(const FunctionDecl *FD, BSCBorrowChecker(const_cast(FD)); } } - } - // TODO: add RequireFatPtrReduntantCheck according to whether - // current function contains fat ptr feature - if (getLangOpts().EnableFatPtr && FD && AC.getCFG()) { - runFatPtrReduntantCheck(*FD, *AC.getCFG(), AC, Context); + if (getLangOpts().EnableFatPtr) { + runFatPtrReduntantCheck(*FD, *cfg, AC, Context); + } } } diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 1a65e55cd7b8..9c7a7f12d004 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -9720,6 +9720,16 @@ ExprResult Sema::ActOnConditionalOp(SourceLocation QuestionLoc, RHS.isInvalid()) return ExprError(); +#if ENABLE_BSC + if (getLangOpts().BSC && getLangOpts().EnableFatPtr) { + bool CondResult = CheckSelfIncOrDecOfFatPtr(CondExpr); + bool LHSResult = CheckSelfIncOrDecOfFatPtr(LHSExpr); + bool RHSResult = CheckSelfIncOrDecOfFatPtr(RHSExpr); + if (!CondResult || !LHSResult || !RHSResult) + return ExprError(); + } +#endif + DiagnoseConditionalPrecedence(*this, QuestionLoc, Cond.get(), LHS.get(), RHS.get()); @@ -10033,12 +10043,6 @@ Sema::CheckAssignmentConstraints(QualType LHSType, ExprResult &RHS, QualType RHSType = RHS.get()->getType(); QualType OrigLHSType = LHSType; - if (getLangOpts().BSC && getLangOpts().EnableFatPtr && - Context.hasSameFatPtrType(LHSType, RHSType)) { - Kind = CK_NoOp; - return Compatible; - } - // Get canonical types. We're not formatting these types, just comparing // them. LHSType = Context.getCanonicalType(LHSType).getUnqualifiedType(); @@ -21020,6 +21024,13 @@ Sema::ConditionResult Sema::ActOnCondition(Scope *S, SourceLocation Loc, break; } +#if ENABLE_BSC + if (getLangOpts().BSC && getLangOpts().EnableFatPtr) { + if (!CheckSelfIncOrDecOfFatPtr(SubExpr)) + Cond = ExprError(); + } +#endif + if (Cond.isInvalid()) { Cond = CreateRecoveryExpr(SubExpr->getBeginLoc(), SubExpr->getEndLoc(), {SubExpr}, PreferredConditionType(CK)); diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp index 1ff622209128..13f9d49d1c17 100644 --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -1753,6 +1753,12 @@ Sema::ActOnDoStmt(SourceLocation DoLoc, Stmt *Body, if (CondResult.isInvalid()) return StmtError(); +#if ENABLE_BSC + if (getLangOpts().BSC && getLangOpts().EnableFatPtr) { + if (!CheckSelfIncOrDecOfFatPtr(Cond)) + return StmtError(); + } +#endif Cond = CondResult.get(); CondResult = ActOnFinishFullExpr(Cond, DoLoc, /*DiscardedValue*/ false); diff --git a/clang/test/BSC/Negative/FatPtr/self_inc_or_dec_of_fat_ptr.cbs b/clang/test/BSC/Negative/FatPtr/self_inc_or_dec_of_fat_ptr.cbs new file mode 100644 index 000000000000..ff136be63880 --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/self_inc_or_dec_of_fat_ptr.cbs @@ -0,0 +1,22 @@ +// RUN: %clang_cc1 -enable-fat-ptr -verify %s + +void test() { + int arr[3] = { 0, 1, 2 }; + int *fat p = &fat arr[0]; + int a = 10; + if (*p++ > 0) { // expected-error {{increment or decrement operation of fat pointer is not allowed here}} + a = *p++ == 1 ? *p++ : *++p; /* expected-error {{increment or decrement operation of fat pointer is not allowed here}} + expected-error {{increment or decrement operation of fat pointer is not allowed here}} + expected-error {{increment or decrement operation of fat pointer is not allowed here}} + */ + } else { + a = *++p == 1 ? *p-- : *--p; /* expected-error {{increment or decrement operation of fat pointer is not allowed here}} + expected-error {{increment or decrement operation of fat pointer is not allowed here}} + expected-error {{increment or decrement operation of fat pointer is not allowed here}} + */ + } + switch (*p++) {} // expected-error {{increment or decrement operation of fat pointer is not allowed here}} + while (*p++ > 0) {}// expected-error {{increment or decrement operation of fat pointer is not allowed here}} + do {} while (*p++ > 0);// expected-error {{increment or decrement operation of fat pointer is not allowed here}} + for (; *p++ > 0; p++) {}// expected-error {{increment or decrement operation of fat pointer is not allowed here}} +} \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds1.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds1.cbs index 0fbfd3e63612..975c7550754f 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds1.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds1.cbs @@ -4,7 +4,7 @@ #include int main() { - int *fat p = (int *fat)checked_malloc(3 * sizeof(int)); + int *fat p = checked_malloc(3 * sizeof(int)); int a = p[3]; return 0; } diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds10.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds10.cbs index 77f601353803..61354828836e 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds10.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds10.cbs @@ -6,7 +6,7 @@ int main() { int *fat p = nullptr; if (!p) { - p = (int *fat)checked_malloc(sizeof(int)); + p = checked_malloc(sizeof(int)); p = p + 1; *p = 10; } diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds11.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds11.cbs index be9b109ef756..2d354b2f52cf 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds11.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds11.cbs @@ -4,10 +4,10 @@ #include int main() { - int *fat p1 = (int *fat)checked_malloc(sizeof(int)); + int *fat p1 = checked_malloc(sizeof(int)); int *fat p2 = p1; if (p1 == p2) { - p2 = (int *fat)checked_malloc(sizeof(int)); + p2 = checked_malloc(sizeof(int)); p2++; *p2 = 10; } diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds12.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds12.cbs index d56eea2a6605..f905d5644648 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds12.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds12.cbs @@ -5,7 +5,7 @@ int main() { int *fat p1 = nullptr; - int *fat p2 = p1 ? nullptr : (int *fat)checked_malloc(sizeof(int) * 2); + int *fat p2 = p1 ? nullptr : checked_malloc(sizeof(int) * 2); *p2 = 0; int *fat p3 = *p2 ? p1 : p2; p3[2] = 10; diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds13.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds13.cbs index 9bf14e9103f5..a6cdfadb2e08 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds13.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds13.cbs @@ -4,7 +4,7 @@ #include int main() { - int *fat p = (int *fat)checked_malloc(sizeof(int) * 10); + int *fat p = checked_malloc(sizeof(int) * 10); for (int i = 0; i < 10; i++) { p[i] = i - 5; } diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds14.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds14.cbs index 3bcc0dd4bad0..0a550df0117d 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds14.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds14.cbs @@ -4,7 +4,7 @@ #include int main() { - int *fat p = (int *fat)checked_malloc(sizeof(int) * 10); + int *fat p = checked_malloc(sizeof(int) * 10); p += 10; *p = 10; return 0; diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds15.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds15.cbs index 73a2921356e5..281f9d9c6dd0 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds15.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds15.cbs @@ -4,8 +4,8 @@ #include int main() { - int *fat *fat p = (int *fat *fat)checked_malloc(sizeof(int *fat) * 10); - p[9] = (int *fat)checked_malloc(sizeof(int) * 10); + int *fat *fat p = checked_malloc(sizeof(int *fat) * 10); + p[9] = checked_malloc(sizeof(int) * 10); p += 9; *p += 10; **p = 10; diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds16.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds16.cbs index a2dced01ec47..4a4060a9a2b8 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds16.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds16.cbs @@ -4,7 +4,7 @@ #include int main() { - int *fat p = (int *fat)checked_malloc(sizeof(int) * 10); + int *fat p = checked_malloc(sizeof(int) * 10); p += 9; *p++ = 9; *p++ = 10; diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds17.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds17.cbs index ec5abdf668aa..7259b6793597 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds17.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds17.cbs @@ -10,7 +10,7 @@ void bar(int *fat p) { Foo foo = bar; int main() { - int *fat p = (int *fat)checked_malloc(sizeof(int) * 10); + int *fat p = checked_malloc(sizeof(int) * 10); int *fat p1 = p + 10; foo(p1); } diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds18.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds18.cbs index 3d46121aa394..9e5dcc4c2519 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds18.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds18.cbs @@ -4,15 +4,15 @@ #include int main() { - int *fat p = (int *fat)checked_malloc(sizeof(int) * 3); + int *fat p = checked_malloc(sizeof(int) * 3); p[0] = 0; p[1] = 1; p[2] = 2; - int *fat q = (int *fat)checked_malloc(sizeof(int) * 3); + int *fat q = checked_malloc(sizeof(int) * 3); for (; *p < 3; p++) { *q = *p; q++; } } // CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds18.cbs:[[@LINE-5]] in main \ No newline at end of file +// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds18.cbs:[[@LINE-6]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds19.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds19.cbs index 7a1b384742c3..64f0f702afa6 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds19.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds19.cbs @@ -4,11 +4,11 @@ #include int main() { - int *fat p = (int *fat)checked_malloc(sizeof(int) * 3); + int *fat p = checked_malloc(sizeof(int) * 3); p[0] = 0; p[1] = 1; p[2] = 2; - int *fat q = (int *fat)checked_malloc(sizeof(int) * 3); + int *fat q = checked_malloc(sizeof(int) * 3); while (*p < 3) { *q = *p; p++; diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds2.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds2.cbs index 3f9fe90d1241..51835b6ee0e2 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds2.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds2.cbs @@ -4,7 +4,7 @@ #include struct S { int *fat p; }; int main() { - struct S s = { (int *fat)checked_malloc(3 * sizeof(int)) }; + struct S s = { checked_malloc(3 * sizeof(int)) }; int a = s.p[3]; return 0; } diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds20.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds20.cbs deleted file mode 100644 index ca43167ee674..000000000000 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds20.cbs +++ /dev/null @@ -1,14 +0,0 @@ -// RUN: %clang -enable-fat-ptr %s -o %t.output -// RUN: not %t.output 2>&1 | FileCheck %s - -#include -int *fat return_fat() { - return (int *fat)checked_malloc(sizeof(int) * 10); -} - -int main() { - int *fat p = return_fat() + 10; - *p = 10; -} -// CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds20.cbs:[[@LINE-3]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds21.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds21.cbs deleted file mode 100644 index 1f153854bb56..000000000000 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds21.cbs +++ /dev/null @@ -1,12 +0,0 @@ -// RUN: %clang -enable-fat-ptr %s -o %t.output -// RUN: not %t.output 2>&1 | FileCheck %s - -#include - -int main() { - int *fat p = (int *fat)checked_malloc(sizeof(int)); - long long *fat p1 = (long long *fat)p; - *p1 = 10; -} -// CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds21.cbs:[[@LINE-3]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds22.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds22.cbs deleted file mode 100644 index d67859854fb7..000000000000 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds22.cbs +++ /dev/null @@ -1,16 +0,0 @@ -// RUN: %clang -enable-fat-ptr %s -o %t.output -// RUN: not %t.output 2>&1 | FileCheck %s - -#include - -int main() { - int arr[2] = { 0, 1 }; - int *fat p = &fat arr[0]; - int a = 10; - if (*p++ == 0) { - a = *p++ == 1 ? *p++ : 0; - } - return 0; -} -// CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds22.cbs:[[@LINE-5]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds23.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds23.cbs deleted file mode 100644 index a9e49d3a7c23..000000000000 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds23.cbs +++ /dev/null @@ -1,16 +0,0 @@ -// RUN: %clang -enable-fat-ptr %s -o %t.output -// RUN: not %t.output 2>&1 | FileCheck %s - -#include - -int main() { - int arr[2] = { 0, 1 }; - int *fat p = &fat arr[0]; - int a = 10; - while (*p++ <= 1) { - a = *p; - } - return 0; -} -// CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds23.cbs:[[@LINE-5]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds24.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds24.cbs deleted file mode 100644 index 1849f3f1dd55..000000000000 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds24.cbs +++ /dev/null @@ -1,16 +0,0 @@ -// RUN: %clang -enable-fat-ptr %s -o %t.output -// RUN: not %t.output 2>&1 | FileCheck %s - -#include - -int main() { - int arr[2] = { 0, 1 }; - int *fat p = &fat arr[0]; - int a = 10; - do { - - } while (*p++ <= 1); - return 0; -} -// CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds24.cbs:[[@LINE-4]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds3.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds3.cbs index ffd3256569c8..26e2f6ce7375 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds3.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds3.cbs @@ -4,7 +4,7 @@ #include int main() { - int *fat p = (int *fat)checked_malloc(3 * sizeof(int)); + int *fat p = checked_malloc(3 * sizeof(int)); int *fat p1 = p + 3; int a = *p1; return 0; diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds4.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds4.cbs index 50a853dd1568..7b7305318d45 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds4.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds4.cbs @@ -5,7 +5,7 @@ struct S { int a; }; int main() { - struct S *fat p = (struct S *fat)checked_malloc(sizeof(struct S)); + struct S *fat p = checked_malloc(sizeof(struct S)); struct S *fat p1 = p + 1; p1->a = 10; return 0; diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds5.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds5.cbs index 5065a33b6391..b80177c9e6ae 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds5.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds5.cbs @@ -5,7 +5,7 @@ struct S { int a; int arr[3]; }; int main() { - struct S *fat p1 = (struct S *fat)checked_malloc(sizeof(struct S) * 2); + struct S *fat p1 = checked_malloc(sizeof(struct S) * 2); struct S *fat p2 = p1 + 1; int a = p2->arr[3]; return 0; diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds6.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds6.cbs index 6b2be24bedba..cd4c9e508722 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds6.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds6.cbs @@ -5,7 +5,7 @@ struct S { int arr[3]; }; int main() { - struct S *fat p = (struct S *fat)checked_malloc(sizeof(struct S)); + struct S *fat p = checked_malloc(sizeof(struct S)); int a = p->arr[3]; } // CHECK: Error: Pointer offset exceeds the allocation size! diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds7.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds7.cbs index 6c41e1f26999..42b814927dcc 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds7.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds7.cbs @@ -4,7 +4,7 @@ #include int main() { - int *fat p = (int *fat)checked_malloc(sizeof(int) * 10); + int *fat p = checked_malloc(sizeof(int) * 10); for (int i = 0; i < 10; i++) { p[i] = i - 5; } diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds8.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds8.cbs index 7f02135b5b61..f29da60932a4 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds8.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds8.cbs @@ -4,7 +4,7 @@ #include int main() { - int *fat p = (int *fat)checked_malloc(sizeof(int) * 10); + int *fat p = checked_malloc(sizeof(int) * 10); for (int i = 0; i < 10; i++) { p[i] = i - 5; } diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds9.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds9.cbs index e39e5e37957c..904126abeadb 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds9.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds9.cbs @@ -4,7 +4,7 @@ #include int main() { - int *fat p = (int *fat)checked_malloc(sizeof(int)); + int *fat p = checked_malloc(sizeof(int)); if (p) { p = p + 1; *p = 10; diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/buffer_overflow.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/buffer_overflow.cbs index 8a11f4223d8a..a40d1bc46c3b 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/buffer_overflow.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/buffer_overflow.cbs @@ -14,7 +14,7 @@ char *fat fat_strcpy(char *fat dest, const char *src) { } int main(int argc, char **argv) { - char *fat buf = (char *fat)checked_malloc(sizeof(char)*BUFSIZE); + char *fat buf = checked_malloc(sizeof(char)*BUFSIZE); fat_strcpy(buf, argv[1]); return 0; } diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free1.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free1.cbs index 76ce095d0468..a9c67262df80 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free1.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free1.cbs @@ -4,9 +4,9 @@ #include int main() { - int *fat p = (int *fat)checked_malloc(sizeof(int)); - checked_free((void *fat)p); - checked_free((void *fat)p); + int *fat p = checked_malloc(sizeof(int)); + checked_free(p); + checked_free(p); return 0; } diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free2.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free2.cbs index 0011f1b6ca6c..64a65afc5220 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free2.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free2.cbs @@ -4,10 +4,10 @@ #include int main() { - int *fat p1 = (int *fat)checked_malloc(sizeof(int)); + int *fat p1 = checked_malloc(sizeof(int)); int *fat p2 = p1; - checked_free((void *fat)p1); - checked_free((void *fat)p2); + checked_free(p1); + checked_free(p2); return 0; } diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free3.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free3.cbs index 621c6515254b..26db032c3e72 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free3.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free3.cbs @@ -5,11 +5,11 @@ struct G { int *fat a; }; int main() { - struct G g = { .a = (int *fat)checked_malloc(sizeof(int)) }; - struct G *fat p = (struct G *fat)checked_malloc(sizeof(struct G)); + struct G g = { .a = checked_malloc(sizeof(int)) }; + struct G *fat p = checked_malloc(sizeof(struct G)); p->a = g.a; - checked_free((void *fat)p->a); - checked_free((void *fat)g.a); + checked_free(p->a); + checked_free(g.a); return 0; } diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free4.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free4.cbs index 3872ebea1bc9..8d294688d9f8 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free4.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free4.cbs @@ -4,14 +4,14 @@ #include int *fat test() { - int *fat p = (int *fat)checked_malloc(sizeof(int)); - checked_free((void *fat)p); + int *fat p = checked_malloc(sizeof(int)); + checked_free(p); return p; } int main() { int *fat p = test(); - checked_free((void *fat)p); + checked_free(p); return 0; } diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free1.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free1.cbs index da6ab753e4e1..fbf926b906af 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free1.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free1.cbs @@ -4,9 +4,9 @@ #include int main() { - int *fat p = (int *fat)checked_malloc(sizeof(int)); + int *fat p = checked_malloc(sizeof(int)); *p = 10; - checked_free((void *fat)p); + checked_free(p); *p = 10; return 0; } diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free2.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free2.cbs index 02d21d1a8651..9faa09e3f4c3 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free2.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free2.cbs @@ -5,9 +5,9 @@ struct S { int a; }; int main() { - struct S *fat p = (struct S *fat)checked_malloc(sizeof(struct S)); + struct S *fat p = checked_malloc(sizeof(struct S)); p->a = 10; - checked_free((void *fat)p); + checked_free(p); p->a = 10; return 0; } diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free3.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free3.cbs index fdc25eaec623..fe79ee91d3f8 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free3.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free3.cbs @@ -4,8 +4,8 @@ #include int *fat test() { - int *fat p = (int *fat)checked_malloc(sizeof(int)); - checked_free((void *fat)p); + int *fat p = checked_malloc(sizeof(int)); + checked_free(p); return p; } diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free4.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free4.cbs index 4a0f2ee93bf7..08ce74e36d4d 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free4.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free4.cbs @@ -5,10 +5,10 @@ struct G { int *fat a; }; int main() { - struct G g = { .a = (int *fat)checked_malloc(sizeof(int)) }; - struct G *fat p = (struct G *fat)checked_malloc(sizeof(struct G)); + struct G g = { .a = checked_malloc(sizeof(int)) }; + struct G *fat p = checked_malloc(sizeof(struct G)); p->a = g.a; - checked_free((void *fat)p->a); + checked_free(p->a); *g.a = 10; return 0; } diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free5.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free5.cbs index 3568698813ac..1c37a3ec2e69 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free5.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free5.cbs @@ -16,14 +16,14 @@ char *fat fat_strncpy(char *fat dest, const char *src, size_t n) { } int main() { - char *fat buf1 = (char *fat)checked_malloc(BUFSIZE); - checked_free((void *fat)buf1); + char *fat buf1 = checked_malloc(BUFSIZE); + checked_free(buf1); - char *fat buf2 = (char *fat)checked_malloc(BUFSIZE); + char *fat buf2 = checked_malloc(BUFSIZE); memset((char *)buf2, 0, BUFSIZE); fat_strncpy(buf1, "hack", 5); - checked_free((void *fat)buf2); + checked_free(buf2); return 0; } // CHECK: Error: Cannot use this pointer because the allocation may have been freed or reseored! diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free6.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free6.cbs index 4292d301d94b..1159a594974c 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free6.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free6.cbs @@ -6,8 +6,8 @@ void pass_fat(int *p) {} int main() { - int *fat p = (int *fat)checked_malloc(sizeof(int)); - checked_free((void *fat)p); + int *fat p = checked_malloc(sizeof(int)); + checked_free(p); pass_fat((int *)p); return 0; } diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free7.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free7.cbs index 9b6f5b78119c..0e96d5e77f6f 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free7.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free7.cbs @@ -4,8 +4,8 @@ #include int main() { - int *fat p = (int *fat)checked_malloc(sizeof(int)); - checked_free((void *fat)p); + int *fat p = checked_malloc(sizeof(int)); + checked_free(p); if (*p == 5) { return 5; } diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free8.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free8.cbs index 28b7648db77d..04b70ea5aa83 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free8.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free8.cbs @@ -6,8 +6,8 @@ int *fat p = nullptr; int main() { - p = (int *fat)checked_malloc(sizeof(int)); - checked_free((void *fat)p); + p = checked_malloc(sizeof(int)); + checked_free(p); if (*p == 5) { return 5; } diff --git a/clang/test/BSC/Positive/FatPtr/heap.cbs b/clang/test/BSC/Positive/FatPtr/heap.cbs index 4d904d01d50a..f30b12beda57 100644 --- a/clang/test/BSC/Positive/FatPtr/heap.cbs +++ b/clang/test/BSC/Positive/FatPtr/heap.cbs @@ -3,65 +3,64 @@ // expected-no-diagnostics #include -#include struct S { int a; }; void test1() { - int *fat p1 = (int *fat)checked_malloc(sizeof(int)); + int *fat p1 = checked_malloc(sizeof(int)); *p1 = 10; - checked_free((void *fat)p1); + checked_free(p1); - struct S *fat p2 = (struct S *fat)checked_malloc(sizeof(struct S)); + struct S *fat p2 = checked_malloc(sizeof(struct S)); p2->a = 10; - checked_free((void *fat)p2); + checked_free(p2); } struct G { int *fat a; }; void test2() { - struct G g = { .a = (int *fat)checked_malloc(sizeof(int)) }; - struct G *fat p = (struct G *fat)checked_malloc(sizeof(struct G)); + struct G g = { .a = checked_malloc(sizeof(int)) }; + struct G *fat p = checked_malloc(sizeof(struct G)); *p = g; *p->a = 10; - checked_free((void *fat)p->a); - checked_free((void *fat)p); + checked_free(p->a); + checked_free(p); } void test3() { - int *fat p1 = (int *fat)checked_malloc(sizeof(int)); - int *fat p2 = (int *fat)checked_malloc(sizeof(int)); + int *fat p1 = checked_malloc(sizeof(int)); + int *fat p2 = checked_malloc(sizeof(int)); *p1 = *p2; - checked_free((void *fat)p1); - checked_free((void *fat)p2); + checked_free(p1); + checked_free(p2); } void test4() { - int *fat p1 = (int *fat)checked_malloc(sizeof(int)); - int *fat p2 = (int *fat)checked_malloc(sizeof(int)); + int *fat p1 = checked_malloc(sizeof(int)); + int *fat p2 = checked_malloc(sizeof(int)); p1 = p2; - checked_free((void *fat)p1); //only free once + checked_free(p1); //only free once } void test5() { - int *fat p = (int *fat)checked_malloc(sizeof(int) * 3); + int *fat p = checked_malloc(sizeof(int) * 3); int a = p[2]; - checked_free((void *fat)p); + checked_free(p); } void test6() { - int *fat p1 = (int *fat)checked_malloc(sizeof(int)); + int *fat p1 = checked_malloc(sizeof(int)); int *fat p2 = p1; int *p3 = (int *)p1; float *fat p4 = (float *fat)p1; - checked_free((void *fat)p1); + checked_free(p1); } void test7() { - int *fat p = (int *fat)checked_malloc(3 * sizeof(int)); + int *fat p = checked_malloc(3 * sizeof(int)); int *fat p1 = p + 2; int a = *p1; } void test8(int *fat p1, int *fat p2) { - p1 = (int *fat)checked_malloc(3 * sizeof(int)); + p1 = checked_malloc(3 * sizeof(int)); p2 = p1 + 2; int a = *p1; int b = *p2; @@ -69,7 +68,7 @@ void test8(int *fat p1, int *fat p2) { struct K { int arr[3]; }; void test9() { - struct K *fat p1 = (struct K *fat)checked_malloc(sizeof(struct K) * 2); + struct K *fat p1 = checked_malloc(sizeof(struct K) * 2); int a = p1->arr[3]; int *fat p2 = &fat p1->arr[3]; *p2 = 10; @@ -81,8 +80,8 @@ void test9() { } void test10() { - int *fat *fat p = (int *fat *fat)checked_malloc(sizeof(int *fat) * 10); - p[9] = (int *fat)checked_malloc(sizeof(int) * 10); + int *fat *fat p = checked_malloc(sizeof(int *fat) * 10); + p[9] = checked_malloc(sizeof(int) * 10); p += 9; *p += 9; **p = 10; @@ -94,34 +93,11 @@ void bar(int *fat p) { } Foo foo = bar; void test11() { - int *fat p = (int *fat)checked_malloc(sizeof(int) * 10); + int *fat p = checked_malloc(sizeof(int) * 10); int *fat p1 = p + 9; foo(p1); } -int *fat return_fat() { - return (int *fat)checked_malloc(sizeof(int) * 10); -} - -void test12() { - int *fat p = return_fat() + 9; - *p = 10; -} - -void test13() { - int *p = (int *)return_fat(); - *p = 10; - *return_fat() = 10; -} - -void test14() { - int *fat * p; - int *fat *p1 = p + 1; - int *fat *p2 = (int *fat *)p + 1; - assert((long long)p2 - (long long)p == 16); - assert((long long)p2 - (long long)p == 16); -} - int main() { test1(); test2(); @@ -136,8 +112,5 @@ int main() { test9(); test10(); test11(); - test12(); - test13(); - test14(); return 0; } \ No newline at end of file diff --git a/clang/test/BSC/Positive/FatPtr/multi_level_pointer.cbs b/clang/test/BSC/Positive/FatPtr/multi_level_pointer.cbs index b0cfd7a418fd..2989912aba2b 100644 --- a/clang/test/BSC/Positive/FatPtr/multi_level_pointer.cbs +++ b/clang/test/BSC/Positive/FatPtr/multi_level_pointer.cbs @@ -10,7 +10,7 @@ void test1() { } void test2() { - char *fat p = (char *fat)checked_malloc(sizeof(char) * 6); + char *fat p = checked_malloc(sizeof(char) * 6); p[0] = 'H'; p[1] = 'e'; p[2] = 'l'; @@ -20,20 +20,10 @@ void test2() { printf("%s!\n", (char *)*p1); } -void test3() { - char *fat *fat p = (char *fat *fat)checked_malloc(sizeof(char *fat) * 2); - p[0] = (char *fat)"Hello"; - p[1] = (char *fat)"world"; - printf("%s %s!\n", (char *)p[0], (char *)p[1]); - checked_free((void *fat)p); -} - int main() { test1(); test2(); - test3(); return 0; } // CHECK: Hello world! -// CHECK-NEXT: Hello! -// CHECK-NEXT: Hello world! \ No newline at end of file +// CHECK-NEXT: Hello! \ No newline at end of file diff --git a/clang/test/BSC/Positive/FatPtr/perform_anaysis/deref_loop/deref_loop.cbs b/clang/test/BSC/Positive/FatPtr/perform_anaysis/deref_loop/deref_loop.cbs new file mode 100644 index 000000000000..43ba61776199 --- /dev/null +++ b/clang/test/BSC/Positive/FatPtr/perform_anaysis/deref_loop/deref_loop.cbs @@ -0,0 +1,39 @@ +// RUN: %clang %s -enable-fat-ptr -o %test.output +// RUN: %test.output 20 + +#include + +long dealwithargs(int argc, char *argv[]) +{ + int num = 0; + if (argc > 2) + num = atoi(argv[2]); + return 1L << num; +} + +void test_raw(long n) { + long *p = malloc(sizeof(long)); + while (n > 0) { + *p = n; + n--; + } +} + +void test_fat(long n) { + long *fat p = checked_malloc(sizeof(long)); + while (n > 0) { + *p = n; + n--; + } +} + +int main(int argc, char *argv[]) { + long n = dealwithargs(argc, argv); + int flag = atoi(argv[1]); + if (flag == 0) { + test_raw(n); + } else { + test_fat(n); + } + return 0; +} diff --git a/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/bisort/bitonic.c b/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/bisort/bitonic.c new file mode 100644 index 000000000000..cc3f2cb278aa --- /dev/null +++ b/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/bisort/bitonic.c @@ -0,0 +1,303 @@ +// RUN: %clang %s -o %test.output +// RUN: %test.output 300 + +#include +#include + +#define CONST_m1 10000 +#define CONST_b 31415821 +#define RANGE 100 + +typedef struct node HANDLE; + +struct node { + int value; + HANDLE* left; + HANDLE *right; +}; + +typedef struct future_cell_int{ + HANDLE *value; +} future_cell_int; + +extern void *malloc(unsigned long); + +#define NIL ((HANDLE *) 0) + +int NumNodes, NDim; + +int random_c(int); + +int flag=0,foo=0; + +#define LocalNewNode(h,v) \ +{ \ + h = (HANDLE *) malloc(sizeof(struct node)); \ + h->value = v; \ + h->left = NIL; \ + h->right = NIL; \ + }; + +#define NewNode(h,v,procid) LocalNewNode(h,v) + +int mylog(int num) { + int j=0,k=1; + + while(k 3) + flag = atoi(argv[3]); + else + flag = 1; + + if (argc > 2) + NumNodes = atoi(argv[2]); + else + NumNodes = 4; + + if (argc > 1) + size = atoi(argv[1]); + else + size = 1 << 15; + + NDim = mylog(NumNodes); + return size; +} + +void InOrder(HANDLE *h) { + HANDLE *l, *r; + if ((h != NIL)) { + l = h->left; + r = h->right; + InOrder(l); + static unsigned char counter = 0; + if (counter++ == 0) /* reduce IO */ + printf("%d @ 0x%x\n",h->value, 0); + InOrder(r); + } +} + +int mult(int p, int q) { + int p1, p0, q1, q0; + + p1 = p/CONST_m1; p0 = p%CONST_m1; + q1 = q/CONST_m1; q0 = q%CONST_m1; + return ((p0*q1+p1*q0) % CONST_m1)*CONST_m1+p0*q0; +} + +/* Generate the nth random_c # */ +int skiprand(int seed, int n) { + for (; n; n--) seed=random_c(seed); + return seed; +} + +int random_c(int seed) { + return mult(seed,CONST_b)+1; +} + +HANDLE* RandTree(int n, int seed, int node, int level) { + int next_val,my_name; + future_cell_int f_left, f_right; + HANDLE *h; + my_name=foo++; + if (n > 1) { + int newnode; + if (levelleft = f_left.value; + h->right = f_right.value; + } else { + h = 0; + } + return h; +} + +void SwapValue(HANDLE *l, HANDLE *r) { + int temp,temp2; + + temp = l->value; + temp2 = r->value; + r->value = temp; + l->value = temp2; +} + +void +/***********/ +SwapValLeft(HANDLE *l, HANDLE *r, HANDLE *ll, HANDLE *rl, int lval, int rval) +/***********/ +{ + r->value = lval; + r->left = ll; + l->left = rl; + l->value = rval; +} + + +void +/************/ +SwapValRight(HANDLE *l, HANDLE *r, HANDLE *lr, HANDLE *rr, int lval, int rval) +/************/ +{ + r->value = lval; + r->right = lr; + l->right = rr; + l->value = rval; + /*printf("Swap Val Right l 0x%x,r 0x%x val: %d %d\n",l,r,lval,rval);*/ +} + +int +/********************/ +Bimerge(HANDLE *root, int spr_val, int dir) +/********************/ +{ int rightexchange; + int elementexchange; + HANDLE *pl,*pll,*plr; + HANDLE *pr,*prl,*prr; + HANDLE *rl; + HANDLE *rr; + int rv,lv; + + + /*printf("enter bimerge %x\n", root);*/ + rv = root->value; + + pl = root->left; + pr = root->right; + rightexchange = ((rv > spr_val) ^ dir); + if (rightexchange) + { + root->value = spr_val; + spr_val = rv; + } + + while ((pl != NIL)) + { + /*printf("pl = 0x%x,pr = 0x%x\n",pl,pr);*/ + lv = pl->value; /* <------- 8.2% load penalty */ + pll = pl->left; + plr = pl->right; /* <------- 1.35% load penalty */ + rv = pr->value; /* <------ 57% load penalty */ + prl = pr->left; /* <------ 7.6% load penalty */ + prr = pr->right; /* <------ 7.7% load penalty */ + elementexchange = ((lv > rv) ^ dir); + if (rightexchange) + if (elementexchange) + { + SwapValRight(pl,pr,plr,prr,lv,rv); + pl = pll; + pr = prl; + } + else + { pl = plr; + pr = prr; + } + else + if (elementexchange) + { + SwapValLeft(pl,pr,pll,prl,lv,rv); + pl = plr; + pr = prr; + } + else + { pl = pll; + pr = prl; + } + } + if ((root->left != NIL)) + { + int value; + rl = root->left; + rr = root->right; + value = root->value; + + root->value=Bimerge(rl,value,dir); + spr_val=Bimerge(rr,spr_val,dir); + } + /*printf("exit bimerge %x\n", root);*/ + return spr_val; +} + +int +/*******************/ +Bisort(HANDLE *root, int spr_val, int dir) +/*******************/ +{ HANDLE *l; + HANDLE *r; + int val; + /*printf("bisort %x\n", root);*/ + if (root->left == NIL) /* <---- 8.7% load penalty */ + { + if (((root->value > spr_val) ^ dir)) + { + val = spr_val; + spr_val = root->value; + root->value =val; + } + } + else + { + int ndir; + l = root->left; + r = root->right; + val = root->value; + /*printf("root 0x%x, l 0x%x, r 0x%x\n", root,l,r);*/ + root->value=Bisort(l,val,dir); + ndir = !dir; + spr_val=Bisort(r,spr_val,ndir); + spr_val=Bimerge(root,spr_val,dir); + } + /*printf("exit bisort %x\n", root);*/ + return spr_val; +} + +int main(int argc, char **argv) { + HANDLE *h; + int sval; + int n; + + n = dealwithargs(argc,argv); + + printf("Bisort with %d size of dim %d\n", n, NDim); + + h = RandTree(n,12345768,0,0); + sval = random_c(245867) % RANGE; + if (flag) { + InOrder(h); + printf("%d\n",sval); + } + printf("**************************************\n"); + printf("BEGINNING BITONIC SORT ALGORITHM HERE\n"); + printf("**************************************\n"); + + sval=Bisort(h,sval,0); + + if (flag) { + printf("Sorted Tree:\n"); + InOrder(h); + printf("%d\n",sval); + } + + sval=Bisort(h,sval,1); + + if (flag) { + printf("Sorted Tree:\n"); + InOrder(h); + printf("%d\n",sval); + } + + return 0; +} diff --git a/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/bisort_bsc/bitonic.cbs b/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/bisort_bsc/bitonic.cbs new file mode 100644 index 000000000000..8065f9620351 --- /dev/null +++ b/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/bisort_bsc/bitonic.cbs @@ -0,0 +1,302 @@ +// RUN: %clang %s -enable-fat-ptr -o %test.output +// RUN: %test.output 3000 + +#include +#include +#include + +#define CONST_m1 10000 +#define CONST_b 31415821 +#define RANGE 100 + +typedef struct node HANDLE; + +struct node { + int value; + HANDLE *fat left; + HANDLE *fat right; +}; + +typedef struct future_cell_int{ + HANDLE *fat value; +} future_cell_int; + +int NumNodes, NDim; + +int random_c(int); + +int flag=0,foo=0; + +//FIXME +#define LocalNewNode(h,v) \ +{ \ + h = checked_malloc(sizeof(HANDLE)); \ + h->value = v; \ + h->left = nullptr; \ + h->right = nullptr; \ +}; + +#define NewNode(h,v,procid) LocalNewNode(h,v) + +int mylog(int num) { + int j=0,k=1; + + while(k 3) + flag = atoi(argv[3]); + else + flag = 1; + + if (argc > 2) + NumNodes = atoi(argv[2]); + else + NumNodes = 4; + + if (argc > 1) + size = atoi(argv[1]); + else + size = 1 << 15; + + NDim = mylog(NumNodes); + return size; +} + +void InOrder(HANDLE *fat h) { + HANDLE *fat l, *fat r; + if ((h != nullptr)) { + l = h->left; + r = h->right; + InOrder(l); + static unsigned char counter = 0; + if (counter++ == 0) /* reduce IO */ + // printf("%d @ 0x%x\n",h->value, 0); + InOrder(r); + } +} + +int mult(int p, int q) { + int p1, p0, q1, q0; + + p1 = p/CONST_m1; p0 = p%CONST_m1; + q1 = q/CONST_m1; q0 = q%CONST_m1; + return ((p0*q1+p1*q0) % CONST_m1)*CONST_m1+p0*q0; +} + +/* Generate the nth random_c # */ +int skiprand(int seed, int n) { + for (; n; n--) seed=random_c(seed); + return seed; +} + +int random_c(int seed) { + return mult(seed, CONST_b) + 1; +} + +HANDLE *fat RandTree(int n, int seed, int node, int level) { + int next_val,my_name; + future_cell_int f_left, f_right; + HANDLE *fat h; + my_name=foo++; + if (n > 1) { + int newnode; + if (levelleft = f_left.value; + h->right = f_right.value; + } else { + h = checked_malloc(sizeof(HANDLE)); //FIXME + } + return h; +} + +void SwapValue(HANDLE *fat l, HANDLE *fat r) { + int temp,temp2; + + temp = l->value; + temp2 = r->value; + r->value = temp; + l->value = temp2; +} + +void +/***********/ +SwapValLeft(HANDLE *fat l, HANDLE *fat r, HANDLE *fat ll, HANDLE *fat rl, int lval, int rval) +/***********/ +{ + r->value = lval; + r->left = ll; + l->left = rl; + l->value = rval; +} + + +void +/************/ +SwapValRight(HANDLE *fat l, HANDLE *fat r, HANDLE *fat lr, HANDLE *fat rr, int lval, int rval) +/************/ +{ + r->value = lval; + r->right = lr; + l->right = rr; + l->value = rval; + /*printf("Swap Val Right l 0x%x,r 0x%x val: %d %d\n",l,r,lval,rval);*/ +} + +int +/********************/ +Bimerge(HANDLE *fat root, int spr_val, int dir) +/********************/ +{ int rightexchange; + int elementexchange; + HANDLE *fat pl,*fat pll,*fat plr; + HANDLE *fat pr,*fat prl,*fat prr; + HANDLE *fat rl; + HANDLE *fat rr; + int rv,lv; + + + /*printf("enter bimerge %x\n", root);*/ + rv = root->value; + + pl = root->left; + pr = root->right; + rightexchange = ((rv > spr_val) ^ dir); + if (rightexchange) + { + root->value = spr_val; + spr_val = rv; + } + + while ((pl != nullptr)) + { + /*printf("pl = 0x%x,pr = 0x%x\n",pl,pr);*/ + lv = pl->value; /* <------- 8.2% load penalty */ + pll = pl->left; + plr = pl->right; /* <------- 1.35% load penalty */ + rv = pr->value; /* <------ 57% load penalty */ + prl = pr->left; /* <------ 7.6% load penalty */ + prr = pr->right; /* <------ 7.7% load penalty */ + elementexchange = ((lv > rv) ^ dir); + if (rightexchange) + if (elementexchange) + { + SwapValRight(pl,pr,plr,prr,lv,rv); + pl = pll; + pr = prl; + } + else + { pl = plr; + pr = prr; + } + else + if (elementexchange) + { + SwapValLeft(pl,pr,pll,prl,lv,rv); + pl = plr; + pr = prr; + } + else + { pl = pll; + pr = prl; + } + } + HANDLE *fat root_left = root->left; + if (root_left != nullptr) + { + int value; + rl = root->left; + rr = root->right; + value = root->value; + + root->value=Bimerge(rl,value,dir); + spr_val=Bimerge(rr,spr_val,dir); + } + /*printf("exit bimerge %x\n", root);*/ + return spr_val; +} + +int +/*******************/ +Bisort(HANDLE *fat root, int spr_val, int dir) +/*******************/ +{ HANDLE *fat l; + HANDLE *fat r; + int val; + /*printf("bisort %x\n", root);*/ + HANDLE *fat root_left = root->left; + if (root_left == nullptr) /* <---- 8.7% load penalty */ + { + if (((root->value > spr_val) ^ dir)){ + val = spr_val; + spr_val = root->value; + root->value =val; + } + } + else + { + int ndir; + l = root->left; + r = root->right; + val = root->value; + /*printf("root 0x%x, l 0x%x, r 0x%x\n", root,l,r);*/ + root->value=Bisort(l,val,dir); + ndir = !dir; + spr_val=Bisort(r,spr_val,ndir); + spr_val=Bimerge(root,spr_val,dir); + } + /*printf("exit bisort %x\n", root);*/ + return spr_val; +} + +int main(int argc, char **argv) { + HANDLE *fat h; + int sval; + int n; + + n = dealwithargs(argc,argv); + + printf("Bisort with %d size of dim %d\n", n, NDim); + + h = RandTree(n,12345768,0,0); + sval = random_c(245867) % RANGE; + if (flag) { + InOrder(h); + printf("%d\n",sval); + } + printf("**************************************\n"); + printf("BEGINNING BITONIC SORT ALGORITHM HERE\n"); + printf("**************************************\n"); + + sval=Bisort(h,sval,0); + + if (flag) { + printf("Sorted Tree:\n"); + InOrder(h); + printf("%d\n",sval); + } + + sval=Bisort(h,sval,1); + + if (flag) { + printf("Sorted Tree:\n"); + InOrder(h); + printf("%d\n",sval); + } + + return 0; +} diff --git a/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd/node.c b/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd/node.c new file mode 100644 index 000000000000..c8af4371bf4b --- /dev/null +++ b/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd/node.c @@ -0,0 +1,233 @@ +// RUN: %clang %s -DTORONTO -o %test.output +// RUN: %test.output 16 + +/* For copyright information, see olden_v1.0/COPYRIGHT */ + +/* node.c + */ +#ifndef TORONTO +#include +#include +#include "mem-ref.h" +#endif + +#ifdef FUTURES +#include "future-cell.h" +#endif + +#include +#include "tree.h" + +#ifdef TORONTO +int NumNodes; +#else +int __NumNodes; +#endif + +typedef struct { + long level; +} startmsg_t; + +#ifndef TORONTO +void filestuff() +{ + CMMD_fset_io_mode(stdout, CMMD_independent); + fcntl(fileno(stdout), F_SETFL, O_APPEND); + if (CMMD_self_address()) exit(0); + __InitRegs(0); +} +#endif + +int dealwithargs(int argc, char *argv[]) +{ + int level; + +#ifdef TORONTO + if (argc > 2) + NumNodes = atoi(argv[2]); + else + NumNodes = 4; +#else + if (argc > 2) + __NumNodes = atoi(argv[2]); + else + __NumNodes = 4; +#endif + + if (argc > 1) + level = atoi(argv[1]); + else + level = 16; + + return level; +} + +tree_t *TreeAlloc (int level, int lo, int proc) { + if (level == 0) + return NULL; + else { + struct tree *new, *right, *left; + new = (struct tree *) malloc(sizeof(tree_t)); + left = TreeAlloc(level -1, lo+proc/2, proc/2); + right=TreeAlloc(level-1,lo,proc/2); + new->val = 1; + new->left = (struct tree *) left; + new->right = (struct tree *) right; + return new; + } +} + +/* TreeAdd: + */ +int TreeAdd (tree_t *t) +{ + if (t == NULL) { + return 0; + } + else { +#ifdef FUTURES + future_cell_int leftval; + int rightval; + tree_t *tleft, *tright; + int value; + + tleft = t->left; + RPC(tleft, tleft,TreeAdd,&(leftval)); + NOTEST(); + tright = t->right; + rightval = TreeAdd(tright); + RTOUCH(&leftval); + /*chatting("after touch @ 0x%x\n",t);*/ + value = t->val; + /*chatting("returning from treeadd %d\n",*/ + /*leftval.value + rightval.value + value);*/ + return leftval.value + rightval + value; + RETEST(); +#else + int leftval; + int rightval; + tree_t *tleft, *tright; + int value; + + tleft = t->left; /* <---- 57% load penalty */ + leftval = TreeAdd(tleft); + tright = t->right; /* <---- 11.4% load penalty */ + rightval = TreeAdd(tright); + /*chatting("after touch\n");*/ + value = t->val; + /*chatting("returning from treeadd %d\n",*/ + /*leftval.value + rightval.value + value);*/ + return leftval + rightval + value; +#endif + } +} /* end of TreeAdd */ + +int main (int argc, char *argv[]) +{ + tree_t *root; + int level,result; + +#ifdef FUTURES + level = SPMDInit(argc,argv); +#else +#ifndef TORONTO + filestuff(); +#endif + level = dealwithargs(argc, argv); +#endif +#ifndef TORONTO + CMMD_node_timer_clear(0); + CMMD_node_timer_clear(1); +#endif +#ifdef TORONTO + chatting("Treeadd with %d levels on %d processors \n", + level, NumNodes); +#else + chatting("Treeadd with %d levels on %d processors \n", + level, __NumNodes); +#endif + /* only processor 0 will continue here. */ + chatting("About to enter TreeAlloc\n"); +#ifndef TORONTO + CMMD_node_timer_start(0); +#endif + +#ifdef TORONTO + root = TreeAlloc (level, 0, NumNodes); +#else + root = TreeAlloc (level, 0, __NumNodes); +#endif + +#ifndef TORONTO + CMMD_node_timer_stop(0); +#endif + chatting("About to enter TreeAdd\n"); + +#ifndef PLAIN + ClearAllStats(); +#endif +#ifndef TORONTO + CMMD_node_timer_start(1); +#endif +{ int i; for (i = 0; i < 100; ++i) + result = TreeAdd (root); +} +#ifndef TORONTO + CMMD_node_timer_stop(1); +#endif + chatting("Received result of %d\n",result); + +#ifndef TORONTO + chatting("Alloc Time = %f seconds\n", CMMD_node_timer_elapsed(0)); + chatting(/* TreeAdd: + */ +int TreeAdd (tree_t *t) +{ + if (t == NULL) { + return 0; + } + else { +#ifdef FUTURES + future_cell_int leftval; + int rightval; + tree_t *tleft, *tright; + int value; + + tleft = t->left; + RPC(tleft, tleft,TreeAdd,&(leftval)); + NOTEST(); + tright = t->right; + rightval = TreeAdd(tright); + RTOUCH(&leftval); + /*chatting("after touch @ 0x%x\n",t);*/ + value = t->val; + /*chatting("returning from treeadd %d\n",*/ + /*leftval.value + rightval.value + value);*/ + return leftval.value + rightval + value; + RETEST(); +#else + int leftval; + int rightval; + tree_t *tleft, *tright; + int value; + + tleft = t->left; /* <---- 57% load penalty */ + leftval = TreeAdd(tleft); + tright = t->right; /* <---- 11.4% load penalty */ + rightval = TreeAdd(tright); + /*chatting("after touch\n");*/ + value = t->val; + /*chatting("returning from treeadd %d\n",*/ + /*leftval.value + rightval.value + value);*/ + return leftval + rightval + value; +#endif + } +} /* end of TreeAdd */ +"Add Time = %f seconds\n", CMMD_node_timer_elapsed(1)); +#endif + +#ifdef FUTURES + __ShutDown(); +#endif + exit(0); +} diff --git a/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd/tree.h b/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd/tree.h new file mode 100644 index 000000000000..b8d075e41cb0 --- /dev/null +++ b/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd/tree.h @@ -0,0 +1,25 @@ +/* For copyright information, see olden_v1.0/COPYRIGHT */ + +/* tree.h + */ + +#ifdef TORONTO +#include +#define chatting printf +#define PLAIN +#endif + +typedef struct tree { + int val; + struct tree *left, *right; +} tree_t; + +tree_t *TreeAlloc (int level, int lo, int hi); +int TreeAdd (tree_t *t); + + + + + + + diff --git a/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd_bsc/node.cbs b/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd_bsc/node.cbs new file mode 100644 index 000000000000..ad2fac0b6695 --- /dev/null +++ b/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd_bsc/node.cbs @@ -0,0 +1,193 @@ +// RUN: %clang %s -DTORONTO -enable-fat-ptr -o %test.output +// RUN: %test.output 16 + +#ifndef TORONTO +#include +#include +#endif + +#ifdef FUTURES +#include "future-cell.h" +#endif + +#ifndef TORONTO +#include "mem-ref.h" +#endif + +#include +#include "tree.hbs" + +#ifdef TORONTO +int NumNodes; +#else +int __NumNodes; +#endif + +extern int atoi(const char *); + +#ifndef TORONTO +void filestuff() +{ + CMMD_fset_io_mode(stdout, CMMD_independent); + fcntl(fileno(stdout), F_SETFL, O_APPEND); + if (CMMD_self_address()) exit(0); + __InitRegs(0); +} +#endif + +int dealwithargs(int argc, char *argv[]) +{ + int level; + +#ifdef TORONTO + if (argc > 2) + NumNodes = atoi(argv[2]); + else + NumNodes = 4; +#else + if (argc > 2) + __NumNodes = atoi(argv[2]); + else + __NumNodes = 4; +#endif + + if (argc > 1) + level = atoi(argv[1]); + else + level = 16; + + return level; +} + +typedef struct { + long level; +} startmsg_t; + +tree_t *fat TreeAlloc (int level, int lo, int proc) { + if (level == 0) + return nullptr; + else { + tree_t *fat new; + tree_t *fat right; + tree_t *fat left; + new = checked_malloc(sizeof(tree_t)); + left = TreeAlloc(level -1, lo+proc/2, proc/2); + right = TreeAlloc(level-1,lo,proc/2); + new->val = 1; + new->left = left; + new->right = right; + return new; + } +} + +/* TreeAdd: + */ +int TreeAdd (tree_t *fat t) +{ + if (t == nullptr) { + return 0; + } + else { +#ifdef FUTURES + future_cell_int leftval; + int rightval; + tree_t *fat tleft, *fat tright; + int value; + + tleft = t->left; + RPC(tleft, tleft,TreeAdd,&(leftval)); + NOTEST(); + tright = t->right; + rightval = TreeAdd(tright); + RTOUCH(&leftval); + /*chatting("after touch @ 0x%x\n",t);*/ + value = t->val; + /*chatting("returning from treeadd %d\n",*/ + /*leftval.value + rightval.value + value);*/ + return leftval.value + rightval + value; + RETEST(); +#else + int leftval; + int rightval; + tree_t *fat tleft; + tree_t *fat tright; + int value; + + tleft = t->left; /* <---- 57% load penalty */ + leftval = TreeAdd(tleft); + tright = t->right; /* <---- 11.4% load penalty */ + rightval = TreeAdd(tright); + /*chatting("after touch\n");*/ + value = t->val; + /*chatting("returning from treeadd %d\n",*/ + /*leftval.value + rightval.value + value);*/ + return leftval + rightval + value; +#endif + } +} /* end of TreeAdd */ + +int main (int argc, char *argv[]) +{ + tree_t *fat root; + int level, result; + +#ifdef FUTURES + level = SPMDInit(argc,argv); +#else +#ifndef TORONTO + filestuff(); +#endif + level = dealwithargs(argc, argv); +#endif +#ifndef TORONTO + CMMD_node_timer_clear(0); + CMMD_node_timer_clear(1); +#endif +#ifdef TORONTO + chatting("Treeadd with %d levels on %d processors \n", + level, NumNodes); +#else + chatting("Treeadd with %d levels on %d processors \n", + level, __NumNodes); +#endif + /* only processor 0 will continue here. */ + chatting("About to enter TreeAlloc\n"); +#ifndef TORONTO + CMMD_node_timer_start(0); +#endif + int a0 = 0; +#ifdef TORONTO + root = TreeAlloc (level, a0, NumNodes); +#else + root = TreeAlloc (level, a0, __NumNodes); +#endif + +#ifndef TORONTO + CMMD_node_timer_stop(0); +#endif + chatting("About to enter TreeAdd\n"); + +#ifndef PLAIN + ClearAllStats(); +#endif +#ifndef TORONTO + CMMD_node_timer_start(1); +#endif +{ int i; for (i = 0; i < 100; ++i) + result = TreeAdd(root); +} +#ifndef TORONTO + CMMD_node_timer_stop(1); +#endif + chatting("Received result of %d\n",result); + +#ifndef TORONTO + chatting("Alloc Time = %f seconds\n", CMMD_node_timer_elapsed(0)); + chatting("Add Time = %f seconds\n", CMMD_node_timer_elapsed(1)); +#endif + +#ifdef FUTURES + __ShutDown(); +#endif + exit(0); +} diff --git a/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd_bsc/tree.hbs b/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd_bsc/tree.hbs new file mode 100644 index 000000000000..0b8014a09f3d --- /dev/null +++ b/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd_bsc/tree.hbs @@ -0,0 +1,29 @@ +/* For copyright information, see olden_v1.0/COPYRIGHT */ + +/* tree.h + */ + +#ifdef TORONTO +#include +#define chatting printf +#define PLAIN +#endif + +#include +typedef struct tree tree_t; + +struct tree { + int val; + tree_t *fat left; + tree_t *fat right; +}; + +tree_t *fat TreeAlloc (int level, int lo, int hi); +int TreeAdd (tree_t *fat t); + + + + + + + diff --git a/clang/test/BSC/Positive/FatPtr/stack.cbs b/clang/test/BSC/Positive/FatPtr/stack.cbs index 22b307601f5b..c44dab32adca 100644 --- a/clang/test/BSC/Positive/FatPtr/stack.cbs +++ b/clang/test/BSC/Positive/FatPtr/stack.cbs @@ -17,18 +17,8 @@ void test2() { *p1 = 10; } -void test3() { - int arr[2] = { 0, 1 }; - int *fat p = &fat arr[0]; - int a = 10; - if (*p++ == 0) { - a = *p++ == 0 ? *p++ : 0; - } -} - int main() { test1(); test2(); - test3(); return 0; } \ No newline at end of file -- Gitee From ce247069463bfb3dbc430ca4872c7d64332804e0 Mon Sep 17 00:00:00 2001 From: zhaoxuhui Date: Fri, 21 Mar 2025 16:33:02 +0800 Subject: [PATCH 25/29] Revert "[FatPtr] Performance optimization for fatptr check, and also fix some bugs" This reverts commit 7e99fe47a7f7551cd31ec085a2a1b2d2fb9cdce7. --- clang/include/clang/AST/Expr.h | 7 -- clang/lib/AST/ExprConstant.cpp | 8 -- clang/lib/Analysis/CMakeLists.txt | 1 - clang/lib/Headers/bsc_include/bsc_fat_ptr.hbs | 21 +---- clang/lib/Sema/BSC/SemaBSCFatPtr.cpp | 77 +------------------ clang/lib/Sema/BSC/SemaDeclBSC.cpp | 12 +-- clang/lib/Sema/SemaExpr.cpp | 13 +--- clang/lib/Sema/SemaExprCXX.cpp | 7 -- 8 files changed, 10 insertions(+), 136 deletions(-) diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h index 98615e310b26..867dede1da1b 100644 --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -151,13 +151,6 @@ public: bool IsDesugaredCastExpr = false; // Add BSC Member func desugar flag. bool IsDesugaredBSCMethodCall = false; - // Stores the check status of a fat pointer used in the expression. - // Possible values: - // Unchecked: 0 - No checks have been performed. - // KeyCheckedOnly: 1 - Only the key was checked. - // OffsetCheckedOnly: 2 - Only the offset was checked. - // BothChecked: 3 - Both the key and the offset were checked. - uint8_t FatPtrCheckedStatus = 0; // Default: Unchecked SourceLocation getExtendedTypeBeginLoc() { return EBLoc; } void setExtendedTypeBeginLoc(SourceLocation L) { EBLoc = L; } diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index c46223656371..0c9154cfe4ea 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -8823,15 +8823,7 @@ public: static bool EvaluatePointer(const Expr* E, LValue& Result, EvalInfo &Info, bool InvalidBaseOK) { assert(!E->isValueDependent()); -#if ENABLE_BSC - if(E->getType().isFatPtrRecordType()) { - assert(E->isPRValue()); - } else { -#endif assert(E->isPRValue() && E->getType()->hasPointerRepresentation()); -#if ENABLE_BSC - } -#endif return PointerExprEvaluator(Info, Result, InvalidBaseOK).Visit(E); } diff --git a/clang/lib/Analysis/CMakeLists.txt b/clang/lib/Analysis/CMakeLists.txt index 387bcd09a808..f0c0a46c2750 100644 --- a/clang/lib/Analysis/CMakeLists.txt +++ b/clang/lib/Analysis/CMakeLists.txt @@ -8,7 +8,6 @@ add_clang_library(clangAnalysis BodyFarm.cpp BSC/BSCBorrowCheck.cpp BSC/BSCBorrowChecker.cpp - BSC/BSCFatPtrCheck.cpp BSC/BSCNullabilityCheck.cpp BSC/BSCOwnership.cpp CalledOnceCheck.cpp diff --git a/clang/lib/Headers/bsc_include/bsc_fat_ptr.hbs b/clang/lib/Headers/bsc_include/bsc_fat_ptr.hbs index 5b1c32a1cb87..e973722e54b0 100644 --- a/clang/lib/Headers/bsc_include/bsc_fat_ptr.hbs +++ b/clang/lib/Headers/bsc_include/bsc_fat_ptr.hbs @@ -111,7 +111,7 @@ void checked_free(_FatPtr ptr) { free(phead); } -static __always_inline void _check_version( +static void _check_version( void *raw_ptr, uint32_t key_version, int32_t offset, char *file_name, int line_no, char *func_name) { // if key_version == 0, means this fat ptr is from a raw ptr, @@ -126,7 +126,7 @@ static __always_inline void _check_version( } } -static __always_inline void _check_offset( +static void _check_offset( int32_t bytes, void *raw_ptr, uint32_t key_version, int32_t offset, char *file_name, int line_no, char *func_name) { // if key_version == 0, means this fat ptr is from a raw ptr, @@ -141,21 +141,4 @@ static __always_inline void _check_offset( } } -static __always_inline void _check_version_and_offset( - int32_t bytes,void *raw_ptr, uint32_t key_version, int32_t offset, - char *file_name, int line_no, char *func_name) { - // if key_version == 0, means this fat ptr is from a raw ptr, skip both check - // if key_version == 1, means this fat ptr points to a global array, skip offset check - // we should skip check version. - if (key_version != 0) { - const uint8_t *head = (const uint8_t *)raw_ptr - offset - 8; - const struct _AllocationUnit *phead = (const struct _AllocationUnit *)head; - if (key_version != 1 && key_version != phead->lock_version) - _report_error(UseAfterFree, file_name, line_no, func_name); - int32_t new_offset = offset + bytes; - if (phead->size != 0 &&new_offset > phead->size) - _report_error(OutOfBoundsAccess, file_name, line_no, func_name); - } -} - #endif /* BSC_FAT_PTR_HBS */ \ No newline at end of file diff --git a/clang/lib/Sema/BSC/SemaBSCFatPtr.cpp b/clang/lib/Sema/BSC/SemaBSCFatPtr.cpp index 563545220405..8c3494be693d 100644 --- a/clang/lib/Sema/BSC/SemaBSCFatPtr.cpp +++ b/clang/lib/Sema/BSC/SemaBSCFatPtr.cpp @@ -550,7 +550,6 @@ private: Expr *BuildFatPtrCall(SourceLocation SLoc, std::string FDName, llvm::SmallVector &Args); MemberExpr *BuildMemberRawPtrAndInsertCheckCallForFatPtr(Expr *FatPtrBaseE); - Expr *BuildFatPtrCStyleCastExpr(SourceLocation SLoc, QualType ToType, Expr *SubExpr); Expr *BuildFatPtrCompoundLiteralExprForRawPtr(Expr *RawPtrE, QualType QT); Expr *BuildFatPtrCompoundLiteralExpr(Expr *FirstInitE, Expr *SecondInitE, Expr *ThirdInitE, QualType QT); @@ -1249,7 +1248,7 @@ ExprResult TransformFatPtr::TransformCStyleCastExpr(CStyleCastExpr *CSCE) { SourceLocation SLoc = CSCE->getBeginLoc(); QualType QT = CSCE->getType(); Expr *SubE = CSCE->getSubExpr(); - QualType SubQT = SubE->getType(); + QualType SubQT = SubE->getType(); if (QT->isPointerType() && !QT.isFatPtrType() && SubQT.isFatPtrType()) { // desuagr `(int*)fat_p` to `(int*)fat_p.raw_ptr` CSCE->setCastKind(CK_NoOp); @@ -1269,15 +1268,6 @@ ExprResult TransformFatPtr::TransformCStyleCastExpr(CStyleCastExpr *CSCE) { return ImplicitCastExpr::Create(SemaRef.Context, QT, CK_NoOp, FatPtrE, nullptr, VK_PRValue, FPOptionsOverride()); } else if (QT.isFatPtrType() && SubQT.isFatPtrType()) { - if (DesugarToMemberPtr) { - QualType ToPointerTy = - SemaRef.Context.getPointerType(QT.getFatPtrPointeeType()); - Expr *Res = BaseTransform::TransformExpr(SubE).get(); - Res = ImplicitCastExpr::Create(SemaRef.Context, ToPointerTy, - CK_NoOp, Res, nullptr, VK_PRValue, - FPOptionsOverride()); - return Res; - } return BaseTransform::TransformExpr(SubE).get(); } CSCE->setSubExpr(BaseTransform::TransformExpr(SubE).get()); @@ -1394,18 +1384,6 @@ ExprResult TransformFatPtr::TransformBinaryOperator(BinaryOperator *BO) { QualType LHSQT = LHS->getType(); QualType RHSQT = RHS->getType(); BinaryOperator::Opcode Op = BO->getOpcode(); - if (Op == BO_LAnd || Op == BO_LOr) { - if (LHSQT.isFatPtrType() && isa(LHS)) { - // `p1 && Expr2` should be desugared to `p1.raw_ptr && ` - DesugarToMemberPtr = true; - BO->setLHS(BaseTransform::TransformExpr(LHS).get()); - } - if (RHSQT.isFatPtrType() && isa(RHS)) { - // `Expr1 && p2` should be desugared to `p2.raw_ptr && ` - DesugarToMemberPtr = true; - BO->setRHS(BaseTransform::TransformExpr(RHS).get()); - } - } if ((Op == BO_Sub || BO->isComparisonOp()) && LHSQT.isFatPtrType() && RHSQT.isFatPtrType()) { // Substraction between two fat ptr @@ -1472,7 +1450,7 @@ ExprResult TransformFatPtr::HandleBOFatPtrAddOrSubInteger(BinaryOperator *BO) { Expr *RHS = BaseTransform::TransformExpr(BO->getRHS()).get(); Expr *FirstInitE = SemaRef.CreateBuiltinBinOp(SLoc, Op, LHS, RHS).get(); - if (auto MemberFatPtr = dyn_cast(LHS->IgnoreParenImpCasts())) { + if (auto MemberFatPtr = dyn_cast(LHS->IgnoreImpCasts())) { Expr *FatPtrBase = MemberFatPtr->getBase(); Expr *SecondInitE = BuildMemberForFatPtr(FatPtrBase, "key_version"); Expr *ThirdInitLHS = BuildMemberForFatPtr(FatPtrBase, "offset"); @@ -1882,40 +1860,6 @@ TransformFatPtr::DesugarFatPtrType(SourceLocation SLoc, QualType T) { return T; } -Expr *TransformFatPtr::BuildFatPtrCStyleCastExpr(SourceLocation SLoc, QualType ToType, Expr *SubExpr) { - QualType FromType = SubExpr->getType(); - - if (FromType.isFatPtrType() && ToType.isFatPtrType()) { - // 1. Extract raw_ptr and perform raw pointer CStyleCastExpr - if (auto ICE = dyn_cast(SubExpr)) { - SubExpr = ICE->getSubExpr(); - } - SubExpr = BaseTransform::TransformExpr(SubExpr).get(); - Expr *RawPtr = BuildMemberForFatPtr(SubExpr, "raw_ptr"); - QualType ToPointerTy = - SemaRef.Context.getPointerType(ToType.getFatPtrPointeeType()); - Expr *CastedRawPtr = - SemaRef - .BuildCStyleCastExpr( - SLoc, SemaRef.Context.getTrivialTypeSourceInfo(ToPointerTy), - SLoc, RawPtr) - .get(); - - // 2. Pass key_version and offset directly - Expr *KeyVersion = BuildMemberForFatPtr(SubExpr, "key_version"); - Expr *Offset = BuildMemberForFatPtr(SubExpr, "offset"); - - // 3. Assemble the target fat pointer, as _FatPtr - QualType QT = DesugarFatPtrType(SLoc, ToType); - Expr *CLE = BuildFatPtrCompoundLiteralExpr(CastedRawPtr, KeyVersion, Offset, QT); - CLE = SemaRef.DefaultFunctionArrayLvalueConversion(CLE).get(); - return CLE; - } - - // Otherwise, keep the original transformation - return BaseTransform::TransformExpr(SubExpr).get(); -} - // Build CallExpr for _check_version, _check_offset or _new_version_number. Expr *TransformFatPtr::BuildFatPtrCall(SourceLocation SLoc, std::string FDName, llvm::SmallVector &Args) { @@ -1997,23 +1941,6 @@ MemberExpr *TransformFatPtr::BuildMemberForFatPtr(Expr *FatPtrBaseE, MemberExpr *TransformFatPtr::BuildMemberRawPtrAndInsertCheckCallForFatPtr( Expr *FatPtrBaseE) { SourceLocation SLoc = FatPtrBaseE->getBeginLoc(); - if ((InsertCheckVersionCall || InsertCheckOffsetCall.first) && FatPtrBaseE->FatPtrCheckedStatus) { - if (FatPtrBaseE->FatPtrCheckedStatus == 1 || FatPtrBaseE->FatPtrCheckedStatus == 3) { - InsertCheckVersionCall = false; - } - if (FatPtrBaseE->FatPtrCheckedStatus == 2 || FatPtrBaseE->FatPtrCheckedStatus == 3) { - InsertCheckOffsetCall.first = false; - InsertCheckOffsetCall.second = nullptr; - } - } - if (InsertCheckOffsetCall.first && InsertCheckVersionCall) { - llvm::SmallVector Args{InsertCheckOffsetCall.second, FatPtrBaseE}; - Expr *CE = BuildFatPtrCall(SLoc, "_check_version_and_offset", Args); - CheckCallExpr.push(std::make_pair(FatPtrBaseE, CE)); - InsertCheckOffsetCall.first = false; - InsertCheckOffsetCall.second = nullptr; - InsertCheckVersionCall = false; - } if (InsertCheckOffsetCall.first) { // Build CallExpr to check offset. llvm::SmallVector Args{InsertCheckOffsetCall.second, diff --git a/clang/lib/Sema/BSC/SemaDeclBSC.cpp b/clang/lib/Sema/BSC/SemaDeclBSC.cpp index 7e4cb9653242..d9dff2e4add7 100644 --- a/clang/lib/Sema/BSC/SemaDeclBSC.cpp +++ b/clang/lib/Sema/BSC/SemaDeclBSC.cpp @@ -16,7 +16,6 @@ #include "TreeTransform.h" #include "clang/AST/BSC/WalkerBSC.h" #include "clang/Analysis/Analyses/BSC/BSCBorrowChecker.h" -#include "clang/Analysis/Analyses/BSC/BSCFatPtrCheck.h" #include "clang/Analysis/Analyses/BSC/BSCNullabilityCheck.h" #include "clang/Analysis/Analyses/BSC/BSCOwnership.h" #include "clang/Analysis/AnalysisDeclContext.h" @@ -160,7 +159,7 @@ bool Sema::HasSafeZoneInFunction(const FunctionDecl* FnDecl) { /// | | | | /// FuncDecl--> CFG --> | NullabilityCheck | --> | OwnershipAnalysis | --> /// |__________________| |___________________| -/// __________________ +/// __________________ /// | | /// --> | BorrowCheck | --> FuncDecl --> CodeGen /// |__________________| @@ -186,17 +185,17 @@ void Sema::BSCDataflowAnalysis(const Decl *D, bool EnableOwnershipCheck, bool RequireBorrowCheck = LangOpts.BSC ? FindSafeFeatures(FD) : false; // nullability-check happens in mode: {SafeOnly, All}. // For SafeOnly, do not build cfg when there is no SafeZone in Function. - bool RequireNullabilityCheck = EnableNullabilityCheck; + bool RequireNullabilityCheck = EnableNullabilityCheck; if (getLangOpts().getNullabilityCheck() == LangOptions::NC_SAFE) { if(HasSafeZoneInFunction(FD)) { - RequireNullabilityCheck = EnableNullabilityCheck; + RequireNullabilityCheck = EnableNullabilityCheck; } else { RequireNullabilityCheck = false; } } bool RequireCFGAnalysis = RequireNullabilityCheck || RequireBorrowCheck; if (RequireCFGAnalysis && FD && AC.getCFG()) { // Only build the CFG when needed. - // Step one: Run NullabilityCheck + // Step one: Run NullabilityCheck unsigned NumNullabilityCheckErrorsInCurrFD = 0; if (RequireNullabilityCheck) { NullabilityCheckDiagReporter NullabilityCheckReporter(*this); @@ -220,9 +219,6 @@ void Sema::BSCDataflowAnalysis(const Decl *D, bool EnableOwnershipCheck, BSCBorrowChecker(const_cast(FD)); } } - if (getLangOpts().EnableFatPtr) { - runFatPtrReduntantCheck(*FD, *cfg, AC, Context); - } } } diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 9c7a7f12d004..92ed21bdec69 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -13485,7 +13485,7 @@ QualType Sema::CheckCompareOperands(ExprResult &LHS, ExprResult &RHS, } #if ENABLE_BSC - if (getLangOpts().BSC && (RHSType.isFatPtrType() || RHSType->isNullPtrType()) && + if (getLangOpts().BSC && RHSType->isNullPtrType() && (LHSType->isPointerType() || LHSType.isFatPtrType())) return computeResultTy(); #endif @@ -14034,16 +14034,7 @@ inline QualType Sema::CheckLogicalOperands(ExprResult &LHS, ExprResult &RHS, RHS = UsualUnaryConversions(RHS.get()); if (RHS.isInvalid()) return QualType(); -#if ENABLE_BSC - if (getLangOpts().BSC && getLangOpts().EnableFatPtr) { - QualType LT = LHS.get()->getType(); - QualType RT = RHS.get()->getType(); - if ((LT->isScalarType() || LT.isFatPtrType()) && - (RT->isScalarType() || RT.isFatPtrType())) { - return Context.IntTy; - } - } -#endif + if (!LHS.get()->getType()->isScalarType() || !RHS.get()->getType()->isScalarType()) return InvalidOperands(Loc, LHS, RHS); diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index a97fd9eaefde..4a98035b77d9 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -7559,13 +7559,6 @@ ExprResult Sema::ActOnStartCXXMemberReference(Scope *S, Expr *Base, CTypes.insert(Context.getCanonicalType(BaseType)); while (BaseType->isRecordType()) { -#if ENABLE_BSC - if (getLangOpts().BSC && getLangOpts().EnableFatPtr && - BaseType.isFatPtrRecordType()) { - BaseType = BaseType.getFatPtrPointeeType(); - break; - } -#endif if (OperatorArrows.size() >= getLangOpts().ArrowDepth) { Diag(OpLoc, diag::err_operator_arrow_depth_exceeded) << StartingType << getLangOpts().ArrowDepth << Base->getSourceRange(); -- Gitee From 4cd1c2f1b04c47077d22a94691ecb0fdabdda5ed Mon Sep 17 00:00:00 2001 From: zhaoxuhui Date: Fri, 21 Mar 2025 16:38:20 +0800 Subject: [PATCH 26/29] Revert "[FatPtr] optimize bsc_fat_ptr.hbs and bugfix" This reverts commit db4fff3e09788cdc1fa50281abfb356f3d5bceee. --- clang/include/clang/AST/ASTContext.h | 4 +- clang/include/clang/AST/BSC/WalkerBSC.h | 3 +- clang/include/clang/AST/Type.h | 22 +- .../clang/Basic/BSC/DiagnosticBSCSemaKinds.td | 8 +- clang/include/clang/Sema/Sema.h | 9 +- clang/lib/AST/ASTContext.cpp | 44 +- clang/lib/AST/BSC/TypeBSC.cpp | 111 +- clang/lib/AST/ExprConstant.cpp | 28 +- clang/lib/CodeGen/CGExprAgg.cpp | 4 +- clang/lib/Headers/bsc_include/bsc_fat_ptr.hbs | 96 +- clang/lib/Parse/ParseDecl.cpp | 4 +- clang/lib/Sema/BSC/SemaBSCFatPtr.cpp | 1431 ++++------------- clang/lib/Sema/SemaCast.cpp | 15 +- clang/lib/Sema/SemaDecl.cpp | 25 +- clang/lib/Sema/SemaExpr.cpp | 399 ++--- clang/lib/Sema/SemaExprMember.cpp | 12 +- clang/lib/Sema/SemaOverload.cpp | 7 - clang/lib/Sema/SemaStmt.cpp | 7 - clang/lib/Sema/SemaType.cpp | 21 +- clang/test/BSC/Negative/FatPtr/addr_fat.cbs | 8 +- .../{fat_explicit_cast.cbs => fat_cast.cbs} | 61 +- .../FatPtr/fat_func_decl_several_times.cbs | 29 - .../BSC/Negative/FatPtr/fat_implicit_cast.cbs | 73 - .../FatPtr/fat_qulifier_only_for_pointer.cbs | 1 + .../BSC/Negative/FatPtr/func_ptr_has_fat.cbs | 23 - .../FatPtr/self_inc_or_dec_of_fat_ptr.cbs | 22 - .../global_array/array_out_of_bounds1.cbs | 5 +- .../global_array/array_out_of_bounds2.cbs | 3 +- .../heap/array_out_of_bounds1.cbs | 5 +- .../heap/array_out_of_bounds10.cbs | 16 - .../heap/array_out_of_bounds11.cbs | 17 - .../heap/array_out_of_bounds12.cbs | 15 - .../heap/array_out_of_bounds13.cbs | 16 - .../heap/array_out_of_bounds14.cbs | 13 - .../heap/array_out_of_bounds15.cbs | 15 - .../heap/array_out_of_bounds16.cbs | 14 - .../heap/array_out_of_bounds17.cbs | 18 - .../heap/array_out_of_bounds18.cbs | 18 - .../heap/array_out_of_bounds19.cbs | 19 - .../heap/array_out_of_bounds2.cbs | 5 +- .../heap/array_out_of_bounds3.cbs | 5 +- .../heap/array_out_of_bounds4.cbs | 5 +- .../heap/array_out_of_bounds5.cbs | 5 +- .../heap/array_out_of_bounds6.cbs | 5 +- .../heap/array_out_of_bounds7.cbs | 17 - .../heap/array_out_of_bounds8.cbs | 17 - .../heap/array_out_of_bounds9.cbs | 15 - .../heap/buffer_overflow.cbs | 5 +- .../heap/double_free1.cbs | 8 +- .../heap/double_free2.cbs | 8 +- .../heap/double_free3.cbs | 10 +- .../heap/double_free4.cbs | 8 +- .../heap/use_after_free1.cbs | 7 +- .../heap/use_after_free2.cbs | 7 +- .../heap/use_after_free3.cbs | 9 +- .../heap/use_after_free4.cbs | 9 +- .../heap/use_after_free5.cbs | 11 +- .../heap/use_after_free6.cbs | 7 +- .../heap/use_after_free7.cbs | 15 - .../heap/use_after_free8.cbs | 17 - .../stack/array_out_of_bounds1.cbs | 3 +- .../stack/array_out_of_bounds2.cbs | 3 +- .../stack/array_out_of_bounds3.cbs | 14 - .../stack/array_out_of_bounds4.cbs | 14 - .../stack/buffer_overflow.cbs | 3 +- .../stack/return_local_address.cbs | 3 +- clang/test/BSC/Positive/FatPtr/heap.cbs | 66 +- .../Positive/FatPtr/multi_level_pointer.cbs | 29 - .../perform_anaysis/deref_loop/deref_loop.cbs | 2 +- .../olden/bisort_bsc/bitonic.cbs | 14 +- .../olden/treeadd_bsc/node.cbs | 2 +- .../BSC/Positive/FatPtr/string_literal.cbs | 27 - 72 files changed, 760 insertions(+), 2256 deletions(-) rename clang/test/BSC/Negative/FatPtr/{fat_explicit_cast.cbs => fat_cast.cbs} (40%) delete mode 100644 clang/test/BSC/Negative/FatPtr/fat_func_decl_several_times.cbs delete mode 100644 clang/test/BSC/Negative/FatPtr/fat_implicit_cast.cbs delete mode 100644 clang/test/BSC/Negative/FatPtr/func_ptr_has_fat.cbs delete mode 100644 clang/test/BSC/Negative/FatPtr/self_inc_or_dec_of_fat_ptr.cbs delete mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds10.cbs delete mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds11.cbs delete mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds12.cbs delete mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds13.cbs delete mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds14.cbs delete mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds15.cbs delete mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds16.cbs delete mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds17.cbs delete mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds18.cbs delete mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds19.cbs delete mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds7.cbs delete mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds8.cbs delete mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds9.cbs delete mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free7.cbs delete mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free8.cbs delete mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds3.cbs delete mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds4.cbs delete mode 100644 clang/test/BSC/Positive/FatPtr/multi_level_pointer.cbs delete mode 100644 clang/test/BSC/Positive/FatPtr/string_literal.cbs diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index 64d6e5ec52a9..328205520b01 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -2564,9 +2564,7 @@ public: bool hasSameType(const Type *T1, const Type *T2) const { return getCanonicalType(T1) == getCanonicalType(T2); } -#if ENABLE_BSC - bool hasSameFatPtrType(QualType LHSType, QualType RHSType); -#endif + /// Return this type as a completely-unqualified array type, /// capturing the qualifiers in \p Quals. /// diff --git a/clang/include/clang/AST/BSC/WalkerBSC.h b/clang/include/clang/AST/BSC/WalkerBSC.h index fd5868207d95..c98e20d67d41 100644 --- a/clang/include/clang/AST/BSC/WalkerBSC.h +++ b/clang/include/clang/AST/BSC/WalkerBSC.h @@ -89,7 +89,8 @@ public: bool VisitQualType(QualType QT) { if (QT.isOwnedQualified() || QT.isBorrowQualified() || - QT->hasBorrowFields() || QT->hasOwnedFields() || QT.hasFat()) { + QT->hasBorrowFields() || QT->hasOwnedFields() || QT.isFatQualified() || + QT->hasFatFields()) { return true; } if (IsDesugaredFromTraitType(QT)) { diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h index 58edda5c4989..72fcecf7ad5b 100644 --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -939,9 +939,6 @@ public: /// Determine whether this type is fat-qualified. bool isFatQualified() const; bool isFatPtrType() const; - bool isFatPtrRecordType() const; - QualType getFatPtrPointeeType() const; - bool hasFat() const; #endif /// Determine whether this particular QualType instance has the @@ -1068,6 +1065,7 @@ public: /// Add the `fat` type qualifier to this QualType. void addFat() { addFastQualifiers(Qualifiers::Fat); } QualType withFat() const { return withFastQualifiers(Qualifiers::Fat); } + bool hasFat() const; #endif /// Add the `volatile` type qualifier to this QualType. @@ -2303,6 +2301,8 @@ public: bool isTraitType() const; bool isTraitPointerType() const; bool hasTraitType() const; + bool isFatPtrRecordType() const; + QualType getFatPtrPointeeType() const; bool isBSCCalculatedTypeInCompileTime() const; bool checkFunctionProtoType(SafeZoneSpecifier SZS) const; bool isOwnedStructureType() const; @@ -2942,10 +2942,12 @@ public: } static bool classof(const Type *T) { return T->getTypeClass() == Pointer; } - -#if ENABLE_BSC + #if ENABLE_BSC bool hasOwnedFields() const; + bool hasBorrowFields() const; + + bool hasFatFields() const; #endif }; @@ -4973,8 +4975,10 @@ protected: withoutBorrow }; + enum fatStatus{unInitFat, withFat, withoutFat}; mutable ownedStatus hasOwn = ownedStatus::unInitOwned; mutable borrowStatus hasBorrow = borrowStatus::unInitBorrow; + mutable fatStatus hasFat = fatStatus::unInitFat; #endif public: @@ -5000,6 +5004,8 @@ public: void initBorrowStatus() const; bool hasFatFields() const; + + void initFatStatus() const; #endif bool isSugared() const { return false; } @@ -5037,13 +5043,13 @@ public: // If the value of CondExpr is true, then UnderlyingType is Type1, otherwise is Type2. class ConditionalType : public Type { friend class ASTContext; // ASTContext creates these. - + llvm::Optional CondResult; Expr* CondExpr; QualType Type1; QualType Type2; QualType UnderlyingType; - + ConditionalType(llvm::Optional CondRes, Expr* CondE, QualType T1, QualType T2, QualType can); public: @@ -5052,7 +5058,7 @@ public: QualType getConditionalType1() const { return Type1; } QualType getConditionalType2() const { return Type2; } QualType getUnderlyingType() const { return UnderlyingType; } - + /// Remove a single level of sugar. QualType desugar() const; diff --git a/clang/include/clang/Basic/BSC/DiagnosticBSCSemaKinds.td b/clang/include/clang/Basic/BSC/DiagnosticBSCSemaKinds.td index c70e09f31e8b..f0add7662c1a 100644 --- a/clang/include/clang/Basic/BSC/DiagnosticBSCSemaKinds.td +++ b/clang/include/clang/Basic/BSC/DiagnosticBSCSemaKinds.td @@ -277,10 +277,8 @@ def err_fat_ptr_checked_attr_global_array_var : Error< "fat_ptr_checked attribute can only used for global or static array variables">; def err_typecheck_invalid_fat_not_pointer : Error< "only pointer type can be qualified by fat">; -def err_typecheck_invalid_fat_not_function_pointer : Error< - "function pointer type cannot be qualified by fat">; def err_fat_qualcheck_incompatible : Error< - "incompatible fat types, cannot %select{implicitly|explicitly}0 cast %1 to %2">; + "incompatible fat types, cannot cast %0 to %1">; def err_fat_funcPtr_incompatible : Error< "incompatible fat function pointer types, cannot cast %0 to %1">; def err_addr_fat_on_no_fat_pointer : Error< @@ -292,6 +290,4 @@ def err_fat_ptr_type_not_found : Error< def err_fat_ptr_func_not_found : Error< "fat ptr related function not found, you need to include %0">; def err_fat_ptr_missing_specialization : Error< - "missing definition of template specialization for fat ptr %0">; -def err_no_inc_or_dec_of_fat_ptr : Error< - "increment or decrement operation of fat pointer is not allowed here">; \ No newline at end of file + "missing definition of template specialization for fat ptr %0">; \ No newline at end of file diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index efac59d36178..642785ee11c9 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -3221,17 +3221,16 @@ public: bool IsBSCCompatibleFutureType(QualType Ty); // BSC Fat Ptr related. - bool CheckFatQualTypeCStyleCast(QualType LHSType, Expr *RHSExpr); + bool CheckFatQualTypeCStyleCast(QualType LHSType, QualType RHSType, + SourceLocation RLoc); bool CheckFatQualTypeCStyleCast(QualType LHSType, QualType RHSType); bool CheckFatQualTypeAssignment(QualType LHSType, Expr *RHSExpr); - bool CheckFatQualTypeAssignment(QualType LHSType, QualType RHSType); + bool CheckFatQualTypeAssignment(QualType LHSType, QualType RHSType, + SourceLocation RLoc); bool CheckFatFunctionPointerType(QualType LHSType, Expr *RHSExpr); - bool HasDiffFatTypeAtBothFunction(QualType LHSType, QualType RHSType); - bool CheckSelfIncOrDecOfFatPtr(Expr *E); void DesugarFunctionDeclWithFatPtr(FunctionDecl *FD); void DesugarRecordDeclWithFatPtr(RecordDecl *RD); void DesugarGlobalVarDeclWithFatPtr(VarDecl *VD); - void DesugarTypedefNameDeclWithFatPtr(TypedefNameDecl *TND); std::pair BuildAllocationUnitForGlobalArrayVar(VarDecl *VD); // BSC Destructor related. diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index d730b1638a11..0d34dfc80377 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -3247,45 +3247,6 @@ bool ASTContext::hasSameFunctionTypeIgnoringPtrSizes(QualType T, QualType U) { getFunctionTypeWithoutPtrSizes(U)); } -#if ENABLE_BSC -bool ASTContext::hasSameFatPtrType(QualType LHSType, QualType RHSType) { - if (LHSType.hasFat() != RHSType.hasFat()) - return false; - if (LHSType->isFunctionProtoType() && RHSType->isFunctionProtoType()) { - const FunctionProtoType *LHSFuncType = LHSType->getAs(); - const FunctionProtoType *RHSFuncType = RHSType->getAs(); - if (LHSFuncType->hasFatRetOrParams() != RHSFuncType->hasFatRetOrParams() || - !hasSameFatPtrType(LHSFuncType->getReturnType(), - RHSFuncType->getReturnType()) || - LHSFuncType->getNumParams() != RHSFuncType->getNumParams()) { - return false; - } - for (unsigned i = 0; i < LHSFuncType->getNumParams(); i++) { - if (!hasSameFatPtrType(LHSFuncType->getParamType(i), - RHSFuncType->getParamType(i))) { - return false; - } - } - return true; - } - bool LHSIsFatPtrType = LHSType.isFatPtrType(); - bool RHSIsFatPtrType = RHSType.isFatPtrType(); - if (LHSIsFatPtrType && RHSIsFatPtrType) { - // _FatPtr and T *fat are the same type. - return hasSameFatPtrType(LHSType.getFatPtrPointeeType(), - RHSType.getFatPtrPointeeType()); - } else if (LHSIsFatPtrType != RHSIsFatPtrType) { - return false; - } else if (LHSType->isPointerType() && RHSType->isPointerType()) { - // _FatPtr * and T *fat * are the same type. - return hasSameFatPtrType(LHSType->getPointeeType(), - RHSType->getPointeeType()); - } else { - return hasSameType(LHSType, RHSType); - } -} -#endif - void ASTContext::adjustExceptionSpec( FunctionDecl *FD, const FunctionProtoType::ExceptionSpecInfo &ESI, bool AsWritten) { @@ -10190,8 +10151,9 @@ bool ASTContext::typesAreCompatible(QualType LHS, QualType RHS, if (getLangOpts().CPlusPlus) return hasSameType(LHS, RHS); #if ENABLE_BSC - if (getLangOpts().BSC && (LHS.hasFat() || RHS.hasFat())) - return hasSameFatPtrType(LHS, RHS); + if (getLangOpts().BSC && getLangOpts().EnableFatPtr && + LHS->isFatPtrRecordType() && RHS->isFatPtrRecordType()) + return true; #endif return !mergeTypes(LHS, RHS, false, CompareUnqualified).isNull(); } diff --git a/clang/lib/AST/BSC/TypeBSC.cpp b/clang/lib/AST/BSC/TypeBSC.cpp index 375c94e78cd3..640f1344fb4c 100644 --- a/clang/lib/AST/BSC/TypeBSC.cpp +++ b/clang/lib/AST/BSC/TypeBSC.cpp @@ -66,91 +66,49 @@ bool Type::hasBorrowFields() const { return false; } -// T *fat, T *fat *, T *fat[n], struct S which has fat fields... -bool QualType::hasFat() const { - auto Ty = getTypePtr(); - // getCanonicalType from FunctionProtoType will lose fat qualifier - // of return type or parameter types, from ArrayType will lose fat - // qualifier of element type, so we handle these two kinds type first. - if (Ty->isFunctionPointerType()) { - auto FPT = (*this) - ->getAs() - ->getPointeeType() - ->getAs(); - return FPT->hasFatRetOrParams(); - } - if (Ty->isFunctionProtoType()) { - auto FPT = (*this)->getAs(); - return FPT->hasFatRetOrParams(); - } - if (Ty->isArrayType()) { - if (auto AT = dyn_cast(Ty)) - return AT->getElementType().hasFat(); +bool PointerType::hasFatFields() const { + QualType R = getPointeeType(); + if (R.isFatQualified()) { + return true; } - auto CanQT = getCanonicalType(); - if (CanQT.isFatPtrType()) + if (R.getTypePtr()->hasFatFields()) { return true; - if (auto RT = dyn_cast(CanQT)) - if (RT->hasFatFields()) - return true; - if (auto PT = dyn_cast(CanQT)) - return PT->getPointeeType().hasFat(); + } return false; } -// return true T *fat, _FatPtr, ... -bool QualType::isFatPtrType() const { - if (getTypePtr()->isArrayType()) - return false; - QualType CanQT = getCanonicalType(); - return CanQT.isLocalFatQualified() || CanQT.isFatPtrRecordType(); +bool Type::hasFatFields() const { + if (const auto *RecTy = dyn_cast(CanonicalType)) { + return RecTy->hasFatFields(); + } else if (const auto *PointerTy = dyn_cast(CanonicalType)) { + return PointerTy->hasFatFields(); + } + return false; } -bool QualType::isFatPtrRecordType() const { - if (auto RT = dyn_cast(getCanonicalType())) - if (auto CTSD = dyn_cast(RT->getDecl())) - return CTSD->getNameAsString() == "_FatPtr" && - CTSD->getTemplateArgs().size() == 1; +bool Type::isFatPtrRecordType() const { + if (this->isRecordType()) + if (RecordDecl *RD = this->getAs()->getDecl()) + if (RD->getNameAsString() == "_FatPtr") + return true; return false; } -QualType QualType::getFatPtrPointeeType() const { - QualType CanQT = getCanonicalType(); - assert(CanQT.isFatPtrType() && "Can only get pointee type from fat ptr type"); - if (auto RT = dyn_cast(CanQT)) { - if (auto CTSD = dyn_cast(RT->getDecl())) { - const TemplateArgumentList &Args = CTSD->getTemplateArgs(); - assert( - CTSD->getNameAsString() == "_FatPtr" && Args.size() == 1 && - Args.get(0).getKind() == TemplateArgument::Type && - "Fat pointer type is illegal, you need to include "); - return Args.get(0).getAsType(); - } - } - if (auto PT = dyn_cast(CanQT)) +QualType Type::getFatPtrPointeeType() const { + if (this->isFatPtrRecordType()) + if (auto TST = dyn_cast(this)) + if (TST->getNumArgs() == 1) + return TST->begin()->getAsType(); + if (auto PT = dyn_cast(this)) return PT->getPointeeType(); return QualType(); } -bool RecordType::hasFatFields() const { - std::vector RecordTypeList; - RecordTypeList.push_back(this); - unsigned NextToCheckIndex = 0; - - while (RecordTypeList.size() > NextToCheckIndex) { - for (FieldDecl *FD : - RecordTypeList[NextToCheckIndex]->getDecl()->fields()) { - QualType FieldTy = FD->getType(); - if (FieldTy.isFatPtrType()) - return true; - if (const auto *FieldRecTy = - FieldTy.getCanonicalType()->getAs()) { - if (!llvm::is_contained(RecordTypeList, FieldRecTy)) - RecordTypeList.push_back(FieldRecTy); - } - } - ++NextToCheckIndex; - } +bool QualType::isFatPtrType() const { + if (isFatQualified()) + return true; + if (const Type *ty = getTypePtr()) + return ty->isFatPtrRecordType(); return false; } @@ -186,12 +144,11 @@ bool FunctionProtoType::hasBorrowRetOrParams() const { } bool FunctionProtoType::hasFatRetOrParams() const { - QualType ReturnType = getReturnType(); - if (ReturnType.hasFat()) { + if (getReturnType().isFatQualified()) { return true; } for (auto ParamType : getParamTypes()) { - if (ParamType.hasFat()) { + if (ParamType.isFatQualified()) { return true; } } @@ -422,6 +379,12 @@ bool QualType::hasBorrow() const { return getTypePtr()->hasBorrowFields(); } +bool QualType::hasFat() const { + if (isFatQualified()) + return true; + return getTypePtr()->hasFatFields(); +} + bool QualType::isConstBorrow() const { QualType QT = QualType(getTypePtr(), getLocalFastQualifiers()); if (QT.isLocalBorrowQualified()) { diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 0c9154cfe4ea..894692ab2d5c 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -8038,11 +8038,10 @@ public: bool EvalOK; if (E->isArrow()) { #if ENABLE_BSC - if (this->Info.getLangOpts().BSC && - this->Info.getLangOpts().EnableFatPtr && - E->getBase()->getType().isFatPtrRecordType()) { + if (this->Info.getLangOpts().BSC && this->Info.getLangOpts().EnableFatPtr && + E->getBase()->getType()->isFatPtrRecordType()) { EvalOK = true; - BaseTy = E->getBase()->getType().getFatPtrPointeeType(); + BaseTy = E->getBase()->getType()->getFatPtrPointeeType(); } else { #endif EvalOK = evaluatePointer(E->getBase(), Result); @@ -8482,11 +8481,6 @@ bool LValueExprEvaluator::VisitArraySubscriptExpr(const ArraySubscriptExpr *E) { // C++17's rules require us to evaluate the LHS first, regardless of which // side is the base. for (const Expr *SubExpr : {E->getLHS(), E->getRHS()}) { -#if ENABLE_BSC - if (Info.getLangOpts().BSC && Info.getLangOpts().EnableFatPtr && - SubExpr->getType().isFatPtrRecordType()) - return true; -#endif if (SubExpr == E->getBase() ? !evaluatePointer(SubExpr, Result) : !EvaluateInteger(SubExpr, Index, Info)) { if (!Info.noteFailure()) @@ -8502,7 +8496,7 @@ bool LValueExprEvaluator::VisitArraySubscriptExpr(const ArraySubscriptExpr *E) { bool LValueExprEvaluator::VisitUnaryDeref(const UnaryOperator *E) { #if ENABLE_BSC if (Info.getLangOpts().BSC && Info.getLangOpts().EnableFatPtr && - E->getSubExpr()->getType().isFatPtrRecordType()) + E->getSubExpr()->getType()->isFatPtrRecordType()) return true; #endif return evaluatePointer(E->getSubExpr(), Result); @@ -8831,11 +8825,7 @@ bool PointerExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) { if (E->getOpcode() != BO_Add && E->getOpcode() != BO_Sub) return ExprEvaluatorBaseTy::VisitBinaryOperator(E); -#if ENABLE_BSC - if (Info.getLangOpts().BSC && Info.getLangOpts().EnableFatPtr && - E->getLHS()->getType().isFatPtrRecordType()) - return true; -#endif + const Expr *PExp = E->getLHS(); const Expr *IExp = E->getRHS(); if (IExp->getType()->isPointerType()) @@ -14843,12 +14833,8 @@ static bool Evaluate(APValue &Result, EvalInfo &Info, const Expr *E) { LValue LV; APValue &Value = Info.CurrentCall->createTemporary(E, T, ScopeKind::FullExpression, LV); -#if ENABLE_BSC - if (!(Info.getLangOpts().BSC && Info.getLangOpts().EnableFatPtr && - T.isFatPtrRecordType())) -#endif - if (!EvaluateRecord(E, LV, Value, Info)) - return false; + if (!EvaluateRecord(E, LV, Value, Info)) + return false; Result = Value; } else if (T->isVoidType()) { if (!Info.getLangOpts().CPlusPlus11) diff --git a/clang/lib/CodeGen/CGExprAgg.cpp b/clang/lib/CodeGen/CGExprAgg.cpp index c9d21233d4ea..f5d307974bd6 100644 --- a/clang/lib/CodeGen/CGExprAgg.cpp +++ b/clang/lib/CodeGen/CGExprAgg.cpp @@ -1168,8 +1168,8 @@ void AggExprEmitter::VisitBinAssign(const BinaryOperator *E) { // to be compatible with the value on the left. #if ENABLE_BSC if (!(CGF.getLangOpts().BSC && CGF.getLangOpts().EnableFatPtr && - E->getLHS()->getType().isFatPtrRecordType() && - E->getRHS()->getType().isFatPtrRecordType())) + E->getLHS()->getType()->isFatPtrRecordType() && + E->getRHS()->getType()->isFatPtrRecordType())) #endif assert(CGF.getContext().hasSameUnqualifiedType(E->getLHS()->getType(), E->getRHS()->getType()) diff --git a/clang/lib/Headers/bsc_include/bsc_fat_ptr.hbs b/clang/lib/Headers/bsc_include/bsc_fat_ptr.hbs index e973722e54b0..e0558ee48b36 100644 --- a/clang/lib/Headers/bsc_include/bsc_fat_ptr.hbs +++ b/clang/lib/Headers/bsc_include/bsc_fat_ptr.hbs @@ -16,9 +16,6 @@ #include #include -// only CHECKED marked global array var can be taked address by &fat. -#define CHECKED __attribute__((fat_ptr_checked)) - struct _FatPtr { T* raw_ptr; uint32_t key_version; @@ -31,37 +28,8 @@ struct _AllocationUnit { uint8_t payload[]; }; -enum _FatPtrErrorKind { - AllocateZero = 0, - FreeNotHeadPointer, - DoubleFree, - UseAfterFree, - OutOfBoundsAccess -}; - -static void _report_error(enum _FatPtrErrorKind kind, char *file_name, - int line_no, char *func_name) { - char *msg; - switch (kind) { - case AllocateZero: - msg = "Allocated size cannot be ZERO"; - break; - case FreeNotHeadPointer: - msg = "Cannot free this pointer because it does not point to the head of an allocation"; - break; - case DoubleFree: - msg = "Cannot free this pointer because the allocation may have been freed"; - break; - case UseAfterFree: - msg = "Cannot use this pointer because the allocation may have been freed or reseored"; - break; - case OutOfBoundsAccess: - msg = "Pointer offset exceeds the allocation size"; - break; - } - fprintf(stderr, "Error: %s!\n", msg); - if (kind == UseAfterFree || kind == OutOfBoundsAccess) - fprintf(stderr, "Error at: %s:%d in %s\n", file_name, line_no, func_name); +void _report_error(const char* msg) { + fprintf(stderr, "%s", msg); void *callstack[10]; int num_frames = backtrace(callstack, 10); char **symbols = backtrace_symbols(callstack, num_frames); @@ -74,71 +42,49 @@ static void _report_error(enum _FatPtrErrorKind kind, char *file_name, exit(EXIT_FAILURE); } -static uint32_t _new_version_number() { +uint32_t _new_version_number() { static _Atomic(uint32_t) uuid = 10000; atomic_fetch_add(&uuid, 1); return atomic_load(&uuid); } -_FatPtr checked_malloc(size_t payload_bytes) { +_FatPtr checked_malloc(size_t payload_bytes) { if (payload_bytes == 0) - _report_error(AllocateZero, 0, 0, 0); + _report_error("allocated size is ZERO!\n"); size_t all_size = payload_bytes + 8; void *raw = malloc(all_size); uint32_t *p = (uint32_t*)raw; uint32_t ver = _new_version_number(); *p = ver; *(p + 1) = payload_bytes; - _FatPtr ptr = { .raw_ptr = (T*)(p + 2), .key_version = ver, .offset = 0 }; + _FatPtr ptr = { .raw_ptr = (void*)(p + 2), .key_version = ver, .offset = 0 }; return ptr; } -void checked_free(_FatPtr ptr) { - if (ptr.raw_ptr == nullptr) - return; - if (ptr.key_version == 0) { - free((uint8_t*)ptr.raw_ptr - 8); - return; - } +void checked_free(_FatPtr ptr) { uint8_t* head = (uint8_t*)ptr.raw_ptr - ptr.offset - 8; struct _AllocationUnit* phead = (struct _AllocationUnit *)head; - if (ptr.key_version != 0 && ptr.key_version != phead->lock_version) { - _report_error(DoubleFree, 0, 0, 0); - } + if (ptr.key_version != phead->lock_version) + _report_error("version number error when free!\n"); if (ptr.offset != 0) - _report_error(FreeNotHeadPointer, 0, 0, 0); + _report_error("free a pointer which is not point to the head of an allocation!\n"); memset(phead, 0, phead->size + 8); free(phead); } -static void _check_version( - void *raw_ptr, uint32_t key_version, int32_t offset, - char *file_name, int line_no, char *func_name) { - // if key_version == 0, means this fat ptr is from a raw ptr, - // if key_version == 1, means this fat ptr points to a global array, - // we should skip check version. - if (key_version != 0 && key_version != 1) { - const uint8_t *head = (const uint8_t *)raw_ptr - offset - 8; - const struct _AllocationUnit *phead = (const struct _AllocationUnit *)head; - if (key_version != phead->lock_version) { - _report_error(UseAfterFree, file_name, line_no, func_name); - } - } +void _check_version(_FatPtr ptr) { + const uint8_t *head = (const uint8_t *)ptr.raw_ptr - ptr.offset - 8; + const struct _AllocationUnit *phead = (const struct _AllocationUnit *)head; + if (ptr.key_version != 0 && ptr.key_version != phead->lock_version) + _report_error("version number error!\n"); } -static void _check_offset( - int32_t bytes, void *raw_ptr, uint32_t key_version, int32_t offset, - char *file_name, int line_no, char *func_name) { - // if key_version == 0, means this fat ptr is from a raw ptr, - // we should skip check offset. - if (key_version != 0) { - const uint8_t *head = (const uint8_t *)raw_ptr - offset - 8; - const struct _AllocationUnit *phead = (const struct _AllocationUnit *)head; - int32_t new_offset = offset + bytes; - if (phead->size != 0 && new_offset > phead->size) { - _report_error(OutOfBoundsAccess, file_name, line_no, func_name); - } - } +void _check_offset(_FatPtr ptr, int32_t bytes) { + const uint8_t *head = (const uint8_t *)ptr.raw_ptr - ptr.offset - 8; + const struct _AllocationUnit *phead = (const struct _AllocationUnit *)head; + int32_t new_offset = ptr.offset + bytes; + if (phead->size > 0 && new_offset > phead->size) + _report_error("pointer offset exceed the allocation size!\n"); } #endif /* BSC_FAT_PTR_HBS */ \ No newline at end of file diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index a577b8ff8cc1..d314128f5ff7 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -2235,8 +2235,6 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS, #if ENABLE_BSC if (getLangOpts().BSC && FirstDecl) { if (FunctionDecl *FD = dyn_cast_or_null(FirstDecl)) { - if (getLangOpts().EnableFatPtr) - Actions.DesugarFunctionDeclWithFatPtr(FD); if (FD->isAsyncSpecified()) { SmallVector Decls = Actions.ActOnAsyncFunctionDeclaration(FD); for (auto &D : Decls) { @@ -2252,7 +2250,7 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS, Actions.BuildAllocationUnitForGlobalArrayVar(VD); DeclsInGroup.push_back(Decls.first); DeclsInGroup.push_back(Decls.second); - } else if (QT.hasFat()) { + } else if (QT.isFatQualified() || QT->hasFatFields()) { Actions.DesugarGlobalVarDeclWithFatPtr(VD); } } diff --git a/clang/lib/Sema/BSC/SemaBSCFatPtr.cpp b/clang/lib/Sema/BSC/SemaBSCFatPtr.cpp index 8c3494be693d..e789857a4099 100644 --- a/clang/lib/Sema/BSC/SemaBSCFatPtr.cpp +++ b/clang/lib/Sema/BSC/SemaBSCFatPtr.cpp @@ -156,280 +156,7 @@ public: } }; -// Preprocess FunctionDecl before desugar -// 1. a DeclStmt may contain more than one VarDecl, we should -// split it into several DeclStmts, for example: -// int a, *fat p = nullptr; -> int a; int *fat p = nullptr; -// 2. if return type of a CallExpr is fat ptr type, build new -// temporary VarDecl to avoid duplicate call. If we don't -// build such a temporary VarDecl, `int *fat p = foo() + 1;` -// will be desugared to: -// @code -// _FatPtr p = (_FatPtr) { -// foo().ptr + 1, -// foo().key_version, -// foo().offset + sizeof(int) }; -// @endcode -// we can see foo() is called for third times which may -// cause error, so we build a temp VarDecl for foo() and -// replace original CallExpr by this temp VarDecl. -// For example, we preprocess `int *fat p = foo() + 1;` to: -// @code -// int *fat temp = foo(); -// int *fat p = temp + 1; -// @endcode -// 3. If body of IfStmt/WhileStmt/DoStmt/ForStmt is not a -// CompoundStmt, build CompoundStmt as its body. -// For example: -// 1) while (*p > 0) foo(); -> while (*p > 0) { foo(); } -// 2) if (*p > 0 ) foo(); else foo(); -// ->if (*p > 0) { foo(); } else { foo(); } -// 4. for SelfIncExpr or SelfDecExpr of FatPtr, we split it -// into two expr. For example: -// 1) int a = *p++; -> int a = *p; p = p + 1; -// 2) int a = *++p; -> p = p + 1; int a = *p; -// It should be noted that in this preprocess stage, we don't -// desugar fat ptr pointer to _FatPtr struct, but only do some -// stmt replacement. -class FatPtrPreprocessor : public TreeTransform { - typedef TreeTransform BaseTransform; - - // whether need to create a temporary variable to replace - // the current CallExpr. - bool NeedToReplace = false; - - // The first element of pair records if current SelfIncExpr or - // SelfDecExpr is postfix or not: - // 1) true means `p++` or `p--`, then the second element of pair - // will be inserted behind this SelfIncExpr or SelfDecExpr; - // 2) false means `++p` or `--p`, then the second element of pair - // will be inserted before this SelfIncExpr or SelfDecExpr. - std::pair IncOrDecFatPtrExpr = std::make_pair(false, nullptr); - -public: - FatPtrPreprocessor(Sema &SemaRef) : BaseTransform(SemaRef) {} - StmtResult TransformCompoundStmt(CompoundStmt *CS); - StmtResult TransformCompoundStmt(CompoundStmt *CS, bool IsStmtExpr); - StmtResult TransformIfStmt(IfStmt *IS); - StmtResult TransformWhileStmt(WhileStmt *WS); - StmtResult TransformDoStmt(DoStmt *DS); - StmtResult TransformForStmt(ForStmt *FS); - StmtResult TransformSwitchStmt(SwitchStmt *SS); - StmtResult TransformCaseStmt(CaseStmt *CS); - StmtResult TransformDefaultStmt(DefaultStmt *DS); - StmtResult TransformDeclStmt(DeclStmt *DS); - ExprResult TransformInitListExpr(InitListExpr *ILE); - ExprResult TransformConditionalOperator(ConditionalOperator *CO); - ExprResult TransformUnaryOperator(UnaryOperator *UO); - ExprResult TransformBinaryOperator(BinaryOperator *BO); - ExprResult TransformImplicitCastExpr(ImplicitCastExpr *ICE); - -private: - Stmt *BuildCompoundStmtForIfOrLoopStmtBody(Stmt *Body); -}; - -StmtResult FatPtrPreprocessor::TransformCompoundStmt(CompoundStmt *CS) { - return TransformCompoundStmt(CS, false); -} - -StmtResult FatPtrPreprocessor::TransformCompoundStmt(CompoundStmt *CS, - bool IsStmtExpr) { - if (CS == nullptr) - return CS; - llvm::SmallVector Stmts; - for (Stmt *S : CS->body()) { - if (auto DS = dyn_cast(S)) { - if (!DS->isSingleDecl()) { - // a DeclStmt may contain more than one VarDecl, we - // split it into several DeclStmts. - for (auto D : DS->decls()) { - DeclStmt *NewDS = new (SemaRef.Context) - DeclStmt(DeclGroupRef(D), D->getLocation(), D->getLocation()); - Stmt *NewS = BaseTransform::TransformStmt(NewDS).get(); - if (IncOrDecFatPtrExpr.second) { - if (IncOrDecFatPtrExpr.first) { - Stmts.push_back(NewS); - Stmts.push_back(IncOrDecFatPtrExpr.second); - } else { - Stmts.push_back(IncOrDecFatPtrExpr.second); - Stmts.push_back(NewS); - } - IncOrDecFatPtrExpr.second = nullptr; - } else { - Stmts.push_back(NewS); - } - } - continue; - } - } - Stmt *NewS = BaseTransform::TransformStmt(S).get(); - if (IncOrDecFatPtrExpr.second) { - if (IncOrDecFatPtrExpr.first) { - Stmts.push_back(NewS); - Stmts.push_back(IncOrDecFatPtrExpr.second); - } else { - Stmts.push_back(IncOrDecFatPtrExpr.second); - Stmts.push_back(NewS); - } - IncOrDecFatPtrExpr.second = nullptr; - } else { - Stmts.push_back(NewS); - } - } - CompoundStmt *NewCS = CompoundStmt::Create( - SemaRef.Context, Stmts, FPOptionsOverride(), CS->getLBracLoc(), - CS->getRBracLoc(), CS->getSafeSpecifier(), CS->getSafeSpecifierLoc(), - CS->getCompSafeZoneSpecifier()); - return NewCS; -} - -StmtResult FatPtrPreprocessor::TransformIfStmt(IfStmt *IS) { - IS->setCond(BaseTransform::TransformExpr(IS->getCond()).get()); - IS->setThen(BaseTransform::TransformStmt( - BuildCompoundStmtForIfOrLoopStmtBody(IS->getThen())) - .get()); - if (IS->getElse()) - IS->setElse(BaseTransform::TransformStmt( - BuildCompoundStmtForIfOrLoopStmtBody(IS->getElse())) - .get()); - return IS; -} - -StmtResult FatPtrPreprocessor::TransformWhileStmt(WhileStmt *WS) { - WS->setCond(BaseTransform::TransformExpr(WS->getCond()).get()); - WS->setBody(BaseTransform::TransformStmt( - BuildCompoundStmtForIfOrLoopStmtBody(WS->getBody())) - .get()); - return WS; -} - -StmtResult FatPtrPreprocessor::TransformDoStmt(DoStmt *DS) { - DS->setCond(BaseTransform::TransformExpr(DS->getCond()).get()); - DS->setBody(BaseTransform::TransformStmt( - BuildCompoundStmtForIfOrLoopStmtBody(DS->getBody())) - .get()); - return DS; -} - -StmtResult FatPtrPreprocessor::TransformForStmt(ForStmt *FS) { - FS->setCond(BaseTransform::TransformExpr(FS->getCond()).get()); - FS->setInc(BaseTransform::TransformExpr(FS->getInc()).get()); - FS->setBody(BaseTransform::TransformStmt( - BuildCompoundStmtForIfOrLoopStmtBody(FS->getBody())) - .get()); - return FS; -} - -StmtResult FatPtrPreprocessor::TransformSwitchStmt(SwitchStmt *SS) { - SS->setCond(BaseTransform::TransformExpr(SS->getCond()).get()); - SS->setBody(BaseTransform::TransformStmt(SS->getBody()).get()); - return SS; -} - -StmtResult FatPtrPreprocessor::TransformCaseStmt(CaseStmt *CS) { - CS->setLHS(BaseTransform::TransformExpr(CS->getLHS()).get()); - Expr *RHS = CS->getRHS(); - if (RHS) - CS->setRHS(BaseTransform::TransformExpr(RHS).get()); - CS->setSubStmt(BaseTransform::TransformStmt(CS->getSubStmt()).get()); - return CS; -} - -StmtResult FatPtrPreprocessor::TransformDefaultStmt(DefaultStmt *DS) { - DS->setSubStmt(BaseTransform::TransformStmt(DS->getSubStmt()).get()); - return DS; -} - -StmtResult FatPtrPreprocessor::TransformDeclStmt(DeclStmt *DS) { - assert(DS->isSingleDecl() && "DeclStmt can only contain a Decl here"); - Decl *D = DS->getSingleDecl(); - if (VarDecl *VD = dyn_cast(D)) { - if (VD->getInit()) - VD->setInit(BaseTransform::TransformExpr(VD->getInit()).get()); - } - return DS; -} - -ExprResult FatPtrPreprocessor::TransformInitListExpr(InitListExpr *ILE) { - for (unsigned i = 0; i < ILE->getNumInits(); i++) - ILE->setInit(i, BaseTransform::TransformExpr(ILE->getInit(i)).get()); - return ILE; -} - -ExprResult -FatPtrPreprocessor::TransformConditionalOperator(ConditionalOperator *CO) { - Expr *CondExpr = BaseTransform::TransformExpr(CO->getCond()).get(); - Expr *LHS = BaseTransform::TransformExpr(CO->getLHS()).get(); - Expr *RHS = BaseTransform::TransformExpr(CO->getRHS()).get(); - return BaseTransform::RebuildConditionalOperator( - CondExpr, CO->getQuestionLoc(), LHS, CO->getColonLoc(), RHS); -} - -ExprResult FatPtrPreprocessor::TransformUnaryOperator(UnaryOperator *UO) { - // 1) int a = *p++; -> int a = *p; p = p + 1; - // 2) int a = *++p; -> p = p + 1; int a = *p; - SourceLocation SLoc = UO->getBeginLoc(); - Expr *SubE = UO->getSubExpr(); - if ((UO->isPrefix() || UO->isPostfix()) && SubE->getType().isFatPtrType()) { - // Build p = p + 1 for p++/++p, build p = p - 1 for p--/--p - Expr *InitRHS = IntegerLiteral::Create(SemaRef.Context, llvm::APInt(32, 1), - SemaRef.Context.IntTy, SLoc); - Expr *InitE = - SemaRef - .CreateBuiltinBinOp(SLoc, UO->isIncrementOp() ? BO_Add : BO_Sub, - SubE, InitRHS) - .get(); - IncOrDecFatPtrExpr.first = UO->isPostfix(); - IncOrDecFatPtrExpr.second = - SemaRef.CreateBuiltinBinOp(SLoc, BO_Assign, SubE, InitE).get(); - return ImplicitCastExpr::Create(SemaRef.Context, SubE->getType(), - CK_LValueToRValue, SubE, nullptr, - VK_PRValue, FPOptionsOverride()); - } - UO->setSubExpr(BaseTransform::TransformExpr(SubE).get()); - return UO; -} - -ExprResult FatPtrPreprocessor::TransformBinaryOperator(BinaryOperator *BO) { - Expr *LHS = BO->getLHS(); - Expr *RHS = BO->getRHS(); - BO->setLHS(BaseTransform::TransformExpr(LHS).get()); - BO->setRHS(BaseTransform::TransformExpr(RHS).get()); - return BO; -} - -ExprResult -FatPtrPreprocessor::TransformImplicitCastExpr(ImplicitCastExpr *ICE) { - ICE->setSubExpr(BaseTransform::TransformExpr(ICE->getSubExpr()).get()); - return ICE; -} - -// If body of IfStmt/WhileStmt/DoStmt/ForStmt is not CompoundStmt, -// we build CompoundStmt and add original body into it. -Stmt *FatPtrPreprocessor::BuildCompoundStmtForIfOrLoopStmtBody(Stmt *Body) { - if (isa(Body) && !IncOrDecFatPtrExpr.second) - return Body; - llvm::SmallVector Statements; - if (auto CS = dyn_cast(Body)) { - for (Stmt *S : CS->body()) - Statements.push_back(S); - } else if (!isa(Body)) { - Statements.push_back(Body); - } - - // IncExpr of ForStmt maybe SelfIncExpr or SelfDecExpr of fat ptr, - // we add it in the end of ForStmt body, for example: - // for (;;p++) {} -> for (;;p) { p = p + 1; } - if (IncOrDecFatPtrExpr.second) { - Statements.push_back(IncOrDecFatPtrExpr.second); - IncOrDecFatPtrExpr.second = nullptr; - } - - return CompoundStmt::Create(SemaRef.Context, Statements, FPOptionsOverride(), - Body->getBeginLoc(), Body->getEndLoc()); -} - -// Desugar fat ptr for FunctionDecl, VarDecl, RecordDecl and TypedefNameDecl +// Desugar fat ptr for FunctionDecl, VarDecl and RecordDecl. class TransformFatPtr : public TreeTransform { typedef TreeTransform BaseTransform; FunctionDecl *FD = nullptr; @@ -456,23 +183,7 @@ class TransformFatPtr : public TreeTransform { bool InsertCheckVersionCall = false; std::pair InsertCheckOffsetCall = std::make_pair(false, nullptr); - - CompoundStmt *CurrentCompoundStmt = nullptr; - - // Key is every original CompoundStmt in function, - // value is desugared statements of this CompoundStmt. - llvm::DenseMap> - DesugaredStatements; - - // Use stack to store check calls when traversing. - // When traverse a complex expr, such as `*p->a` where both p and p->a - // are fat ptr, we build check call for p->a firstly and p secondly, - // but we should insert check call for p firstly and p->a secondly. - // So we use stack to store check call and pop them after ending - // traversing this complex expr. - // First element of pair is fat ptr which should be checked, - // second element is check calls should be inserted to AST. - std::stack> CheckCallExpr; + std::stack CheckCallExpr; // when accessing array member through FatPtr, // such as `p->arr[3]` or `p->a.b.arr[3]` @@ -489,25 +200,17 @@ public: FatPtrFD["_new_version_number"] = nullptr; } + bool IsNullPtrExpr(Expr *E); // Entry to desugar FunctionDecl void TransformFunctionDecl(); // Entry to desugar global or local VarDecl void TransformVarDecl(VarDecl *VD); // Entry to desugar global or local RecordDecl void TransformRecordDecl(RecordDecl *RD); - // Entry to desugar global or local TypedefNameDecl, - // including TypedefDecl and TypeAliasDecl. - void TransformTypedefNameDecl(TypedefNameDecl *TND); + void TransformSelfCallExprFunctionType(CallExpr *CE); StmtResult TransformCompoundStmt(CompoundStmt *CS, bool IsStmtExpr); StmtResult TransformCompoundStmt(CompoundStmt *CS); - StmtResult TransformIfStmt(IfStmt *IS); - StmtResult TransformSwitchStmt(SwitchStmt *SS); - StmtResult TransformCaseStmt(CaseStmt *CS); - StmtResult TransformDefaultStmt(DefaultStmt *DS); - StmtResult TransformWhileStmt(WhileStmt *WS); - StmtResult TransformDoStmt(DoStmt *DS); - StmtResult TransformForStmt(ForStmt *FS); StmtResult TransformDeclStmt(DeclStmt *DS); StmtResult TransformReturnStmt(ReturnStmt *RS); ExprResult TransformDeclRefExpr(DeclRefExpr *DRE); @@ -520,11 +223,10 @@ public: ExprResult TransformCStyleCastExpr(CStyleCastExpr *CSCE); ExprResult TransformUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *UETT); ExprResult TransformMemberExpr(MemberExpr *ME); - ExprResult TransformConditionalOperator(ConditionalOperator *CO); - ExprResult TransformCompoundAssignOperator(CompoundAssignOperator *CAO); ExprResult TransformBinaryOperator(BinaryOperator *BO); ExprResult HandleBOFatPtrAddOrSubInteger(BinaryOperator *BO); + ExprResult HandleBOFatPtrSubFatPtr(BinaryOperator *BO); ExprResult TransformUnaryOperator(UnaryOperator *UO); ExprResult HandleUODerefFatPtr(UnaryOperator *UO); @@ -549,7 +251,6 @@ private: MemberExpr *BuildMemberForFatPtr(Expr *FatPtrBaseE, std::string FieldName); Expr *BuildFatPtrCall(SourceLocation SLoc, std::string FDName, llvm::SmallVector &Args); - MemberExpr *BuildMemberRawPtrAndInsertCheckCallForFatPtr(Expr *FatPtrBaseE); Expr *BuildFatPtrCompoundLiteralExprForRawPtr(Expr *RawPtrE, QualType QT); Expr *BuildFatPtrCompoundLiteralExpr(Expr *FirstInitE, Expr *SecondInitE, Expr *ThirdInitE, QualType QT); @@ -559,6 +260,13 @@ private: SmallVector &Comps); }; +bool TransformFatPtr::IsNullPtrExpr (Expr *E) { + if (auto *CastExpr = dyn_cast(E)) { + E = CastExpr->getSubExpr(); + } + return isa(E); +} + void TransformFatPtr::TransformFunctionDecl() { // Desugar fat qualified return type. QualType ReturnTy = @@ -576,43 +284,26 @@ void TransformFatPtr::TransformFunctionDecl() { ReturnTy, ParamTys, FD->getType()->getAs()->getExtProtoInfo())); - if (!FD->getBody()) - return; - - // First traversal: do preprocess - FatPtrPreprocessor Preprocessor(SemaRef); - FD->setBody(Preprocessor.TransformStmt(FD->getBody()).get()); - - // Second traversal: + // First traversal: // find local vars whose address is taken by `&fat` expr. Finder.VisitStmt(FD->getBody()); - // Third traversal + // Second traversal CompoundStmt *NewCS = HandleLocalAllocationUnit(dyn_cast(FD->getBody()), true); - // the last traversal: desugar stmt and expr of function body. + // Third traversal: desugar stmt and expr of function body. FD->setBody(TransformStmt(NewCS).get()); } void TransformFatPtr::TransformVarDecl(VarDecl *VD) { QualType QT = DesugarFatPtrType(VD->getLocation(), VD->getType()); VD->setType(QT); - VD->setTypeSourceInfo(SemaRef.Context.getTrivialTypeSourceInfo(QT)); if (Expr *Init = VD->getInit()) { - if (QT.isFatPtrRecordType()) { - if (Init->isNullExpr(SemaRef.Context)) { - // `int *fat p = nullptr;` should be desugared to - // `_FatPtr p = (_FatPtr) { nullptr, 0, 0 };` - VD->setInit(BuildFatPtrCompoundLiteralExprForRawPtr(Init, QT)); - } else { - VD->setInit(BaseTransform::TransformExpr(Init).get()); - } - if (VD->hasGlobalStorage() && isa(VD->getInit())) { - VD->setInit(ImplicitCastExpr::Create(SemaRef.Context, QT, CK_NoOp, - VD->getInit(), nullptr, VK_PRValue, - FPOptionsOverride())); - } + if (QT->isFatPtrRecordType() && IsNullPtrExpr(Init)) { + // `int *fat p = nullptr;` should be desugared to + // `_FatPtr p = (_FatPtr) { nullptr, 0, 0 };` + VD->setInit(BuildFatPtrCompoundLiteralExprForRawPtr(Init, QT)); } else { VD->setInit(BaseTransform::TransformExpr(Init).get()); } @@ -625,12 +316,6 @@ void TransformFatPtr::TransformRecordDecl(RecordDecl *RD) { FD->setType(DesugarFatPtrType(FD->getLocation(), FD->getType())); } -void TransformFatPtr::TransformTypedefNameDecl(TypedefNameDecl *TND) { - SourceLocation SLoc = TND->getTypeSourceInfo()->getTypeLoc().getBeginLoc(); - QualType QT = DesugarFatPtrType(SLoc, TND->getUnderlyingType()); - TND->setTypeSourceInfo(SemaRef.Context.getTrivialTypeSourceInfo(QT)); -} - // 1. For all local vars whose type has no array and address is // taken by &fat, we think they are packed in an allocation unit // in stack and share a lock_version, so we build 2 VarDecl @@ -645,8 +330,7 @@ void TransformFatPtr::TransformTypedefNameDecl(TypedefNameDecl *TND) { CompoundStmt * TransformFatPtr::HandleLocalAllocationUnit(CompoundStmt *CS, bool IsTopLevelCompoundStmt) { - if (CS == nullptr || (!Finder.LocalNoArrayVarAddressIsFatTaken && - Finder.LocalArrayVarsSet.size() == 0)) + if (CS == nullptr) return CS; llvm::SmallVector Statements; if (IsTopLevelCompoundStmt && Finder.LocalNoArrayVarAddressIsFatTaken) { @@ -655,29 +339,37 @@ TransformFatPtr::HandleLocalAllocationUnit(CompoundStmt *CS, Statements.push_back(Headers.second); Statements.push_back(Headers.first); } - for (auto *B : CS->body()) { - if (auto ChildCS = dyn_cast(B)) { - Statements.push_back(HandleLocalAllocationUnit(ChildCS, false)); - } else if (isa(B) && Finder.LocalNoArrayVarAddressIsFatTaken) { - Statements.push_back( - BuildLockVersionAssignmentForLocalNoArrayVars(B->getBeginLoc())); + if (Finder.LocalArrayVarsSet.size() == 0) { + for (auto *B : CS->body()) Statements.push_back(B); - } else if (auto DS = dyn_cast(B)) { - assert(DS->isSingleDecl() && "DeclStmt can only contain a Decl here"); - bool NeedToReplace = false; - if (VarDecl *VD = dyn_cast(DS->getSingleDecl())) { - if (Finder.LocalArrayVarsSet.count(VD)) { - std::pair Decls = - BuildAllocationUnitForLocalArrayVar(VD); - Statements.push_back(Decls.first); - Statements.push_back(Decls.second); - NeedToReplace = true; + } else { + for (auto *B : CS->body()) { + if (auto ChildCS = dyn_cast(B)) { + Statements.push_back(HandleLocalAllocationUnit(ChildCS, false)); + } else if (auto DS = dyn_cast(B)) { + if (DS->isSingleDecl()) { + if (VarDecl *VD = dyn_cast(DS->getSingleDecl())) { + if (Finder.LocalArrayVarsSet.count(VD)) { + std::pair Decls = + BuildAllocationUnitForLocalArrayVar(VD); + Statements.push_back(Decls.first); + Statements.push_back(Decls.second); + continue; + } + } + Statements.push_back(B); + } else { + // TODO: a DeclStmt contains more than one VarDecl, we should + // divide it into several DeclStmts. + Statements.push_back(B); } - } - if (!NeedToReplace) + } else if (isa(B) && Finder.LocalNoArrayVarAddressIsFatTaken) { + Statements.push_back( + BuildLockVersionAssignmentForLocalNoArrayVars(B->getBeginLoc())); Statements.push_back(B); - } else { - Statements.push_back(B); + } else { + Statements.push_back(B); + } } } if (IsTopLevelCompoundStmt && Finder.LocalNoArrayVarAddressIsFatTaken) @@ -777,10 +469,10 @@ TransformFatPtr::BuildAllocationUnitForLocalArrayVar(VarDecl *VD) { llvm::SmallVector Args; Expr *LockVersionInit = BuildFatPtrCall(SLoc, "_new_version_number", Args); // Build second init expr by array size - auto ArraySize = - SemaRef.Context.getTypeSize(DesugarFatPtrType(SLoc, VD->getType())) / 8; - Expr *SizeInit = IntegerLiteral::Create( - SemaRef.Context, llvm::APInt(32, ArraySize), UInt32Ty, SLoc); + Expr *SizeInit = + IntegerLiteral::Create(SemaRef.Context, + llvm::APInt(32, SemaRef.Context.getTypeSize(VD->getType()) / 8), + UInt32Ty, SLoc); // Build InitListExpr SmallVector Inits{LockVersionInit, SizeInit}; if (VD->getInit()) @@ -816,208 +508,48 @@ StmtResult TransformFatPtr::TransformCompoundStmt(CompoundStmt *CS, bool IsStmtExpr) { if (CS == nullptr) return CS; - - // If current CompoundStmt is the body of WhileStmt, DoStmt and - // ForStmt, and there is fat ptr deref operation in CondExpr, - // after we have treetransformed CondExpr, check calls are added - // into DesugaredStatements[CurrentCompoundStmt] and these check - // calls should be added in the end of current CompoundStmt. - llvm::SmallVector TempStatements; - if (DesugaredStatements.count(CS)) { - TempStatements = DesugaredStatements[CS]; - DesugaredStatements[CS].clear(); - } + llvm::SmallVector Statements; for (auto *B : CS->body()) { - CurrentCompoundStmt = CS; StmtResult Result = BaseTransform::TransformStmt(B); while (!CheckCallExpr.empty()) { - DesugaredStatements[CS].push_back(CheckCallExpr.top().second); + Statements.push_back(CheckCallExpr.top()); CheckCallExpr.pop(); } - DesugaredStatements[CS].push_back(Result.getAs()); - } - for (auto S : TempStatements) { - DesugaredStatements[CS].push_back(S); + Statements.push_back(Result.getAs()); } auto NewCS = CompoundStmt::Create( - SemaRef.Context, DesugaredStatements[CS], FPOptionsOverride(), - CS->getLBracLoc(), CS->getRBracLoc(), CS->getSafeSpecifier(), - CS->getSafeSpecifierLoc(), CS->getCompSafeZoneSpecifier()); + SemaRef.Context, Statements, FPOptionsOverride(), CS->getLBracLoc(), + CS->getRBracLoc(), CS->getSafeSpecifier(), CS->getSafeSpecifierLoc(), + CS->getCompSafeZoneSpecifier()); return NewCS; } -// If there is fat ptr deref operation in CondExpr of IfStmt or -// SwitchStmt, we insert check call before this Stmt, for example: -// `if (*p > 0) { xxxx; }` where p is fat ptr should be desugared to: -// @code -// _check_version(p); -// _check_offset(p); -// if (*p.raw_ptr > 0) { xxxx; } -// @endcode -StmtResult TransformFatPtr::TransformIfStmt(IfStmt *IS) { - Expr *CondExpr = IS->getCond(); - if (CondExpr->getType().isFatPtrType()) - // `if (p)` where p is fat ptr should be resugared to `if (p.raw_ptr)` - DesugarToMemberPtr = true; - IS->setCond(BaseTransform::TransformExpr(CondExpr).get()); - while (!CheckCallExpr.empty()) { - DesugaredStatements[CurrentCompoundStmt].push_back( - CheckCallExpr.top().second); - CheckCallExpr.pop(); - } - IS->setThen(BaseTransform::TransformStmt(IS->getThen()).get()); - if (IS->getElse()) - IS->setElse(BaseTransform::TransformStmt(IS->getElse()).get()); - return IS; -} - -StmtResult TransformFatPtr::TransformSwitchStmt(SwitchStmt *SS) { - Expr *CondExpr = SS->getCond(); - if (CondExpr->getType().isFatPtrType()) - DesugarToMemberPtr = true; - SS->setCond(BaseTransform::TransformExpr(CondExpr).get()); - while (!CheckCallExpr.empty()) { - DesugaredStatements[CurrentCompoundStmt].push_back( - CheckCallExpr.top().second); - CheckCallExpr.pop(); - } - SS->setBody(BaseTransform::TransformStmt(SS->getBody()).get()); - return SS; -} - -StmtResult TransformFatPtr::TransformCaseStmt(CaseStmt *CS) { - CS->setLHS(BaseTransform::TransformExpr(CS->getLHS()).get()); - Expr *RHS = CS->getRHS(); - if (RHS) - CS->setRHS(BaseTransform::TransformExpr(RHS).get()); - CS->setSubStmt(BaseTransform::TransformStmt(CS->getSubStmt()).get()); - return CS; -} - -StmtResult TransformFatPtr::TransformDefaultStmt(DefaultStmt *DS) { - DS->setSubStmt(BaseTransform::TransformStmt(DS->getSubStmt()).get()); - return DS; -} - -// If there is fat ptr deref operation in CondExpr of WhileStmt, -// we insert check call before this Stmt and in the end of Stmt body, -// for example: -// `while (*p > 0) { xxxx; }` where p is fat ptr should be desugared to: -// @code -// _check_version(p); -// _check_offset(p); -// while (*p.raw_ptr > 0) { -// xxxx; -// _check_version(p); -// _check_offset(p); -// } -// @endcode -StmtResult TransformFatPtr::TransformWhileStmt(WhileStmt *WS) { - CompoundStmt *BodyCS = dyn_cast(WS->getBody()); - assert(BodyCS && "WhileStmt must have CompoundStmt as body"); - - Expr *CondExpr = WS->getCond(); - if (CondExpr->getType().isFatPtrType()) - DesugarToMemberPtr = true; - WS->setCond(BaseTransform::TransformExpr(CondExpr).get()); - if (CheckCallExpr.empty()) { - WS->setBody(BaseTransform::TransformStmt(BodyCS).get()); - return WS; - } - while (!CheckCallExpr.empty()) { - DesugaredStatements[CurrentCompoundStmt].push_back( - CheckCallExpr.top().second); - DesugaredStatements[BodyCS].push_back(CheckCallExpr.top().second); - CheckCallExpr.pop(); - } - WS->setBody(BaseTransform::TransformStmt(BodyCS).get()); - return WS; -} - -// If there is fat ptr deref operation in CondExpr of DoStmt, -// we insert check call in the end of Stmt body, for example: -// `do { xxxx; } while (*p > 0);` where p is fat ptr -// should be desugared to: -// @code -// do { -// xxxx; -// _check_version(p); -// _check_offset(p); -// } while (*p.raw_ptr > 0); -// @endcode -StmtResult TransformFatPtr::TransformDoStmt(DoStmt *DS) { - CompoundStmt *BodyCS = dyn_cast(DS->getBody()); - assert(BodyCS && "DoStmt must have CompoundStmt as body"); - - Expr *CondExpr = DS->getCond(); - if (CondExpr->getType().isFatPtrType()) - DesugarToMemberPtr = true; - DS->setCond(BaseTransform::TransformExpr(CondExpr).get()); - if (CheckCallExpr.empty()) { - DS->setBody(BaseTransform::TransformStmt(BodyCS).get()); - return DS; - } - while (!CheckCallExpr.empty()) { - DesugaredStatements[BodyCS].push_back(CheckCallExpr.top().second); - CheckCallExpr.pop(); - } - DS->setBody(BaseTransform::TransformStmt(BodyCS).get()); - return DS; -} - -// If there is fat ptr deref operation in CondExpr of ForStmt, -// we insert check call before this Stmt and in the end of Stmt body. -// for example: -// `for (; *p < 5;) { xxxx; }` where p is fat ptr -// should be desugared to: -// @code -// _check_version(p); -// _check_offset(p); -// for (; *p.ptr < 5;) -// xxxx; -// _check_version(p); -// _check_offset(p); -// } -// @endcode -StmtResult TransformFatPtr::TransformForStmt(ForStmt *FS) { - CompoundStmt *BodyCS = dyn_cast(FS->getBody()); - assert(BodyCS && "ForStmt must have CompoundStmt as body"); - - FS->setInit(BaseTransform::TransformStmt(FS->getInit()).get()); - while (!CheckCallExpr.empty()) { - DesugaredStatements[CurrentCompoundStmt].push_back( - CheckCallExpr.top().second); - CheckCallExpr.pop(); - } - FS->setInc(BaseTransform::TransformExpr(FS->getInc()).get()); - Expr *CondExpr = FS->getCond(); - if (CondExpr->getType().isFatPtrType()) - DesugarToMemberPtr = true; - FS->setCond(BaseTransform::TransformExpr(CondExpr).get()); - if (CheckCallExpr.empty()) { - FS->setBody(BaseTransform::TransformStmt(BodyCS).get()); - return FS; - } - while (!CheckCallExpr.empty()) { - DesugaredStatements[CurrentCompoundStmt].push_back( - CheckCallExpr.top().second); - DesugaredStatements[BodyCS].push_back(CheckCallExpr.top().second); - CheckCallExpr.pop(); - } - FS->setBody(BaseTransform::TransformStmt(BodyCS).get()); - return FS; -} - ExprResult TransformFatPtr::TransformDeclRefExpr(DeclRefExpr *DRE) { SourceLocation SLoc = DRE->getBeginLoc(); QualType QT = DesugarFatPtrType(SLoc, DRE->getType()); DRE->setType(QT); - - if (QT.isFatPtrRecordType() && DesugarToMemberPtr) - // Desugar p to p.raw_ptr - return BuildMemberRawPtrAndInsertCheckCallForFatPtr(DRE); - - if (auto VD = dyn_cast(DRE->getDecl())) { + if (QT->isFatPtrRecordType()) { + if (InsertCheckOffsetCall.first) { + // Build CallExpr to check offset. + llvm::SmallVector Args{DRE, InsertCheckOffsetCall.second}; + Expr *CE = BuildFatPtrCall(SLoc, "_check_offset", Args); + CheckCallExpr.push(CE); + InsertCheckOffsetCall.first = false; + InsertCheckOffsetCall.second = nullptr; + } + if (InsertCheckVersionCall) { + // Build CallExpr to check version. + llvm::SmallVector Args{DRE}; + Expr *CE = BuildFatPtrCall(SLoc, "_check_version", Args); + CheckCallExpr.push(CE); + InsertCheckVersionCall = false; + } + if (DesugarToMemberPtr) { + DesugarToMemberPtr = false; + // Desugar p to p.raw_ptr + return BuildMemberForFatPtr(DRE, "raw_ptr"); + } + } else if (auto VD = dyn_cast(DRE->getDecl())) { if (AllocationUnitForLocalArrayVarMap.count(VD) || SemaRef.Context.BSCDesugaredMap.count(VD)) { // Desugar arr to _arr.arr where arr has its owned AllocationUnit. @@ -1049,32 +581,30 @@ ExprResult TransformFatPtr::TransformDeclRefExpr(DeclRefExpr *DRE) { } ExprResult TransformFatPtr::TransformImplicitCastExpr(ImplicitCastExpr *ICE) { - SourceLocation SLoc = ICE->getBeginLoc(); QualType QT = ICE->getType(); if (DesugarToMemberPtr && QT.isFatPtrType()) { - QT = DesugarFatPtrType(SLoc, QT.getFatPtrPointeeType()); + QT = QT->getFatPtrPointeeType(); ICE->setType(SemaRef.Context.getPointerType(QT)); ICE->setCastKind(CK_LValueToRValue); } else { - QT = DesugarFatPtrType(SLoc, QT); + QT = DesugarFatPtrType(ICE->getBeginLoc(), QT); ICE->setType(QT); } Expr *SubE = ICE->getSubExpr(); ICE->setSubExpr(BaseTransform::TransformExpr(SubE).get()); - if (ICE->getCastKind() == CK_NoOp && QT.isFatPtrRecordType() && - SubE->getType().isFatPtrRecordType()) + if (ICE->getCastKind() == CK_NoOp && QT->isFatPtrRecordType() && + SubE->getType()->isFatPtrRecordType()) ICE->setType(SubE->getType()); return ICE; } ExprResult TransformFatPtr::TransformParenExpr(ParenExpr *PE) { - SourceLocation SLoc = PE->getBeginLoc(); QualType QT = PE->getType(); if (DesugarToMemberPtr && QT.isFatPtrType()) { - QT = DesugarFatPtrType(SLoc, QT.getFatPtrPointeeType()); + QT = QT->getFatPtrPointeeType(); PE->setType(SemaRef.Context.getPointerType(QT)); } else { - QT = DesugarFatPtrType(SLoc, QT); + QT = DesugarFatPtrType(PE->getBeginLoc(), QT); PE->setType(QT); } PE->setSubExpr(BaseTransform::TransformExpr(PE->getSubExpr()).get()); @@ -1082,14 +612,14 @@ ExprResult TransformFatPtr::TransformParenExpr(ParenExpr *PE) { } StmtResult TransformFatPtr::TransformDeclStmt(DeclStmt *DS) { - assert(DS->isSingleDecl() && "DeclStmt can only contain a Decl here"); - Decl *D = DS->getSingleDecl(); - if (VarDecl *VD = dyn_cast(D)) - TransformVarDecl(VD); - else if (RecordDecl *RD = dyn_cast(D)) - TransformRecordDecl(RD); - else - BaseTransform::TransformDefinition(D->getLocation(), D); + for (auto D : DS->decls()) { + if (VarDecl *VD = dyn_cast(D)) + TransformVarDecl(VD); + else if (RecordDecl *RD = dyn_cast(D)) + TransformRecordDecl(RD); + else + BaseTransform::TransformDefinition(D->getLocation(), D); + } return DS; } @@ -1111,7 +641,7 @@ ExprResult TransformFatPtr::TransformInitListExpr(InitListExpr *ILE) { StmtResult TransformFatPtr::TransformReturnStmt(ReturnStmt *RS) { Expr *RV = RS->getRetValue(); QualType ReturnQT = FD->getReturnType(); - if (ReturnQT.isFatPtrRecordType() && RV->isNullExpr(SemaRef.Context)) { + if (ReturnQT->isFatPtrRecordType() && IsNullPtrExpr(RV)) { // if a function has fat pointer return type, such as `int *fat foo();` // `return nullptr;` should be desugared to // `return (_FatPtr){ nullptr, 0, 0 };` @@ -1122,78 +652,41 @@ StmtResult TransformFatPtr::TransformReturnStmt(ReturnStmt *RS) { return RS; } -ExprResult TransformFatPtr::TransformCallExpr(CallExpr *CE) { - SourceLocation SLoc = CE->getBeginLoc(); - QualType QT = DesugarFatPtrType(SLoc, CE->getType()); - CE->setType(QT); - - Expr *MemberRawPtr = nullptr; - if (QT.isFatPtrRecordType() && DesugarToMemberPtr) - // Desugar foo() to foo().raw_ptr - MemberRawPtr = BuildMemberRawPtrAndInsertCheckCallForFatPtr(CE); - - Decl *CalleeD = CE->getCalleeDecl(); - if (CalleeD == FD) { - // For self-call Expr, desugar the callee function type - if (auto CalleeICE = dyn_cast(CE->getCallee())) { - CalleeICE->setType(SemaRef.Context.getPointerType(FD->getType())); - if (auto CalleeDRE = dyn_cast(CalleeICE->getSubExpr())) { - CalleeDRE->setType(FD->getType()); +void TransformFatPtr::TransformSelfCallExprFunctionType(CallExpr *CE) { + QualType FQT = FD->getType(); + Expr *CalleeExpr = CE->getCallee(); + if (auto *ImplCastExpr = dyn_cast(CalleeExpr)) { + ImplCastExpr->setType(SemaRef.Context.getPointerType(FQT)); + if (Expr *subExpr = ImplCastExpr->getSubExpr()) { + if (auto *declRefExpr = dyn_cast(subExpr)) { + declRefExpr->setType(FQT); } } } - if (auto CalleeFD = dyn_cast(CalleeD)) { - for (unsigned i = 0; i < CE->getNumArgs(); i++) { - Expr *ArgE = CE->getArg(i); - if (i < CalleeFD->getNumParams() && CalleeFD->getParamDecl(i) && - CalleeFD->getParamDecl(i)->getType().isFatPtrRecordType() && - ArgE->isNullExpr(SemaRef.Context)) { - // if a function has fat pointer parameter, such as - // `void foo(int *fat p);` - // `foo(nullptr)` should be desugared to - // `foo((_FatPtr){ nullptr, 0, 0 })` - Expr *CLE = BuildFatPtrCompoundLiteralExprForRawPtr( - ArgE, CalleeFD->getParamDecl(i)->getType()); - CE->setArg(i, SemaRef.DefaultFunctionArrayLvalueConversion(CLE).get()); - } else { - CE->setArg(i, BaseTransform::TransformExpr(ArgE).get()); - if (auto CLE = dyn_cast(CE->getArg(i))) - CE->setArg(i, - SemaRef.DefaultFunctionArrayLvalueConversion(CLE).get()); - } - } - } else if (auto CalleeVD = dyn_cast(CalleeD)) { - // call a function by a function pointer VarDecl, for example: - // @code - // typedef void (*Foo) (int *fat); - // void bar(int *fat p) {} - // Foo foo = bar; - // void test(int *fat p) { - // foo(p); //foo is a function pointer VarDecl - // } - // @endcode - QualType FuncQT = CalleeVD->getType().getCanonicalType(); - if (FuncQT->isFunctionPointerType()) { - auto FuncType = FuncQT->getPointeeType()->getAs(); - for (unsigned i = 0; i < CE->getNumArgs(); i++) { - Expr *ArgE = CE->getArg(i); - if (i < FuncType->getNumParams() && - FuncType->getParamType(i).isFatPtrRecordType() && - ArgE->isNullExpr(SemaRef.Context)) { - Expr *CLE = BuildFatPtrCompoundLiteralExprForRawPtr( - ArgE, FuncType->getParamType(i)); - CE->setArg(i, - SemaRef.DefaultFunctionArrayLvalueConversion(CLE).get()); - } else { - CE->setArg(i, BaseTransform::TransformExpr(ArgE).get()); - if (auto CLE = dyn_cast(CE->getArg(i))) - CE->setArg(i, - SemaRef.DefaultFunctionArrayLvalueConversion(CLE).get()); - } - } - } - } - return MemberRawPtr ? MemberRawPtr : CE; +} + +ExprResult TransformFatPtr::TransformCallExpr(CallExpr *CE) { + CE->setType(DesugarFatPtrType(CE->getBeginLoc(), CE->getType())); + FunctionDecl *CalleeFD = CE->getDirectCallee(); + // For self-call Expr, desugar the function type of the calling expression + if (CalleeFD == FD) { + TransformSelfCallExprFunctionType(CE); + } + for (unsigned i = 0; i < CalleeFD->getNumParams(); i++) { + QualType ParamQT = CalleeFD->getParamDecl(i)->getType(); + Expr *ArgE = CE->getArg(i); + if (ParamQT->isFatPtrRecordType() && IsNullPtrExpr(ArgE)) { + // if a function has fat pointer parameter, such as + // `void foo(int *fat p);` + // `foo(nullptr)` should be desugared to + // `foo((_FatPtr){ nullptr, 0, 0 })` + Expr *CLE = BuildFatPtrCompoundLiteralExprForRawPtr(ArgE, ParamQT); + CLE = SemaRef.DefaultFunctionArrayLvalueConversion(CLE).get(); + CE->setArg(i, CLE); + } else + CE->setArg(i, BaseTransform::TransformExpr(ArgE).get()); + } + return CE; } ExprResult @@ -1202,11 +695,6 @@ TransformFatPtr::TransformArraySubscriptExpr(ArraySubscriptExpr *ASE) { QualType QT = DesugarFatPtrType(SLoc, ASE->getType()); ASE->setType(QT); - Expr *MemberRawPtr = nullptr; - if (QT.isFatPtrRecordType() && DesugarToMemberPtr) - // Desugar arr[i] to arr[i].raw_ptr - MemberRawPtr = BuildMemberRawPtrAndInsertCheckCallForFatPtr(ASE); - Expr *LHS = ASE->getLHS(); Expr *RHS = ASE->getRHS(); QualType LHSQT = LHS->getType(); @@ -1232,16 +720,16 @@ TransformFatPtr::TransformArraySubscriptExpr(ArraySubscriptExpr *ASE) { SemaRef.CreateBuiltinBinOp(SLoc, BO_Add, OffsetExprLHS, OffsetExprRHS).get(); InsertCheckOffsetCall.second = OffsetExpr; ASE->setLHS(BaseTransform::TransformExpr(LHS).get()); - return MemberRawPtr ? MemberRawPtr : ASE; + return ASE; } else if (IsAccessingArrayMemberThroughFatPtr(ASE)) { // If current ASE is accessing array member through fat ptr, // such as `p->arr[i]` or `p->b.arr[i]`, in order to calculate - // offset when transform inner MemberExpr, we record current ASE. + // offset when transform inner MemberEXpr, we record current ASE. CurrentASEToAccessMemberThroughFatPtr = ASE; } ASE->setLHS(BaseTransform::TransformExpr(LHS).get()); ASE->setRHS(BaseTransform::TransformExpr(RHS).get()); - return MemberRawPtr ? MemberRawPtr : ASE; + return ASE; } ExprResult TransformFatPtr::TransformCStyleCastExpr(CStyleCastExpr *CSCE) { @@ -1251,7 +739,6 @@ ExprResult TransformFatPtr::TransformCStyleCastExpr(CStyleCastExpr *CSCE) { QualType SubQT = SubE->getType(); if (QT->isPointerType() && !QT.isFatPtrType() && SubQT.isFatPtrType()) { // desuagr `(int*)fat_p` to `(int*)fat_p.raw_ptr` - CSCE->setCastKind(CK_NoOp); DesugarToMemberPtr = true; InsertCheckVersionCall = true; InsertCheckOffsetCall.first = true; @@ -1264,9 +751,7 @@ ExprResult TransformFatPtr::TransformCStyleCastExpr(CStyleCastExpr *CSCE) { QT = DesugarFatPtrType(SLoc, QT); // desugar `(int *fat)raw_p` to `(_FatPtr) { raw_p, 0, 0 }` Expr *SubRawPtrE = BaseTransform::TransformExpr(SubE).get(); - Expr *FatPtrE = BuildFatPtrCompoundLiteralExprForRawPtr(SubRawPtrE, QT); - return ImplicitCastExpr::Create(SemaRef.Context, QT, CK_NoOp, FatPtrE, - nullptr, VK_PRValue, FPOptionsOverride()); + return BuildFatPtrCompoundLiteralExprForRawPtr(SubRawPtrE, QT); } else if (QT.isFatPtrType() && SubQT.isFatPtrType()) { return BaseTransform::TransformExpr(SubE).get(); } @@ -1295,142 +780,38 @@ ExprResult TransformFatPtr::TransformUnaryExprOrTypeTraitExpr( return UETT; } -ExprResult -TransformFatPtr::TransformConditionalOperator(ConditionalOperator *CO) { - SourceLocation SLoc = CO->getBeginLoc(); - QualType QT = DesugarFatPtrType(SLoc, CO->getType()); - CO->setType(QT); - - Expr *CondExpr = CO->getCond(); - if (CondExpr->getType().isFatPtrType()) - DesugarToMemberPtr = true; - CondExpr = BaseTransform::TransformExpr(CondExpr).get(); - Expr *LHS = CO->getLHS(); - Expr *RHS = CO->getRHS(); - if (QT.isFatPtrRecordType() && LHS->isNullExpr(SemaRef.Context)) { - RHS = BaseTransform::TransformExpr(RHS).get(); - LHS = BuildFatPtrCompoundLiteralExprForRawPtr(LHS, QT); - } else if (QT.isFatPtrRecordType() && RHS->isNullExpr(SemaRef.Context)) { - LHS = BaseTransform::TransformExpr(LHS).get(); - RHS = BuildFatPtrCompoundLiteralExprForRawPtr(RHS, QT); - } else { - LHS = BaseTransform::TransformExpr(LHS).get(); - RHS = BaseTransform::TransformExpr(RHS).get(); - } - // ConditionalOperator class has not setCond/setLHS/setRHS method, - // so we build it. - return BaseTransform::RebuildConditionalOperator( - CondExpr, CO->getQuestionLoc(), LHS, CO->getColonLoc(), RHS); -} - -ExprResult -TransformFatPtr::TransformCompoundAssignOperator(CompoundAssignOperator *CAO) { - SourceLocation SLoc = CAO->getBeginLoc(); - QualType QT = DesugarFatPtrType(SLoc, CAO->getType()); - CAO->setType(QT); - BinaryOperator::Opcode Op = CAO->getOpcode(); - CAO->setLHS(BaseTransform::TransformExpr(CAO->getLHS()).get()); - CAO->setRHS(BaseTransform::TransformExpr(CAO->getRHS()).get()); - if (QT.isFatPtrType() && (Op == BO_AddAssign || Op == BO_SubAssign)) { - // `p += 1` should be desugared to - // `p = (_FatPtr) { p.raw_ptr + 1, p.key_version, p.offset + 1 * - // sizeof(int) }` - Expr *LHS = CAO->getLHS(); - Expr *RHS = CAO->getRHS(); - bool OpIsAddAssign = Op == BO_AddAssign ? true : false; - Expr *FirstInitE = - SemaRef - .CreateBuiltinBinOp(SLoc, OpIsAddAssign ? BO_Add : BO_Sub, - BuildMemberForFatPtr(LHS, "raw_ptr"), RHS) - .get(); - Expr *SecondInitE = BuildMemberForFatPtr(LHS, "key_version"); - Expr *ThirdInitLHS = BuildMemberForFatPtr(LHS, "offset"); - QualType PointeeQT = LHS->getType().getFatPtrPointeeType(); - QualType Int32Ty = SemaRef.Context.getIntTypeForBitwidth(32, true); - Expr *ThirdInitRHSRHS = IntegerLiteral::Create( - SemaRef.Context, - llvm::APInt(32, SemaRef.Context.getTypeSize(PointeeQT) / 8), Int32Ty, - SLoc); - Expr *ThirdInitRHS = - SemaRef.CreateBuiltinBinOp(SLoc, BO_Mul, RHS, ThirdInitRHSRHS).get(); - Expr *ThirdInitE = - SemaRef - .CreateBuiltinBinOp(SLoc, OpIsAddAssign ? BO_Add : BO_Sub, - ThirdInitLHS, ThirdInitRHS) - .get(); - RHS = - BuildFatPtrCompoundLiteralExpr(FirstInitE, SecondInitE, ThirdInitE, QT); - return SemaRef.CreateBuiltinBinOp(SLoc, BO_Assign, LHS, RHS); - } - return CAO; -} - // BinaryOperator includes: // reassign fat ptr : p1 = p2, p1 = nullptr -// comparison between fat ptr : p1 != p2, p == nullptr, p1 < p2 // calculation between fat ptr : p + 1, p - 1, p1 - p2 ExprResult TransformFatPtr::TransformBinaryOperator(BinaryOperator *BO) { SourceLocation SLoc = BO->getBeginLoc(); - QualType QT = DesugarFatPtrType(SLoc, BO->getType()); - BO->setType(QT); - - MemberExpr *MemberRawPtr = nullptr; - if (QT.isFatPtrRecordType() && DesugarToMemberPtr) - // Desugar p + 1 to (p + 1).raw_ptr - MemberRawPtr = BuildMemberRawPtrAndInsertCheckCallForFatPtr(BO); - + BO->setType(DesugarFatPtrType(SLoc, BO->getType())); Expr *LHS = BO->getLHS(); Expr *RHS = BO->getRHS(); QualType LHSQT = LHS->getType(); QualType RHSQT = RHS->getType(); BinaryOperator::Opcode Op = BO->getOpcode(); - if ((Op == BO_Sub || BO->isComparisonOp()) && LHSQT.isFatPtrType() && - RHSQT.isFatPtrType()) { + if (Op == BO_Sub && LHSQT.isFatPtrType() && RHSQT.isFatPtrType()) { // Substraction between two fat ptr // `p1 - p2` should be desugared to `p1.raw_ptr - p2.raw_ptr` - // Comparison between two fat ptr - // `p1 == p2` should be desugared to `p1.raw_ptr == p2.raw_ptr` - DesugarToMemberPtr = true; - BO->setLHS(BaseTransform::TransformExpr(LHS).get()); - DesugarToMemberPtr = true; - BO->setRHS(BaseTransform::TransformExpr(RHS).get()); - return BO; - } else if (BO->isAdditiveOp() && LHSQT.isFatPtrType() && - RHSQT->isIntegerType()) { + return HandleBOFatPtrSubFatPtr(BO); + } else if (BO->isAdditiveOp() && LHSQT.isFatPtrType() && RHSQT->isIntegerType()) { // addition or substraction between fat ptr and integer // `p + 1` should be desugared to // `(_FatPtr) { p.raw_ptr + 1, p.key_version, p.offset + 1 * sizeof(int) }` - Expr *DesugaredE = HandleBOFatPtrAddOrSubInteger(BO).get(); - if (MemberRawPtr) { - MemberRawPtr->setBase(DesugaredE); - std::stack> CheckCallExprCopy = CheckCallExpr; - while (!CheckCallExprCopy.empty()) { - if (CheckCallExprCopy.top().first == BO) { - for (auto ArgE : dyn_cast(CheckCallExprCopy.top().second) - ->arguments()) { - if (auto ME = dyn_cast(ArgE->IgnoreImpCasts())) { - ME->setBase(DesugaredE); - } - } - } - CheckCallExprCopy.pop(); - } - return MemberRawPtr; - } - return DesugaredE; + return HandleBOFatPtrAddOrSubInteger(BO); } else if (BO->isAssignmentOp() && LHSQT.isFatPtrType() && - RHS->isNullExpr(SemaRef.Context)) { + IsNullPtrExpr(RHS)) { // `p = nullptr;` should be desugared to // `p = (_FatPtr){ nullptr, 0, 0 };` LHSQT = DesugarFatPtrType(SLoc, LHSQT); BO->setLHS(BaseTransform::TransformExpr(LHS).get()); BO->setRHS(BuildFatPtrCompoundLiteralExprForRawPtr(RHS, LHSQT)); return BO; - } else if (BO->isEqualityOp() && LHSQT.isFatPtrType() && - RHS->isNullExpr(SemaRef.Context)) { - // `p == nullptr` should be desugared to `p.raw_ptr == nullptr` + } else if (BO->isEqualityOp() && LHSQT.isFatPtrType() && IsNullPtrExpr(RHS)) { + // `p1 == p2` should be desugared to `p1.ptr == nullptr` DesugarToMemberPtr = true; - BO->setLHS(BaseTransform::TransformExpr(LHS).get()); + BO->setLHS(BaseTransform::TransformExpr(BO->getLHS()).get()); return BO; } BO->setLHS(BaseTransform::TransformExpr(LHS).get()); @@ -1438,6 +819,14 @@ ExprResult TransformFatPtr::TransformBinaryOperator(BinaryOperator *BO) { return BO; } +ExprResult TransformFatPtr::HandleBOFatPtrSubFatPtr(BinaryOperator *BO) { + DesugarToMemberPtr = true; + BO->setLHS(BaseTransform::TransformExpr(BO->getLHS()).get()); + DesugarToMemberPtr = true; + BO->setRHS(BaseTransform::TransformExpr(BO->getRHS()).get()); + return BO; +} + // addition or substraction between fat ptr and integer // `p + 1` should be desugared to // `(_FatPtr) { p.ptr + 1, p.key_version, p.offset + 1 * sizeof(int) }` @@ -1445,89 +834,56 @@ ExprResult TransformFatPtr::HandleBOFatPtrAddOrSubInteger(BinaryOperator *BO) { SourceLocation SLoc = BO->getBeginLoc(); QualType QT = BO->getType(); BinaryOperator::Opcode Op = BO->getOpcode(); + Expr *FatPtrLHS = BO->getLHS()->IgnoreImpCasts(); DesugarToMemberPtr = true; Expr *LHS = BaseTransform::TransformExpr(BO->getLHS()).get(); Expr *RHS = BaseTransform::TransformExpr(BO->getRHS()).get(); Expr *FirstInitE = SemaRef.CreateBuiltinBinOp(SLoc, Op, LHS, RHS).get(); - if (auto MemberFatPtr = dyn_cast(LHS->IgnoreImpCasts())) { - Expr *FatPtrBase = MemberFatPtr->getBase(); - Expr *SecondInitE = BuildMemberForFatPtr(FatPtrBase, "key_version"); - Expr *ThirdInitLHS = BuildMemberForFatPtr(FatPtrBase, "offset"); - QualType PointeeQT = FatPtrBase->getType().getFatPtrPointeeType(); - QualType Int32Ty = SemaRef.Context.getIntTypeForBitwidth(32, true); - Expr *ThirdInitRHSRHS = IntegerLiteral::Create( - SemaRef.Context, - llvm::APInt(32, SemaRef.Context.getTypeSize(PointeeQT) / 8), Int32Ty, - SLoc); - Expr *ThirdInitRHS = - SemaRef.CreateBuiltinBinOp(SLoc, BO_Mul, RHS, ThirdInitRHSRHS).get(); - Expr *ThirdInitE = - SemaRef.CreateBuiltinBinOp(SLoc, Op, ThirdInitLHS, ThirdInitRHS).get(); - return BuildFatPtrCompoundLiteralExpr(FirstInitE, SecondInitE, ThirdInitE, - QT); - } - return BO; + Expr *SecondInitE = BuildMemberForFatPtr(FatPtrLHS, "key_version"); + + Expr *ThirdInitLHS = BuildMemberForFatPtr(FatPtrLHS, "offset"); + QualType PointeeQT = FatPtrLHS->getType()->getFatPtrPointeeType(); + Expr *SizeOfExpr = + SemaRef + .CreateUnaryExprOrTypeTraitExpr( + SemaRef.Context.getTrivialTypeSourceInfo(PointeeQT), + SLoc, UETT_SizeOf, SourceRange()) + .get(); + Expr *ThirdInitRHS = + SemaRef.CreateBuiltinBinOp(SLoc, BO_Mul, RHS, SizeOfExpr).get(); + Expr *ThirdInitE = + SemaRef.CreateBuiltinBinOp(SLoc, Op, ThirdInitLHS, ThirdInitRHS).get(); + return BuildFatPtrCompoundLiteralExpr(FirstInitE, SecondInitE, ThirdInitE, QT); } ExprResult TransformFatPtr::TransformUnaryOperator(UnaryOperator *UO) { QualType QT = DesugarFatPtrType(UO->getBeginLoc(), UO->getType()); UO->setType(QT); - MemberExpr *MemberRawPtr = nullptr; - if (QT.isFatPtrRecordType() && DesugarToMemberPtr) - // Desugar *p to (*p).raw_ptr - // Desugar p++ to p.raw_ptr and p = p + 1 - // Desugar &fat a to (&fat a).raw_ptr - MemberRawPtr = BuildMemberRawPtrAndInsertCheckCallForFatPtr(UO); - UnaryOperator::Opcode Op = UO->getOpcode(); Expr *SubE = UO->getSubExpr(); QualType SubQT = SubE->getType(); if (Op == UO_Deref && SubQT.isFatPtrType()) { // handle *p if p is fat ptr. - if (MemberRawPtr) { - MemberRawPtr->setBase(HandleUODerefFatPtr(UO).get()); - return MemberRawPtr; - } return HandleUODerefFatPtr(UO); } else if (Op == UO_AddrFat) { // handle &fat expr. AddrFatExprBaseKind BaseKind = Finder.AddrFatExprAndBaseMap[UO].first; switch (BaseKind) { case AddrFatExprBaseKind::FatPtr: - if (MemberRawPtr) { - MemberRawPtr->setBase(HandleUOAddrFatOnExprWithFatPtrBase( - UO, Finder.AddrFatExprAndBaseMap[UO].second) - .get()); - return MemberRawPtr; - } - return HandleUOAddrFatOnExprWithFatPtrBase( - UO, Finder.AddrFatExprAndBaseMap[UO].second); + return HandleUOAddrFatOnExprWithFatPtrBase(UO, Finder.AddrFatExprAndBaseMap[UO].second); case AddrFatExprBaseKind::LocalNoArrayVar: - if (MemberRawPtr) { - MemberRawPtr->setBase(HandleUOAddrFatOnLocalNoArrayVar(UO).get()); - return MemberRawPtr; - } return HandleUOAddrFatOnLocalNoArrayVar(UO); case AddrFatExprBaseKind::LocalArrayVar: case AddrFatExprBaseKind::GlobalArrayVar: - if (MemberRawPtr) { - MemberRawPtr->setBase(HandleUOAddrFatOnArrayVar( - UO, Finder.AddrFatExprAndBaseMap[UO].second) - .get()); - return MemberRawPtr; - } return HandleUOAddrFatOnArrayVar(UO, Finder.AddrFatExprAndBaseMap[UO].second); default: break; } - } else if (Op == UO_LNot && SubQT.isFatPtrType()) { - // `!p` where p is fat ptr should be desugared to `!p.raw_ptr` - DesugarToMemberPtr = true; } UO->setSubExpr(BaseTransform::TransformExpr(SubE).get()); - return MemberRawPtr ? (Expr *)MemberRawPtr : UO; + return UO; } ExprResult TransformFatPtr::HandleUODerefFatPtr(UnaryOperator *UO) { @@ -1537,21 +893,14 @@ ExprResult TransformFatPtr::HandleUODerefFatPtr(UnaryOperator *UO) { InsertCheckOffsetCall.first = true; // Build offset arg expr, for example: // for *p, offset should be `sizeof(int)` - QualType Int32Ty = SemaRef.Context.getIntTypeForBitwidth(32, true); - Expr *OffsetExpr = IntegerLiteral::Create( - SemaRef.Context, - llvm::APInt(32, SemaRef.Context.getTypeSize(UO->getType()) / 8), Int32Ty, - UO->getBeginLoc()); + Expr *OffsetExpr = + SemaRef + .CreateUnaryExprOrTypeTraitExpr( + SemaRef.Context.getTrivialTypeSourceInfo(UO->getType()), + UO->getBeginLoc(), UETT_SizeOf, SourceRange()) + .get(); InsertCheckOffsetCall.second = OffsetExpr; - SubE = BaseTransform::TransformExpr(SubE).get(); - if (auto ICE = dyn_cast(SubE)) { - if (ICE->getCastKind() == CK_LValueToRValue) { - return UO; - } - } - UO->setSubExpr(ImplicitCastExpr::Create(SemaRef.Context, SubE->getType(), - CK_LValueToRValue, SubE, nullptr, - VK_PRValue, FPOptionsOverride())); + UO->setSubExpr(BaseTransform::TransformExpr(SubE).get()); return UO; } @@ -1749,11 +1098,27 @@ ExprResult TransformFatPtr::TransformMemberExpr(MemberExpr *ME) { QualType QT = DesugarFatPtrType(SLoc, ME->getType()); ME->setType(QT); - Expr *MemberRawPtr = nullptr; - if (QT.isFatPtrRecordType() && DesugarToMemberPtr) - // Desugar s.p to s.p.raw_ptr - MemberRawPtr = BuildMemberRawPtrAndInsertCheckCallForFatPtr(ME); - + MemberExpr *MemberPtr = nullptr; + if (QT->isFatPtrRecordType()) { + if (InsertCheckOffsetCall.first) { + llvm::SmallVector Args{ME, InsertCheckOffsetCall.second}; + Expr *CE = BuildFatPtrCall(SLoc, "_check_offset", Args); + CheckCallExpr.push(CE); + InsertCheckOffsetCall.first = false; + InsertCheckOffsetCall.second = nullptr; + } + if (InsertCheckVersionCall) { + llvm::SmallVector Args{ME}; + Expr *CE = BuildFatPtrCall(SLoc, "_check_version", Args); + CheckCallExpr.push(CE); + InsertCheckVersionCall = false; + } + if (DesugarToMemberPtr) { + // Desugar s.p to s.p.raw_ptr + MemberPtr = BuildMemberForFatPtr(ME, "raw_ptr"); + DesugarToMemberPtr = false; + } + } QualType BaseType = ME->getBase()->getType(); if (ME->isArrow() && BaseType.isFatPtrType()) { DesugarToMemberPtr = true; @@ -1764,7 +1129,7 @@ ExprResult TransformFatPtr::TransformMemberExpr(MemberExpr *ME) { // offset is `__builtin_offsetof(struct S, arr[3]) + sizeof(int)` // 2. for p->a, where p is fat ptr and p->a is int type, // offset is `__builtin_offsetof(struct S, a) + sizeof(int)` - QualType PointeeQT = BaseType.getFatPtrPointeeType(); + QualType PointeeQT = BaseType->getFatPtrPointeeType(); TypeSourceInfo *PointeeTInfo = SemaRef.Context.getTrivialTypeSourceInfo(PointeeQT, SLoc); QualType ElementType; @@ -1781,15 +1146,14 @@ ExprResult TransformFatPtr::TransformMemberExpr(MemberExpr *ME) { Expr *OffsetExprLHS = SemaRef.BuildBuiltinOffsetOf(SLoc, PointeeTInfo, Comps, SLoc).get(); QualType Int32Ty = SemaRef.Context.getIntTypeForBitwidth(32, true); - Expr *OffsetExprRHS = IntegerLiteral::Create( - SemaRef.Context, - llvm::APInt(32, SemaRef.Context.getTypeSize(ElementType) / 8), Int32Ty, - SLoc); + Expr *OffsetExprRHS = + IntegerLiteral::Create(SemaRef.Context, + llvm::APInt(32, SemaRef.Context.getTypeSize(ElementType) / 8), Int32Ty, SLoc); InsertCheckOffsetCall.second = SemaRef.CreateBuiltinBinOp(SLoc, BO_Add, OffsetExprLHS, OffsetExprRHS).get(); } ME->setBase(BaseTransform::TransformExpr(ME->getBase()).get()); - return MemberRawPtr ? MemberRawPtr : ME; + return MemberPtr ? MemberPtr : ME; } // Desugar fat qualified QualType ,for example: @@ -1799,34 +1163,8 @@ ExprResult TransformFatPtr::TransformMemberExpr(MemberExpr *ME) { // 4. `int *fat *fat` will be desugared to `_FatPtr<_FatPtr>`; QualType TransformFatPtr::DesugarFatPtrType(SourceLocation SLoc, QualType T) { - if (T->isFunctionPointerType()) { - const FunctionProtoType *FuncType = - T->getAs()->getPointeeType()->getAs(); - if (!FuncType->hasFatRetOrParams()) - return T; - // Desugar fat qualified return type. - QualType ReturnTy = DesugarFatPtrType(SLoc, FuncType->getReturnType()); - // Desugar fat qualified parameters. - llvm::SmallVector ParamTys; - for (auto ParamTy : FuncType->getParamTypes()) { - ParamTys.push_back(DesugarFatPtrType(SLoc, ParamTy)); - } - return SemaRef.Context.getPointerType(SemaRef.Context.getFunctionType( - ReturnTy, ParamTys, FuncType->getExtProtoInfo())); - } - if (auto AT = dyn_cast(T)) { - QualType ElementT = DesugarFatPtrType(SLoc, AT->getElementType()); - if (auto CAT = dyn_cast(T)) { - return SemaRef.Context.getConstantArrayType( - ElementT, CAT->getSize(), CAT->getSizeExpr(), CAT->getSizeModifier(), - CAT->getIndexTypeCVRQualifiers()); - } else if (auto VAT = dyn_cast(T)) { - return SemaRef.Context.getVariableArrayType( - ElementT, VAT->getSizeExpr(), VAT->getSizeModifier(), - VAT->getIndexTypeCVRQualifiers(), VAT->getBracketsRange()); - } - } - if (!T.hasFat()) + T = T.getCanonicalType(); + if (!T.isFatQualified() && !T->hasFatFields()) return T; if (!FatPtrTD) { DeclContext::lookup_result Decls = @@ -1837,22 +1175,21 @@ TransformFatPtr::DesugarFatPtrType(SourceLocation SLoc, QualType T) { FatPtrTD = CTD; } if (FatPtrTD) { - T = T.getCanonicalType(); - if (T.isFatPtrType()) { - QualType PointeeT = DesugarFatPtrType(SLoc, T.getFatPtrPointeeType()); - TemplateArgumentListInfo Args(SLoc, SLoc); - Args.addArgument(TemplateArgumentLoc( - TemplateArgument(PointeeT), - SemaRef.Context.getTrivialTypeSourceInfo(PointeeT, SLoc))); - QualType FatPtrRecordType = - SemaRef.CheckTemplateIdType(TemplateName(FatPtrTD), SLoc, Args); - if (!FatPtrRecordType.isNull() && - !SemaRef.RequireCompleteType( - SLoc, FatPtrRecordType, diag::err_fat_ptr_missing_specialization)) - return FatPtrRecordType; - } else if (auto PT = dyn_cast(T)) { + if (auto PT = dyn_cast(T)) { QualType PointeeT = DesugarFatPtrType(SLoc, PT->getPointeeType()); - return SemaRef.Context.getPointerType(PointeeT); + if (T.isFatQualified()) { + TemplateArgumentListInfo Args(SLoc, SLoc); + Args.addArgument(TemplateArgumentLoc( + TemplateArgument(PointeeT), + SemaRef.Context.getTrivialTypeSourceInfo(PointeeT, SLoc))); + QualType FatPtrRecordType = + SemaRef.CheckTemplateIdType(TemplateName(FatPtrTD), SLoc, Args); + if (!FatPtrRecordType.isNull() && + !SemaRef.RequireCompleteType(SLoc, FatPtrRecordType, + diag::err_fat_ptr_missing_specialization)) + return FatPtrRecordType; + } else + return SemaRef.Context.getPointerType(PointeeT); } return T; } @@ -1860,7 +1197,6 @@ TransformFatPtr::DesugarFatPtrType(SourceLocation SLoc, QualType T) { return T; } -// Build CallExpr for _check_version, _check_offset or _new_version_number. Expr *TransformFatPtr::BuildFatPtrCall(SourceLocation SLoc, std::string FDName, llvm::SmallVector &Args) { if (!FatPtrFD[FDName]) { @@ -1870,45 +1206,17 @@ Expr *TransformFatPtr::BuildFatPtrCall(SourceLocation SLoc, std::string FDName, if (Decls.isSingleResult()) FatPtrFD[FDName] = dyn_cast(Decls.front()); } - if (FunctionDecl *CalleeFD = FatPtrFD[FDName]) { - // Handle arg for _check_version and _check_offset: - // 1. split the first _FatPtr argument into three fields argument - // 2. add file name, line number and function name to report error - if (FDName != "_new_version_number") { - Expr *FatPtrE = Args.pop_back_val(); - Args.push_back(BuildMemberForFatPtr(FatPtrE, "raw_ptr")); - Args.push_back(BuildMemberForFatPtr(FatPtrE, "key_version")); - Args.push_back(BuildMemberForFatPtr(FatPtrE, "offset")); - // Build argE for file name. - StringRef FileName = - SemaRef.Context.getSourceManager().getBufferName(SLoc); - QualType FileNameStrType = SemaRef.Context.getConstantArrayType( - SemaRef.Context.CharTy, llvm::APInt(32, FileName.size() + 1), nullptr, - ArrayType::Normal, 0); - Expr *FileNameE = StringLiteral::Create(SemaRef.Context, FileName, - StringLiteral::Ordinary, false, - FileNameStrType, SLoc); - Args.push_back(FileNameE); - // Build argE for line number. - unsigned LineNo = - SemaRef.Context.getSourceManager().getSpellingLineNumber(SLoc); - Expr *LineNoE = - IntegerLiteral::Create(SemaRef.Context, llvm::APInt(32, LineNo), - SemaRef.Context.IntTy, SLoc); - Args.push_back(LineNoE); - // Build argE for function name. - StringRef FuncName = FD->getName(); - QualType FuncNameStrType = SemaRef.Context.getConstantArrayType( - SemaRef.Context.CharTy, llvm::APInt(32, FuncName.size() + 1), nullptr, - ArrayType::Normal, 0); - Expr *FuncNameE = StringLiteral::Create(SemaRef.Context, FuncName, - StringLiteral::Ordinary, false, - FuncNameStrType, SLoc); - Args.push_back(FuncNameE); - } - DeclRefExpr *CalleeFDRef = SemaRef.BuildDeclRefExpr( - CalleeFD, CalleeFD->getType(), VK_LValue, SLoc); - return SemaRef.BuildCallExpr(nullptr, CalleeFDRef, SLoc, Args, SLoc).get(); + if (FunctionDecl *FD = FatPtrFD[FDName]) { + DeclRefExpr *FDRef = + SemaRef.BuildDeclRefExpr(FD, FD->getType(), VK_LValue, SLoc); + CallExpr *CE = + dyn_cast(SemaRef.BuildCallExpr(nullptr, FDRef, SLoc, Args, SLoc).get()); + // Handle first arg of _check_version and _check_version. + if (CE && CE->getNumArgs() > 0 && + CE->getArg(0)->getType()->isFatPtrRecordType()) + if (auto ICE = dyn_cast(CE->getArg(0))) + ICE->setType(ICE->getSubExpr()->getType()); + return CE; } SemaRef.Diag(SLoc, diag::err_fat_ptr_func_not_found) << ""; return nullptr; @@ -1931,36 +1239,13 @@ MemberExpr *TransformFatPtr::BuildMemberForFatPtr(Expr *FatPtrBaseE, FieldDecl *FD = *it; return SemaRef.BuildMemberExpr( FatPtrBaseE, false, FatPtrBaseE->getBeginLoc(), - NestedNameSpecifierLoc(), SourceLocation(), FD, + NestedNameSpecifierLoc(), FatPtrBaseE->getBeginLoc(), FD, DeclAccessPair::make(FatPtrRD, FD->getAccess()), false, DeclarationNameInfo(), FD->getType(), VK_LValue, OK_Ordinary); } return nullptr; } -MemberExpr *TransformFatPtr::BuildMemberRawPtrAndInsertCheckCallForFatPtr( - Expr *FatPtrBaseE) { - SourceLocation SLoc = FatPtrBaseE->getBeginLoc(); - if (InsertCheckOffsetCall.first) { - // Build CallExpr to check offset. - llvm::SmallVector Args{InsertCheckOffsetCall.second, - FatPtrBaseE}; - Expr *CE = BuildFatPtrCall(SLoc, "_check_offset", Args); - CheckCallExpr.push(std::make_pair(FatPtrBaseE, CE)); - InsertCheckOffsetCall.first = false; - InsertCheckOffsetCall.second = nullptr; - } - if (InsertCheckVersionCall) { - // Build CallExpr to check version. - llvm::SmallVector Args{FatPtrBaseE}; - Expr *CE = BuildFatPtrCall(SLoc, "_check_version", Args); - CheckCallExpr.push(std::make_pair(FatPtrBaseE, CE)); - InsertCheckVersionCall = false; - } - DesugarToMemberPtr = false; - return BuildMemberForFatPtr(FatPtrBaseE, "raw_ptr"); -} - Expr *TransformFatPtr::BuildFatPtrCompoundLiteralExprForRawPtr( Expr *RawPtrE, QualType QT) { if (auto *ICE = dyn_cast(RawPtrE)) { @@ -2068,155 +1353,126 @@ void Sema::DesugarRecordDeclWithFatPtr(RecordDecl *RD) { TFP.TransformRecordDecl(RD); } -void Sema::DesugarTypedefNameDeclWithFatPtr(TypedefNameDecl *TND) { - if (Diags.hasErrorOccurred()) - return; - - TransformFatPtr TFP(*this); - TFP.TransformTypedefNameDecl(TND); -} - -bool Sema::CheckFatQualTypeCStyleCast(QualType LHSType, Expr *RHSExpr) { - // nullptr can be explicitly cast to T *fat or T *fat * - if ((LHSType.isFatPtrType() || LHSType->isPointerType()) && - isa(RHSExpr)) - return true; - - // string literal can be cast to char *fat, such as `(char *fat)"xxxx";` - if (LHSType.isFatPtrType() && LHSType.getFatPtrPointeeType()->isCharType() && - isa(RHSExpr)) - return true; - - QualType RHSType = RHSExpr->getType(); +bool Sema::CheckFatQualTypeCStyleCast(QualType LHSType, QualType RHSType, + SourceLocation RLoc) { if (!CheckFatQualTypeCStyleCast(LHSType, RHSType)) { - Diag(RHSExpr->getBeginLoc(), diag::err_fat_qualcheck_incompatible) - << 1 << RHSType << LHSType; + Diag(RLoc, diag::err_fat_qualcheck_incompatible) + << CompleteTraitType(RHSType) << CompleteTraitType(LHSType); return false; } return true; } bool Sema::CheckFatQualTypeCStyleCast(QualType LHSType, QualType RHSType) { - QualType LHSCanType = LHSType.getCanonicalType(); QualType RHSCanType = RHSType.getCanonicalType(); - - // Array Type can be cast to fat pointer type - if (LHSCanType.isFatPtrType() && RHSCanType->isArrayType()) { - QualType LHSPointeeType = LHSCanType.getFatPtrPointeeType(); - QualType RHSElementType = - Context.getAsArrayType(RHSCanType)->getElementType(); - if (LHSPointeeType.isFatPtrType() != RHSElementType.isFatPtrType()) - return false; + QualType LHSCanType = LHSType.getCanonicalType(); + bool IsSameType = (LHSCanType.getTypePtr() == RHSCanType.getTypePtr()); + const auto *LHSPtrType = LHSType->getAs(); + const auto *RHSPtrType = RHSType->getAs(); + bool IsPointer = LHSPtrType && RHSPtrType; + QualType RHSRawType = RHSCanType.getUnqualifiedType(); + QualType LHSRawType = LHSCanType.getUnqualifiedType(); + + if (IsSameType) { + return true; } + if (LHSCanType.isFatPtrType() && RHSCanType.isFatPtrType()) { + return true; + } + if (IsPointer) { + if (Context.hasSameType(LHSRawType, RHSRawType)) { + return true; + } else if (LHSCanType.isFatQualified() && RHSCanType.isFatQualified()) { + // T1 *fat is allowed to cast to T2 *fat + return true; + } else if (TryDesugarTrait(RHSType)) { + return true; + } else { + return CheckFatQualTypeCStyleCast(LHSPtrType->getPointeeType(), + RHSPtrType->getPointeeType()); + } + } + return false; +} - if ((LHSCanType.isFatPtrType() && RHSCanType->isIntegerType()) || - (RHSCanType.isFatPtrType() && LHSCanType->isIntegerType())) - return false; +bool Sema::CheckFatQualTypeAssignment(QualType LHSType, QualType RHSType, + SourceLocation RLoc) { + QualType RHSCanType = RHSType.getCanonicalType(); + QualType LHSCanType = LHSType.getCanonicalType(); + const auto *LHSPtrType = LHSType->getAs(); + const auto *RHSPtrType = RHSType->getAs(); + bool IsPointer = LHSPtrType && RHSPtrType; - // LHSType and RHSType are both pointer type - if ((LHSCanType.isFatPtrType() || LHSCanType->isPointerType()) && - (RHSCanType.isFatPtrType() || RHSCanType->isPointerType())) { - QualType LHSPointeeType = - LHSCanType.isFatPtrType() - ? LHSCanType.getFatPtrPointeeType() - : LHSCanType->getAs()->getPointeeType(); - QualType RHSPointeeType = - RHSCanType.isFatPtrType() - ? RHSCanType.getFatPtrPointeeType() - : RHSCanType->getAs()->getPointeeType(); - - if (LHSPointeeType.isFatPtrType() != RHSPointeeType.isFatPtrType()) { - // T1 *fat * <-> T2 * is OK - if ((LHSPointeeType.isFatPtrType() && !RHSPointeeType->isPointerType()) || - (RHSPointeeType.isFatPtrType() && !LHSPointeeType->isPointerType())) + if (LHSCanType.isFatQualified() == RHSCanType.isFatQualified()) { + if (TraitDecl *TD = TryDesugarTrait(LHSCanType)) { + if (TD->getTypeImpledVarDecl(RHSCanType->getPointeeType())) + return true; + } + if (Context.hasSameType(LHSCanType, RHSCanType)) { + return true; + } + if (LHSCanType->isVoidPointerType()) { + if (LHSCanType->getPointeeType().isConstQualified() == + RHSCanType->getPointeeType().isConstQualified()) return true; - // T1 *fat *fat <-> T2 **fat is ERROR - // T1 *fat * <-> T2 ** is ERROR - // T1 *fat *fat <-> T2 ** is ERROR + } + if (!IsPointer) { return false; + } else { + return CheckFatQualTypeAssignment(LHSPtrType->getPointeeType(), + RHSPtrType->getPointeeType(), RLoc); } - - // T1 *fat <-> T2 *fat is OK - // T1 *fat <-> T2 * is OK - // T1 *fat *fat <-> T2 *fat * is OK - // T1 ** fat <-> T2 ** is OK - return true; } - - return true; + return false; } bool Sema::CheckFatQualTypeAssignment(QualType LHSType, Expr *RHSExpr) { - // T *fat or T *fat * can be inited by nullptr. - if ((LHSType.isFatPtrType() || LHSType->isPointerType()) && - isa(RHSExpr)) - return true; - - QualType RHSType = RHSExpr->getType(); - if (!CheckFatQualTypeAssignment(LHSType, RHSType)) { - Diag(RHSExpr->getBeginLoc(), diag::err_fat_qualcheck_incompatible) - << 0 << RHSType << LHSType; - return false; - } - return true; -} - -bool Sema::CheckFatQualTypeAssignment(QualType LHSType, QualType RHSType) { QualType LHSCanType = LHSType.getCanonicalType(); - QualType RHSCanType = RHSType.getCanonicalType(); - if (LHSCanType.isFatPtrType() && RHSCanType.isFatPtrType()) { - if (Context.hasSameFatPtrType(LHSCanType, RHSCanType)) - return true; - - QualType LHSPointeeType = LHSCanType.getFatPtrPointeeType(); - QualType RHSPointeeType = RHSCanType.getFatPtrPointeeType(); + QualType RHSCanType = RHSExpr->getType().getCanonicalType(); + SourceLocation ExprLoc = RHSExpr->getBeginLoc(); + bool Res = true; - // const T *fat -> T *fat is ERROR - if (!LHSPointeeType.isLocalConstQualified() && - RHSPointeeType.isLocalConstQualified()) - return false; + // Fat pointer can be inited by nullptr. + if (LHSCanType.isFatPtrType() && isa(RHSExpr)) + return true; - if (LHSPointeeType.isLocalConstQualified()) { - LHSPointeeType.removeLocalConst(); - if (RHSPointeeType.isLocalConstQualified()) - RHSPointeeType.removeLocalConst(); - // T *fat -> const T *fat is OK - if (Context.hasSameFatPtrType(LHSPointeeType, RHSPointeeType)) - return true; - // T *fat -> const void *fat is OK - if (LHSPointeeType->isVoidType()) + if (LHSCanType.isFatQualified() || RHSCanType.isFatQualified()) { + if (TraitDecl *TD = TryDesugarTrait(LHSCanType)) { + if (RHSCanType->isPointerType()) { + QualType ImplType = RHSCanType->getPointeeType() + .getUnqualifiedType() + .getCanonicalType(); + ImplType.removeLocalOwned(); + if (TD->getTypeImpledVarDecl(ImplType)) + return true; + } + // trait T* fat <-> trait T* fat // legal + if (TD) { + QualType QT = + DesugarTraitToStructTrait(TD, LHSCanType, RHSExpr->getExprLoc()); + if (QT.getCanonicalType() == RHSCanType) { + return true; + } + } + } + if (LHSCanType->isVoidPointerType() && RHSCanType->isPointerType()) { + if (LHSCanType->getPointeeType().isConstQualified() == + RHSCanType->getPointeeType().isConstQualified()) return true; - // T1 *fat -> const T2 *fat is ERROR } - return false; + if (!Context.hasSameType(LHSCanType, RHSCanType)) + Res = false; + } else { + Res = CheckFatQualTypeAssignment(LHSType, RHSCanType, ExprLoc); } - - // T *fat -> T * is ERROR - // T * -> T *fat is ERROR - if (LHSCanType.isFatPtrType() != RHSCanType.isFatPtrType()) - return false; - - if (!LHSCanType.isFatPtrType() && !RHSCanType.isFatPtrType() && - LHSCanType->isPointerType() && RHSCanType->isPointerType()) { - if (LHSCanType->isVoidPointerType() || RHSCanType->isVoidPointerType()) - return true; - QualType LHSPointeeType = - LHSCanType->getAs()->getPointeeType(); - QualType RHSPointeeType = - RHSCanType->getAs()->getPointeeType(); - return CheckFatQualTypeAssignment(LHSPointeeType, RHSPointeeType); + if ((LHSCanType.isFatPtrType() && RHSCanType.isFatPtrType()) && + !(LHSCanType.isFatQualified() && RHSCanType.isFatQualified())) + return true; + if (!Res) { + Diag(ExprLoc, diag::err_fat_qualcheck_incompatible) + << CompleteTraitType(RHSExpr->getType()) << CompleteTraitType(LHSType); } - return true; -} - -bool Sema::HasDiffFatTypeAtBothFunction(QualType LHSType, QualType RHSType) { - if (LHSType.isNull() || RHSType.isNull()) - return false; - const FunctionProtoType *LHSFuncType = LHSType->getAs(); - const FunctionProtoType *RHSFuncType = RHSType->getAs(); - if (!LHSFuncType || !RHSFuncType) - return false; - return !Context.hasSameFatPtrType(LHSType, RHSType); + return Res; } bool Sema::CheckFatFunctionPointerType(QualType LHSType, Expr *RHSExpr) { @@ -2231,37 +1487,26 @@ bool Sema::CheckFatFunctionPointerType(QualType LHSType, Expr *RHSExpr) { ->getAs() : RHSExpr->getType()->getAs(); SourceLocation ExprLoc = RHSExpr->getBeginLoc(); - if (HasDiffFatTypeAtBothFunction(QualType(LHSFuncType, 0), - QualType(RHSFuncType, 0))) { + + // return if no 'fat' in both side + if (!LHSFuncType->hasFatRetOrParams() && !RHSFuncType->hasFatRetOrParams()) { + return true; + } + + if (!Context.hasSameType(LHSFuncType, RHSFuncType)) { Diag(ExprLoc, diag::err_fat_funcPtr_incompatible) << LHSType << RHSExpr->getType(); return false; } - return true; -} - -// In some context, it is hard to desugar SelfIncExpr or -// SelfDecExpr of fat ptr, so we report error when: -// 1) the CondExpr of IfStmt/WhileStmt/DoStmt/ForStmt/ -// SwitchStmt/ConditionalOperator has SelfIncExpr or -// SelfDecExpr of fat ptr, such as `while (*p++ > 0)`, -// `*p++ > 0 ? : *p : *q;` -// 2) the LHS or RHS of ConditionalOperator has SelfIncExpr -// or SelfDecExpr of fat ptr, such as `*p ? : p++ : q++;` -bool Sema::CheckSelfIncOrDecOfFatPtr(Expr *E) { - if (auto UO = dyn_cast(E)) { - if ((UO->isPrefix() || UO->isPostfix()) && - UO->getSubExpr()->getType().isFatPtrType()) { - Diag(UO->getBeginLoc(), diag::err_no_inc_or_dec_of_fat_ptr); + for (unsigned i = 0; i < LHSFuncType->getNumParams(); i++) { + QualType LHSParamType = LHSFuncType->getParamType(i); + QualType RHSParamType = RHSFuncType->getParamType(i); + if (!Context.hasSameType(LHSParamType, RHSParamType)) { + Diag(ExprLoc, diag::err_fat_funcPtr_incompatible) + << LHSType << RHSExpr->getType(); return false; } } - for (auto C : E->children()) { - if (auto ChildE = dyn_cast_or_null(C)) { - if (!CheckSelfIncOrDecOfFatPtr(ChildE)) - return false; - } - } return true; } @@ -2277,7 +1522,7 @@ bool Sema::CheckSelfIncOrDecOfFatPtr(Expr *E) { // uint32_t size; // int arr[3]; // }; -// struct _arr_AllocationUnit _arr = { 1, 12, {1, 2, 3} }; +// struct _arr_AllocationUnit _arr = { 0, 12, {1, 2, 3} }; // @endcode std::pair Sema::BuildAllocationUnitForGlobalArrayVar(VarDecl *VD) { @@ -2315,9 +1560,9 @@ Sema::BuildAllocationUnitForGlobalArrayVar(VarDecl *VD) { &Context.Idents.get("_" + VD->getNameAsString()), QualType(RD->getTypeForDecl(), 0), nullptr, SC_None); // Build InitExpr - // Build first init expr : 1 + // Build first init expr : 0 Expr *LockVersionInit = - IntegerLiteral::Create(Context, llvm::APInt(32, 1), UInt32Ty, SLoc); + IntegerLiteral::Create(Context, llvm::APInt(32, 0), UInt32Ty, SLoc); // Build second init expr : array size Expr *SizeInit = IntegerLiteral::Create( Context, llvm::APInt(32, Context.getTypeSize(VD->getType()) / 8), diff --git a/clang/lib/Sema/SemaCast.cpp b/clang/lib/Sema/SemaCast.cpp index 1df1d1a28d4b..741617c32050 100644 --- a/clang/lib/Sema/SemaCast.cpp +++ b/clang/lib/Sema/SemaCast.cpp @@ -2899,8 +2899,9 @@ void CastOperation::CheckCStyleCast() { } } // bsc fat type CStyleCast - if (SrcType.hasFat() || DestType.hasFat()) { - if (!Self.CheckFatQualTypeCStyleCast(DestType, SrcExpr.get())) { + if (SrcCanType.isFatQualified() || DestCanType.isFatQualified()) { + if (!Self.CheckFatQualTypeCStyleCast(DestType, SrcType, + SrcExpr.get()->getExprLoc())) { SrcExpr = ExprError(); return; } @@ -2921,6 +2922,13 @@ void CastOperation::CheckCStyleCast() { return; } } + if (LHSPtrType->hasFatFields() || RHSPtrType->hasFatFields()) { + if (!Self.CheckFatQualTypeCStyleCast(DestType, SrcType, + SrcExpr.get()->getExprLoc())) { + SrcExpr = ExprError(); + return; + } + } } } if (Self.IsTraitExpr(SrcExpr.get()) && DestType->isPointerType()) { @@ -2932,8 +2940,7 @@ void CastOperation::CheckCStyleCast() { Kind = CK_BitCast; } return; - } else if (SrcCanType.isFatPtrRecordType() || - DestCanType.isFatPtrRecordType()) { + } else if (SrcCanType->isFatPtrRecordType() && DestType.isFatQualified()) { return; } } diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index a3848d8a1f5d..035c35f4c709 100755 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -4106,7 +4106,6 @@ bool Sema::MergeFunctionDecl(FunctionDecl *New, NamedDecl *&OldD, Scope *S, // If any declaration of a BSC function or function // template has a constexpr specifier then all its declarations shall // contain the constexpr specifier. - bool AreCompatibleFunctionTypeWithFatPtr = false; if (getLangOpts().BSC) { if (New->getConstexprKind() != Old->getConstexprKind()) { Diag(New->getLocation(), diag::err_constexpr_redecl_mismatch) @@ -4115,25 +4114,12 @@ bool Sema::MergeFunctionDecl(FunctionDecl *New, NamedDecl *&OldD, Scope *S, Diag(Old->getLocation(), diag::note_previous_declaration); return true; } - if (New->getSafeZoneSpecifier() != Old->getSafeZoneSpecifier()) { - Diag(New->getLocation(), diag::err_conflicting_types) << New; - Diag(Old->getLocation(), PrevDiag) << Old << Old->getType(); - return true; - } if (HasDiffBorrowOrOwnedParamsTypeAtBothFunction(Old->getType(), New->getType())) { Diag(New->getLocation(), diag::err_conflicting_types) << New; Diag(Old->getLocation(), PrevDiag) << Old << Old->getType(); return true; } - if (Old->getType().hasFat() || New->getType().hasFat()) { - if (HasDiffFatTypeAtBothFunction(Old->getType(), New->getType())) { - Diag(New->getLocation(), diag::err_conflicting_types) << New; - Diag(Old->getLocation(), PrevDiag) << Old << Old->getType(); - return true; - } - AreCompatibleFunctionTypeWithFatPtr = true; - } if (New->getOverloadedOperator() != Old->getOverloadedOperator()) { Diag(New->getLocation(), diag::err_conflicting_types) << New; Diag(Old->getLocation(), PrevDiag) << Old << Old->getType(); @@ -4211,11 +4197,7 @@ bool Sema::MergeFunctionDecl(FunctionDecl *New, NamedDecl *&OldD, Scope *S, } } - if (Context.typesAreCompatible(OldQType, NewQType) -#if ENABLE_BSC - || AreCompatibleFunctionTypeWithFatPtr -#endif - ) { + if (Context.typesAreCompatible(OldQType, NewQType)) { const FunctionType *OldFuncType = OldQType->getAs(); const FunctionType *NewFuncType = NewQType->getAs(); const FunctionProtoType *OldProto = nullptr; @@ -6896,11 +6878,6 @@ Sema::ActOnTypedefNameDecl(Scope *S, DeclContext *DC, TypedefNameDecl *NewTD, Context.setucontext_tDecl(NewTD); } -#if ENABLE_BSC - if (getLangOpts().BSC && getLangOpts().EnableFatPtr) - DesugarTypedefNameDeclWithFatPtr(NewTD); -#endif - return NewTD; } diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 92ed21bdec69..30381de78101 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -5159,8 +5159,7 @@ ExprResult Sema::ActOnArraySubscriptExpr(Scope *S, Expr *base, << base->getType() << base->getSourceRange(); return ExprError(); } - if (getLangOpts().BSC && base->getType()->isRecordType() && - !base->getType().isFatPtrRecordType()) { + if (getLangOpts().BSC && base->getType()->isRecordType()) { return CreateOverloadedArraySubscriptExpr(lbLoc, rbLoc, base, ArgExprs); } #endif @@ -5898,10 +5897,10 @@ Sema::CreateBuiltinArraySubscriptExpr(Expr *Base, SourceLocation LLoc, ResultType = PTy->getPointeeType(); #if ENABLE_BSC } else if (getLangOpts().BSC && getLangOpts().EnableFatPtr && - LHSTy.isFatPtrRecordType()) { + LHSTy->isFatPtrRecordType()) { BaseExpr = LHSExp; IndexExpr = RHSExp; - ResultType = LHSTy.getFatPtrPointeeType(); + ResultType = LHSTy->getFatPtrPointeeType(); #endif } else if (const ObjCObjectPointerType *PTy = LHSTy->getAs()) { @@ -8663,11 +8662,6 @@ static bool checkCondition(Sema &S, Expr *Cond, SourceLocation QuestionLoc) { // C99 6.5.15p2 if (CondTy->isScalarType()) return false; -#if ENABLE_BSC - if (S.getLangOpts().BSC && S.getLangOpts().EnableFatPtr && - CondTy.isFatPtrRecordType()) - return false; -#endif S.Diag(QuestionLoc, diag::err_typecheck_cond_expect_scalar) << CondTy << Cond->getSourceRange(); return true; @@ -9247,17 +9241,7 @@ QualType Sema::CheckConditionalOperands(ExprResult &Cond, ExprResult &LHS, // the type of the other operand." if (!checkConditionalNullPointer(*this, RHS, LHSTy)) return LHSTy; if (!checkConditionalNullPointer(*this, LHS, RHSTy)) return RHSTy; -#if ENABLE_BSC - if (getLangOpts().BSC && getLangOpts().EnableFatPtr) { - if (RHS.get()->isNullExpr(Context) && LHSTy.isFatPtrType()) - return LHSTy; - if (LHS.get()->isNullExpr(Context) && RHSTy.isFatPtrType()) - return RHSTy; - if (LHSTy.isFatPtrType() && RHSTy.isFatPtrType() && - Context.hasSameFatPtrType(LHSTy, RHSTy)) - return LHSTy; - } -#endif + // All objective-c pointer type analysis is done here. QualType compositeType = FindCompositeObjCPointerType(LHS, RHS, QuestionLoc); @@ -9720,16 +9704,6 @@ ExprResult Sema::ActOnConditionalOp(SourceLocation QuestionLoc, RHS.isInvalid()) return ExprError(); -#if ENABLE_BSC - if (getLangOpts().BSC && getLangOpts().EnableFatPtr) { - bool CondResult = CheckSelfIncOrDecOfFatPtr(CondExpr); - bool LHSResult = CheckSelfIncOrDecOfFatPtr(LHSExpr); - bool RHSResult = CheckSelfIncOrDecOfFatPtr(RHSExpr); - if (!CondResult || !LHSResult || !RHSResult) - return ExprError(); - } -#endif - DiagnoseConditionalPrecedence(*this, QuestionLoc, Cond.get(), LHS.get(), RHS.get()); @@ -10475,13 +10449,16 @@ static bool IsTraitEqualExpr(Sema &S, QualType DstType, QualType SrcType, } static bool IsFatPtrEqualExpr(Sema &S, QualType DstType, QualType SrcType) { + if (auto *Typedef = dyn_cast(DstType)) { + DstType = Typedef->getDecl()->getUnderlyingType(); + } + if (auto *Typedef = dyn_cast(SrcType)) { + SrcType = Typedef->getDecl()->getUnderlyingType(); + } if (DstType.isFatPtrType() && (SrcType.isFatPtrType() || SrcType->isNullPtrType())) { return true; } - if (DstType->isVoidPointerType() && SrcType.isFatPtrType()) { - return true; - } return false; } #endif @@ -10527,6 +10504,10 @@ Sema::CheckSingleAssignmentConstraints(QualType LHSType, ExprResult &CallerRHS, if (!CheckBorrowQualTypeAssignment(LHSType, RHS.get())) return IncompatibleBorrowPointer; } + if (RHSCanType.isFatPtrType() || LHSCanType.isFatPtrType()) { + if (!CheckFatQualTypeAssignment(LHSType, RHS.get())) + return IncompatibleFatPointer; + } if (const auto *LHSPtrType = LHSType->getAs()) { if (const auto *RHSPtrType = RHS.get()->getType()->getAs()) { if (LHSPtrType->hasOwnedFields() || RHSPtrType->hasOwnedFields()) { @@ -10539,15 +10520,13 @@ Sema::CheckSingleAssignmentConstraints(QualType LHSType, ExprResult &CallerRHS, return IncompatibleBorrowPointer; } } + if (LHSPtrType->hasFatFields() || RHSPtrType->hasFatFields()) { + if (!CheckFatQualTypeAssignment(LHSType, RHS.get())) { + return IncompatibleFatPointer; + } + } } } - if (LHSType.hasFat() || RHS.get()->getType().hasFat()) { - if (!CheckFatQualTypeAssignment(LHSType, RHS.get())) - return IncompatibleFatPointer; - if (LHSType->isFunctionPointerType() && - !CheckFatFunctionPointerType(LHSType, RHS.get())) - return IncompatibleFatPointer; - } if (LHSType->isFunctionPointerType() && (RHS.get()->getType()->isFunctionPointerType() || RHS.get()->getType()->isFunctionType())) { @@ -10555,6 +10534,8 @@ Sema::CheckSingleAssignmentConstraints(QualType LHSType, ExprResult &CallerRHS, return IncompatibleOwnedPointer; if (!CheckBorrowFunctionPointerType(LHSType, RHS.get())) return IncompatibleBorrowPointer; + if (!CheckFatFunctionPointerType(LHSType, RHS.get())) + return IncompatibleFatPointer; } } #endif @@ -13485,8 +13466,7 @@ QualType Sema::CheckCompareOperands(ExprResult &LHS, ExprResult &RHS, } #if ENABLE_BSC - if (getLangOpts().BSC && RHSType->isNullPtrType() && - (LHSType->isPointerType() || LHSType.isFatPtrType())) + if (getLangOpts().BSC && LHSType->isPointerType() && RHSType->isNullPtrType()) return computeResultTy(); #endif @@ -15266,6 +15246,7 @@ QualType Sema::CheckAddressOfOperand(ExprResult &OrigOp, SourceLocation OpLoc) { return Context.getObjCObjectPointerType(op->getType()); CheckAddressOfPackedMember(op); + return Context.getPointerType(op->getType()); } @@ -15313,9 +15294,8 @@ static QualType CheckIndirectionOperand(Sema &S, Expr *Op, ExprValueKind &VK, OpTy->getAs()) Result = OPT->getPointeeType(); #if ENABLE_BSC - else if (S.getLangOpts().BSC && S.getLangOpts().EnableFatPtr && - OpTy.isFatPtrRecordType()) - Result = OpTy.getFatPtrPointeeType(); + else if (S.getLangOpts().BSC && S.getLangOpts().EnableFatPtr && OpTy->isFatPtrRecordType()) + Result = OpTy->getFatPtrPointeeType(); #endif else { ExprResult PR = S.CheckPlaceholderExpr(Op); @@ -15704,166 +15684,157 @@ ExprResult Sema::CreateBuiltinBinOp(SourceLocation OpLoc, checkTypeSupport(LHSExpr->getType(), OpLoc, /*ValueDecl*/ nullptr); checkTypeSupport(RHSExpr->getType(), OpLoc, /*ValueDecl*/ nullptr); -#if ENABLE_BSC - if (getLangOpts().BSC && getLangOpts().EnableFatPtr && - LHSExpr->getType().isFatPtrRecordType() && - (Opc == BO_Add || Opc == BO_Sub)) { - ResultTy = - Context.getPointerType(LHSExpr->getType().getFatPtrPointeeType()); - ResultTy.addFat(); - } else -#endif - switch (Opc) { - case BO_Assign: + + switch (Opc) { + case BO_Assign: + ResultTy = CheckAssignmentOperands(LHS.get(), RHS, OpLoc, QualType(), Opc); + if (getLangOpts().CPlusPlus && + LHS.get()->getObjectKind() != OK_ObjCProperty) { + VK = LHS.get()->getValueKind(); + OK = LHS.get()->getObjectKind(); + } + if (!ResultTy.isNull()) { + DiagnoseSelfAssignment(*this, LHS.get(), RHS.get(), OpLoc, true); + DiagnoseSelfMove(LHS.get(), RHS.get(), OpLoc); + + // Avoid copying a block to the heap if the block is assigned to a local + // auto variable that is declared in the same scope as the block. This + // optimization is unsafe if the local variable is declared in an outer + // scope. For example: + // + // BlockTy b; + // { + // b = ^{...}; + // } + // // It is unsafe to invoke the block here if it wasn't copied to the + // // heap. + // b(); + + if (auto *BE = dyn_cast(RHS.get()->IgnoreParens())) + if (auto *DRE = dyn_cast(LHS.get()->IgnoreParens())) + if (auto *VD = dyn_cast(DRE->getDecl())) + if (VD->hasLocalStorage() && getCurScope()->isDeclScope(VD)) + BE->getBlockDecl()->setCanAvoidCopyToHeap(); + + if (LHS.get()->getType().hasNonTrivialToPrimitiveCopyCUnion()) + checkNonTrivialCUnion(LHS.get()->getType(), LHS.get()->getExprLoc(), + NTCUC_Assignment, NTCUK_Copy); + } + RecordModifiableNonNullParam(*this, LHS.get()); + break; + case BO_PtrMemD: + case BO_PtrMemI: + ResultTy = CheckPointerToMemberOperands(LHS, RHS, VK, OpLoc, + Opc == BO_PtrMemI); + break; + case BO_Mul: + case BO_Div: + ConvertHalfVec = true; + ResultTy = CheckMultiplyDivideOperands(LHS, RHS, OpLoc, false, + Opc == BO_Div); + break; + case BO_Rem: + ResultTy = CheckRemainderOperands(LHS, RHS, OpLoc); + break; + case BO_Add: + ConvertHalfVec = true; + ResultTy = CheckAdditionOperands(LHS, RHS, OpLoc, Opc); + break; + case BO_Sub: + ConvertHalfVec = true; + ResultTy = CheckSubtractionOperands(LHS, RHS, OpLoc); + break; + case BO_Shl: + case BO_Shr: + ResultTy = CheckShiftOperands(LHS, RHS, OpLoc, Opc); + break; + case BO_LE: + case BO_LT: + case BO_GE: + case BO_GT: + ConvertHalfVec = true; + ResultTy = CheckCompareOperands(LHS, RHS, OpLoc, Opc); + break; + case BO_EQ: + case BO_NE: + ConvertHalfVec = true; + ResultTy = CheckCompareOperands(LHS, RHS, OpLoc, Opc); + break; + case BO_Cmp: + ConvertHalfVec = true; + ResultTy = CheckCompareOperands(LHS, RHS, OpLoc, Opc); + assert(ResultTy.isNull() || ResultTy->getAsCXXRecordDecl()); + break; + case BO_And: + checkObjCPointerIntrospection(*this, LHS, RHS, OpLoc); + LLVM_FALLTHROUGH; + case BO_Xor: + case BO_Or: + ResultTy = CheckBitwiseOperands(LHS, RHS, OpLoc, Opc); + break; + case BO_LAnd: + case BO_LOr: + ConvertHalfVec = true; + ResultTy = CheckLogicalOperands(LHS, RHS, OpLoc, Opc); + break; + case BO_MulAssign: + case BO_DivAssign: + ConvertHalfVec = true; + CompResultTy = CheckMultiplyDivideOperands(LHS, RHS, OpLoc, true, + Opc == BO_DivAssign); + CompLHSTy = CompResultTy; + if (!CompResultTy.isNull() && !LHS.isInvalid() && !RHS.isInvalid()) ResultTy = - CheckAssignmentOperands(LHS.get(), RHS, OpLoc, QualType(), Opc); - if (getLangOpts().CPlusPlus && - LHS.get()->getObjectKind() != OK_ObjCProperty) { - VK = LHS.get()->getValueKind(); - OK = LHS.get()->getObjectKind(); - } - if (!ResultTy.isNull()) { - DiagnoseSelfAssignment(*this, LHS.get(), RHS.get(), OpLoc, true); - DiagnoseSelfMove(LHS.get(), RHS.get(), OpLoc); - - // Avoid copying a block to the heap if the block is assigned to a local - // auto variable that is declared in the same scope as the block. This - // optimization is unsafe if the local variable is declared in an outer - // scope. For example: - // - // BlockTy b; - // { - // b = ^{...}; - // } - // // It is unsafe to invoke the block here if it wasn't copied to the - // // heap. - // b(); - - if (auto *BE = dyn_cast(RHS.get()->IgnoreParens())) - if (auto *DRE = dyn_cast(LHS.get()->IgnoreParens())) - if (auto *VD = dyn_cast(DRE->getDecl())) - if (VD->hasLocalStorage() && getCurScope()->isDeclScope(VD)) - BE->getBlockDecl()->setCanAvoidCopyToHeap(); - - if (LHS.get()->getType().hasNonTrivialToPrimitiveCopyCUnion()) - checkNonTrivialCUnion(LHS.get()->getType(), LHS.get()->getExprLoc(), - NTCUC_Assignment, NTCUK_Copy); - } - RecordModifiableNonNullParam(*this, LHS.get()); - break; - case BO_PtrMemD: - case BO_PtrMemI: + CheckAssignmentOperands(LHS.get(), RHS, OpLoc, CompResultTy, Opc); + break; + case BO_RemAssign: + CompResultTy = CheckRemainderOperands(LHS, RHS, OpLoc, true); + CompLHSTy = CompResultTy; + if (!CompResultTy.isNull() && !LHS.isInvalid() && !RHS.isInvalid()) ResultTy = - CheckPointerToMemberOperands(LHS, RHS, VK, OpLoc, Opc == BO_PtrMemI); - break; - case BO_Mul: - case BO_Div: - ConvertHalfVec = true; + CheckAssignmentOperands(LHS.get(), RHS, OpLoc, CompResultTy, Opc); + break; + case BO_AddAssign: + ConvertHalfVec = true; + CompResultTy = CheckAdditionOperands(LHS, RHS, OpLoc, Opc, &CompLHSTy); + if (!CompResultTy.isNull() && !LHS.isInvalid() && !RHS.isInvalid()) ResultTy = - CheckMultiplyDivideOperands(LHS, RHS, OpLoc, false, Opc == BO_Div); - break; - case BO_Rem: - ResultTy = CheckRemainderOperands(LHS, RHS, OpLoc); - break; - case BO_Add: - ConvertHalfVec = true; - ResultTy = CheckAdditionOperands(LHS, RHS, OpLoc, Opc); - break; - case BO_Sub: - ConvertHalfVec = true; - ResultTy = CheckSubtractionOperands(LHS, RHS, OpLoc); - break; - case BO_Shl: - case BO_Shr: - ResultTy = CheckShiftOperands(LHS, RHS, OpLoc, Opc); - break; - case BO_LE: - case BO_LT: - case BO_GE: - case BO_GT: - ConvertHalfVec = true; - ResultTy = CheckCompareOperands(LHS, RHS, OpLoc, Opc); - break; - case BO_EQ: - case BO_NE: - ConvertHalfVec = true; - ResultTy = CheckCompareOperands(LHS, RHS, OpLoc, Opc); - break; - case BO_Cmp: - ConvertHalfVec = true; - ResultTy = CheckCompareOperands(LHS, RHS, OpLoc, Opc); - assert(ResultTy.isNull() || ResultTy->getAsCXXRecordDecl()); - break; - case BO_And: - checkObjCPointerIntrospection(*this, LHS, RHS, OpLoc); - LLVM_FALLTHROUGH; - case BO_Xor: - case BO_Or: - ResultTy = CheckBitwiseOperands(LHS, RHS, OpLoc, Opc); - break; - case BO_LAnd: - case BO_LOr: - ConvertHalfVec = true; - ResultTy = CheckLogicalOperands(LHS, RHS, OpLoc, Opc); - break; - case BO_MulAssign: - case BO_DivAssign: - ConvertHalfVec = true; - CompResultTy = CheckMultiplyDivideOperands(LHS, RHS, OpLoc, true, - Opc == BO_DivAssign); - CompLHSTy = CompResultTy; - if (!CompResultTy.isNull() && !LHS.isInvalid() && !RHS.isInvalid()) - ResultTy = - CheckAssignmentOperands(LHS.get(), RHS, OpLoc, CompResultTy, Opc); - break; - case BO_RemAssign: - CompResultTy = CheckRemainderOperands(LHS, RHS, OpLoc, true); - CompLHSTy = CompResultTy; - if (!CompResultTy.isNull() && !LHS.isInvalid() && !RHS.isInvalid()) - ResultTy = - CheckAssignmentOperands(LHS.get(), RHS, OpLoc, CompResultTy, Opc); - break; - case BO_AddAssign: - ConvertHalfVec = true; - CompResultTy = CheckAdditionOperands(LHS, RHS, OpLoc, Opc, &CompLHSTy); - if (!CompResultTy.isNull() && !LHS.isInvalid() && !RHS.isInvalid()) - ResultTy = - CheckAssignmentOperands(LHS.get(), RHS, OpLoc, CompResultTy, Opc); - break; - case BO_SubAssign: - ConvertHalfVec = true; - CompResultTy = CheckSubtractionOperands(LHS, RHS, OpLoc, &CompLHSTy); - if (!CompResultTy.isNull() && !LHS.isInvalid() && !RHS.isInvalid()) - ResultTy = - CheckAssignmentOperands(LHS.get(), RHS, OpLoc, CompResultTy, Opc); - break; - case BO_ShlAssign: - case BO_ShrAssign: - CompResultTy = CheckShiftOperands(LHS, RHS, OpLoc, Opc, true); - CompLHSTy = CompResultTy; - if (!CompResultTy.isNull() && !LHS.isInvalid() && !RHS.isInvalid()) - ResultTy = - CheckAssignmentOperands(LHS.get(), RHS, OpLoc, CompResultTy, Opc); - break; - case BO_AndAssign: - case BO_OrAssign: // fallthrough - DiagnoseSelfAssignment(*this, LHS.get(), RHS.get(), OpLoc, true); - LLVM_FALLTHROUGH; - case BO_XorAssign: - CompResultTy = CheckBitwiseOperands(LHS, RHS, OpLoc, Opc); - CompLHSTy = CompResultTy; - if (!CompResultTy.isNull() && !LHS.isInvalid() && !RHS.isInvalid()) - ResultTy = - CheckAssignmentOperands(LHS.get(), RHS, OpLoc, CompResultTy, Opc); - break; - case BO_Comma: - ResultTy = CheckCommaOperands(*this, LHS, RHS, OpLoc); - if (getLangOpts().CPlusPlus && !RHS.isInvalid()) { - VK = RHS.get()->getValueKind(); - OK = RHS.get()->getObjectKind(); - } - break; + CheckAssignmentOperands(LHS.get(), RHS, OpLoc, CompResultTy, Opc); + break; + case BO_SubAssign: + ConvertHalfVec = true; + CompResultTy = CheckSubtractionOperands(LHS, RHS, OpLoc, &CompLHSTy); + if (!CompResultTy.isNull() && !LHS.isInvalid() && !RHS.isInvalid()) + ResultTy = + CheckAssignmentOperands(LHS.get(), RHS, OpLoc, CompResultTy, Opc); + break; + case BO_ShlAssign: + case BO_ShrAssign: + CompResultTy = CheckShiftOperands(LHS, RHS, OpLoc, Opc, true); + CompLHSTy = CompResultTy; + if (!CompResultTy.isNull() && !LHS.isInvalid() && !RHS.isInvalid()) + ResultTy = + CheckAssignmentOperands(LHS.get(), RHS, OpLoc, CompResultTy, Opc); + break; + case BO_AndAssign: + case BO_OrAssign: // fallthrough + DiagnoseSelfAssignment(*this, LHS.get(), RHS.get(), OpLoc, true); + LLVM_FALLTHROUGH; + case BO_XorAssign: + CompResultTy = CheckBitwiseOperands(LHS, RHS, OpLoc, Opc); + CompLHSTy = CompResultTy; + if (!CompResultTy.isNull() && !LHS.isInvalid() && !RHS.isInvalid()) + ResultTy = + CheckAssignmentOperands(LHS.get(), RHS, OpLoc, CompResultTy, Opc); + break; + case BO_Comma: + ResultTy = CheckCommaOperands(*this, LHS, RHS, OpLoc); + if (getLangOpts().CPlusPlus && !RHS.isInvalid()) { + VK = RHS.get()->getValueKind(); + OK = RHS.get()->getObjectKind(); } + break; + } if (ResultTy.isNull() || LHS.isInvalid() || RHS.isInvalid()) return ExprError(); @@ -16358,10 +16329,7 @@ ExprResult Sema::BuildBinOp(Scope *S, SourceLocation OpLoc, // overloadable type. if (LHSExpr->getType()->isOverloadableType() || RHSExpr->getType()->isOverloadableType()) -#if ENABLE_BSC - if (!(getLangOpts().BSC && LHSExpr->getType().isFatPtrRecordType())) -#endif - return BuildOverloadedBinOp(*this, S, OpLoc, Opc, LHSExpr, RHSExpr); + return BuildOverloadedBinOp(*this, S, OpLoc, Opc, LHSExpr, RHSExpr); } if (getLangOpts().RecoveryAST && @@ -16569,11 +16537,6 @@ ExprResult Sema::CreateBuiltinUnaryOp(SourceLocation OpLoc, if (resultType->isDependentType()) break; -#if ENABLE_BSC - if (getLangOpts().BSC && getLangOpts().EnableFatPtr && - resultType.isFatPtrRecordType()) - resultType = Context.getPointerType(resultType.getFatPtrPointeeType()); -#endif if (resultType->isScalarType() && !isScopedEnumerationType(resultType)) { // C99 6.5.3.3p1: ok, fallthrough; if (Context.getLangOpts().CPlusPlus) { @@ -17862,7 +17825,8 @@ bool Sema::DiagnoseAssignmentResult(AssignConvertType ConvTy, case IncompatiblePointer: #if ENABLE_BSC // int a = 1; trait F* f = &a; - if (getLangOpts().BSC && IsTraitEqualExpr(*this, DstType, SrcType, Loc)) + if (getLangOpts().BSC && (IsTraitEqualExpr(*this, DstType, SrcType, Loc) || + IsFatPtrEqualExpr(*this, DstType, SrcType))) return false; #endif if (Action == AA_Passing_CFAudited) { @@ -20976,11 +20940,6 @@ ExprResult Sema::CheckBooleanCondition(SourceLocation Loc, Expr *E, E = ERes.get(); QualType T = E->getType(); -#if ENABLE_BSC - if (getLangOpts().BSC && getLangOpts().EnableFatPtr && - T.isFatPtrRecordType()) - return E; -#endif if (!T->isScalarType()) { // C99 6.8.4.1p1 Diag(Loc, diag::err_typecheck_statement_requires_scalar) << T << E->getSourceRange(); @@ -21014,14 +20973,6 @@ Sema::ConditionResult Sema::ActOnCondition(Scope *S, SourceLocation Loc, Cond = CheckSwitchCondition(Loc, SubExpr); break; } - -#if ENABLE_BSC - if (getLangOpts().BSC && getLangOpts().EnableFatPtr) { - if (!CheckSelfIncOrDecOfFatPtr(SubExpr)) - Cond = ExprError(); - } -#endif - if (Cond.isInvalid()) { Cond = CreateRecoveryExpr(SubExpr->getBeginLoc(), SubExpr->getEndLoc(), {SubExpr}, PreferredConditionType(CK)); diff --git a/clang/lib/Sema/SemaExprMember.cpp b/clang/lib/Sema/SemaExprMember.cpp index 1615ed62b836..4d6becec6f43 100644 --- a/clang/lib/Sema/SemaExprMember.cpp +++ b/clang/lib/Sema/SemaExprMember.cpp @@ -1077,8 +1077,8 @@ Sema::BuildMemberReferenceExpr(Expr *BaseExpr, QualType BaseExprType, if (IsArrow) { #if ENABLE_BSC if (getLangOpts().BSC && getLangOpts().EnableFatPtr && - BaseType.isFatPtrRecordType()) - BaseType = BaseType.getFatPtrPointeeType(); + BaseType->isFatPtrRecordType()) + BaseType = BaseType->getFatPtrPointeeType(); else { #endif assert(BaseType->isPointerType()); @@ -1507,8 +1507,8 @@ static ExprResult LookupMemberExpr(Sema &S, LookupResult &R, BaseType = Ptr->getPointeeType(); # if ENABLE_BSC else if (S.getLangOpts().BSC && S.getLangOpts().EnableFatPtr && - BaseType.isFatPtrRecordType()) - BaseType = BaseType.getFatPtrPointeeType(); + BaseType->isFatPtrRecordType()) + BaseType = BaseType->getFatPtrPointeeType(); # endif else if (BaseType->isRecordType()) { // Recover from arrow accesses to records, e.g.: @@ -2124,8 +2124,8 @@ Sema::BuildFieldReferenceExpr(Expr *BaseExpr, bool IsArrow, # if ENABLE_BSC { if (getLangOpts().BSC && getLangOpts().EnableFatPtr && - BaseType.isFatPtrRecordType()) - BaseType = BaseType.getFatPtrPointeeType(); + BaseType->isFatPtrRecordType()) + BaseType = BaseType->getFatPtrPointeeType(); else # endif BaseType = BaseType->castAs()->getPointeeType(); diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index 2cfb27547633..5599d29754fe 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -2017,13 +2017,6 @@ static bool IsStandardConversion(Sema &S, Expr* From, QualType ToType, if (TD == S.TryDesugarTrait(FromType)) return true; } - if (S.getLangOpts().EnableFatPtr) { - if (FromType.isFatPtrType() && ToType.isFatPtrType() && - S.Context.hasSameType(FromType.getFatPtrPointeeType(), - ToType.getFatPtrPointeeType())) { - return true; - } - } } #endif diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp index 13f9d49d1c17..64a83ad41060 100644 --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -1752,13 +1752,6 @@ Sema::ActOnDoStmt(SourceLocation DoLoc, Stmt *Body, ExprResult CondResult = CheckBooleanCondition(DoLoc, Cond); if (CondResult.isInvalid()) return StmtError(); - -#if ENABLE_BSC - if (getLangOpts().BSC && getLangOpts().EnableFatPtr) { - if (!CheckSelfIncOrDecOfFatPtr(Cond)) - return StmtError(); - } -#endif Cond = CondResult.get(); CondResult = ActOnFinishFullExpr(Cond, DoLoc, /*DiscardedValue*/ false); diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index e4c4c48200dd..5882e115de86 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -1999,22 +1999,13 @@ QualType Sema::BuildQualifiedType(QualType T, SourceLocation Loc, Qs.removeVolatile(); } #if ENABLE_BSC - if (getLangOpts().BSC) { - if (Qs.hasBorrow() && !T->isPointerType()) { + if (getLangOpts().BSC && !T->isPointerType()) { + if (Qs.hasBorrow()) Diag(DS ? DS->getBorrowSpecLoc() : Loc, - diag::err_typecheck_invalid_borrow_not_pointer) - << T; - } - if (Qs.hasFat()) { - if (!T->isPointerType()) { - Diag(DS ? DS->getFatSpecLoc() : Loc, - diag::err_typecheck_invalid_fat_not_pointer); - } - if (T->isFunctionPointerType()) { - Diag(DS ? DS->getFatSpecLoc() : Loc, - diag::err_typecheck_invalid_fat_not_function_pointer); - } - } + diag::err_typecheck_invalid_borrow_not_pointer) << T; + if (Qs.hasFat()) + Diag(DS ? DS->getFatSpecLoc() : Loc, + diag::err_typecheck_invalid_fat_not_pointer); } #endif diff --git a/clang/test/BSC/Negative/FatPtr/addr_fat.cbs b/clang/test/BSC/Negative/FatPtr/addr_fat.cbs index c9c3f31b38d8..09c1b718876a 100644 --- a/clang/test/BSC/Negative/FatPtr/addr_fat.cbs +++ b/clang/test/BSC/Negative/FatPtr/addr_fat.cbs @@ -6,23 +6,23 @@ void test1() { struct S *p2; int *fat p3 = &fat p1->a; int *fat p4 = &fat p2->a;// expected-error {{'&fat' on member of no-fat pointer is not allowed}} - float *fat p5 = &fat p1->a;// expected-error {{incompatible fat types, cannot implicitly cast 'int *fat' to 'float *fat'}} + float *fat p5 = &fat p1->a;// expected-error {{incompatible fat types, cannot cast 'int *fat' to 'float *fat'}} float *fat p6 = (float *fat)&fat p1->a; int *fat p7; int *p8; int *fat p9 = &fat p7[1]; int *fat p10 = &fat p8[1]; // expected-error {{'&fat' on member of no-fat pointer is not allowed}} - float *fat p11 = &fat p7[1];// expected-error {{incompatible fat types, cannot implicitly cast 'int *fat' to 'float *fat'}} + float *fat p11 = &fat p7[1];// expected-error {{incompatible fat types, cannot cast 'int *fat' to 'float *fat'}} float *fat p12 = (float *fat)&fat p7[1]; struct S s; int arr[10]; int *fat p13 = &fat s.a; - float *fat p14 = &fat s.a; // expected-error {{incompatible fat types, cannot implicitly cast 'int *fat' to 'float *fat'}} + float *fat p14 = &fat s.a; // expected-error {{incompatible fat types, cannot cast 'int *fat' to 'float *fat'}} float *fat p15 = (float *fat)&fat s.a; int *fat p16 = &fat arr[5]; - float *fat p17 = &fat arr[5];// expected-error {{incompatible fat types, cannot implicitly cast 'int *fat' to 'float *fat'}} + float *fat p17 = &fat arr[5];// expected-error {{incompatible fat types, cannot cast 'int *fat' to 'float *fat'}} float *fat p18 = (float *fat)&fat arr[5]; } diff --git a/clang/test/BSC/Negative/FatPtr/fat_explicit_cast.cbs b/clang/test/BSC/Negative/FatPtr/fat_cast.cbs similarity index 40% rename from clang/test/BSC/Negative/FatPtr/fat_explicit_cast.cbs rename to clang/test/BSC/Negative/FatPtr/fat_cast.cbs index 970a18d4398c..50008ac52448 100644 --- a/clang/test/BSC/Negative/FatPtr/fat_explicit_cast.cbs +++ b/clang/test/BSC/Negative/FatPtr/fat_cast.cbs @@ -4,13 +4,29 @@ void foo2(int *p); int *fat foo3(); int * foo4(); -void test1() { //cast between zero and fat pointer - int * fat b1 = (int *fat)0; // expected-error {{incompatible fat types, cannot explicitly cast 'int' to 'int *fat'}} - int * fat b2 = {(int *fat)0};// expected-error {{incompatible fat types, cannot explicitly cast 'int' to 'int *fat'}} - foo1((int *fat)0); // expected-error {{incompatible fat types, cannot explicitly cast 'int' to 'int *fat'}} +typedef void (*PF1)(int *fat p); +typedef void (*PF2)(int *p); +typedef int *fat (*PF3)(); +typedef int * (*PF4)(); + +void test1() { //cast between function pointer and function + PF1 p1 = foo1; + PF1 p2 = foo2;// expected-error {{incompatible fat function pointer types, cannot cast 'PF1' (aka 'void (*)(int *fat)') to 'void (int *)'}} + PF2 p3 = foo1;// expected-error {{incompatible fat function pointer types, cannot cast 'PF2' (aka 'void (*)(int *)') to 'void (int *fat)'}} + PF2 p4 = foo2; + PF3 p5 = foo3; + PF3 p6 = foo4;// expected-error {{incompatible fat function pointer types, cannot cast 'PF3' (aka 'int *fat (*)(void)') to 'int *(void)'}} + PF4 p7 = foo3;// expected-error {{incompatible fat function pointer types, cannot cast 'PF4' (aka 'int *(*)(void)') to 'int *fat (void)'}} + PF4 p8 = foo4; +} + +void test2() { //cast between zero and fat pointer + int * fat b1 = 0; // expected-error {{incompatible fat types, cannot cast 'int' to 'int *fat'}} + int * fat b2 = {0};// expected-error {{incompatible fat types, cannot cast 'int' to 'int *fat'}} + foo1(0); // expected-error {{incompatible fat types, cannot cast 'int' to 'int *fat'}} } -void test2() { //cast between raw pointer and fat ptr +void test3() { //cast between raw pointer and fat ptr int a = 1; // explicit cast between raw pointer and fat ptr is allowed. int *fat p1 = nullptr; @@ -29,6 +45,17 @@ void test2() { //cast between raw pointer and fat ptr p4 = (const int *)p2; p3 = (int *)foo3(); foo2((int *)p1); + // implicit cast between raw pointer and fat ptr is not allowed. + // T * --> T *fat + p1 = p3; // expected-error {{incompatible fat types, cannot cast 'int *' to 'int *fat'}} + p2 = p4; // expected-error {{incompatible fat types, cannot cast 'const int *' to 'const int *fat'}} + p1 = foo4();// expected-error {{incompatible fat types, cannot cast 'int *' to 'int *fat'}} + foo1(p3);// expected-error {{incompatible fat types, cannot cast 'int *' to 'int *fat'}} + // T *fat --> T * + p3 = p1; // expected-error {{incompatible fat types, cannot cast 'int *fat' to 'int *'}} + p4 = p2; // expected-error {{incompatible fat types, cannot cast 'const int *fat' to 'const int *'}} + p3 = foo3();// expected-error {{incompatible fat types, cannot cast 'int *fat' to 'int *'}} + foo2(p1);// expected-error {{incompatible fat types, cannot cast 'int *fat' to 'int *'}} safe { int *fat p1 = nullptr; @@ -42,28 +69,4 @@ void test2() { //cast between raw pointer and fat ptr p3 = (int *)p1; // expected-error {{conversion from type 'int *fat' to 'int *' is forbidden in the safe zone}} p4 = (const int *)p2; // expected-error {{conversion from type 'const int *fat' to 'const int *' is forbidden in the safe zone}} } -} - -void test3() { - int *fat p1 = nullptr; - float *fat p2 = nullptr; - int * p3 = nullptr; - float * p4 = nullptr; - p1 = (int *fat)p2; - p1 = (int *fat)p4; - p3 = (int *)p2; - p3 = (int *)p4; -} - -void foo5(int ** p); -void foo6(int *fat * p); -void test4() { - int *fat p = nullptr; - foo5((int **)&p); // expected-error {{incompatible fat types, cannot explicitly cast 'int *fat *' to 'int **'}} - int *fat *p1 = nullptr; - int **p2 = nullptr; - p1 = (int *fat *)p2;// expected-error {{incompatible fat types, cannot explicitly cast 'int **' to 'int *fat *'}} - p2 = (int **)p1;// expected-error {{incompatible fat types, cannot explicitly cast 'int *fat *' to 'int **'}} - foo5((int **)p1);// expected-error {{incompatible fat types, cannot explicitly cast 'int *fat *' to 'int **'}} - foo6((int *fat *)p2);// expected-error {{incompatible fat types, cannot explicitly cast 'int **' to 'int *fat *'}} } \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/fat_func_decl_several_times.cbs b/clang/test/BSC/Negative/FatPtr/fat_func_decl_several_times.cbs deleted file mode 100644 index 1c705bf1af81..000000000000 --- a/clang/test/BSC/Negative/FatPtr/fat_func_decl_several_times.cbs +++ /dev/null @@ -1,29 +0,0 @@ -// RUN: %clang_cc1 -verify %s - -void foo1(int *fat p);// expected-note {{previous declaration is here}} -void foo1(int *p); // expected-error {{conflicting types for 'foo1'}} - -void foo2(T *fat a);// expected-note {{previous declaration is here}} -void foo2(T *p); // expected-error {{conflicting types for 'foo2'}} - -int *fat foo3();// expected-note {{previous declaration is here}} -int *foo3(); // expected-error {{conflicting types for 'foo3'}} - -T *fat foo4();// expected-note {{previous declaration is here}} -T *foo4(); // expected-error {{conflicting types for 'foo4'}} - -void foo5(int *fat p);// expected-note {{previous declaration is here}} -void foo5(float *fat p);// expected-error {{conflicting types for 'foo5'}} - -void foo6(int *fat p);// expected-note {{previous declaration is here}} -void foo6(const int *fat p);// expected-error {{conflicting types for 'foo6'}} - -typedef struct S { - int *fat p; -} SS; -void foo7(struct S *fat p); -void foo7(SS *fat p); - -typedef int *fat Int; -void foo8(int *fat p); -void foo8(Int p); \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/fat_implicit_cast.cbs b/clang/test/BSC/Negative/FatPtr/fat_implicit_cast.cbs deleted file mode 100644 index 56d1eec8a217..000000000000 --- a/clang/test/BSC/Negative/FatPtr/fat_implicit_cast.cbs +++ /dev/null @@ -1,73 +0,0 @@ -// RUN: %clang_cc1 -verify %s -void foo1(int *fat p); -void foo2(int *p); -int *fat foo3(); -int * foo4(); - -int *fat test1() { //cast between zero and fat pointer - int *fat b1 = 0; // expected-error {{incompatible fat types, cannot implicitly cast 'int' to 'int *fat'}} - int *fat b2 = {0};// expected-error {{incompatible fat types, cannot implicitly cast 'int' to 'int *fat'}} - foo1(0); // expected-error {{incompatible fat types, cannot implicitly cast 'int' to 'int *fat'}} - return 0; // expected-error {{incompatible fat types, cannot implicitly cast 'int' to 'int *fat'}} -} - -void test2() { //cast between raw pointer and fat ptr - int a = 1; - int *fat p1 = nullptr; - const int *fat p2 = nullptr; - int *p3 = &a; - const int *p4 = &a; - // implicit cast between raw pointer and fat ptr is not allowed. - // T * --> T *fat ERROR - p1 = p3; // expected-error {{incompatible fat types, cannot implicitly cast 'int *' to 'int *fat'}} - p2 = p4; // expected-error {{incompatible fat types, cannot implicitly cast 'const int *' to 'const int *fat'}} - p1 = foo4();// expected-error {{incompatible fat types, cannot implicitly cast 'int *' to 'int *fat'}} - foo1(p3);// expected-error {{incompatible fat types, cannot implicitly cast 'int *' to 'int *fat'}} - // T *fat --> T * ERROR - p3 = p1; // expected-error {{incompatible fat types, cannot implicitly cast 'int *fat' to 'int *'}} - p4 = p2; // expected-error {{incompatible fat types, cannot implicitly cast 'const int *fat' to 'const int *'}} - p3 = foo3();// expected-error {{incompatible fat types, cannot implicitly cast 'int *fat' to 'int *'}} - foo2(p1);// expected-error {{incompatible fat types, cannot implicitly cast 'int *fat' to 'int *'}} -} - -void test3() { //cast between fat pointer and fat ptr - int a = 1; - int *fat p1 = nullptr; - const int *fat p2 = nullptr; - float *fat p3 = nullptr; - const float *fat p4 = nullptr; - // implicit cast between fat pointer with different pointee type is not allowed. - // T1 *fat --> T2 *fat ERROR - p1 = p3; // expected-error {{incompatible fat types, cannot implicitly cast 'float *fat' to 'int *fat'}} - p2 = p4; // expected-error {{incompatible fat types, cannot implicitly cast 'const float *fat' to 'const int *fat'}} - foo1(p3);// expected-error {{incompatible fat types, cannot implicitly cast 'float *fat' to 'int *fat'}} - p3 = foo3();// expected-error {{incompatible fat types, cannot implicitly cast 'int *fat' to 'float *fat'}} -} - -void test4() { - int a = 1; - int *fat p1 = nullptr; - const int *fat p2 = nullptr; - // T *fat --> const T *fat OK - p2 = p1; - p2 = foo3(); - // const T *fat --> T *fat ERROR - p1 = p2; // expected-error {{incompatible fat types, cannot implicitly cast 'const int *fat' to 'int *fat'}} - foo1(p2); // expected-error {{incompatible fat types, cannot implicitly cast 'const int *fat' to 'int *fat'}} -} - -void foo5(int ** p); -void foo6(int *fat* p); -void test5() { - int *fat *p1 = nullptr; - int **p2 = nullptr; - p1 = p2;// expected-error {{incompatible fat types, cannot implicitly cast 'int **' to 'int *fat *'}} - p2 = p1;// expected-error {{incompatible fat types, cannot implicitly cast 'int *fat *' to 'int **'}} - foo5(p1); // expected-error {{incompatible fat types, cannot implicitly cast 'int *fat *' to 'int **'}} - foo6(p2); // expected-error {{incompatible fat types, cannot implicitly cast 'int **' to 'int *fat *'}} - - int *fat p3; - int *p4; - foo5(&p3); // expected-error {{incompatible fat types, cannot implicitly cast 'int *fat *' to 'int **'}} - foo6(&p4);// expected-error {{incompatible fat types, cannot implicitly cast 'int **' to 'int *fat *'}} -} \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/fat_qulifier_only_for_pointer.cbs b/clang/test/BSC/Negative/FatPtr/fat_qulifier_only_for_pointer.cbs index 2ef4bd8a7e41..3006aa21235d 100644 --- a/clang/test/BSC/Negative/FatPtr/fat_qulifier_only_for_pointer.cbs +++ b/clang/test/BSC/Negative/FatPtr/fat_qulifier_only_for_pointer.cbs @@ -15,6 +15,7 @@ void f3(T fat a) {} // expected-error {{only pointer type can be qualified by void f4(int *fat a) {} void f5(T *fat a) {} + void test() { int fat b1; // expected-error {{only pointer type can be qualified by fat}} const int fat b2; // expected-error {{only pointer type can be qualified by fat}} diff --git a/clang/test/BSC/Negative/FatPtr/func_ptr_has_fat.cbs b/clang/test/BSC/Negative/FatPtr/func_ptr_has_fat.cbs deleted file mode 100644 index 2e2e1f07225e..000000000000 --- a/clang/test/BSC/Negative/FatPtr/func_ptr_has_fat.cbs +++ /dev/null @@ -1,23 +0,0 @@ -// RUN: %clang_cc1 -verify %s -void foo1(int *fat p); -void foo2(int *p); -int *fat foo3(); -int * foo4(); - -typedef void (*fat PF)();// expected-error {{function pointer type cannot be qualified by fat}} - -typedef void (*PF1)(int *fat p); -typedef void (*PF2)(int *p); -typedef int *fat (*PF3)(); -typedef int * (*PF4)(); - -void test() { //cast between function pointer and function - PF1 p1 = foo1; - PF1 p2 = foo2;// expected-error {{incompatible fat function pointer types, cannot cast 'PF1' (aka 'void (*)(int *fat)') to 'void (int *)'}} - PF2 p3 = foo1;// expected-error {{incompatible fat function pointer types, cannot cast 'PF2' (aka 'void (*)(int *)') to 'void (int *fat)'}} - PF2 p4 = foo2; - PF3 p5 = foo3; - PF3 p6 = foo4;// expected-error {{incompatible fat function pointer types, cannot cast 'PF3' (aka 'int *fat (*)(void)') to 'int *(void)'}} - PF4 p7 = foo3;// expected-error {{incompatible fat function pointer types, cannot cast 'PF4' (aka 'int *(*)(void)') to 'int *fat (void)'}} - PF4 p8 = foo4; -} \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/self_inc_or_dec_of_fat_ptr.cbs b/clang/test/BSC/Negative/FatPtr/self_inc_or_dec_of_fat_ptr.cbs deleted file mode 100644 index ff136be63880..000000000000 --- a/clang/test/BSC/Negative/FatPtr/self_inc_or_dec_of_fat_ptr.cbs +++ /dev/null @@ -1,22 +0,0 @@ -// RUN: %clang_cc1 -enable-fat-ptr -verify %s - -void test() { - int arr[3] = { 0, 1, 2 }; - int *fat p = &fat arr[0]; - int a = 10; - if (*p++ > 0) { // expected-error {{increment or decrement operation of fat pointer is not allowed here}} - a = *p++ == 1 ? *p++ : *++p; /* expected-error {{increment or decrement operation of fat pointer is not allowed here}} - expected-error {{increment or decrement operation of fat pointer is not allowed here}} - expected-error {{increment or decrement operation of fat pointer is not allowed here}} - */ - } else { - a = *++p == 1 ? *p-- : *--p; /* expected-error {{increment or decrement operation of fat pointer is not allowed here}} - expected-error {{increment or decrement operation of fat pointer is not allowed here}} - expected-error {{increment or decrement operation of fat pointer is not allowed here}} - */ - } - switch (*p++) {} // expected-error {{increment or decrement operation of fat pointer is not allowed here}} - while (*p++ > 0) {}// expected-error {{increment or decrement operation of fat pointer is not allowed here}} - do {} while (*p++ > 0);// expected-error {{increment or decrement operation of fat pointer is not allowed here}} - for (; *p++ > 0; p++) {}// expected-error {{increment or decrement operation of fat pointer is not allowed here}} -} \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/global_array/array_out_of_bounds1.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/global_array/array_out_of_bounds1.cbs index bcd766bdb6e4..5594d6b6d211 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/global_array/array_out_of_bounds1.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/global_array/array_out_of_bounds1.cbs @@ -3,11 +3,10 @@ #include -CHECKED int arr[3]; +__attribute__((fat_ptr_checked)) int arr[3]; int main() { int *fat p = &fat arr[3]; *p = 10; return 0; } -// CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/global_array/array_out_of_bounds1.cbs:[[@LINE-4]] in main \ No newline at end of file +// CHECK: pointer offset exceed the allocation size! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/global_array/array_out_of_bounds2.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/global_array/array_out_of_bounds2.cbs index 5b260692ffc0..2d8b7aae4bb6 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/global_array/array_out_of_bounds2.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/global_array/array_out_of_bounds2.cbs @@ -11,5 +11,4 @@ int main() { *p1 = 10; return 0; } -// CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/global_array/array_out_of_bounds2.cbs:[[@LINE-4]] in main \ No newline at end of file +// CHECK: pointer offset exceed the allocation size! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds1.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds1.cbs index 975c7550754f..43d004ddc475 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds1.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds1.cbs @@ -4,9 +4,8 @@ #include int main() { - int *fat p = checked_malloc(3 * sizeof(int)); + int *fat p = checked_malloc(3 * sizeof(int)); int a = p[3]; return 0; } -// CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds1.cbs:[[@LINE-4]] in main \ No newline at end of file +// CHECK: pointer offset exceed the allocation size! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds10.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds10.cbs deleted file mode 100644 index 61354828836e..000000000000 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds10.cbs +++ /dev/null @@ -1,16 +0,0 @@ -// RUN: %clang -enable-fat-ptr %s -o %t.output -// RUN: not %t.output 2>&1 | FileCheck %s - -#include - -int main() { - int *fat p = nullptr; - if (!p) { - p = checked_malloc(sizeof(int)); - p = p + 1; - *p = 10; - } - return 0; -} -// CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds10.cbs:[[@LINE-5]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds11.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds11.cbs deleted file mode 100644 index 2d354b2f52cf..000000000000 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds11.cbs +++ /dev/null @@ -1,17 +0,0 @@ -// RUN: %clang -enable-fat-ptr %s -o %t.output -// RUN: not %t.output 2>&1 | FileCheck %s - -#include - -int main() { - int *fat p1 = checked_malloc(sizeof(int)); - int *fat p2 = p1; - if (p1 == p2) { - p2 = checked_malloc(sizeof(int)); - p2++; - *p2 = 10; - } - return 0; -} -// CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds11.cbs:[[@LINE-5]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds12.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds12.cbs deleted file mode 100644 index f905d5644648..000000000000 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds12.cbs +++ /dev/null @@ -1,15 +0,0 @@ -// RUN: %clang -enable-fat-ptr %s -o %t.output -// RUN: not %t.output 2>&1 | FileCheck %s - -#include - -int main() { - int *fat p1 = nullptr; - int *fat p2 = p1 ? nullptr : checked_malloc(sizeof(int) * 2); - *p2 = 0; - int *fat p3 = *p2 ? p1 : p2; - p3[2] = 10; - return 0; -} -// CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds12.cbs:[[@LINE-4]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds13.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds13.cbs deleted file mode 100644 index a6cdfadb2e08..000000000000 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds13.cbs +++ /dev/null @@ -1,16 +0,0 @@ -// RUN: %clang -enable-fat-ptr %s -o %t.output -// RUN: not %t.output 2>&1 | FileCheck %s - -#include - -int main() { - int *fat p = checked_malloc(sizeof(int) * 10); - for (int i = 0; i < 10; i++) { - p[i] = i - 5; - } - for ( ; *p < 5; p++) { - *p = 10; - } -} -// CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds13.cbs:[[@LINE-5]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds14.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds14.cbs deleted file mode 100644 index 0a550df0117d..000000000000 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds14.cbs +++ /dev/null @@ -1,13 +0,0 @@ -// RUN: %clang -enable-fat-ptr %s -o %t.output -// RUN: not %t.output 2>&1 | FileCheck %s - -#include - -int main() { - int *fat p = checked_malloc(sizeof(int) * 10); - p += 10; - *p = 10; - return 0; -} -// CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds14.cbs:[[@LINE-4]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds15.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds15.cbs deleted file mode 100644 index 281f9d9c6dd0..000000000000 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds15.cbs +++ /dev/null @@ -1,15 +0,0 @@ -// RUN: %clang -enable-fat-ptr %s -o %t.output -// RUN: not %t.output 2>&1 | FileCheck %s - -#include - -int main() { - int *fat *fat p = checked_malloc(sizeof(int *fat) * 10); - p[9] = checked_malloc(sizeof(int) * 10); - p += 9; - *p += 10; - **p = 10; - return 0; -} -// CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds15.cbs:[[@LINE-4]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds16.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds16.cbs deleted file mode 100644 index 4a4060a9a2b8..000000000000 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds16.cbs +++ /dev/null @@ -1,14 +0,0 @@ -// RUN: %clang -enable-fat-ptr %s -o %t.output -// RUN: not %t.output 2>&1 | FileCheck %s - -#include - -int main() { - int *fat p = checked_malloc(sizeof(int) * 10); - p += 9; - *p++ = 9; - *p++ = 10; - return 0; -} -// CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds16.cbs:[[@LINE-4]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds17.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds17.cbs deleted file mode 100644 index 7259b6793597..000000000000 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds17.cbs +++ /dev/null @@ -1,18 +0,0 @@ -// RUN: %clang -enable-fat-ptr %s -o %t.output -// RUN: not %t.output 2>&1 | FileCheck %s - -#include - -typedef void (*Foo) (int *fat); -void bar(int *fat p) { - *p = 10; -} -Foo foo = bar; - -int main() { - int *fat p = checked_malloc(sizeof(int) * 10); - int *fat p1 = p + 10; - foo(p1); -} -// CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds17.cbs:[[@LINE-10]] in bar \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds18.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds18.cbs deleted file mode 100644 index 9e5dcc4c2519..000000000000 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds18.cbs +++ /dev/null @@ -1,18 +0,0 @@ -// RUN: %clang -enable-fat-ptr %s -o %t.output -// RUN: not %t.output 2>&1 | FileCheck %s - -#include - -int main() { - int *fat p = checked_malloc(sizeof(int) * 3); - p[0] = 0; - p[1] = 1; - p[2] = 2; - int *fat q = checked_malloc(sizeof(int) * 3); - for (; *p < 3; p++) { - *q = *p; - q++; - } -} -// CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds18.cbs:[[@LINE-6]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds19.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds19.cbs deleted file mode 100644 index 64f0f702afa6..000000000000 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds19.cbs +++ /dev/null @@ -1,19 +0,0 @@ -// RUN: %clang -enable-fat-ptr %s -o %t.output -// RUN: not %t.output 2>&1 | FileCheck %s - -#include - -int main() { - int *fat p = checked_malloc(sizeof(int) * 3); - p[0] = 0; - p[1] = 1; - p[2] = 2; - int *fat q = checked_malloc(sizeof(int) * 3); - while (*p < 3) { - *q = *p; - p++; - q++; - } -} -// CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds19.cbs:[[@LINE-7]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds2.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds2.cbs index 51835b6ee0e2..c3b97993ee12 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds2.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds2.cbs @@ -4,9 +4,8 @@ #include struct S { int *fat p; }; int main() { - struct S s = { checked_malloc(3 * sizeof(int)) }; + struct S s = { checked_malloc(3 * sizeof(int)) }; int a = s.p[3]; return 0; } -// CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds2.cbs:[[@LINE-4]] in main \ No newline at end of file +// CHECK: pointer offset exceed the allocation size! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds3.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds3.cbs index 26e2f6ce7375..910359d28473 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds3.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds3.cbs @@ -4,10 +4,9 @@ #include int main() { - int *fat p = checked_malloc(3 * sizeof(int)); + int *fat p = checked_malloc(3 * sizeof(int)); int *fat p1 = p + 3; int a = *p1; return 0; } -// CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds3.cbs:[[@LINE-4]] in main \ No newline at end of file +// CHECK: pointer offset exceed the allocation size! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds4.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds4.cbs index 7b7305318d45..9a6cbe7ba543 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds4.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds4.cbs @@ -5,10 +5,9 @@ struct S { int a; }; int main() { - struct S *fat p = checked_malloc(sizeof(struct S)); + struct S *fat p = checked_malloc(sizeof(struct S)); struct S *fat p1 = p + 1; p1->a = 10; return 0; } -// CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds4.cbs:[[@LINE-4]] in main \ No newline at end of file +// CHECK: pointer offset exceed the allocation size! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds5.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds5.cbs index b80177c9e6ae..8267ee6635cd 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds5.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds5.cbs @@ -5,10 +5,9 @@ struct S { int a; int arr[3]; }; int main() { - struct S *fat p1 = checked_malloc(sizeof(struct S) * 2); + struct S *fat p1 = checked_malloc(sizeof(struct S) * 2); struct S *fat p2 = p1 + 1; int a = p2->arr[3]; return 0; } -// CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds5.cbs:[[@LINE-4]] in main \ No newline at end of file +// CHECK: pointer offset exceed the allocation size! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds6.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds6.cbs index cd4c9e508722..dedb1e9f8d70 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds6.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds6.cbs @@ -5,8 +5,7 @@ struct S { int arr[3]; }; int main() { - struct S *fat p = checked_malloc(sizeof(struct S)); + struct S *fat p = checked_malloc(sizeof(struct S)); int a = p->arr[3]; } -// CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds6.cbs:[[@LINE-3]] in main \ No newline at end of file +// CHECK: pointer offset exceed the allocation size! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds7.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds7.cbs deleted file mode 100644 index 42b814927dcc..000000000000 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds7.cbs +++ /dev/null @@ -1,17 +0,0 @@ -// RUN: %clang -enable-fat-ptr %s -o %t.output -// RUN: not %t.output 2>&1 | FileCheck %s - -#include - -int main() { - int *fat p = checked_malloc(sizeof(int) * 10); - for (int i = 0; i < 10; i++) { - p[i] = i - 5; - } - while (*p < 5) { - p = p + 1; - } - return 0; -} -// CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds7.cbs:[[@LINE-6]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds8.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds8.cbs deleted file mode 100644 index f29da60932a4..000000000000 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds8.cbs +++ /dev/null @@ -1,17 +0,0 @@ -// RUN: %clang -enable-fat-ptr %s -o %t.output -// RUN: not %t.output 2>&1 | FileCheck %s - -#include - -int main() { - int *fat p = checked_malloc(sizeof(int) * 10); - for (int i = 0; i < 10; i++) { - p[i] = i - 5; - } - do { - p = p + 1; - } while (*p < 5); - return 0; -} -// CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds8.cbs:[[@LINE-4]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds9.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds9.cbs deleted file mode 100644 index 904126abeadb..000000000000 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds9.cbs +++ /dev/null @@ -1,15 +0,0 @@ -// RUN: %clang -enable-fat-ptr %s -o %t.output -// RUN: not %t.output 2>&1 | FileCheck %s - -#include - -int main() { - int *fat p = checked_malloc(sizeof(int)); - if (p) { - p = p + 1; - *p = 10; - } - return 0; -} -// CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds9.cbs:[[@LINE-5]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/buffer_overflow.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/buffer_overflow.cbs index a40d1bc46c3b..474e6c77a4ab 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/buffer_overflow.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/buffer_overflow.cbs @@ -14,9 +14,8 @@ char *fat fat_strcpy(char *fat dest, const char *src) { } int main(int argc, char **argv) { - char *fat buf = checked_malloc(sizeof(char)*BUFSIZE); + char *fat buf = checked_malloc(sizeof(char)*BUFSIZE); fat_strcpy(buf, argv[1]); return 0; } -// CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/buffer_overflow.cbs:[[@LINE-13]] in fat_strcpy \ No newline at end of file +// CHECK: pointer offset exceed the allocation size! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free1.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free1.cbs index a9c67262df80..b7cf5d4ce5e7 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free1.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free1.cbs @@ -4,10 +4,10 @@ #include int main() { - int *fat p = checked_malloc(sizeof(int)); - checked_free(p); - checked_free(p); + int *fat p = checked_malloc(sizeof(int)); + checked_free(p); + checked_free(p); return 0; } -// CHECK: Error: Cannot free this pointer because the allocation may have been freed! \ No newline at end of file +// CHECK: version number error when free! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free2.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free2.cbs index 64a65afc5220..adb31c07c81e 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free2.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free2.cbs @@ -4,11 +4,11 @@ #include int main() { - int *fat p1 = checked_malloc(sizeof(int)); + int *fat p1 = checked_malloc(sizeof(int)); int *fat p2 = p1; - checked_free(p1); - checked_free(p2); + checked_free(p1); + checked_free(p2); return 0; } -// CHECK: Error: Cannot free this pointer because the allocation may have been freed! \ No newline at end of file +// CHECK: version number error when free! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free3.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free3.cbs index 26db032c3e72..a5a4b818647f 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free3.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free3.cbs @@ -5,12 +5,12 @@ struct G { int *fat a; }; int main() { - struct G g = { .a = checked_malloc(sizeof(int)) }; - struct G *fat p = checked_malloc(sizeof(struct G)); + struct G g = { .a = checked_malloc(sizeof(int)) }; + struct G *fat p = checked_malloc(sizeof(struct G)); p->a = g.a; - checked_free(p->a); - checked_free(g.a); + checked_free(p->a); + checked_free(g.a); return 0; } -// CHECK: Error: Cannot free this pointer because the allocation may have been freed! \ No newline at end of file +// CHECK: version number error when free! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free4.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free4.cbs index 8d294688d9f8..f9ddbb207a9c 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free4.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free4.cbs @@ -4,15 +4,15 @@ #include int *fat test() { - int *fat p = checked_malloc(sizeof(int)); - checked_free(p); + int *fat p = checked_malloc(sizeof(int)); + checked_free(p); return p; } int main() { int *fat p = test(); - checked_free(p); + checked_free(p); return 0; } -// CHECK: Error: Cannot free this pointer because the allocation may have been freed! \ No newline at end of file +// CHECK: version number error when free! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free1.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free1.cbs index fbf926b906af..42fc9676d354 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free1.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free1.cbs @@ -4,12 +4,11 @@ #include int main() { - int *fat p = checked_malloc(sizeof(int)); + int *fat p = checked_malloc(sizeof(int)); *p = 10; - checked_free(p); + checked_free(p); *p = 10; return 0; } -// CHECK: Error: Cannot use this pointer because the allocation may have been freed or reseored! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free1.cbs:[[@LINE-5]] in main \ No newline at end of file +// CHECK: version number error! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free2.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free2.cbs index 9faa09e3f4c3..31ff1217ea98 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free2.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free2.cbs @@ -5,12 +5,11 @@ struct S { int a; }; int main() { - struct S *fat p = checked_malloc(sizeof(struct S)); + struct S *fat p = checked_malloc(sizeof(struct S)); p->a = 10; - checked_free(p); + checked_free(p); p->a = 10; return 0; } -// CHECK: Error: Cannot use this pointer because the allocation may have been freed or reseored! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free2.cbs:[[@LINE-5]] in main \ No newline at end of file +// CHECK: version number error! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free3.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free3.cbs index fe79ee91d3f8..3c9ef85f86d0 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free3.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free3.cbs @@ -3,9 +3,9 @@ #include -int *fat test() { - int *fat p = checked_malloc(sizeof(int)); - checked_free(p); +int *fat test() __attribute__((fat_ptr_checked)) { + int *fat p = checked_malloc(sizeof(int)); + checked_free(p); return p; } @@ -14,5 +14,4 @@ int main() { *p = 10; return 0; } -// CHECK: Error: Cannot use this pointer because the allocation may have been freed or reseored! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free3.cbs:[[@LINE-4]] in main \ No newline at end of file +// CHECK: version number error! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free4.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free4.cbs index 08ce74e36d4d..e0227f63c4db 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free4.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free4.cbs @@ -5,12 +5,11 @@ struct G { int *fat a; }; int main() { - struct G g = { .a = checked_malloc(sizeof(int)) }; - struct G *fat p = checked_malloc(sizeof(struct G)); + struct G g = { .a = checked_malloc(sizeof(int)) }; + struct G *fat p = checked_malloc(sizeof(struct G)); p->a = g.a; - checked_free(p->a); + checked_free(p->a); *g.a = 10; return 0; } -// CHECK: Error: Cannot use this pointer because the allocation may have been freed or reseored! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free4.cbs:[[@LINE-4]] in main \ No newline at end of file +// CHECK: version number error! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free5.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free5.cbs index 1c37a3ec2e69..0c420fce218d 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free5.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free5.cbs @@ -16,15 +16,14 @@ char *fat fat_strncpy(char *fat dest, const char *src, size_t n) { } int main() { - char *fat buf1 = checked_malloc(BUFSIZE); - checked_free(buf1); + char *fat buf1 = checked_malloc(BUFSIZE); + checked_free(buf1); - char *fat buf2 = checked_malloc(BUFSIZE); + char *fat buf2 = checked_malloc(BUFSIZE); memset((char *)buf2, 0, BUFSIZE); fat_strncpy(buf1, "hack", 5); - checked_free(buf2); + checked_free(buf2); return 0; } -// CHECK: Error: Cannot use this pointer because the allocation may have been freed or reseored! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free5.cbs:[[@LINE-20]] in fat_strncpy \ No newline at end of file +// CHECK: version number error! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free6.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free6.cbs index 1159a594974c..4b00fec29811 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free6.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free6.cbs @@ -6,10 +6,9 @@ void pass_fat(int *p) {} int main() { - int *fat p = checked_malloc(sizeof(int)); - checked_free(p); + int *fat p = checked_malloc(sizeof(int)); + checked_free(p); pass_fat((int *)p); return 0; } -// CHECK: Error: Cannot use this pointer because the allocation may have been freed or reseored! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free6.cbs:[[@LINE-4]] in main \ No newline at end of file +// CHECK: version number error! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free7.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free7.cbs deleted file mode 100644 index 0e96d5e77f6f..000000000000 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free7.cbs +++ /dev/null @@ -1,15 +0,0 @@ -// RUN: %clang -enable-fat-ptr %s -o %t.output -// RUN: not %t.output 2>&1 | FileCheck %s - -#include - -int main() { - int *fat p = checked_malloc(sizeof(int)); - checked_free(p); - if (*p == 5) { - return 5; - } - return 0; -} -// CHECK: Error: Cannot use this pointer because the allocation may have been freed or reseored! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free7.cbs:[[@LINE-6]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free8.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free8.cbs deleted file mode 100644 index 04b70ea5aa83..000000000000 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free8.cbs +++ /dev/null @@ -1,17 +0,0 @@ -// RUN: %clang -enable-fat-ptr %s -o %t.output -// RUN: not %t.output 2>&1 | FileCheck %s - -#include - -int *fat p = nullptr; - -int main() { - p = checked_malloc(sizeof(int)); - checked_free(p); - if (*p == 5) { - return 5; - } - return 0; -} -// CHECK: Error: Cannot use this pointer because the allocation may have been freed or reseored! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free8.cbs:[[@LINE-6]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds1.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds1.cbs index e99f7725e1b5..07ea7ca2204c 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds1.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds1.cbs @@ -9,5 +9,4 @@ int main() { *p = 10; return 0; } -// CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds1.cbs:[[@LINE-4]] in main \ No newline at end of file +// CHECK: pointer offset exceed the allocation size! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds2.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds2.cbs index 2b1425733499..80280bf71a33 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds2.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds2.cbs @@ -10,5 +10,4 @@ int main() { *p1 = 10; return 0; } -// CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds2.cbs:[[@LINE-4]] in main \ No newline at end of file +// CHECK: pointer offset exceed the allocation size! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds3.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds3.cbs deleted file mode 100644 index 52a72be91eeb..000000000000 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds3.cbs +++ /dev/null @@ -1,14 +0,0 @@ -// RUN: %clang -enable-fat-ptr %s -o %t.output -// RUN: not %t.output 2>&1 | FileCheck %s - -#include - -int main() { - int arr1[3] = {1, 2, 3}, arr2[2] = { 1, 2 }; - int *fat p = &fat arr1[2]; - int *fat p1 = p + 1; - *p1 = 10; - return 0; -} -// CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds3.cbs:[[@LINE-4]] in main \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds4.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds4.cbs deleted file mode 100644 index 2c6abd37814b..000000000000 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds4.cbs +++ /dev/null @@ -1,14 +0,0 @@ -// RUN: %clang -enable-fat-ptr %s -o %t.output -// RUN: not %t.output 2>&1 | FileCheck %s - -#include - -int main() { - char *fat arr[2] = { (char *fat)"Hello", (char *fat)"world" }; - char *fat *fat p = &fat arr[0]; - printf("%s\n", (char *)p[2]); - return 0; -} -// CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds4.cbs:[[@LINE-4]] in main - diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/buffer_overflow.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/buffer_overflow.cbs index 610d45c170c3..d4230082860d 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/buffer_overflow.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/buffer_overflow.cbs @@ -19,5 +19,4 @@ int main(int argc, char **argv) { fat_strcpy(p, argv[1]); return 0; } -// CHECK: Error: Pointer offset exceeds the allocation size! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/buffer_overflow.cbs:[[@LINE-14]] in fat_strcpy \ No newline at end of file +// CHECK: pointer offset exceed the allocation size! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/return_local_address.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/return_local_address.cbs index c2d89d83e759..c39bf785d13b 100644 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/return_local_address.cbs +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/return_local_address.cbs @@ -14,5 +14,4 @@ int main() { *p = 10; return 0; } -// CHECK: Error: Cannot use this pointer because the allocation may have been freed or reseored! -// CHECK-NEXT: Error at: {{[^ ]*}}clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/return_local_address.cbs:[[@LINE-4]] in main \ No newline at end of file +// CHECK: version number error! \ No newline at end of file diff --git a/clang/test/BSC/Positive/FatPtr/heap.cbs b/clang/test/BSC/Positive/FatPtr/heap.cbs index f30b12beda57..5c8a83374c7e 100644 --- a/clang/test/BSC/Positive/FatPtr/heap.cbs +++ b/clang/test/BSC/Positive/FatPtr/heap.cbs @@ -5,62 +5,62 @@ #include struct S { int a; }; void test1() { - int *fat p1 = checked_malloc(sizeof(int)); + int *fat p1 = checked_malloc(sizeof(int)); *p1 = 10; - checked_free(p1); + checked_free(p1); - struct S *fat p2 = checked_malloc(sizeof(struct S)); + struct S *fat p2 = checked_malloc(sizeof(struct S)); p2->a = 10; - checked_free(p2); + checked_free(p2); } struct G { int *fat a; }; void test2() { - struct G g = { .a = checked_malloc(sizeof(int)) }; - struct G *fat p = checked_malloc(sizeof(struct G)); + struct G g = { .a = checked_malloc(sizeof(int)) }; + struct G *fat p = checked_malloc(sizeof(struct G)); *p = g; *p->a = 10; - checked_free(p->a); - checked_free(p); + checked_free(p->a); + checked_free(p); } void test3() { - int *fat p1 = checked_malloc(sizeof(int)); - int *fat p2 = checked_malloc(sizeof(int)); + int *fat p1 = checked_malloc(sizeof(int)); + int *fat p2 = checked_malloc(sizeof(int)); *p1 = *p2; - checked_free(p1); - checked_free(p2); + checked_free(p1); + checked_free(p2); } void test4() { - int *fat p1 = checked_malloc(sizeof(int)); - int *fat p2 = checked_malloc(sizeof(int)); + int *fat p1 = checked_malloc(sizeof(int)); + int *fat p2 = checked_malloc(sizeof(int)); p1 = p2; - checked_free(p1); //only free once + checked_free(p1); //only free once } void test5() { - int *fat p = checked_malloc(sizeof(int) * 3); + int *fat p = checked_malloc(sizeof(int) * 3); int a = p[2]; - checked_free(p); + checked_free(p); } void test6() { - int *fat p1 = checked_malloc(sizeof(int)); + int *fat p1 = checked_malloc(sizeof(int)); int *fat p2 = p1; int *p3 = (int *)p1; float *fat p4 = (float *fat)p1; - checked_free(p1); + checked_free(p1); } void test7() { - int *fat p = checked_malloc(3 * sizeof(int)); + int *fat p = checked_malloc(3 * sizeof(int)); int *fat p1 = p + 2; int a = *p1; } void test8(int *fat p1, int *fat p2) { - p1 = checked_malloc(3 * sizeof(int)); + p1 = checked_malloc(3 * sizeof(int)); p2 = p1 + 2; int a = *p1; int b = *p2; @@ -68,7 +68,7 @@ void test8(int *fat p1, int *fat p2) { struct K { int arr[3]; }; void test9() { - struct K *fat p1 = checked_malloc(sizeof(struct K) * 2); + struct K *fat p1 = checked_malloc(sizeof(struct K) * 2); int a = p1->arr[3]; int *fat p2 = &fat p1->arr[3]; *p2 = 10; @@ -79,25 +79,6 @@ void test9() { *p4 = 10; } -void test10() { - int *fat *fat p = checked_malloc(sizeof(int *fat) * 10); - p[9] = checked_malloc(sizeof(int) * 10); - p += 9; - *p += 9; - **p = 10; -} - -typedef void (*Foo) (int *fat); -void bar(int *fat p) { - *p = 10; -} -Foo foo = bar; -void test11() { - int *fat p = checked_malloc(sizeof(int) * 10); - int *fat p1 = p + 9; - foo(p1); -} - int main() { test1(); test2(); @@ -109,8 +90,5 @@ int main() { int *fat p1; int *fat p2; test8(p1, p2); - test9(); - test10(); - test11(); return 0; } \ No newline at end of file diff --git a/clang/test/BSC/Positive/FatPtr/multi_level_pointer.cbs b/clang/test/BSC/Positive/FatPtr/multi_level_pointer.cbs deleted file mode 100644 index 2989912aba2b..000000000000 --- a/clang/test/BSC/Positive/FatPtr/multi_level_pointer.cbs +++ /dev/null @@ -1,29 +0,0 @@ -// RUN: %clang %s -enable-fat-ptr -o %t.output -// RUN: %t.output | FileCheck %s -// expected-no-diagnostics - -#include -void test1() { - char *fat arr[2] = { (char *fat)"Hello", (char *fat)"world" }; - char *fat *fat p = &fat arr[0]; - printf("%s %s!\n", (char *)*p, (char *)p[1]); -} - -void test2() { - char *fat p = checked_malloc(sizeof(char) * 6); - p[0] = 'H'; - p[1] = 'e'; - p[2] = 'l'; - p[3] = 'l'; - p[4] = 'o'; - char *fat *p1 = &p; - printf("%s!\n", (char *)*p1); -} - -int main() { - test1(); - test2(); - return 0; -} -// CHECK: Hello world! -// CHECK-NEXT: Hello! \ No newline at end of file diff --git a/clang/test/BSC/Positive/FatPtr/perform_anaysis/deref_loop/deref_loop.cbs b/clang/test/BSC/Positive/FatPtr/perform_anaysis/deref_loop/deref_loop.cbs index 43ba61776199..ac7ec4d457e9 100644 --- a/clang/test/BSC/Positive/FatPtr/perform_anaysis/deref_loop/deref_loop.cbs +++ b/clang/test/BSC/Positive/FatPtr/perform_anaysis/deref_loop/deref_loop.cbs @@ -20,7 +20,7 @@ void test_raw(long n) { } void test_fat(long n) { - long *fat p = checked_malloc(sizeof(long)); + long *fat p = checked_malloc(sizeof(long)); while (n > 0) { *p = n; n--; diff --git a/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/bisort_bsc/bitonic.cbs b/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/bisort_bsc/bitonic.cbs index 8065f9620351..4535551ee5b1 100644 --- a/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/bisort_bsc/bitonic.cbs +++ b/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/bisort_bsc/bitonic.cbs @@ -21,6 +21,8 @@ typedef struct future_cell_int{ HANDLE *fat value; } future_cell_int; +#define NIL ((HANDLE *) 0) + int NumNodes, NDim; int random_c(int); @@ -30,11 +32,11 @@ int flag=0,foo=0; //FIXME #define LocalNewNode(h,v) \ { \ - h = checked_malloc(sizeof(HANDLE)); \ - h->value = v; \ - h->left = nullptr; \ - h->right = nullptr; \ -}; + h = checked_malloc(sizeof(HANDLE)); \ + h->value = v; \ + h->left = (HANDLE *fat)NIL; \ + h->right = (HANDLE *fat)NIL; \ + }; #define NewNode(h,v,procid) LocalNewNode(h,v) @@ -119,7 +121,7 @@ HANDLE *fat RandTree(int n, int seed, int node, int level) { h->left = f_left.value; h->right = f_right.value; } else { - h = checked_malloc(sizeof(HANDLE)); //FIXME + h = checked_malloc(sizeof(HANDLE)); //FIXME } return h; } diff --git a/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd_bsc/node.cbs b/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd_bsc/node.cbs index ad2fac0b6695..f859e368b7ae 100644 --- a/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd_bsc/node.cbs +++ b/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd_bsc/node.cbs @@ -70,7 +70,7 @@ tree_t *fat TreeAlloc (int level, int lo, int proc) { tree_t *fat new; tree_t *fat right; tree_t *fat left; - new = checked_malloc(sizeof(tree_t)); + new = checked_malloc(sizeof(tree_t)); left = TreeAlloc(level -1, lo+proc/2, proc/2); right = TreeAlloc(level-1,lo,proc/2); new->val = 1; diff --git a/clang/test/BSC/Positive/FatPtr/string_literal.cbs b/clang/test/BSC/Positive/FatPtr/string_literal.cbs deleted file mode 100644 index 7460656d2b6d..000000000000 --- a/clang/test/BSC/Positive/FatPtr/string_literal.cbs +++ /dev/null @@ -1,27 +0,0 @@ -// RUN: %clang %s -enable-fat-ptr -o %t.output -// RUN: %t.output | FileCheck %s -// expected-no-diagnostics - -#include - -void foo(const char *fat p) { - printf("%s", p); -} - -char *fat bar(char *fat p) { - return p; -} - -int main() { - char *fat p = (char *fat)"1: Hello world!\n"; - printf("%s", (char *)p); - foo((char *fat)"2: Hello world!\n"); - char *fat ss[] = { (char *fat)"Hello", (char *fat)"world"}; - printf("3: %s %s!\n", (char *)ss[0], (char *)ss[1]); - printf("4: %s %s!\n", (char *)bar(ss[0]), (char *)bar(ss[1])); - return 0; -} -// CHECK: 1: Hello world! -// CHECK-NEXT: 2: Hello world! -// CHECK-NEXT: 3: Hello world! -// CHECK-NEXT: 4: Hello world! \ No newline at end of file -- Gitee From df7a997b7e2391195ceb9bca57e4f1403a025727 Mon Sep 17 00:00:00 2001 From: zhaoxuhui Date: Fri, 21 Mar 2025 16:39:06 +0800 Subject: [PATCH 27/29] Revert "[FatPtr] Desugar the function type of the self-call Expr" This reverts commit fcdc22c9e6a16a8ff2a67b98ef99111763aafd70. --- clang/include/clang/Sema/Sema.h | 1 - clang/lib/Sema/BSC/SemaBSCDestructor.cpp | 15 - clang/lib/Sema/BSC/SemaBSCFatPtr.cpp | 65 +--- clang/lib/Sema/SemaExpr.cpp | 45 ++- .../FatPtr/nullptr_cast/nullptr_cast.cbs | 41 --- .../perform_anaysis/deref_loop/deref_loop.cbs | 39 --- .../perform_anaysis/olden/bisort/bitonic.c | 303 ----------------- .../olden/bisort_bsc/bitonic.cbs | 304 ------------------ .../perform_anaysis/olden/treeadd/node.c | 233 -------------- .../perform_anaysis/olden/treeadd/tree.h | 25 -- .../olden/treeadd_bsc/node.cbs | 193 ----------- .../olden/treeadd_bsc/tree.hbs | 29 -- .../destructor_call/destructor_call.cbs | 21 -- 13 files changed, 46 insertions(+), 1268 deletions(-) delete mode 100644 clang/test/BSC/Positive/FatPtr/nullptr_cast/nullptr_cast.cbs delete mode 100644 clang/test/BSC/Positive/FatPtr/perform_anaysis/deref_loop/deref_loop.cbs delete mode 100644 clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/bisort/bitonic.c delete mode 100644 clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/bisort_bsc/bitonic.cbs delete mode 100644 clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd/node.c delete mode 100644 clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd/tree.h delete mode 100644 clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd_bsc/node.cbs delete mode 100644 clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd_bsc/tree.hbs delete mode 100644 clang/test/BSC/Positive/SafeZone/destructor_call/destructor_call.cbs diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 642785ee11c9..1c7e7c9d7b98 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -3234,7 +3234,6 @@ public: std::pair BuildAllocationUnitForGlobalArrayVar(VarDecl *VD); // BSC Destructor related. - bool IsCallDestructorExpr(Expr *E); BSCMethodDecl *getOrInsertBSCDestructor(RecordDecl *RD); void HandleBSCDestructorBody(RecordDecl *RD, BSCMethodDecl *Destructor, std::stack InstanceFields); diff --git a/clang/lib/Sema/BSC/SemaBSCDestructor.cpp b/clang/lib/Sema/BSC/SemaBSCDestructor.cpp index 7ab37e21fc0d..fc88350e2ba8 100644 --- a/clang/lib/Sema/BSC/SemaBSCDestructor.cpp +++ b/clang/lib/Sema/BSC/SemaBSCDestructor.cpp @@ -734,21 +734,6 @@ public: } }; -bool Sema::IsCallDestructorExpr(Expr *E) { - Expr *NakedE = E->IgnoreParens(); - if (auto *CastExpr = llvm::dyn_cast(NakedE)) { - if (Expr *SubExpr = CastExpr->getSubExpr()) { - if (auto *DRE = llvm::dyn_cast(SubExpr)) { - NamedDecl *NDecl = DRE->getDecl(); - if (auto *MD = dyn_cast(NDecl)) { - return MD->isDestructor(); - } - } - } - } - return false; -} - void Sema::DesugarDestructorCall(FunctionDecl *FD) { if (!getLangOpts().BSC) return; diff --git a/clang/lib/Sema/BSC/SemaBSCFatPtr.cpp b/clang/lib/Sema/BSC/SemaBSCFatPtr.cpp index e789857a4099..41a5c1ae7b21 100644 --- a/clang/lib/Sema/BSC/SemaBSCFatPtr.cpp +++ b/clang/lib/Sema/BSC/SemaBSCFatPtr.cpp @@ -200,14 +200,12 @@ public: FatPtrFD["_new_version_number"] = nullptr; } - bool IsNullPtrExpr(Expr *E); // Entry to desugar FunctionDecl void TransformFunctionDecl(); // Entry to desugar global or local VarDecl void TransformVarDecl(VarDecl *VD); // Entry to desugar global or local RecordDecl void TransformRecordDecl(RecordDecl *RD); - void TransformSelfCallExprFunctionType(CallExpr *CE); StmtResult TransformCompoundStmt(CompoundStmt *CS, bool IsStmtExpr); StmtResult TransformCompoundStmt(CompoundStmt *CS); @@ -260,13 +258,6 @@ private: SmallVector &Comps); }; -bool TransformFatPtr::IsNullPtrExpr (Expr *E) { - if (auto *CastExpr = dyn_cast(E)) { - E = CastExpr->getSubExpr(); - } - return isa(E); -} - void TransformFatPtr::TransformFunctionDecl() { // Desugar fat qualified return type. QualType ReturnTy = @@ -300,7 +291,7 @@ void TransformFatPtr::TransformVarDecl(VarDecl *VD) { QualType QT = DesugarFatPtrType(VD->getLocation(), VD->getType()); VD->setType(QT); if (Expr *Init = VD->getInit()) { - if (QT->isFatPtrRecordType() && IsNullPtrExpr(Init)) { + if (QT->isFatPtrRecordType() && isa(Init)) { // `int *fat p = nullptr;` should be desugared to // `_FatPtr p = (_FatPtr) { nullptr, 0, 0 };` VD->setInit(BuildFatPtrCompoundLiteralExprForRawPtr(Init, QT)); @@ -641,7 +632,7 @@ ExprResult TransformFatPtr::TransformInitListExpr(InitListExpr *ILE) { StmtResult TransformFatPtr::TransformReturnStmt(ReturnStmt *RS) { Expr *RV = RS->getRetValue(); QualType ReturnQT = FD->getReturnType(); - if (ReturnQT->isFatPtrRecordType() && IsNullPtrExpr(RV)) { + if (ReturnQT->isFatPtrRecordType() && isa(RV)) { // if a function has fat pointer return type, such as `int *fat foo();` // `return nullptr;` should be desugared to // `return (_FatPtr){ nullptr, 0, 0 };` @@ -652,37 +643,18 @@ StmtResult TransformFatPtr::TransformReturnStmt(ReturnStmt *RS) { return RS; } -void TransformFatPtr::TransformSelfCallExprFunctionType(CallExpr *CE) { - QualType FQT = FD->getType(); - Expr *CalleeExpr = CE->getCallee(); - if (auto *ImplCastExpr = dyn_cast(CalleeExpr)) { - ImplCastExpr->setType(SemaRef.Context.getPointerType(FQT)); - if (Expr *subExpr = ImplCastExpr->getSubExpr()) { - if (auto *declRefExpr = dyn_cast(subExpr)) { - declRefExpr->setType(FQT); - } - } - } -} - ExprResult TransformFatPtr::TransformCallExpr(CallExpr *CE) { CE->setType(DesugarFatPtrType(CE->getBeginLoc(), CE->getType())); FunctionDecl *CalleeFD = CE->getDirectCallee(); - // For self-call Expr, desugar the function type of the calling expression - if (CalleeFD == FD) { - TransformSelfCallExprFunctionType(CE); - } for (unsigned i = 0; i < CalleeFD->getNumParams(); i++) { QualType ParamQT = CalleeFD->getParamDecl(i)->getType(); Expr *ArgE = CE->getArg(i); - if (ParamQT->isFatPtrRecordType() && IsNullPtrExpr(ArgE)) { + if (ParamQT->isFatPtrRecordType() && isa(ArgE)) { // if a function has fat pointer parameter, such as // `void foo(int *fat p);` // `foo(nullptr)` should be desugared to // `foo((_FatPtr){ nullptr, 0, 0 })` - Expr *CLE = BuildFatPtrCompoundLiteralExprForRawPtr(ArgE, ParamQT); - CLE = SemaRef.DefaultFunctionArrayLvalueConversion(CLE).get(); - CE->setArg(i, CLE); + CE->setArg(i, BuildFatPtrCompoundLiteralExprForRawPtr(ArgE, ParamQT)); } else CE->setArg(i, BaseTransform::TransformExpr(ArgE).get()); } @@ -801,18 +773,13 @@ ExprResult TransformFatPtr::TransformBinaryOperator(BinaryOperator *BO) { // `(_FatPtr) { p.raw_ptr + 1, p.key_version, p.offset + 1 * sizeof(int) }` return HandleBOFatPtrAddOrSubInteger(BO); } else if (BO->isAssignmentOp() && LHSQT.isFatPtrType() && - IsNullPtrExpr(RHS)) { + isa(RHS)) { // `p = nullptr;` should be desugared to // `p = (_FatPtr){ nullptr, 0, 0 };` LHSQT = DesugarFatPtrType(SLoc, LHSQT); BO->setLHS(BaseTransform::TransformExpr(LHS).get()); BO->setRHS(BuildFatPtrCompoundLiteralExprForRawPtr(RHS, LHSQT)); return BO; - } else if (BO->isEqualityOp() && LHSQT.isFatPtrType() && IsNullPtrExpr(RHS)) { - // `p1 == p2` should be desugared to `p1.ptr == nullptr` - DesugarToMemberPtr = true; - BO->setLHS(BaseTransform::TransformExpr(BO->getLHS()).get()); - return BO; } BO->setLHS(BaseTransform::TransformExpr(LHS).get()); BO->setRHS(BaseTransform::TransformExpr(RHS).get()); @@ -1248,11 +1215,6 @@ MemberExpr *TransformFatPtr::BuildMemberForFatPtr(Expr *FatPtrBaseE, Expr *TransformFatPtr::BuildFatPtrCompoundLiteralExprForRawPtr( Expr *RawPtrE, QualType QT) { - if (auto *ICE = dyn_cast(RawPtrE)) { - if (isa(ICE->getSubExpr())) { - RawPtrE = ICE->getSubExpr(); - } - } SourceLocation SLoc = RawPtrE->getBeginLoc(); llvm::APInt Zero(32, 0); QualType UInt32Ty = SemaRef.Context.getIntTypeForBitwidth(32, false); @@ -1376,7 +1338,8 @@ bool Sema::CheckFatQualTypeCStyleCast(QualType LHSType, QualType RHSType) { if (IsSameType) { return true; } - if (LHSCanType.isFatPtrType() && RHSCanType.isFatPtrType()) { + if ((LHSCanType.isFatQualified() || LHSCanType->isFatPtrRecordType()) && + (RHSCanType.isFatQualified() || RHSCanType->isFatPtrRecordType())) { return true; } if (IsPointer) { @@ -1432,10 +1395,6 @@ bool Sema::CheckFatQualTypeAssignment(QualType LHSType, Expr *RHSExpr) { SourceLocation ExprLoc = RHSExpr->getBeginLoc(); bool Res = true; - // Fat pointer can be inited by nullptr. - if (LHSCanType.isFatPtrType() && isa(RHSExpr)) - return true; - if (LHSCanType.isFatQualified() || RHSCanType.isFatQualified()) { if (TraitDecl *TD = TryDesugarTrait(LHSCanType)) { if (RHSCanType->isPointerType()) { @@ -1455,18 +1414,24 @@ bool Sema::CheckFatQualTypeAssignment(QualType LHSType, Expr *RHSExpr) { } } } + + // Fat pointer can be inited by nullptr. + if (LHSCanType.isFatQualified() && isa(RHSExpr)) + return true; + if (LHSCanType->isVoidPointerType() && RHSCanType->isPointerType()) { if (LHSCanType->getPointeeType().isConstQualified() == RHSCanType->getPointeeType().isConstQualified()) return true; } + if (!Context.hasSameType(LHSCanType, RHSCanType)) Res = false; } else { Res = CheckFatQualTypeAssignment(LHSType, RHSCanType, ExprLoc); } - if ((LHSCanType.isFatPtrType() && RHSCanType.isFatPtrType()) && - !(LHSCanType.isFatQualified() && RHSCanType.isFatQualified())) + if ((LHSCanType.isFatQualified() || LHSCanType->isFatPtrRecordType()) && + (RHSCanType.isFatQualified() || RHSCanType->isFatPtrRecordType())) return true; if (!Res) { Diag(ExprLoc, diag::err_fat_qualcheck_incompatible) diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 30381de78101..1f10537f65c5 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -7128,7 +7128,7 @@ ExprResult Sema::BuildCallExpr(Scope *Scope, Expr *Fn, SourceLocation LParenLoc, #if ENABLE_BSC if (getLangOpts().BSC) { // unsafe function call is forbidden in the safe zone - if (!IsCallDestructorExpr(Fn) && IsInSafeZone() && + if ((IsInSafeZone()) && (Fn->getType()->checkFunctionProtoType(SZ_None) || Fn->getType()->checkFunctionProtoType(SZ_Unsafe))) { Diag(Fn->getBeginLoc(), diag::err_unsafe_action) @@ -10449,16 +10449,33 @@ static bool IsTraitEqualExpr(Sema &S, QualType DstType, QualType SrcType, } static bool IsFatPtrEqualExpr(Sema &S, QualType DstType, QualType SrcType) { - if (auto *Typedef = dyn_cast(DstType)) { - DstType = Typedef->getDecl()->getUnderlyingType(); - } - if (auto *Typedef = dyn_cast(SrcType)) { - SrcType = Typedef->getDecl()->getUnderlyingType(); - } - if (DstType.isFatPtrType() && - (SrcType.isFatPtrType() || SrcType->isNullPtrType())) { - return true; - } + if (DstType.getCanonicalType().isFatQualified()) + if (SrcType.getCanonicalType()->isRecordType()) + if (RecordDecl *RD = + SrcType.getCanonicalType()->getAs()->getDecl()) + if (RD->getNameAsString() == "_FatPtr") + return true; + if (SrcType.getCanonicalType().isFatQualified()) + if (DstType.getCanonicalType()->isRecordType()) + if (RecordDecl *RD = + DstType.getCanonicalType()->getAs()->getDecl()) + if (RD->getNameAsString() == "_FatPtr") + return true; + if (DstType.getCanonicalType().isFatQualified()) + if (SrcType.getCanonicalType()->isRecordType()) + if (RecordDecl *RD = + SrcType.getCanonicalType()->getAs()->getDecl()) + if (RD->getNameAsString() == "_FatPtr") + return true; + if (SrcType.getCanonicalType()->isRecordType() && + DstType.getCanonicalType()->isRecordType()) + if (RecordDecl *DstRD = + DstType.getCanonicalType()->getAs()->getDecl()) + if (DstRD->getNameAsString() == "_FatPtr") + if (RecordDecl *SrcRD = + SrcType.getCanonicalType()->getAs()->getDecl()) + if (SrcRD->getNameAsString() == "_FatPtr") + return true; return false; } #endif @@ -10504,7 +10521,8 @@ Sema::CheckSingleAssignmentConstraints(QualType LHSType, ExprResult &CallerRHS, if (!CheckBorrowQualTypeAssignment(LHSType, RHS.get())) return IncompatibleBorrowPointer; } - if (RHSCanType.isFatPtrType() || LHSCanType.isFatPtrType()) { + if ((RHSCanType.isFatQualified() || RHSCanType->isFatPtrRecordType()) && + (LHSCanType.isFatQualified() || LHSCanType->isFatPtrRecordType())) { if (!CheckFatQualTypeAssignment(LHSType, RHS.get())) return IncompatibleFatPointer; } @@ -17825,8 +17843,7 @@ bool Sema::DiagnoseAssignmentResult(AssignConvertType ConvTy, case IncompatiblePointer: #if ENABLE_BSC // int a = 1; trait F* f = &a; - if (getLangOpts().BSC && (IsTraitEqualExpr(*this, DstType, SrcType, Loc) || - IsFatPtrEqualExpr(*this, DstType, SrcType))) + if (getLangOpts().BSC && IsTraitEqualExpr(*this, DstType, SrcType, Loc)) return false; #endif if (Action == AA_Passing_CFAudited) { diff --git a/clang/test/BSC/Positive/FatPtr/nullptr_cast/nullptr_cast.cbs b/clang/test/BSC/Positive/FatPtr/nullptr_cast/nullptr_cast.cbs deleted file mode 100644 index 9a47ca5dac2b..000000000000 --- a/clang/test/BSC/Positive/FatPtr/nullptr_cast/nullptr_cast.cbs +++ /dev/null @@ -1,41 +0,0 @@ -// RUN: %clang %s -enable-fat-ptr -o %t.output -// RUN: %t.output -// expected-no-diagnostics - -#include -#include - -typedef struct node HANDLE; - -struct node { - int value; - HANDLE *fat left; -}; - -#define NIL ((HANDLE *) 0) -typedef int * fat myFatPtr; - -myFatPtr test() { - return nullptr; -} - -myFatPtr foo(int *fat p) { - return p; -} - -int main() { - HANDLE * fat a = nullptr; - myFatPtr b = foo(nullptr); - myFatPtr c = test(); - - if (a == nullptr) { - printf("a == nullptr\n"); - } - if (b == nullptr) { - printf("b == nullptr\n"); - } - if (c == nullptr) { - printf("c == nullptr\n"); - } - return 0; -} \ No newline at end of file diff --git a/clang/test/BSC/Positive/FatPtr/perform_anaysis/deref_loop/deref_loop.cbs b/clang/test/BSC/Positive/FatPtr/perform_anaysis/deref_loop/deref_loop.cbs deleted file mode 100644 index ac7ec4d457e9..000000000000 --- a/clang/test/BSC/Positive/FatPtr/perform_anaysis/deref_loop/deref_loop.cbs +++ /dev/null @@ -1,39 +0,0 @@ -// RUN: %clang %s -enable-fat-ptr -o %test.output -// RUN: %test.output 20 - -#include - -long dealwithargs(int argc, char *argv[]) -{ - int num = 0; - if (argc > 2) - num = atoi(argv[2]); - return 1L << num; -} - -void test_raw(long n) { - long *p = malloc(sizeof(long)); - while (n > 0) { - *p = n; - n--; - } -} - -void test_fat(long n) { - long *fat p = checked_malloc(sizeof(long)); - while (n > 0) { - *p = n; - n--; - } -} - -int main(int argc, char *argv[]) { - long n = dealwithargs(argc, argv); - int flag = atoi(argv[1]); - if (flag == 0) { - test_raw(n); - } else { - test_fat(n); - } - return 0; -} diff --git a/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/bisort/bitonic.c b/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/bisort/bitonic.c deleted file mode 100644 index cc3f2cb278aa..000000000000 --- a/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/bisort/bitonic.c +++ /dev/null @@ -1,303 +0,0 @@ -// RUN: %clang %s -o %test.output -// RUN: %test.output 300 - -#include -#include - -#define CONST_m1 10000 -#define CONST_b 31415821 -#define RANGE 100 - -typedef struct node HANDLE; - -struct node { - int value; - HANDLE* left; - HANDLE *right; -}; - -typedef struct future_cell_int{ - HANDLE *value; -} future_cell_int; - -extern void *malloc(unsigned long); - -#define NIL ((HANDLE *) 0) - -int NumNodes, NDim; - -int random_c(int); - -int flag=0,foo=0; - -#define LocalNewNode(h,v) \ -{ \ - h = (HANDLE *) malloc(sizeof(struct node)); \ - h->value = v; \ - h->left = NIL; \ - h->right = NIL; \ - }; - -#define NewNode(h,v,procid) LocalNewNode(h,v) - -int mylog(int num) { - int j=0,k=1; - - while(k 3) - flag = atoi(argv[3]); - else - flag = 1; - - if (argc > 2) - NumNodes = atoi(argv[2]); - else - NumNodes = 4; - - if (argc > 1) - size = atoi(argv[1]); - else - size = 1 << 15; - - NDim = mylog(NumNodes); - return size; -} - -void InOrder(HANDLE *h) { - HANDLE *l, *r; - if ((h != NIL)) { - l = h->left; - r = h->right; - InOrder(l); - static unsigned char counter = 0; - if (counter++ == 0) /* reduce IO */ - printf("%d @ 0x%x\n",h->value, 0); - InOrder(r); - } -} - -int mult(int p, int q) { - int p1, p0, q1, q0; - - p1 = p/CONST_m1; p0 = p%CONST_m1; - q1 = q/CONST_m1; q0 = q%CONST_m1; - return ((p0*q1+p1*q0) % CONST_m1)*CONST_m1+p0*q0; -} - -/* Generate the nth random_c # */ -int skiprand(int seed, int n) { - for (; n; n--) seed=random_c(seed); - return seed; -} - -int random_c(int seed) { - return mult(seed,CONST_b)+1; -} - -HANDLE* RandTree(int n, int seed, int node, int level) { - int next_val,my_name; - future_cell_int f_left, f_right; - HANDLE *h; - my_name=foo++; - if (n > 1) { - int newnode; - if (levelleft = f_left.value; - h->right = f_right.value; - } else { - h = 0; - } - return h; -} - -void SwapValue(HANDLE *l, HANDLE *r) { - int temp,temp2; - - temp = l->value; - temp2 = r->value; - r->value = temp; - l->value = temp2; -} - -void -/***********/ -SwapValLeft(HANDLE *l, HANDLE *r, HANDLE *ll, HANDLE *rl, int lval, int rval) -/***********/ -{ - r->value = lval; - r->left = ll; - l->left = rl; - l->value = rval; -} - - -void -/************/ -SwapValRight(HANDLE *l, HANDLE *r, HANDLE *lr, HANDLE *rr, int lval, int rval) -/************/ -{ - r->value = lval; - r->right = lr; - l->right = rr; - l->value = rval; - /*printf("Swap Val Right l 0x%x,r 0x%x val: %d %d\n",l,r,lval,rval);*/ -} - -int -/********************/ -Bimerge(HANDLE *root, int spr_val, int dir) -/********************/ -{ int rightexchange; - int elementexchange; - HANDLE *pl,*pll,*plr; - HANDLE *pr,*prl,*prr; - HANDLE *rl; - HANDLE *rr; - int rv,lv; - - - /*printf("enter bimerge %x\n", root);*/ - rv = root->value; - - pl = root->left; - pr = root->right; - rightexchange = ((rv > spr_val) ^ dir); - if (rightexchange) - { - root->value = spr_val; - spr_val = rv; - } - - while ((pl != NIL)) - { - /*printf("pl = 0x%x,pr = 0x%x\n",pl,pr);*/ - lv = pl->value; /* <------- 8.2% load penalty */ - pll = pl->left; - plr = pl->right; /* <------- 1.35% load penalty */ - rv = pr->value; /* <------ 57% load penalty */ - prl = pr->left; /* <------ 7.6% load penalty */ - prr = pr->right; /* <------ 7.7% load penalty */ - elementexchange = ((lv > rv) ^ dir); - if (rightexchange) - if (elementexchange) - { - SwapValRight(pl,pr,plr,prr,lv,rv); - pl = pll; - pr = prl; - } - else - { pl = plr; - pr = prr; - } - else - if (elementexchange) - { - SwapValLeft(pl,pr,pll,prl,lv,rv); - pl = plr; - pr = prr; - } - else - { pl = pll; - pr = prl; - } - } - if ((root->left != NIL)) - { - int value; - rl = root->left; - rr = root->right; - value = root->value; - - root->value=Bimerge(rl,value,dir); - spr_val=Bimerge(rr,spr_val,dir); - } - /*printf("exit bimerge %x\n", root);*/ - return spr_val; -} - -int -/*******************/ -Bisort(HANDLE *root, int spr_val, int dir) -/*******************/ -{ HANDLE *l; - HANDLE *r; - int val; - /*printf("bisort %x\n", root);*/ - if (root->left == NIL) /* <---- 8.7% load penalty */ - { - if (((root->value > spr_val) ^ dir)) - { - val = spr_val; - spr_val = root->value; - root->value =val; - } - } - else - { - int ndir; - l = root->left; - r = root->right; - val = root->value; - /*printf("root 0x%x, l 0x%x, r 0x%x\n", root,l,r);*/ - root->value=Bisort(l,val,dir); - ndir = !dir; - spr_val=Bisort(r,spr_val,ndir); - spr_val=Bimerge(root,spr_val,dir); - } - /*printf("exit bisort %x\n", root);*/ - return spr_val; -} - -int main(int argc, char **argv) { - HANDLE *h; - int sval; - int n; - - n = dealwithargs(argc,argv); - - printf("Bisort with %d size of dim %d\n", n, NDim); - - h = RandTree(n,12345768,0,0); - sval = random_c(245867) % RANGE; - if (flag) { - InOrder(h); - printf("%d\n",sval); - } - printf("**************************************\n"); - printf("BEGINNING BITONIC SORT ALGORITHM HERE\n"); - printf("**************************************\n"); - - sval=Bisort(h,sval,0); - - if (flag) { - printf("Sorted Tree:\n"); - InOrder(h); - printf("%d\n",sval); - } - - sval=Bisort(h,sval,1); - - if (flag) { - printf("Sorted Tree:\n"); - InOrder(h); - printf("%d\n",sval); - } - - return 0; -} diff --git a/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/bisort_bsc/bitonic.cbs b/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/bisort_bsc/bitonic.cbs deleted file mode 100644 index 4535551ee5b1..000000000000 --- a/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/bisort_bsc/bitonic.cbs +++ /dev/null @@ -1,304 +0,0 @@ -// RUN: %clang %s -enable-fat-ptr -o %test.output -// RUN: %test.output 3000 - -#include -#include -#include - -#define CONST_m1 10000 -#define CONST_b 31415821 -#define RANGE 100 - -typedef struct node HANDLE; - -struct node { - int value; - HANDLE *fat left; - HANDLE *fat right; -}; - -typedef struct future_cell_int{ - HANDLE *fat value; -} future_cell_int; - -#define NIL ((HANDLE *) 0) - -int NumNodes, NDim; - -int random_c(int); - -int flag=0,foo=0; - -//FIXME -#define LocalNewNode(h,v) \ -{ \ - h = checked_malloc(sizeof(HANDLE)); \ - h->value = v; \ - h->left = (HANDLE *fat)NIL; \ - h->right = (HANDLE *fat)NIL; \ - }; - -#define NewNode(h,v,procid) LocalNewNode(h,v) - -int mylog(int num) { - int j=0,k=1; - - while(k 3) - flag = atoi(argv[3]); - else - flag = 1; - - if (argc > 2) - NumNodes = atoi(argv[2]); - else - NumNodes = 4; - - if (argc > 1) - size = atoi(argv[1]); - else - size = 1 << 15; - - NDim = mylog(NumNodes); - return size; -} - -void InOrder(HANDLE *fat h) { - HANDLE *fat l, *fat r; - if ((h != nullptr)) { - l = h->left; - r = h->right; - InOrder(l); - static unsigned char counter = 0; - if (counter++ == 0) /* reduce IO */ - // printf("%d @ 0x%x\n",h->value, 0); - InOrder(r); - } -} - -int mult(int p, int q) { - int p1, p0, q1, q0; - - p1 = p/CONST_m1; p0 = p%CONST_m1; - q1 = q/CONST_m1; q0 = q%CONST_m1; - return ((p0*q1+p1*q0) % CONST_m1)*CONST_m1+p0*q0; -} - -/* Generate the nth random_c # */ -int skiprand(int seed, int n) { - for (; n; n--) seed=random_c(seed); - return seed; -} - -int random_c(int seed) { - return mult(seed, CONST_b) + 1; -} - -HANDLE *fat RandTree(int n, int seed, int node, int level) { - int next_val,my_name; - future_cell_int f_left, f_right; - HANDLE *fat h; - my_name=foo++; - if (n > 1) { - int newnode; - if (levelleft = f_left.value; - h->right = f_right.value; - } else { - h = checked_malloc(sizeof(HANDLE)); //FIXME - } - return h; -} - -void SwapValue(HANDLE *fat l, HANDLE *fat r) { - int temp,temp2; - - temp = l->value; - temp2 = r->value; - r->value = temp; - l->value = temp2; -} - -void -/***********/ -SwapValLeft(HANDLE *fat l, HANDLE *fat r, HANDLE *fat ll, HANDLE *fat rl, int lval, int rval) -/***********/ -{ - r->value = lval; - r->left = ll; - l->left = rl; - l->value = rval; -} - - -void -/************/ -SwapValRight(HANDLE *fat l, HANDLE *fat r, HANDLE *fat lr, HANDLE *fat rr, int lval, int rval) -/************/ -{ - r->value = lval; - r->right = lr; - l->right = rr; - l->value = rval; - /*printf("Swap Val Right l 0x%x,r 0x%x val: %d %d\n",l,r,lval,rval);*/ -} - -int -/********************/ -Bimerge(HANDLE *fat root, int spr_val, int dir) -/********************/ -{ int rightexchange; - int elementexchange; - HANDLE *fat pl,*fat pll,*fat plr; - HANDLE *fat pr,*fat prl,*fat prr; - HANDLE *fat rl; - HANDLE *fat rr; - int rv,lv; - - - /*printf("enter bimerge %x\n", root);*/ - rv = root->value; - - pl = root->left; - pr = root->right; - rightexchange = ((rv > spr_val) ^ dir); - if (rightexchange) - { - root->value = spr_val; - spr_val = rv; - } - - while ((pl != nullptr)) - { - /*printf("pl = 0x%x,pr = 0x%x\n",pl,pr);*/ - lv = pl->value; /* <------- 8.2% load penalty */ - pll = pl->left; - plr = pl->right; /* <------- 1.35% load penalty */ - rv = pr->value; /* <------ 57% load penalty */ - prl = pr->left; /* <------ 7.6% load penalty */ - prr = pr->right; /* <------ 7.7% load penalty */ - elementexchange = ((lv > rv) ^ dir); - if (rightexchange) - if (elementexchange) - { - SwapValRight(pl,pr,plr,prr,lv,rv); - pl = pll; - pr = prl; - } - else - { pl = plr; - pr = prr; - } - else - if (elementexchange) - { - SwapValLeft(pl,pr,pll,prl,lv,rv); - pl = plr; - pr = prr; - } - else - { pl = pll; - pr = prl; - } - } - HANDLE *fat root_left = root->left; - if (root_left != nullptr) - { - int value; - rl = root->left; - rr = root->right; - value = root->value; - - root->value=Bimerge(rl,value,dir); - spr_val=Bimerge(rr,spr_val,dir); - } - /*printf("exit bimerge %x\n", root);*/ - return spr_val; -} - -int -/*******************/ -Bisort(HANDLE *fat root, int spr_val, int dir) -/*******************/ -{ HANDLE *fat l; - HANDLE *fat r; - int val; - /*printf("bisort %x\n", root);*/ - HANDLE *fat root_left = root->left; - if (root_left == nullptr) /* <---- 8.7% load penalty */ - { - if (((root->value > spr_val) ^ dir)){ - val = spr_val; - spr_val = root->value; - root->value =val; - } - } - else - { - int ndir; - l = root->left; - r = root->right; - val = root->value; - /*printf("root 0x%x, l 0x%x, r 0x%x\n", root,l,r);*/ - root->value=Bisort(l,val,dir); - ndir = !dir; - spr_val=Bisort(r,spr_val,ndir); - spr_val=Bimerge(root,spr_val,dir); - } - /*printf("exit bisort %x\n", root);*/ - return spr_val; -} - -int main(int argc, char **argv) { - HANDLE *fat h; - int sval; - int n; - - n = dealwithargs(argc,argv); - - printf("Bisort with %d size of dim %d\n", n, NDim); - - h = RandTree(n,12345768,0,0); - sval = random_c(245867) % RANGE; - if (flag) { - InOrder(h); - printf("%d\n",sval); - } - printf("**************************************\n"); - printf("BEGINNING BITONIC SORT ALGORITHM HERE\n"); - printf("**************************************\n"); - - sval=Bisort(h,sval,0); - - if (flag) { - printf("Sorted Tree:\n"); - InOrder(h); - printf("%d\n",sval); - } - - sval=Bisort(h,sval,1); - - if (flag) { - printf("Sorted Tree:\n"); - InOrder(h); - printf("%d\n",sval); - } - - return 0; -} diff --git a/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd/node.c b/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd/node.c deleted file mode 100644 index c8af4371bf4b..000000000000 --- a/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd/node.c +++ /dev/null @@ -1,233 +0,0 @@ -// RUN: %clang %s -DTORONTO -o %test.output -// RUN: %test.output 16 - -/* For copyright information, see olden_v1.0/COPYRIGHT */ - -/* node.c - */ -#ifndef TORONTO -#include -#include -#include "mem-ref.h" -#endif - -#ifdef FUTURES -#include "future-cell.h" -#endif - -#include -#include "tree.h" - -#ifdef TORONTO -int NumNodes; -#else -int __NumNodes; -#endif - -typedef struct { - long level; -} startmsg_t; - -#ifndef TORONTO -void filestuff() -{ - CMMD_fset_io_mode(stdout, CMMD_independent); - fcntl(fileno(stdout), F_SETFL, O_APPEND); - if (CMMD_self_address()) exit(0); - __InitRegs(0); -} -#endif - -int dealwithargs(int argc, char *argv[]) -{ - int level; - -#ifdef TORONTO - if (argc > 2) - NumNodes = atoi(argv[2]); - else - NumNodes = 4; -#else - if (argc > 2) - __NumNodes = atoi(argv[2]); - else - __NumNodes = 4; -#endif - - if (argc > 1) - level = atoi(argv[1]); - else - level = 16; - - return level; -} - -tree_t *TreeAlloc (int level, int lo, int proc) { - if (level == 0) - return NULL; - else { - struct tree *new, *right, *left; - new = (struct tree *) malloc(sizeof(tree_t)); - left = TreeAlloc(level -1, lo+proc/2, proc/2); - right=TreeAlloc(level-1,lo,proc/2); - new->val = 1; - new->left = (struct tree *) left; - new->right = (struct tree *) right; - return new; - } -} - -/* TreeAdd: - */ -int TreeAdd (tree_t *t) -{ - if (t == NULL) { - return 0; - } - else { -#ifdef FUTURES - future_cell_int leftval; - int rightval; - tree_t *tleft, *tright; - int value; - - tleft = t->left; - RPC(tleft, tleft,TreeAdd,&(leftval)); - NOTEST(); - tright = t->right; - rightval = TreeAdd(tright); - RTOUCH(&leftval); - /*chatting("after touch @ 0x%x\n",t);*/ - value = t->val; - /*chatting("returning from treeadd %d\n",*/ - /*leftval.value + rightval.value + value);*/ - return leftval.value + rightval + value; - RETEST(); -#else - int leftval; - int rightval; - tree_t *tleft, *tright; - int value; - - tleft = t->left; /* <---- 57% load penalty */ - leftval = TreeAdd(tleft); - tright = t->right; /* <---- 11.4% load penalty */ - rightval = TreeAdd(tright); - /*chatting("after touch\n");*/ - value = t->val; - /*chatting("returning from treeadd %d\n",*/ - /*leftval.value + rightval.value + value);*/ - return leftval + rightval + value; -#endif - } -} /* end of TreeAdd */ - -int main (int argc, char *argv[]) -{ - tree_t *root; - int level,result; - -#ifdef FUTURES - level = SPMDInit(argc,argv); -#else -#ifndef TORONTO - filestuff(); -#endif - level = dealwithargs(argc, argv); -#endif -#ifndef TORONTO - CMMD_node_timer_clear(0); - CMMD_node_timer_clear(1); -#endif -#ifdef TORONTO - chatting("Treeadd with %d levels on %d processors \n", - level, NumNodes); -#else - chatting("Treeadd with %d levels on %d processors \n", - level, __NumNodes); -#endif - /* only processor 0 will continue here. */ - chatting("About to enter TreeAlloc\n"); -#ifndef TORONTO - CMMD_node_timer_start(0); -#endif - -#ifdef TORONTO - root = TreeAlloc (level, 0, NumNodes); -#else - root = TreeAlloc (level, 0, __NumNodes); -#endif - -#ifndef TORONTO - CMMD_node_timer_stop(0); -#endif - chatting("About to enter TreeAdd\n"); - -#ifndef PLAIN - ClearAllStats(); -#endif -#ifndef TORONTO - CMMD_node_timer_start(1); -#endif -{ int i; for (i = 0; i < 100; ++i) - result = TreeAdd (root); -} -#ifndef TORONTO - CMMD_node_timer_stop(1); -#endif - chatting("Received result of %d\n",result); - -#ifndef TORONTO - chatting("Alloc Time = %f seconds\n", CMMD_node_timer_elapsed(0)); - chatting(/* TreeAdd: - */ -int TreeAdd (tree_t *t) -{ - if (t == NULL) { - return 0; - } - else { -#ifdef FUTURES - future_cell_int leftval; - int rightval; - tree_t *tleft, *tright; - int value; - - tleft = t->left; - RPC(tleft, tleft,TreeAdd,&(leftval)); - NOTEST(); - tright = t->right; - rightval = TreeAdd(tright); - RTOUCH(&leftval); - /*chatting("after touch @ 0x%x\n",t);*/ - value = t->val; - /*chatting("returning from treeadd %d\n",*/ - /*leftval.value + rightval.value + value);*/ - return leftval.value + rightval + value; - RETEST(); -#else - int leftval; - int rightval; - tree_t *tleft, *tright; - int value; - - tleft = t->left; /* <---- 57% load penalty */ - leftval = TreeAdd(tleft); - tright = t->right; /* <---- 11.4% load penalty */ - rightval = TreeAdd(tright); - /*chatting("after touch\n");*/ - value = t->val; - /*chatting("returning from treeadd %d\n",*/ - /*leftval.value + rightval.value + value);*/ - return leftval + rightval + value; -#endif - } -} /* end of TreeAdd */ -"Add Time = %f seconds\n", CMMD_node_timer_elapsed(1)); -#endif - -#ifdef FUTURES - __ShutDown(); -#endif - exit(0); -} diff --git a/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd/tree.h b/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd/tree.h deleted file mode 100644 index b8d075e41cb0..000000000000 --- a/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd/tree.h +++ /dev/null @@ -1,25 +0,0 @@ -/* For copyright information, see olden_v1.0/COPYRIGHT */ - -/* tree.h - */ - -#ifdef TORONTO -#include -#define chatting printf -#define PLAIN -#endif - -typedef struct tree { - int val; - struct tree *left, *right; -} tree_t; - -tree_t *TreeAlloc (int level, int lo, int hi); -int TreeAdd (tree_t *t); - - - - - - - diff --git a/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd_bsc/node.cbs b/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd_bsc/node.cbs deleted file mode 100644 index f859e368b7ae..000000000000 --- a/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd_bsc/node.cbs +++ /dev/null @@ -1,193 +0,0 @@ -// RUN: %clang %s -DTORONTO -enable-fat-ptr -o %test.output -// RUN: %test.output 16 - -#ifndef TORONTO -#include -#include -#endif - -#ifdef FUTURES -#include "future-cell.h" -#endif - -#ifndef TORONTO -#include "mem-ref.h" -#endif - -#include -#include "tree.hbs" - -#ifdef TORONTO -int NumNodes; -#else -int __NumNodes; -#endif - -extern int atoi(const char *); - -#ifndef TORONTO -void filestuff() -{ - CMMD_fset_io_mode(stdout, CMMD_independent); - fcntl(fileno(stdout), F_SETFL, O_APPEND); - if (CMMD_self_address()) exit(0); - __InitRegs(0); -} -#endif - -int dealwithargs(int argc, char *argv[]) -{ - int level; - -#ifdef TORONTO - if (argc > 2) - NumNodes = atoi(argv[2]); - else - NumNodes = 4; -#else - if (argc > 2) - __NumNodes = atoi(argv[2]); - else - __NumNodes = 4; -#endif - - if (argc > 1) - level = atoi(argv[1]); - else - level = 16; - - return level; -} - -typedef struct { - long level; -} startmsg_t; - -tree_t *fat TreeAlloc (int level, int lo, int proc) { - if (level == 0) - return nullptr; - else { - tree_t *fat new; - tree_t *fat right; - tree_t *fat left; - new = checked_malloc(sizeof(tree_t)); - left = TreeAlloc(level -1, lo+proc/2, proc/2); - right = TreeAlloc(level-1,lo,proc/2); - new->val = 1; - new->left = left; - new->right = right; - return new; - } -} - -/* TreeAdd: - */ -int TreeAdd (tree_t *fat t) -{ - if (t == nullptr) { - return 0; - } - else { -#ifdef FUTURES - future_cell_int leftval; - int rightval; - tree_t *fat tleft, *fat tright; - int value; - - tleft = t->left; - RPC(tleft, tleft,TreeAdd,&(leftval)); - NOTEST(); - tright = t->right; - rightval = TreeAdd(tright); - RTOUCH(&leftval); - /*chatting("after touch @ 0x%x\n",t);*/ - value = t->val; - /*chatting("returning from treeadd %d\n",*/ - /*leftval.value + rightval.value + value);*/ - return leftval.value + rightval + value; - RETEST(); -#else - int leftval; - int rightval; - tree_t *fat tleft; - tree_t *fat tright; - int value; - - tleft = t->left; /* <---- 57% load penalty */ - leftval = TreeAdd(tleft); - tright = t->right; /* <---- 11.4% load penalty */ - rightval = TreeAdd(tright); - /*chatting("after touch\n");*/ - value = t->val; - /*chatting("returning from treeadd %d\n",*/ - /*leftval.value + rightval.value + value);*/ - return leftval + rightval + value; -#endif - } -} /* end of TreeAdd */ - -int main (int argc, char *argv[]) -{ - tree_t *fat root; - int level, result; - -#ifdef FUTURES - level = SPMDInit(argc,argv); -#else -#ifndef TORONTO - filestuff(); -#endif - level = dealwithargs(argc, argv); -#endif -#ifndef TORONTO - CMMD_node_timer_clear(0); - CMMD_node_timer_clear(1); -#endif -#ifdef TORONTO - chatting("Treeadd with %d levels on %d processors \n", - level, NumNodes); -#else - chatting("Treeadd with %d levels on %d processors \n", - level, __NumNodes); -#endif - /* only processor 0 will continue here. */ - chatting("About to enter TreeAlloc\n"); -#ifndef TORONTO - CMMD_node_timer_start(0); -#endif - int a0 = 0; -#ifdef TORONTO - root = TreeAlloc (level, a0, NumNodes); -#else - root = TreeAlloc (level, a0, __NumNodes); -#endif - -#ifndef TORONTO - CMMD_node_timer_stop(0); -#endif - chatting("About to enter TreeAdd\n"); - -#ifndef PLAIN - ClearAllStats(); -#endif -#ifndef TORONTO - CMMD_node_timer_start(1); -#endif -{ int i; for (i = 0; i < 100; ++i) - result = TreeAdd(root); -} -#ifndef TORONTO - CMMD_node_timer_stop(1); -#endif - chatting("Received result of %d\n",result); - -#ifndef TORONTO - chatting("Alloc Time = %f seconds\n", CMMD_node_timer_elapsed(0)); - chatting("Add Time = %f seconds\n", CMMD_node_timer_elapsed(1)); -#endif - -#ifdef FUTURES - __ShutDown(); -#endif - exit(0); -} diff --git a/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd_bsc/tree.hbs b/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd_bsc/tree.hbs deleted file mode 100644 index 0b8014a09f3d..000000000000 --- a/clang/test/BSC/Positive/FatPtr/perform_anaysis/olden/treeadd_bsc/tree.hbs +++ /dev/null @@ -1,29 +0,0 @@ -/* For copyright information, see olden_v1.0/COPYRIGHT */ - -/* tree.h - */ - -#ifdef TORONTO -#include -#define chatting printf -#define PLAIN -#endif - -#include -typedef struct tree tree_t; - -struct tree { - int val; - tree_t *fat left; - tree_t *fat right; -}; - -tree_t *fat TreeAlloc (int level, int lo, int hi); -int TreeAdd (tree_t *fat t); - - - - - - - diff --git a/clang/test/BSC/Positive/SafeZone/destructor_call/destructor_call.cbs b/clang/test/BSC/Positive/SafeZone/destructor_call/destructor_call.cbs deleted file mode 100644 index 9c5c2136db11..000000000000 --- a/clang/test/BSC/Positive/SafeZone/destructor_call/destructor_call.cbs +++ /dev/null @@ -1,21 +0,0 @@ -// RUN: %clang %s -o %t.output -// RUN: %t.output -// expected-no-diagnostics - -owned struct RawVec { -public: - T a; - ~RawVec(This this) {} -}; - -safe int test(void) { - unsafe { - RawVec rv = { .a = 1}; - } - return 0; -} - -int main() { - int a = test(); - return 0; -} -- Gitee From 6f341a5b952d8107e240835c27e9cdfe65b06164 Mon Sep 17 00:00:00 2001 From: zhaoxuhui Date: Fri, 21 Mar 2025 16:42:58 +0800 Subject: [PATCH 28/29] Revert "[FatPtr]support fat ptr for bsc compiler and add std library bsc_fat_ptr.hbs" This reverts commit 416505f87a220f55365eea912931902d6464a3f8. --- clang/include/clang/AST/BSC/WalkerBSC.h | 3 +- clang/include/clang/AST/CanonicalType.h | 4 +- clang/include/clang/AST/OperationKinds.def | 2 - clang/include/clang/AST/StmtVisitor.h | 2 - clang/include/clang/AST/Type.h | 180 +- clang/include/clang/Basic/BSC/BSCAttr.td | 6 - .../clang/Basic/BSC/DiagnosticBSCSemaKinds.td | 32 +- clang/include/clang/Basic/LangOptions.def | 1 - clang/include/clang/Basic/TokenKinds.def | 2 - clang/include/clang/Driver/BSC/BSCOptions.td | 2 - clang/include/clang/Sema/DeclSpec.h | 34 +- clang/include/clang/Sema/Sema.h | 52 +- clang/lib/AST/ASTContext.cpp | 6 +- clang/lib/AST/ASTImporter.cpp | 2 +- clang/lib/AST/BSC/TypeBSC.cpp | 62 +- clang/lib/AST/Expr.cpp | 1 - clang/lib/AST/ExprConstant.cpp | 16 - clang/lib/AST/StmtPrinter.cpp | 12 +- clang/lib/AST/TypePrinter.cpp | 6 - clang/lib/Analysis/BSC/BSCBorrowCheck.cpp | 1 + clang/lib/Analysis/ThreadSafetyCommon.cpp | 3 +- clang/lib/CodeGen/CGCall.cpp | 7 - clang/lib/CodeGen/CGExprAgg.cpp | 5 - clang/lib/CodeGen/CGExprScalar.cpp | 6 - clang/lib/Driver/ToolChains/Clang.cpp | 2 - clang/lib/Headers/CMakeLists.txt | 1 - clang/lib/Headers/bsc_include/bsc_fat_ptr.hbs | 90 - clang/lib/Lex/Lexer.cpp | 14 - clang/lib/Parse/ParseDecl.cpp | 39 +- clang/lib/Parse/ParseExpr.cpp | 1 - clang/lib/Sema/BSC/SemaBSCCoroutine.cpp | 12 +- clang/lib/Sema/BSC/SemaBSCDestructor.cpp | 2 +- clang/lib/Sema/BSC/SemaBSCFatPtr.cpp | 1547 ----------------- clang/lib/Sema/BSC/SemaBSCOwnership.cpp | 2 +- clang/lib/Sema/BSC/SemaBSCSafeZone.cpp | 2 +- clang/lib/Sema/BSC/SemaDeclBSC.cpp | 2 +- clang/lib/Sema/CMakeLists.txt | 1 - clang/lib/Sema/DeclSpec.cpp | 13 +- clang/lib/Sema/Sema.cpp | 28 +- clang/lib/Sema/SemaCast.cpp | 63 +- clang/lib/Sema/SemaDecl.cpp | 40 +- clang/lib/Sema/SemaDeclAttr.cpp | 16 - clang/lib/Sema/SemaExpr.cpp | 121 +- clang/lib/Sema/SemaExprMember.cpp | 28 +- clang/lib/Sema/SemaOverload.cpp | 9 +- clang/lib/Sema/SemaType.cpp | 7 +- clang/lib/Serialization/ASTWriterStmt.cpp | 3 +- clang/test/BSC/Negative/FatPtr/addr_fat.cbs | 58 - clang/test/BSC/Negative/FatPtr/fat_cast.cbs | 72 - .../FatPtr/fat_qulifier_only_for_pointer.cbs | 24 - .../global_array/array_out_of_bounds1.cbs | 12 - .../global_array/array_out_of_bounds2.cbs | 14 - .../heap/array_out_of_bounds1.cbs | 11 - .../heap/array_out_of_bounds2.cbs | 11 - .../heap/array_out_of_bounds3.cbs | 12 - .../heap/array_out_of_bounds4.cbs | 13 - .../heap/array_out_of_bounds5.cbs | 13 - .../heap/array_out_of_bounds6.cbs | 11 - .../heap/buffer_overflow.cbs | 21 - .../heap/double_free1.cbs | 13 - .../heap/double_free2.cbs | 14 - .../heap/double_free3.cbs | 16 - .../heap/double_free4.cbs | 18 - .../heap/use_after_free1.cbs | 14 - .../heap/use_after_free2.cbs | 15 - .../heap/use_after_free3.cbs | 17 - .../heap/use_after_free4.cbs | 15 - .../heap/use_after_free5.cbs | 29 - .../heap/use_after_free6.cbs | 14 - .../stack/array_out_of_bounds1.cbs | 12 - .../stack/array_out_of_bounds2.cbs | 13 - .../stack/buffer_overflow.cbs | 22 - .../stack/return_local_address.cbs | 17 - .../test/BSC/Positive/FatPtr/global_array.cbs | 23 - clang/test/BSC/Positive/FatPtr/heap.cbs | 94 - clang/test/BSC/Positive/FatPtr/stack.cbs | 24 - 76 files changed, 234 insertions(+), 2898 deletions(-) delete mode 100644 clang/lib/Headers/bsc_include/bsc_fat_ptr.hbs delete mode 100644 clang/lib/Sema/BSC/SemaBSCFatPtr.cpp delete mode 100644 clang/test/BSC/Negative/FatPtr/addr_fat.cbs delete mode 100644 clang/test/BSC/Negative/FatPtr/fat_cast.cbs delete mode 100644 clang/test/BSC/Negative/FatPtr/fat_qulifier_only_for_pointer.cbs delete mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/global_array/array_out_of_bounds1.cbs delete mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/global_array/array_out_of_bounds2.cbs delete mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds1.cbs delete mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds2.cbs delete mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds3.cbs delete mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds4.cbs delete mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds5.cbs delete mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds6.cbs delete mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/buffer_overflow.cbs delete mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free1.cbs delete mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free2.cbs delete mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free3.cbs delete mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free4.cbs delete mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free1.cbs delete mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free2.cbs delete mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free3.cbs delete mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free4.cbs delete mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free5.cbs delete mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free6.cbs delete mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds1.cbs delete mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds2.cbs delete mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/buffer_overflow.cbs delete mode 100644 clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/return_local_address.cbs delete mode 100644 clang/test/BSC/Positive/FatPtr/global_array.cbs delete mode 100644 clang/test/BSC/Positive/FatPtr/heap.cbs delete mode 100644 clang/test/BSC/Positive/FatPtr/stack.cbs diff --git a/clang/include/clang/AST/BSC/WalkerBSC.h b/clang/include/clang/AST/BSC/WalkerBSC.h index c98e20d67d41..9def7ca98e82 100644 --- a/clang/include/clang/AST/BSC/WalkerBSC.h +++ b/clang/include/clang/AST/BSC/WalkerBSC.h @@ -89,8 +89,7 @@ public: bool VisitQualType(QualType QT) { if (QT.isOwnedQualified() || QT.isBorrowQualified() || - QT->hasBorrowFields() || QT->hasOwnedFields() || QT.isFatQualified() || - QT->hasFatFields()) { + QT->hasBorrowFields() || QT->hasOwnedFields()) { return true; } if (IsDesugaredFromTraitType(QT)) { diff --git a/clang/include/clang/AST/CanonicalType.h b/clang/include/clang/AST/CanonicalType.h index 89258bedde76..9b7a1d4efe96 100644 --- a/clang/include/clang/AST/CanonicalType.h +++ b/clang/include/clang/AST/CanonicalType.h @@ -146,9 +146,7 @@ public: bool isBorrowQualified() const { return Stored.isLocalBorrowQualified(); } - - bool isFatQualified() const { return Stored.isLocalFatQualified(); } -#endif + #endif bool isVolatileQualified() const { return Stored.isLocalVolatileQualified(); diff --git a/clang/include/clang/AST/OperationKinds.def b/clang/include/clang/AST/OperationKinds.def index b744276b1b46..0c48c0a51353 100644 --- a/clang/include/clang/AST/OperationKinds.def +++ b/clang/include/clang/AST/OperationKinds.def @@ -445,8 +445,6 @@ UNARY_OPERATION(AddrMut, "&mut") UNARY_OPERATION(AddrMutDeref, "&mut *") UNARY_OPERATION(AddrConst, "&const") UNARY_OPERATION(AddrConstDeref, "&const *") -// [BSC Fat pointer] fat pointer operator -UNARY_OPERATION(AddrFat, "&fat") #endif #undef CAST_OPERATION diff --git a/clang/include/clang/AST/StmtVisitor.h b/clang/include/clang/AST/StmtVisitor.h index b013581c178b..da654e5a616b 100644 --- a/clang/include/clang/AST/StmtVisitor.h +++ b/clang/include/clang/AST/StmtVisitor.h @@ -106,7 +106,6 @@ public: case UO_AddrConst: DISPATCH(UnaryAddrConst, UnaryOperator); case UO_AddrMutDeref: DISPATCH(UnaryAddrMutDeref, UnaryOperator); case UO_AddrConstDeref: DISPATCH(UnaryAddrConstDeref, UnaryOperator); - case UO_AddrFat: DISPATCH(UnaryAddrFat, UnaryOperator); #endif } } @@ -178,7 +177,6 @@ public: #if ENABLE_BSC UNARYOP_FALLBACK(AddrMut) UNARYOP_FALLBACK(AddrMutDeref) UNARYOP_FALLBACK(AddrConst) UNARYOP_FALLBACK(AddrConstDeref) - UNARYOP_FALLBACK(AddrFat) #endif #undef UNARYOP_FALLBACK diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h index 72fcecf7ad5b..ccf49eac7dd9 100644 --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -66,11 +66,11 @@ class TemplateParameterList; class Type; enum { -#if ENABLE_BSC - TypeAlignmentInBits = 7, -#else + #if ENABLE_BSC + TypeAlignmentInBits = 6, + #else TypeAlignmentInBits = 4, -#endif + #endif TypeAlignment = 1 << TypeAlignmentInBits }; @@ -154,17 +154,16 @@ using CanQualType = CanQual; class Qualifiers { public: enum TQ { // NOTE: These flags must be kept in sync with DeclSpec::TQ. - Const = 0x1, + Const = 0x1, Restrict = 0x2, Volatile = 0x4, -#if ENABLE_BSC - Owned = 0x8, - Borrow = 0x10, - Fat = 0x20, - CVRMask = Const | Volatile | Restrict | Owned | Borrow | Fat -#else + #if ENABLE_BSC + Owned = 0x8, + Borrow = 0x10, + CVRMask = Const | Volatile | Restrict | Owned | Borrow + #else CVRMask = Const | Volatile | Restrict -#endif + #endif }; enum GC { @@ -196,20 +195,20 @@ public: }; enum { -/// The maximum supported address space number. -/// 20 bits should be enough for anyone. -#if ENABLE_BSC - MaxAddressSpace = 0xfffffu, -#else + /// The maximum supported address space number. + /// 21 bits should be enough for anyone. + #if ENABLE_BSC + MaxAddressSpace = 0x1fffffu, + #else MaxAddressSpace = 0x7fffffu, -#endif + #endif -/// The width of the "fast" qualifier mask. -#if ENABLE_BSC - FastWidth = 6, -#else + /// The width of the "fast" qualifier mask. + #if ENABLE_BSC + FastWidth = 5, + #else FastWidth = 3, -#endif + #endif /// The fast qualifier mask. FastMask = (1 << FastWidth) - 1 @@ -313,17 +312,7 @@ public: Qs.addBorrow(); return Qs; } - - bool hasFat() const { return Mask & Fat; } - bool hasOnlyFat() const { return Mask == Fat; } - void removeFat() { Mask &= ~Fat; } - void addFat() { Mask |= Fat; } - Qualifiers withFat() const { - Qualifiers Qs = *this; - Qs.addFat(); - return Qs; - } -#endif + #endif bool hasVolatile() const { return Mask & Volatile; } bool hasOnlyVolatile() const { return Mask == Volatile; } @@ -663,32 +652,32 @@ public: } private: - // bits: |0 1 2 3 4 5|6|7 .. 8|9 .. 11|12 ... 31| - // |C R V O B F|U|GCAttr|Lifetime|AddressSpace| + // bits: |0 1 2 3 4|5|6 .. 7|8 .. 10|11 ... 31| + // |C R V O B|U|GCAttr|Lifetime|AddressSpace| uint32_t Mask = 0; #if ENABLE_BSC - static const uint32_t UMask = 0x40; - static const uint32_t UShift = 6; - static const uint32_t GCAttrMask = 0x180; - static const uint32_t GCAttrShift = 7; - static const uint32_t LifetimeMask = 0xe00; - static const uint32_t LifetimeShift = 9; -#else + static const uint32_t UMask = 0x20; + static const uint32_t UShift = 5; + static const uint32_t GCAttrMask = 0xC0; + static const uint32_t GCAttrShift = 6; + static const uint32_t LifetimeMask = 0x700; + static const uint32_t LifetimeShift = 8; + #else static const uint32_t UMask = 0x8; static const uint32_t UShift = 3; static const uint32_t GCAttrMask = 0x30; static const uint32_t GCAttrShift = 4; static const uint32_t LifetimeMask = 0x1C0; static const uint32_t LifetimeShift = 6; -#endif + #endif static const uint32_t AddressSpaceMask = ~(CVRMask | UMask | GCAttrMask | LifetimeMask); #if ENABLE_BSC - static const uint32_t AddressSpaceShift = 12; -#else + static const uint32_t AddressSpaceShift = 11; + #else static const uint32_t AddressSpaceShift = 9; -#endif + #endif }; class QualifiersAndAtomic { @@ -707,8 +696,7 @@ public: #if ENABLE_BSC bool hasOwned() const { return Quals.hasOwned(); } bool hasBorrow() const { return Quals.hasBorrow(); } - bool hasFat() const { return Quals.hasFat(); } -#endif + #endif bool hasRestrict() const { return Quals.hasRestrict(); } bool hasAtomic() const { return HasAtomic; } @@ -717,8 +705,7 @@ public: #if ENABLE_BSC void addOwned() { Quals.addOwned(); } void addBorrow() { Quals.addBorrow(); } - void addFat() { Quals.addFat(); } -#endif + #endif void addRestrict() { Quals.addRestrict(); } void addAtomic() { HasAtomic = true; } @@ -727,8 +714,7 @@ public: #if ENABLE_BSC void removeOwned() { Quals.removeOwned(); } void removeBorrow() { Quals.removeBorrow(); } - void removeFat() { Quals.removeFat(); } -#endif + #endif void removeRestrict() { Quals.removeRestrict(); } void removeAtomic() { HasAtomic = false; } @@ -739,8 +725,7 @@ public: #if ENABLE_BSC QualifiersAndAtomic withOwned() { return {Quals.withOwned(), HasAtomic}; } QualifiersAndAtomic withBorrow() { return {Quals.withBorrow(), HasAtomic}; } - QualifiersAndAtomic withFat() { return {Quals.withFat(), HasAtomic}; } -#endif + #endif QualifiersAndAtomic withRestrict() { return {Quals.withRestrict(), HasAtomic}; } @@ -820,8 +805,7 @@ class QualType { // Thankfully, these are efficiently composable. llvm::PointerIntPair, - Qualifiers::FastWidth> - Value; // For BSC, FastWidth = 6 + Qualifiers::FastWidth> Value; // For BSC, FastWidth = 5 const ExtQuals *getExtQualsUnsafe() const { return Value.getPointer().get(); @@ -928,18 +912,7 @@ public: /// Determine whether this type is borrow-qualified. bool isBorrowQualified() const; - - /// Determine whether this particular QualType instance has the - /// "fat" qualifier set, without looking through typedefs that may have - /// added "fat" at a different level. - bool isLocalFatQualified() const { - return (getLocalFastQualifiers() & Qualifiers::Fat); - } - - /// Determine whether this type is fat-qualified. - bool isFatQualified() const; - bool isFatPtrType() const; -#endif + #endif /// Determine whether this particular QualType instance has the /// "restrict" qualifier set, without looking through typedefs that may have @@ -1061,12 +1034,7 @@ public: QualType addConstBorrow(const ASTContext &Context); QualType removeConstForBorrow(const ASTContext &Context); bool hasBorrow() const; - - /// Add the `fat` type qualifier to this QualType. - void addFat() { addFastQualifiers(Qualifiers::Fat); } - QualType withFat() const { return withFastQualifiers(Qualifiers::Fat); } - bool hasFat() const; -#endif + #endif /// Add the `volatile` type qualifier to this QualType. void addVolatile() { @@ -1098,8 +1066,7 @@ public: #if ENABLE_BSC void removeLocalOwned(); void removeLocalBorrow(); - void removeLocalFat(); -#endif + #endif void removeLocalVolatile(); void removeLocalRestrict(); void removeLocalCVRQualifiers(unsigned Mask); @@ -1832,9 +1799,9 @@ protected: /// C++ 8.3.5p4: The return type, the parameter type list and the /// cv-qualifier-seq, [...], are part of the function type. /// - /// After add 'owned, borrow, fat' qualifiers, the FastWidth is now 6(from 3 - /// to 6), then bitNumberOf(FunctionTypeBitFields) is now 66(63 to 66, - /// greater than 8 bytes), then sizeof(Type) is now increased by one byte. + /// After add 'owned, borrow' qualifiers, the FastWidth is now 5(from 3 to 5), + /// then bitNumberOf(FunctionTypeBitFields) is now 65(63 to 65, greater than 8 bytes), + /// then sizeof(Type) is now increased by one byte. unsigned FastTypeQuals : Qualifiers::FastWidth; /// Whether this function has extended Qualifiers. unsigned HasExtQuals : 1; @@ -2072,7 +2039,7 @@ protected: Type(TypeClass tc, QualType canon, TypeDependence Dependence) : ExtQualsTypeCommonBase(this, canon.isNull() ? QualType(this_(), 0) : canon) { - // Add owned, borrow, fat, sizeof(FunctionTypeBitfields) > 8 now. + // Add owned, borrow, sizeof(FunctionTypeBitfields) > 8 now. // sizeof(Type) is larger too. static_assert(sizeof(*this) <= 16 + sizeof(ExtQualsTypeCommonBase), "changing bitfields changed sizeof(Type)!"); @@ -2301,8 +2268,6 @@ public: bool isTraitType() const; bool isTraitPointerType() const; bool hasTraitType() const; - bool isFatPtrRecordType() const; - QualType getFatPtrPointeeType() const; bool isBSCCalculatedTypeInCompileTime() const; bool checkFunctionProtoType(SafeZoneSpecifier SZS) const; bool isOwnedStructureType() const; @@ -2946,9 +2911,7 @@ public: bool hasOwnedFields() const; bool hasBorrowFields() const; - - bool hasFatFields() const; -#endif + #endif }; /// Represents a type which was implicitly adjusted by the semantic @@ -4108,20 +4071,18 @@ public: ExtInfo getExtInfo() const { return ExtInfo(FunctionTypeBits.ExtInfo); } static_assert((~Qualifiers::FastMask & Qualifiers::CVRMask) == 0, -#if ENABLE_BSC - "Const, volatile, restrict, owned, borrow and fat are assumed " - "to be a subset of " -#else + #if ENABLE_BSC + "Const, volatile, restrict, owned and borrow are assumed to be a subset of " + #else "Const, volatile and restrict are assumed to be a subset of " -#endif + #endif "the fast qualifiers."); bool isConst() const { return getFastTypeQuals().hasConst(); } #if ENABLE_BSC bool isOwned() const { return getFastTypeQuals().hasOwned(); } bool isBorrow() const { return getFastTypeQuals().hasBorrow(); } - bool isFat() const { return getFastTypeQuals().hasFat(); } -#endif + #endif bool isVolatile() const { return getFastTypeQuals().hasVolatile(); } bool isRestrict() const { return getFastTypeQuals().hasRestrict(); } @@ -4451,9 +4412,7 @@ public: bool hasOwnedRetOrParams() const; // return true if any 'borrow' here bool hasBorrowRetOrParams() const; - // return true if any 'fat' here - bool hasFatRetOrParams() const; -#endif + #endif /// Return all the available information about this type's exception spec. ExceptionSpecInfo getExceptionSpecInfo() const { @@ -4974,11 +4933,8 @@ protected: withBorrow, withoutBorrow }; - - enum fatStatus{unInitFat, withFat, withoutFat}; mutable ownedStatus hasOwn = ownedStatus::unInitOwned; mutable borrowStatus hasBorrow = borrowStatus::unInitBorrow; - mutable fatStatus hasFat = fatStatus::unInitFat; #endif public: @@ -5002,11 +4958,7 @@ public: bool withBorrowFields() const; void initBorrowStatus() const; - - bool hasFatFields() const; - - void initFatStatus() const; -#endif + #endif bool isSugared() const { return false; } QualType desugar() const { return QualType(this, 0); } @@ -7040,11 +6992,6 @@ inline bool QualType::isBorrowQualified() const { return isLocalBorrowQualified() || getCommonPtr()->CanonicalType.isLocalBorrowQualified(); } - -inline bool QualType::isFatQualified() const { - return isLocalFatQualified() || - getCommonPtr()->CanonicalType.isLocalFatQualified(); -} #endif inline bool QualType::isRestrictQualified() const { @@ -7068,17 +7015,14 @@ inline QualType QualType::getUnqualifiedType() const { int addOwned = getCanonicalType().isOwnedQualified() ? Qualifiers::Owned : 0; int addBorrow = getCanonicalType().isBorrowQualified() ? Qualifiers::Borrow : 0; - int addFat = getCanonicalType().isFatQualified() ? Qualifiers::Fat : 0; -#else + #else int addOwned = 0; int addBorrow = 0; - int addFat = 0; -#endif + #endif if (!getTypePtr()->getCanonicalTypeInternal().hasLocalQualifiers()) - return QualType(getTypePtr(), addOwned | addBorrow | addFat); + return QualType(getTypePtr(), addOwned | addBorrow); - return QualType(getSplitUnqualifiedTypeImpl(*this).Ty, - addOwned | addBorrow | addFat); + return QualType(getSplitUnqualifiedTypeImpl(*this).Ty, addOwned | addBorrow); } inline SplitQualType QualType::getSplitUnqualifiedType() const { @@ -7101,10 +7045,6 @@ inline void QualType::removeLocalOwned() { inline void QualType::removeLocalBorrow() { removeLocalFastQualifiers(Qualifiers::Borrow); } - -inline void QualType::removeLocalFat() { - removeLocalFastQualifiers(Qualifiers::Fat); -} #endif inline void QualType::removeLocalRestrict() { diff --git a/clang/include/clang/Basic/BSC/BSCAttr.td b/clang/include/clang/Basic/BSC/BSCAttr.td index 5de2437c70fd..ee4128dd97b5 100644 --- a/clang/include/clang/Basic/BSC/BSCAttr.td +++ b/clang/include/clang/Basic/BSC/BSCAttr.td @@ -90,9 +90,3 @@ def Operator : InheritableAttr { let Args = [IntArgument<"OperatorKind">]; let Documentation = [Undocumented]; } - -def FatPtrChecked : InheritableAttr { - let Spellings = [GNU<"fat_ptr_checked">]; - let Subjects = SubjectList<[Var]>; - let Documentation = [Undocumented]; -} \ No newline at end of file diff --git a/clang/include/clang/Basic/BSC/DiagnosticBSCSemaKinds.td b/clang/include/clang/Basic/BSC/DiagnosticBSCSemaKinds.td index f0add7662c1a..656bbf15bf35 100644 --- a/clang/include/clang/Basic/BSC/DiagnosticBSCSemaKinds.td +++ b/clang/include/clang/Basic/BSC/DiagnosticBSCSemaKinds.td @@ -8,11 +8,11 @@ def err_cvrqualified_this_type_unsupported : Error< "owned type requires a pointer, '%0' is invalid">; def err_this_type_unsupported : Error< "type of parameter 'this' is wrong, expected 'This*' or 'This' or '%0*' or '%0', but found '%1'">; -def err_incompatible_pointer_cast : Error< +def err_incompatible_pointer_cast : Error< "incompatible conversion from pointer type '%0' to non-pointer type '%1' in member function call">; -def err_incompatible_const_cast : Error< +def err_incompatible_const_cast : Error< "incompatible conversion from const type '%0' to non-const type '%1' in member function call">; -def err_incompatible_owned_cast : Error< +def err_incompatible_owned_cast : Error< "incompatible conversion from non owned type '%0' to owned type '%1' in member function call">; // BSC attributes warnings and errors. @@ -63,7 +63,7 @@ def err_typecheck_invalid_owned_arrsub : Error< "owned pointer type (%0) do not support ArraySubscript operate">; def err_owned_temporary_memLeak : Error< "memory leak because temporary variable '%0' is owned or indirect owned type, please fix it">; -def err_borrow_funcPtr_incompatible : Error< +def err_funcPtr_incompatible : Error< "incompatible borrow function pointer types, cannot cast %0 to %1">; def err_borrow_on_borrow : Error<"%0 on a 'borrow' quialified type is not allowed">; def err_mut_expr_unmodifiable : Error<"the expression after '&mut' must be modifiable">; @@ -78,6 +78,7 @@ def err_borrow_qualcheck_compare : Error< def err_typecheck_borrow_func : Error<"no borrow qualified type found in the function parameters, the return type is not allowed to be borrow qualified">; def err_typecheck_borrow_subscript : Error<"subscript of borrow pointer is not allowed">; + // BSC trait warnings and errors. def err_variables_not_trait_pointer : Error<"only trait pointer type is allowed to be declared">; def err_trait_impl : Error<"function %0 in %1 is not implemented for %2">; @@ -269,25 +270,4 @@ def err_nullable_pointer_access_member : Error< "cannot access member through nullable pointer">, InGroup; def err_nonnull_assigned_by_nullable : Error< - "nonnull pointer cannot be assigned by nullable pointer">, - InGroup; - -// BSC Fat Pointer errors. -def err_fat_ptr_checked_attr_global_array_var : Error< - "fat_ptr_checked attribute can only used for global or static array variables">; -def err_typecheck_invalid_fat_not_pointer : Error< - "only pointer type can be qualified by fat">; -def err_fat_qualcheck_incompatible : Error< - "incompatible fat types, cannot cast %0 to %1">; -def err_fat_funcPtr_incompatible : Error< - "incompatible fat function pointer types, cannot cast %0 to %1">; -def err_addr_fat_on_no_fat_pointer : Error< - "'&fat' on member of no-fat pointer is not allowed">; -def err_addr_fat_on_global_var_without_checked : Error< - "'&fat' on global variables without fat_ptr_checked attribute is not allowed">; -def err_fat_ptr_type_not_found : Error< - "fat ptr type not found, you need to include %0 before using the 'fat' keyword">; -def err_fat_ptr_func_not_found : Error< - "fat ptr related function not found, you need to include %0">; -def err_fat_ptr_missing_specialization : Error< - "missing definition of template specialization for fat ptr %0">; \ No newline at end of file + "nonnull pointer cannot be assigned by nullable pointer">; diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def index 01b65d0b44a6..6530a91d7f78 100644 --- a/clang/include/clang/Basic/LangOptions.def +++ b/clang/include/clang/Basic/LangOptions.def @@ -260,7 +260,6 @@ LANGOPT(RenderScript , 1, 0, "RenderScript") LANGOPT(HLSL, 1, 0, "HLSL") #if ENABLE_BSC LANGOPT(BSC, 1, 0, "BSC") -LANGOPT(EnableFatPtr, 1, 0, "enable fat ptr") LANGOPT(DisableOwnershipCheck, 1, 0, "disable ownership check") ENUM_LANGOPT(NullabilityCheck, NullCheckZone, 2, NC_SAFE, "nullability check") #endif diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def index e2f7b89cff7c..0def6903efe1 100644 --- a/clang/include/clang/Basic/TokenKinds.def +++ b/clang/include/clang/Basic/TokenKinds.def @@ -251,7 +251,6 @@ PUNCTUATOR(caretcaret, "^^") #if ENABLE_BSC PUNCTUATOR(ampmut, "&mut") PUNCTUATOR(ampconst, "&const") -PUNCTUATOR(ampfat, "&fat") #endif // C99 6.4.1: Keywords. These turn into kw_* tokens. @@ -478,7 +477,6 @@ KEYWORD(async , KEYBSC) KEYWORD(await , KEYBSC) KEYWORD(owned , KEYBSC) KEYWORD(borrow , KEYBSC) -KEYWORD(fat , KEYBSC) KEYWORD(trait , KEYBSC) KEYWORD(safe , KEYBSC) KEYWORD(unsafe , KEYBSC) diff --git a/clang/include/clang/Driver/BSC/BSCOptions.td b/clang/include/clang/Driver/BSC/BSCOptions.td index 9c6266b558e8..f07573965822 100644 --- a/clang/include/clang/Driver/BSC/BSCOptions.td +++ b/clang/include/clang/Driver/BSC/BSCOptions.td @@ -4,8 +4,6 @@ def rewrite_bsc : Flag<["-"], "rewrite-bsc">, Flags<[CC1Option]>, Group; def rewrite_bsc_line : Flag<["-"], "line">, Flags<[CC1Option]>, HelpText<"Insert line info when rewrite BSC">; -def enable_fat_ptr : Flag<["-"], "enable-fat-ptr">, Flags<[CC1Option]>, - HelpText<"Enable fat ptr">, MarshallingInfoFlag>; def disable_ownership_check : Flag<["-"], "disable-ownership-check">, Flags<[CC1Option]>, HelpText<"Disable ownership check">, MarshallingInfoFlag>; def nullability_check : Joined<["-"], "nullability-check=">, Flags<[CC1Option]>, diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h index bf2f3c3e1bf4..2f84558ae9fd 100644 --- a/clang/include/clang/Sema/DeclSpec.h +++ b/clang/include/clang/Sema/DeclSpec.h @@ -337,20 +337,19 @@ public: // type-qualifiers enum TQ { // NOTE: These flags must be kept in sync with Qualifiers::TQ. TQ_unspecified = 0, - TQ_const = 1, - TQ_restrict = 2, - TQ_volatile = 4, + TQ_const = 1, + TQ_restrict = 2, + TQ_volatile = 4, #if ENABLE_BSC - TQ_owned = 8, - TQ_borrow = 16, - TQ_fat = 32, - TQ_unaligned = 64, + TQ_owned = 8, + TQ_borrow = 16, + TQ_unaligned = 32, // This has no corresponding Qualifiers::TQ value, because it's not treated // as a qualifier in our type system. - TQ_atomic = 128 + TQ_atomic = 64 #else - TQ_unaligned = 8, - TQ_atomic = 16 + TQ_unaligned = 8, + TQ_atomic = 16 #endif }; @@ -386,7 +385,7 @@ private: // type-qualifiers #if ENABLE_BSC - unsigned TypeQualifiers : 8; // Bitwise OR of TQ. + unsigned TypeQualifiers : 7; // Bitwise OR of TQ. #else unsigned TypeQualifiers : 5; #endif @@ -457,7 +456,7 @@ private: SourceLocation FriendLoc, ModulePrivateLoc, ConstexprLoc; SourceLocation TQ_pipeLoc; #if ENABLE_BSC - SourceLocation TQ_ownedLoc, TQ_borrowLoc, TQ_fatLoc; + SourceLocation TQ_ownedLoc, TQ_borrowLoc; SourceLocation FS_asyncLoc, FS_safe_zone_loc; bool IsImplTrait = false; // if parsing impl trait decl #endif @@ -632,7 +631,6 @@ public: #if ENABLE_BSC SourceLocation getOwnedSpecLoc() const { return TQ_ownedLoc; } SourceLocation getBorrowSpecLoc() const { return TQ_borrowLoc; } - SourceLocation getFatSpecLoc() const { return TQ_fatLoc; } void setImplTrait() { IsImplTrait = true; } bool getImplTrait() { return IsImplTrait; } llvm::Optional getConditionalCondResult() const { return ConditionalCondResult; } @@ -648,7 +646,6 @@ public: #if ENABLE_BSC TQ_ownedLoc = SourceLocation(); TQ_borrowLoc = SourceLocation(); - TQ_fatLoc = SourceLocation(); #endif TQ_restrictLoc = SourceLocation(); TQ_volatileLoc = SourceLocation(); @@ -1332,7 +1329,7 @@ struct DeclaratorChunk { struct PointerTypeInfo { /// The type qualifiers: const/volatile/restrict/owned/unaligned/atomic. #if ENABLE_BSC - unsigned TypeQuals : 8; + unsigned TypeQuals : 7; #else unsigned TypeQuals : 5; #endif @@ -1346,9 +1343,6 @@ struct DeclaratorChunk { /// The location of the borrow-qualifier, if any. SourceLocation BorrowQualLoc; - - /// The location of the fat-qualifier, if any. - SourceLocation FatQualLoc; #endif /// The location of the volatile-qualifier, if any. @@ -1380,7 +1374,7 @@ struct DeclaratorChunk { /// The type qualifiers for the array: /// const/volatile/restrict/owned/__unaligned/_Atomic. #if ENABLE_BSC - unsigned TypeQuals : 8; + unsigned TypeQuals : 7; #else unsigned TypeQuals : 5; #endif @@ -1684,7 +1678,7 @@ struct DeclaratorChunk { struct MemberPointerTypeInfo { /// The type qualifiers: const/volatile/restrict/owned/__unaligned/_Atomic. #if ENABLE_BSC - unsigned TypeQuals : 8; + unsigned TypeQuals : 7; #else unsigned TypeQuals : 5; #endif diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 1c7e7c9d7b98..9b81a134c800 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -1953,7 +1953,11 @@ public: PoppedFunctionScopePtr PopFunctionScopeInfo(const sema::AnalysisBasedWarnings::Policy *WP = nullptr, const Decl *D = nullptr, - QualType BlockType = QualType()); + QualType BlockType = QualType() + #if ENABLE_BSC + , bool isBSCCoroutine = false + #endif + ); sema::FunctionScopeInfo *getCurFunction() const { return FunctionScopes.empty() ? nullptr : FunctionScopes.back(); @@ -3220,19 +3224,6 @@ public: bool IsBSCCompatibleFutureType(QualType Ty); - // BSC Fat Ptr related. - bool CheckFatQualTypeCStyleCast(QualType LHSType, QualType RHSType, - SourceLocation RLoc); - bool CheckFatQualTypeCStyleCast(QualType LHSType, QualType RHSType); - bool CheckFatQualTypeAssignment(QualType LHSType, Expr *RHSExpr); - bool CheckFatQualTypeAssignment(QualType LHSType, QualType RHSType, - SourceLocation RLoc); - bool CheckFatFunctionPointerType(QualType LHSType, Expr *RHSExpr); - void DesugarFunctionDeclWithFatPtr(FunctionDecl *FD); - void DesugarRecordDeclWithFatPtr(RecordDecl *RD); - void DesugarGlobalVarDeclWithFatPtr(VarDecl *VD); - std::pair - BuildAllocationUnitForGlobalArrayVar(VarDecl *VD); // BSC Destructor related. BSCMethodDecl *getOrInsertBSCDestructor(RecordDecl *RD); void HandleBSCDestructorBody(RecordDecl *RD, BSCMethodDecl *Destructor, @@ -12325,25 +12316,20 @@ public: /// object with __weak qualifier. IncompatibleObjCWeakRef, -#if ENABLE_BSC - /// IncompatibleOwnedPointer - The assignment is between a owned qualified - /// pointer type with a unOwned qualified pointer type or two owned - /// qualified pointer type with different base types + #if ENABLE_BSC + /// IncompatibleOwnedPointer - The assignment is between a owned qualified pointer + /// type with a unOwned qualified pointer type or two owned qualified pointer type + /// with different base types IncompatibleOwnedPointer, - /// IncompatibleBorrowPointer - The assignment is between a borrow qualified - /// pointer type with a unBorrow qualified pointer type or two borrow - /// qualified pointer type with different base types + /// IncompatibleBorrowPointer - The assignment is between a borrow qualified pointer + /// type with a unBorrow qualified pointer type or two borrow qualified pointer type + /// with different base types IncompatibleBorrowPointer, - /// IncompatibleFatPointer - The assignment is between a fat qualified - /// pointer type with a unFat qualified pointer type or two fat qualified - /// pointer type with different base types - IncompatibleFatPointer, - /// IncompatibleBSCSafeZone - unsafe convert in the bsc safe zone. IncompatibleBSCSafeZone, -#endif + #endif /// Incompatible - We reject this conversion outright, it is invalid to /// represent it in the AST. @@ -12421,7 +12407,7 @@ public: void CheckOwnedOrIndirectOwnedType(SourceLocation ErrLoc, QualType T, StringRef Env); bool CheckOwnedDecl(SourceLocation ErrLoc, QualType T); bool CheckTemporaryVarMemoryLeak(Expr* E); - void BSCDataflowAnalysis(const FunctionDecl *FD, bool EnableOwnershipCheck = true, + void BSCDataflowAnalysis(const Decl *D, bool EnableOwnershipCheck = true, bool EnableNullabilityCheck = true); void BSCBorrowChecker(FunctionDecl *FD); bool IsInSafeZone(); @@ -12457,11 +12443,11 @@ public: bool CheckBorrowQualTypeCompare(QualType LHSType, QualType RHSType); void CheckBorrowOrIndirectBorrowType(SourceLocation ErrLoc, QualType T, StringRef Env); - QualType GetBSCAddressOperandQualType(QualType resultType, - ExprResult &Input, - const Expr *InputExpr, - UnaryOperatorKind &Opc, - SourceLocation OpLoc); + QualType GetBorrowAddressOperandQualType(QualType resultType, + ExprResult &Input, + const Expr *InputExpr, + UnaryOperatorKind &Opc, + SourceLocation OpLoc); OverloadedOperatorKind getOperatorKindByDeclarator(Declarator &D); bool CheckComparisonKindOperatorFunReturnType(FunctionDecl *FnDecl); bool CheckBSCOverloadedOperatorDeclaration(FunctionDecl *FnDecl); diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 0d34dfc80377..11d3138573fd 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -10150,11 +10150,7 @@ bool ASTContext::typesAreCompatible(QualType LHS, QualType RHS, bool CompareUnqualified) { if (getLangOpts().CPlusPlus) return hasSameType(LHS, RHS); -#if ENABLE_BSC - if (getLangOpts().BSC && getLangOpts().EnableFatPtr && - LHS->isFatPtrRecordType() && RHS->isFatPtrRecordType()) - return true; -#endif + return !mergeTypes(LHS, RHS, false, CompareUnqualified).isNull(); } diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index 38e841e5c02d..196d193d5b24 100644 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -1508,7 +1508,7 @@ ExpectedType ASTNodeImporter::VisitInjectedClassNameType( // return Importer.getToContext().getInjectedClassNameType(D, InjType); enum { #if ENABLE_BSC - TypeAlignmentInBits = 7, + TypeAlignmentInBits = 6, #else TypeAlignmentInBits = 4, #endif diff --git a/clang/lib/AST/BSC/TypeBSC.cpp b/clang/lib/AST/BSC/TypeBSC.cpp index 640f1344fb4c..47c8010a5115 100644 --- a/clang/lib/AST/BSC/TypeBSC.cpp +++ b/clang/lib/AST/BSC/TypeBSC.cpp @@ -112,13 +112,6 @@ bool QualType::isFatPtrType() const { return false; } -bool Type::withBorrowFields() const { - if (const auto *RT = dyn_cast(CanonicalType)) { - return RT->withBorrowFields(); - } - return false; -} - bool FunctionProtoType::hasOwnedRetOrParams() const { if (getReturnType().isOwnedQualified()) { return true; @@ -143,18 +136,6 @@ bool FunctionProtoType::hasBorrowRetOrParams() const { return false; } -bool FunctionProtoType::hasFatRetOrParams() const { - if (getReturnType().isFatQualified()) { - return true; - } - for (auto ParamType : getParamTypes()) { - if (ParamType.isFatQualified()) { - return true; - } - } - return false; -} - bool Type::checkFunctionProtoType(SafeZoneSpecifier SZS) const { const FunctionProtoType *FPT = nullptr; if (isFunctionType()) { @@ -319,8 +300,10 @@ bool RecordType::hasBorrowFields() const { return false; } -bool RecordType::withBorrowFields() const { - std::vector RecordTypeList; +void RecordType::initFatStatus() const { + if (hasFat != fatStatus::unInitFat) + return; + std::vector RecordTypeList; RecordTypeList.push_back(this); unsigned NextToCheckIndex = 0; @@ -328,16 +311,39 @@ bool RecordType::withBorrowFields() const { for (FieldDecl *FD : RecordTypeList[NextToCheckIndex]->getDecl()->fields()) { QualType FieldTy = FD->getType(); - if (FieldTy.isBorrowQualified()) - return true; - FieldTy = FieldTy.getCanonicalType(); + if (FieldTy.isFatQualified()) { + hasFat = fatStatus::withFat; + return; + } + QualType tempQT = FieldTy; + const Type *tempT = tempQT.getTypePtr(); + while (tempT->isPointerType()) { + tempQT = tempT->getPointeeType(); + if (tempQT.isFatQualified()) { + hasFat = fatStatus::withFat; + return; + } else { + tempQT = tempQT.getCanonicalType(); + tempT = tempQT.getTypePtr(); + } + } + FieldTy = tempQT.getCanonicalType(); if (const auto *FieldRecTy = FieldTy->getAs()) { - if (!llvm::is_contained(RecordTypeList, FieldRecTy)) + if (llvm::find(RecordTypeList, FieldRecTy) == RecordTypeList.end()) RecordTypeList.push_back(FieldRecTy); } } ++NextToCheckIndex; } + hasFat = fatStatus::withoutFat; + return; +} + +bool RecordType::hasFatFields() const { + if (hasFat == fatStatus::unInitFat) + initFatStatus(); + if (hasFat == fatStatus::withFat) + return true; return false; } @@ -379,12 +385,6 @@ bool QualType::hasBorrow() const { return getTypePtr()->hasBorrowFields(); } -bool QualType::hasFat() const { - if (isFatQualified()) - return true; - return getTypePtr()->hasFatFields(); -} - bool QualType::isConstBorrow() const { QualType QT = QualType(getTypePtr(), getLocalFastQualifiers()); if (QT.isLocalBorrowQualified()) { diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index 025a70a754fa..c55477431d52 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -2535,7 +2535,6 @@ bool Expr::isUnusedResultAWarning(const Expr *&WarnE, SourceLocation &Loc, case UO_AddrConst: case UO_AddrMutDeref: case UO_AddrConstDeref: - case UO_AddrFat: #endif case UO_Not: case UO_LNot: diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 894692ab2d5c..0d7aa2caeab1 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -8037,18 +8037,8 @@ public: QualType BaseTy; bool EvalOK; if (E->isArrow()) { -#if ENABLE_BSC - if (this->Info.getLangOpts().BSC && this->Info.getLangOpts().EnableFatPtr && - E->getBase()->getType()->isFatPtrRecordType()) { - EvalOK = true; - BaseTy = E->getBase()->getType()->getFatPtrPointeeType(); - } else { -#endif EvalOK = evaluatePointer(E->getBase(), Result); BaseTy = E->getBase()->getType()->castAs()->getPointeeType(); -#if ENABLE_BSC - } -#endif } else if (E->getBase()->isPRValue()) { assert(E->getBase()->getType()->isRecordType()); EvalOK = EvaluateTemporary(E->getBase(), Result, this->Info); @@ -8494,11 +8484,6 @@ bool LValueExprEvaluator::VisitArraySubscriptExpr(const ArraySubscriptExpr *E) { } bool LValueExprEvaluator::VisitUnaryDeref(const UnaryOperator *E) { -#if ENABLE_BSC - if (Info.getLangOpts().BSC && Info.getLangOpts().EnableFatPtr && - E->getSubExpr()->getType()->isFatPtrRecordType()) - return true; -#endif return evaluatePointer(E->getSubExpr(), Result); } @@ -15548,7 +15533,6 @@ static ICEDiag CheckICE(const Expr* E, const ASTContext &Ctx) { case UO_AddrConst: case UO_AddrMutDeref: case UO_AddrConstDeref: - case UO_AddrFat: #endif case UO_Deref: case UO_Coawait: diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp index 444b2e161204..8b829627c235 100644 --- a/clang/lib/AST/StmtPrinter.cpp +++ b/clang/lib/AST/StmtPrinter.cpp @@ -1158,12 +1158,10 @@ void StmtPrinter::VisitDeclRefExpr(DeclRefExpr *Node) { #if ENABLE_BSC if (Policy.RewriteBSC) { if (VarDecl *VD = dyn_cast(Node->getDecl())) { - if (VD->isConstexpr()) { - APValue *Res = VD->evaluateValue(); - if (Res && Res->isInt()) { - OS << Res->getInt(); - return; - } + APValue *Res = VD->getEvaluatedValue(); + if (VD->isConstexpr() && Res && Res->isInt()) { + OS << Res->getInt(); + return; } } if (auto *BD = dyn_cast(Node->getFoundDecl())) { @@ -1485,7 +1483,7 @@ void StmtPrinter::VisitUnaryOperator(UnaryOperator *Node) { #if ENABLE_BSC if (Policy.RewriteBSC) { if (Node->getOpcode() == UO_AddrConst || - Node->getOpcode() == UO_AddrMut || Node->getOpcode() == UO_AddrFat) { + Node->getOpcode() == UO_AddrMut) { OS << UnaryOperator::getOpcodeStr(UO_AddrOf); } else if (Node->getOpcode() == UO_AddrConstDeref || Node->getOpcode() == UO_AddrMutDeref) { diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp index 8279e434ad67..0cc42b5cf96f 100644 --- a/clang/lib/AST/TypePrinter.cpp +++ b/clang/lib/AST/TypePrinter.cpp @@ -200,12 +200,6 @@ static void AppendTypeQualList(raw_ostream &OS, unsigned TypeQuals, OS << "borrow"; appendSpace = true; } - if (TypeQuals & Qualifiers::Fat && !IsRewriteBSC) { - if (appendSpace) - OS << ' '; - OS << "fat"; - appendSpace = true; - } #endif if (TypeQuals & Qualifiers::Volatile) { if (appendSpace) OS << ' '; diff --git a/clang/lib/Analysis/BSC/BSCBorrowCheck.cpp b/clang/lib/Analysis/BSC/BSCBorrowCheck.cpp index 3ba19a587308..8a2295251825 100644 --- a/clang/lib/Analysis/BSC/BSCBorrowCheck.cpp +++ b/clang/lib/Analysis/BSC/BSCBorrowCheck.cpp @@ -18,6 +18,7 @@ #include "clang/Analysis/CFG.h" #include #include +#include #include using namespace clang; using namespace std; diff --git a/clang/lib/Analysis/ThreadSafetyCommon.cpp b/clang/lib/Analysis/ThreadSafetyCommon.cpp index 89b71e7a37e4..e6ab441058d0 100644 --- a/clang/lib/Analysis/ThreadSafetyCommon.cpp +++ b/clang/lib/Analysis/ThreadSafetyCommon.cpp @@ -465,8 +465,7 @@ til::SExpr *SExprBuilder::translateUnaryOperator(const UnaryOperator *UO, #if ENABLE_BSC case UO_AddrMut: case UO_AddrConst: - case UO_AddrFat: -#endif + #endif if (CapabilityExprMode) { // interpret &Graph::mu_ as an existential. if (const auto *DRE = dyn_cast(UO->getSubExpr())) { diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp index 4ff156690c82..dfa78bf59c65 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -4185,13 +4185,6 @@ void CodeGenFunction::EmitCallArgs( CallExpr::const_arg_iterator Arg = ArgRange.begin(); for (QualType Ty : ArgTypes) { assert(Arg != ArgRange.end() && "Running over edge of argument list!"); - if (Ty.getCanonicalType()->isRecordType()) - if (RecordDecl *RD = - Ty.getCanonicalType()->getAs()->getDecl()) - if (RD->getNameAsString() == "_FatPtr") { - ++Arg; - continue; - } assert( (isGenericMethod || Ty->isVariablyModifiedType() || Ty.getNonReferenceType()->isObjCRetainableType() || diff --git a/clang/lib/CodeGen/CGExprAgg.cpp b/clang/lib/CodeGen/CGExprAgg.cpp index f5d307974bd6..73b05690537d 100644 --- a/clang/lib/CodeGen/CGExprAgg.cpp +++ b/clang/lib/CodeGen/CGExprAgg.cpp @@ -1166,11 +1166,6 @@ static bool isBlockVarRef(const Expr *E) { void AggExprEmitter::VisitBinAssign(const BinaryOperator *E) { // For an assignment to work, the value on the right has // to be compatible with the value on the left. -#if ENABLE_BSC - if (!(CGF.getLangOpts().BSC && CGF.getLangOpts().EnableFatPtr && - E->getLHS()->getType()->isFatPtrRecordType() && - E->getRHS()->getType()->isFatPtrRecordType())) -#endif assert(CGF.getContext().hasSameUnqualifiedType(E->getLHS()->getType(), E->getRHS()->getType()) && "Invalid assignment"); diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp index 9a0fba739fe0..c689fa310858 100644 --- a/clang/lib/CodeGen/CGExprScalar.cpp +++ b/clang/lib/CodeGen/CGExprScalar.cpp @@ -637,12 +637,6 @@ public: Value *VisitUnaryAddrConstDeref(const UnaryOperator *E) { return Visit(E->getSubExpr()); } - Value *VisitUnaryAddrFat(const UnaryOperator *E) { - if (isa(E->getType())) // never sugared - return CGF.CGM.getMemberPointerConstant(E); - - return EmitLValue(E->getSubExpr()).getPointer(CGF); - } #endif Value *VisitUnaryDeref(const UnaryOperator *E) { if (E->getType()->isVoidType()) diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index ca169a639f95..8bca3ce34aae 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -4818,8 +4818,6 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, #if ENABLE_BSC if (Args.getLastArg(options::OPT_opt_string)) Args.AddLastArg(CmdArgs, options::OPT_opt_string); - if (Args.hasArg(options::OPT_enable_fat_ptr)) - CmdArgs.push_back("-enable-fat-ptr"); if (Args.hasArg(options::OPT_disable_ownership_check)) CmdArgs.push_back("-disable-ownership-check"); if (Args.hasArg(options::OPT_nullability_check)) { diff --git a/clang/lib/Headers/CMakeLists.txt b/clang/lib/Headers/CMakeLists.txt index 81faf5ebd67c..158dfb759f75 100644 --- a/clang/lib/Headers/CMakeLists.txt +++ b/clang/lib/Headers/CMakeLists.txt @@ -265,7 +265,6 @@ set(openmp_wrapper_files if (ENABLE_BSC) set(bsc_include_files - bsc_include/bsc_fat_ptr.hbs bsc_include/bsc_type_traits.hbs bsc_include/bsc_conditional.hbs bsc_include/future.hbs diff --git a/clang/lib/Headers/bsc_include/bsc_fat_ptr.hbs b/clang/lib/Headers/bsc_include/bsc_fat_ptr.hbs deleted file mode 100644 index e0558ee48b36..000000000000 --- a/clang/lib/Headers/bsc_include/bsc_fat_ptr.hbs +++ /dev/null @@ -1,90 +0,0 @@ -/*===---------- bsc_fat_ptr.hbs - Standard header for fat ptr ----------===*\ - * - * Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. - * See https://llvm.org/LICENSE.txt for license information. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - * -\*===----------------------------------------------------------------------===*/ - -// Define BSC fat ptr type and related functions -#ifndef BSC_FAT_PTR_HBS -#define BSC_FAT_PTR_HBS - -#include -#include -#include -#include -#include - -struct _FatPtr { - T* raw_ptr; - uint32_t key_version; - int32_t offset; -}; - -struct _AllocationUnit { - uint32_t lock_version; - uint32_t size; - uint8_t payload[]; -}; - -void _report_error(const char* msg) { - fprintf(stderr, "%s", msg); - void *callstack[10]; - int num_frames = backtrace(callstack, 10); - char **symbols = backtrace_symbols(callstack, num_frames); - - fprintf(stderr, "Call stack:\n"); - for (int i = 0; i < num_frames; i++) { - fprintf(stderr, "%s\n", symbols[i]); - } - free(symbols); - exit(EXIT_FAILURE); -} - -uint32_t _new_version_number() { - static _Atomic(uint32_t) uuid = 10000; - atomic_fetch_add(&uuid, 1); - return atomic_load(&uuid); -} - -_FatPtr checked_malloc(size_t payload_bytes) { - if (payload_bytes == 0) - _report_error("allocated size is ZERO!\n"); - size_t all_size = payload_bytes + 8; - void *raw = malloc(all_size); - uint32_t *p = (uint32_t*)raw; - uint32_t ver = _new_version_number(); - *p = ver; - *(p + 1) = payload_bytes; - _FatPtr ptr = { .raw_ptr = (void*)(p + 2), .key_version = ver, .offset = 0 }; - return ptr; -} - -void checked_free(_FatPtr ptr) { - uint8_t* head = (uint8_t*)ptr.raw_ptr - ptr.offset - 8; - struct _AllocationUnit* phead = (struct _AllocationUnit *)head; - if (ptr.key_version != phead->lock_version) - _report_error("version number error when free!\n"); - if (ptr.offset != 0) - _report_error("free a pointer which is not point to the head of an allocation!\n"); - memset(phead, 0, phead->size + 8); - free(phead); -} - -void _check_version(_FatPtr ptr) { - const uint8_t *head = (const uint8_t *)ptr.raw_ptr - ptr.offset - 8; - const struct _AllocationUnit *phead = (const struct _AllocationUnit *)head; - if (ptr.key_version != 0 && ptr.key_version != phead->lock_version) - _report_error("version number error!\n"); -} - -void _check_offset(_FatPtr ptr, int32_t bytes) { - const uint8_t *head = (const uint8_t *)ptr.raw_ptr - ptr.offset - 8; - const struct _AllocationUnit *phead = (const struct _AllocationUnit *)head; - int32_t new_offset = ptr.offset + bytes; - if (phead->size > 0 && new_offset > phead->size) - _report_error("pointer offset exceed the allocation size!\n"); -} - -#endif /* BSC_FAT_PTR_HBS */ \ No newline at end of file diff --git a/clang/lib/Lex/Lexer.cpp b/clang/lib/Lex/Lexer.cpp index c38f31d4324a..27cdfc68c13e 100644 --- a/clang/lib/Lex/Lexer.cpp +++ b/clang/lib/Lex/Lexer.cpp @@ -3914,20 +3914,6 @@ LexNextToken: ConsumeChar(ConsumeChar(CurPtr, SizeTmp, Result), SizeTmp2, Result), SizeTmp3, Result), SizeTmp4, Result), SizeTmp5, Result); } - } else if (LangOpts.BSC && Char == 'f' && - getCharAndSize(CurPtr + SizeTmp, SizeTmp2) == 'a' && - getCharAndSize(CurPtr + SizeTmp + SizeTmp2, SizeTmp3) == 't') { - char After = - getCharAndSize(CurPtr + SizeTmp + SizeTmp2 + SizeTmp3, SizeTmp4); - if ((After >= '0' && After <= '9') || (After >= 'a' && After <= 'z') || - (After >= 'A' && After <= 'Z')) { - Kind = tok::amp; - } else { - Kind = tok::ampfat; - CurPtr = ConsumeChar( - ConsumeChar(ConsumeChar(CurPtr, SizeTmp, Result), SizeTmp2, Result), - SizeTmp3, Result); - } } #endif else { diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index d314128f5ff7..3faa4ceba9ca 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -2233,26 +2233,12 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS, DeclsInGroup.push_back(FirstDecl); #if ENABLE_BSC - if (getLangOpts().BSC && FirstDecl) { - if (FunctionDecl *FD = dyn_cast_or_null(FirstDecl)) { - if (FD->isAsyncSpecified()) { - SmallVector Decls = Actions.ActOnAsyncFunctionDeclaration(FD); - for (auto &D : Decls) { - DeclsInGroup.push_back(D); - } - } - } else if (VarDecl *VD = dyn_cast_or_null(FirstDecl)) { - if (getLangOpts().EnableFatPtr && VD->hasGlobalStorage()) { - QualType QT = VD->getType().getCanonicalType(); - if (VD->hasAttr() && - QT->isConstantArrayType()) { - std::pair Decls = - Actions.BuildAllocationUnitForGlobalArrayVar(VD); - DeclsInGroup.push_back(Decls.first); - DeclsInGroup.push_back(Decls.second); - } else if (QT.isFatQualified() || QT->hasFatFields()) { - Actions.DesugarGlobalVarDeclWithFatPtr(VD); - } + if (FirstDecl) { + FunctionDecl *FD = dyn_cast_or_null(FirstDecl); + if (getLangOpts().BSC && FD && FD->isAsyncSpecified()) { + SmallVector Decls = Actions.ActOnAsyncFunctionDeclaration(FD); + for (auto &D : Decls) { + DeclsInGroup.push_back(D); } } } @@ -4533,12 +4519,6 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, isInvalid = DS.SetTypeQual(DeclSpec::TQ_borrow, Loc, PrevSpec, DiagID, getLangOpts()); break; - - // fat-qualifier: - case tok::kw_fat: - isInvalid = DS.SetTypeQual(DeclSpec::TQ_fat, Loc, PrevSpec, DiagID, - getLangOpts()); - break; #endif // C++ typename-specifier: @@ -5630,7 +5610,6 @@ bool Parser::isTypeSpecifierQualifier() { #if ENABLE_BSC case tok::kw_owned: case tok::kw_borrow: - case tok::kw_fat: #endif // Debugger support. @@ -5806,7 +5785,6 @@ bool Parser::isDeclarationSpecifier(bool DisambiguatingWithExpression) { #if ENABLE_BSC case tok::kw_owned: case tok::kw_borrow: - case tok::kw_fat: #endif case tok::kw__Sat: @@ -6130,11 +6108,6 @@ void Parser::ParseTypeQualifierListOpt( isInvalid = DS.SetTypeQual(DeclSpec::TQ_borrow, Loc, PrevSpec, DiagID, getLangOpts()); break; - // fat-qualifier: - case tok::kw_fat: - isInvalid = DS.SetTypeQual(DeclSpec::TQ_fat, Loc, PrevSpec, DiagID, - getLangOpts()); - break; #endif case tok::kw__Atomic: if (!AtomicAllowed) diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp index 6cc6ad5679e9..e711ea022f1b 100644 --- a/clang/lib/Parse/ParseExpr.cpp +++ b/clang/lib/Parse/ParseExpr.cpp @@ -1427,7 +1427,6 @@ ExprResult Parser::ParseCastExpression( #if ENABLE_BSC case tok::ampmut: case tok::ampconst: - case tok::ampfat: #endif case tok::amp: { // unary-expression: '&' cast-expression if (NotPrimaryExpression) diff --git a/clang/lib/Sema/BSC/SemaBSCCoroutine.cpp b/clang/lib/Sema/BSC/SemaBSCCoroutine.cpp index 18aa747e27fa..a67c1c8dfea5 100644 --- a/clang/lib/Sema/BSC/SemaBSCCoroutine.cpp +++ b/clang/lib/Sema/BSC/SemaBSCCoroutine.cpp @@ -917,7 +917,7 @@ static FunctionDecl *buildFutureInitFunctionDefinition(Sema &S, RecordDecl *RD, CompoundStmt::Create(S.Context, Stmts, FPOptionsOverride(), SLoc, ELoc); NewFD->setBody(CS); sema::AnalysisBasedWarnings::Policy *ActivePolicy = nullptr; - S.PopFunctionScopeInfo(ActivePolicy, NewFD, QualType()); + S.PopFunctionScopeInfo(ActivePolicy, NewFD, QualType(), true); return NewFD; } @@ -1094,7 +1094,7 @@ buildFutureStructInitFunctionDefinition(Sema &S, RecordDecl *RD, CompoundStmt::Create(S.Context, Stmts, FPOptionsOverride(), SLoc, ELoc); NewFD->setBody(CS); sema::AnalysisBasedWarnings::Policy *ActivePolicy = nullptr; - S.PopFunctionScopeInfo(ActivePolicy, NewFD, QualType()); + S.PopFunctionScopeInfo(ActivePolicy, NewFD, QualType(), true); return NewFD; } @@ -2414,7 +2414,7 @@ static BSCMethodDecl *buildFreeFunctionDeclaration(Sema &S, RecordDecl *RD, S.PushFunctionScope(); sema::AnalysisBasedWarnings::Policy *ActivePolicy = nullptr; - S.PopFunctionScopeInfo(ActivePolicy, NewFD, QualType()); + S.PopFunctionScopeInfo(ActivePolicy, NewFD, QualType(), true); return NewFD; } @@ -2645,7 +2645,7 @@ static BSCMethodDecl *buildFreeFunctionDefinition(Sema &S, RecordDecl *RD, CompoundStmt::Create(S.Context, Stmts, FPOptionsOverride(), SLoc, ELoc); NewFD->setBody(CS); sema::AnalysisBasedWarnings::Policy *ActivePolicy = nullptr; - S.PopFunctionScopeInfo(ActivePolicy, NewFD, QualType()); + S.PopFunctionScopeInfo(ActivePolicy, NewFD, QualType(), true); return NewFD; } @@ -2706,7 +2706,7 @@ static BSCMethodDecl *buildPollFunctionDeclaration(Sema &S, RecordDecl *RD, NewFD->setType(FuncType); sema::AnalysisBasedWarnings::Policy *ActivePolicy = nullptr; - S.PopFunctionScopeInfo(ActivePolicy, NewFD, QualType()); + S.PopFunctionScopeInfo(ActivePolicy, NewFD, QualType(), true); return NewFD->isInvalidDecl() ? nullptr : NewFD; } @@ -2869,7 +2869,7 @@ static BSCMethodDecl *buildPollFunctionDefinition(Sema &S, RecordDecl *RD, CompoundStmt::Create(S.Context, Stmts, FPOptionsOverride(), SLoc, ELoc); NewFD->setBody(CS); sema::AnalysisBasedWarnings::Policy *ActivePolicy = nullptr; - S.PopFunctionScopeInfo(ActivePolicy, NewFD, QualType()); + S.PopFunctionScopeInfo(ActivePolicy, NewFD, QualType(), true); return NewFD->isInvalidDecl() ? nullptr : NewFD; } diff --git a/clang/lib/Sema/BSC/SemaBSCDestructor.cpp b/clang/lib/Sema/BSC/SemaBSCDestructor.cpp index fc88350e2ba8..edc2674d1980 100644 --- a/clang/lib/Sema/BSC/SemaBSCDestructor.cpp +++ b/clang/lib/Sema/BSC/SemaBSCDestructor.cpp @@ -2,10 +2,10 @@ #include +#include "TreeTransform.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Expr.h" #include "clang/AST/RecursiveASTVisitor.h" -#include "clang/Basic/DiagnosticSema.h" #include "clang/Basic/SourceManager.h" #include "clang/Sema/Scope.h" #include "clang/Sema/Template.h" diff --git a/clang/lib/Sema/BSC/SemaBSCFatPtr.cpp b/clang/lib/Sema/BSC/SemaBSCFatPtr.cpp deleted file mode 100644 index 41a5c1ae7b21..000000000000 --- a/clang/lib/Sema/BSC/SemaBSCFatPtr.cpp +++ /dev/null @@ -1,1547 +0,0 @@ -//===--- SemaBSCFatPtr.cpp - Semantic Analysis for BSC FatPtr -------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// This file implements semantic analysis for BSC FatPtr. -// -//===----------------------------------------------------------------------===// - -#if ENABLE_BSC -#include "TreeTransform.h" -#include "clang/AST/ASTContext.h" -#include "clang/AST/StmtVisitor.h" -#include "clang/Basic/DiagnosticSema.h" -#include "llvm/ADT/SmallSet.h" -#include "llvm/ADT/SmallVector.h" -#include - -using namespace clang; -using namespace sema; - -enum AddrFatExprBaseKind { - // `&fat p->a;` p is fat ptr - // `&fat p->arr[1];` p is fat ptr - // `&fat p[1];` p is fat ptr - // `&fat p[1].a;` p is fat ptr - // `&fat s.p[1].a;` s.p is fat ptr - FatPtr, - - // `&fat a;` a is local no-array variable - // `&fat s.a;` s is local no-array variable whose type has no array field - LocalNoArrayVar, - - // `&fat arr[5];` arr is local array variable - // `&fat s.arr[5];` s is local variable whose type has array field - // `&fat s.a;` s is local variable whose type has array field - LocalArrayVar, - - // `&fat garr[5];` garr is global array variable - // `&fat g.arr[5];` g is global variable whose type has array field - // `&fat g.a;` g is global variable whose type has array field - GlobalArrayVar, - - OtherKind, -}; - -// Collect local vars whose address is taken by &fat in current function, -// also classify &fat expr by its base kind. -class AddressFatTakenFinder : public StmtVisitor { - bool IsVisitingAddrFatExpr = false; - Expr *CurrentAddrFatExpr = nullptr; - -public: - bool LocalNoArrayVarAddressIsFatTaken = false; - llvm::SmallSet LocalArrayVarsSet; - - // Map of AddrFatExpr and its base expr and base kind: - // key is AddrFatExpr, value is its base expr and kind. - // For example: - // 1. &fat p->a` where p is fat ptr - // 2. &fat arr[5]` where arr is local array variable - // 3. &fat g.arr[5]` where g is global variable whose type has array field - // the map will be: - // &fat p->a : (FatPtr, p) - // &fat arr[5] : (LocalArrayVar, arr) - // &fat g.arr[5] : (GlobalArrayVar, g) - llvm::DenseMap> - AddrFatExprAndBaseMap; - - bool hasConstantArray(QualType QT) { - QT = QT.getCanonicalType(); - if (QT->isConstantArrayType()) - return true; - else if (auto RT = dyn_cast(QT)) { - if (auto RD = dyn_cast(RT->getDecl())) - for (auto *FD : RD->fields()) - if (hasConstantArray(FD->getType())) - return true; - } - return false; - } - - void VisitStmt(Stmt *S) { - for (auto *C : S->children()) - if (C) - Visit(C); - } - - void VisitUnaryOperator(UnaryOperator *UO) { - if (UO->getOpcode() == UO_AddrFat) { - IsVisitingAddrFatExpr = true; - CurrentAddrFatExpr = UO; - AddrFatExprAndBaseMap[CurrentAddrFatExpr].first = - AddrFatExprBaseKind::OtherKind; - AddrFatExprAndBaseMap[CurrentAddrFatExpr].second = nullptr; - } - Visit(UO->getSubExpr()); - } - - void VisitMemberExpr(MemberExpr *ME) { - if (IsVisitingAddrFatExpr) { - Expr *BaseE = ME->getBase()->IgnoreImpCasts(); - if (BaseE->getType().isFatPtrType()) { - AddrFatExprAndBaseMap[CurrentAddrFatExpr].first = - AddrFatExprBaseKind::FatPtr; - AddrFatExprAndBaseMap[CurrentAddrFatExpr].second = BaseE; - CurrentAddrFatExpr = nullptr; - IsVisitingAddrFatExpr = false; - return; - } - } - Visit(ME->getBase()); - } - - void VisitArraySubscriptExpr(ArraySubscriptExpr *ASE) { - if (IsVisitingAddrFatExpr) { - Expr *BaseE = ASE->getBase()->IgnoreImpCasts(); - if (BaseE->getType().isFatPtrType()) { - AddrFatExprAndBaseMap[CurrentAddrFatExpr].first = - AddrFatExprBaseKind::FatPtr; - AddrFatExprAndBaseMap[CurrentAddrFatExpr].second = BaseE; - CurrentAddrFatExpr = nullptr; - IsVisitingAddrFatExpr = false; - return; - } - } - Visit(ASE->getBase()); - } - - void VisitDeclRefExpr(DeclRefExpr *DRE) { - auto VD = dyn_cast(DRE->getDecl()); - if (VD && IsVisitingAddrFatExpr) { - QualType QT = VD->getType(); - if (VD->hasLocalStorage()) { - if (hasConstantArray(QT)) { - AddrFatExprAndBaseMap[CurrentAddrFatExpr].first = - AddrFatExprBaseKind::LocalArrayVar; - AddrFatExprAndBaseMap[CurrentAddrFatExpr].second = DRE; - LocalArrayVarsSet.insert(VD); - } else { - AddrFatExprAndBaseMap[CurrentAddrFatExpr].first = - AddrFatExprBaseKind::LocalNoArrayVar; - LocalNoArrayVarAddressIsFatTaken = true; - } - } else if (VD->hasGlobalStorage() && hasConstantArray(QT)) { - AddrFatExprAndBaseMap[CurrentAddrFatExpr].first = - AddrFatExprBaseKind::GlobalArrayVar; - AddrFatExprAndBaseMap[CurrentAddrFatExpr].second = DRE; - } - CurrentAddrFatExpr = nullptr; - IsVisitingAddrFatExpr = false; - } - } -}; - -// Desugar fat ptr for FunctionDecl, VarDecl and RecordDecl. -class TransformFatPtr : public TreeTransform { - typedef TreeTransform BaseTransform; - FunctionDecl *FD = nullptr; - - // cache fat ptr related function to avoid frequent lookup. - std::map FatPtrFD; - // cache fat ptr TemplateDecl to avoid frequent lookup. - ClassTemplateDecl *FatPtrTD = nullptr; - - AddressFatTakenFinder Finder; - VarDecl *LocalLockVersionVD = nullptr; - llvm::DenseMap> - AllocationUnitForLocalArrayVarMap; - - // when deref, access member or some pointer calculation, - // fat ptr should be desugared to its ptr member, for example: - // 1. `*p` should be desugared to `*p.raw_ptr` - // 2. `p->a` should be desugared to `p.raw_ptr->a` - // 3. `p1 - p2` should be desugared to `p1.raw_ptr - p2.raw_ptr` - // 4. `(int*)p` should be desugared to `(int*)p.raw_ptr` - // but `int *fat p1 = p2;` p2 should not be desugared, - // and `float *fat p1 = (float *fat)p2;` p2 should not be desugared. - bool DesugarToMemberPtr = false; - bool InsertCheckVersionCall = false; - std::pair InsertCheckOffsetCall = - std::make_pair(false, nullptr); - std::stack CheckCallExpr; - - // when accessing array member through FatPtr, - // such as `p->arr[3]` or `p->a.b.arr[3]` - // we should check offset to avoid out of bounds. - ArraySubscriptExpr *CurrentASEToAccessMemberThroughFatPtr = nullptr; - -public: - TransformFatPtr(Sema &SemaRef) : BaseTransform(SemaRef) {} - - TransformFatPtr(Sema &SemaRef, FunctionDecl *Fd) - : BaseTransform(SemaRef), FD(Fd) { - FatPtrFD["_check_version"] = nullptr; - FatPtrFD["_check_offset"] = nullptr; - FatPtrFD["_new_version_number"] = nullptr; - } - - // Entry to desugar FunctionDecl - void TransformFunctionDecl(); - // Entry to desugar global or local VarDecl - void TransformVarDecl(VarDecl *VD); - // Entry to desugar global or local RecordDecl - void TransformRecordDecl(RecordDecl *RD); - - StmtResult TransformCompoundStmt(CompoundStmt *CS, bool IsStmtExpr); - StmtResult TransformCompoundStmt(CompoundStmt *CS); - StmtResult TransformDeclStmt(DeclStmt *DS); - StmtResult TransformReturnStmt(ReturnStmt *RS); - ExprResult TransformDeclRefExpr(DeclRefExpr *DRE); - ExprResult TransformImplicitCastExpr(ImplicitCastExpr *ICE); - ExprResult TransformParenExpr(ParenExpr *PE); - ExprResult TransformCompoundLiteralExpr(CompoundLiteralExpr *CLE); - ExprResult TransformInitListExpr(InitListExpr *ILE); - ExprResult TransformCallExpr(CallExpr *CE); - ExprResult TransformArraySubscriptExpr(ArraySubscriptExpr *ASE); - ExprResult TransformCStyleCastExpr(CStyleCastExpr *CSCE); - ExprResult TransformUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *UETT); - ExprResult TransformMemberExpr(MemberExpr *ME); - - ExprResult TransformBinaryOperator(BinaryOperator *BO); - ExprResult HandleBOFatPtrAddOrSubInteger(BinaryOperator *BO); - ExprResult HandleBOFatPtrSubFatPtr(BinaryOperator *BO); - - ExprResult TransformUnaryOperator(UnaryOperator *UO); - ExprResult HandleUODerefFatPtr(UnaryOperator *UO); - ExprResult HandleUOAddrFatOnExprWithFatPtrBase(UnaryOperator *UO, Expr *FatPtrBaseE); - ExprResult HandleUOAddrFatOnLocalNoArrayVar(UnaryOperator *UO); - ExprResult HandleUOAddrFatOnArrayVar(UnaryOperator *UO, Expr *VarBaseE); - -private: - // Handle allocation unit for local vars whose address is taken by &fat. - CompoundStmt *HandleLocalAllocationUnit(CompoundStmt *CS, - bool IsTopLevelCompoundStmt); - - std::pair - BuildAllocationUnitHeaderForLocalNoArrayVars(SourceLocation SLoc); - - Expr *BuildLockVersionAssignmentForLocalNoArrayVars(SourceLocation SLoc); - - std::pair - BuildAllocationUnitForLocalArrayVar(VarDecl *VD); - - QualType DesugarFatPtrType(SourceLocation SLoc, QualType T); - MemberExpr *BuildMemberForFatPtr(Expr *FatPtrBaseE, std::string FieldName); - Expr *BuildFatPtrCall(SourceLocation SLoc, std::string FDName, - llvm::SmallVector &Args); - Expr *BuildFatPtrCompoundLiteralExprForRawPtr(Expr *RawPtrE, QualType QT); - Expr *BuildFatPtrCompoundLiteralExpr(Expr *FirstInitE, Expr *SecondInitE, - Expr *ThirdInitE, QualType QT); - bool IsAccessingArrayMemberThroughFatPtr(Expr *E); - void VisitExprToBuildOffsetOfComponent( - Expr *E, SourceLocation SLoc, - SmallVector &Comps); -}; - -void TransformFatPtr::TransformFunctionDecl() { - // Desugar fat qualified return type. - QualType ReturnTy = - DesugarFatPtrType(FD->getBeginLoc(), FD->getReturnType()); - // Desugar fat qualified parameters. - llvm::SmallVector ParamTys; - for (ParmVarDecl *PVD : FD->parameters()) { - QualType ParamTy = - DesugarFatPtrType(PVD->getLocation(), PVD->getType()); - PVD->setType(ParamTy); - ParamTys.push_back(ParamTy); - } - // Set desugared function type. - FD->setType(SemaRef.Context.getFunctionType( - ReturnTy, ParamTys, - FD->getType()->getAs()->getExtProtoInfo())); - - // First traversal: - // find local vars whose address is taken by `&fat` expr. - Finder.VisitStmt(FD->getBody()); - - // Second traversal - CompoundStmt *NewCS = - HandleLocalAllocationUnit(dyn_cast(FD->getBody()), true); - - // Third traversal: desugar stmt and expr of function body. - FD->setBody(TransformStmt(NewCS).get()); -} - -void TransformFatPtr::TransformVarDecl(VarDecl *VD) { - QualType QT = DesugarFatPtrType(VD->getLocation(), VD->getType()); - VD->setType(QT); - if (Expr *Init = VD->getInit()) { - if (QT->isFatPtrRecordType() && isa(Init)) { - // `int *fat p = nullptr;` should be desugared to - // `_FatPtr p = (_FatPtr) { nullptr, 0, 0 };` - VD->setInit(BuildFatPtrCompoundLiteralExprForRawPtr(Init, QT)); - } else { - VD->setInit(BaseTransform::TransformExpr(Init).get()); - } - } -} - -void TransformFatPtr::TransformRecordDecl(RecordDecl *RD) { - // Desugar fat ptr field. - for (FieldDecl *FD : RD->fields()) - FD->setType(DesugarFatPtrType(FD->getLocation(), FD->getType())); -} - -// 1. For all local vars whose type has no array and address is -// taken by &fat, we think they are packed in an allocation unit -// in stack and share a lock_version, so we build 2 VarDecl -// `_local_lock_version` and `_local_size` as allocation unit -// header and insert them to the beginning of function body. -// 2. Before function return, `_local_lock_version` should be set to -// 0, so we build assignment statement before return stmt and in the -// end of function body. -// 3. For local var whose type has array and address is taken by &fat, -// because every array have its own size, so we pack every var into -// a struct as allocation unit. -CompoundStmt * -TransformFatPtr::HandleLocalAllocationUnit(CompoundStmt *CS, - bool IsTopLevelCompoundStmt) { - if (CS == nullptr) - return CS; - llvm::SmallVector Statements; - if (IsTopLevelCompoundStmt && Finder.LocalNoArrayVarAddressIsFatTaken) { - std::pair Headers = - BuildAllocationUnitHeaderForLocalNoArrayVars(CS->getBeginLoc()); - Statements.push_back(Headers.second); - Statements.push_back(Headers.first); - } - if (Finder.LocalArrayVarsSet.size() == 0) { - for (auto *B : CS->body()) - Statements.push_back(B); - } else { - for (auto *B : CS->body()) { - if (auto ChildCS = dyn_cast(B)) { - Statements.push_back(HandleLocalAllocationUnit(ChildCS, false)); - } else if (auto DS = dyn_cast(B)) { - if (DS->isSingleDecl()) { - if (VarDecl *VD = dyn_cast(DS->getSingleDecl())) { - if (Finder.LocalArrayVarsSet.count(VD)) { - std::pair Decls = - BuildAllocationUnitForLocalArrayVar(VD); - Statements.push_back(Decls.first); - Statements.push_back(Decls.second); - continue; - } - } - Statements.push_back(B); - } else { - // TODO: a DeclStmt contains more than one VarDecl, we should - // divide it into several DeclStmts. - Statements.push_back(B); - } - } else if (isa(B) && Finder.LocalNoArrayVarAddressIsFatTaken) { - Statements.push_back( - BuildLockVersionAssignmentForLocalNoArrayVars(B->getBeginLoc())); - Statements.push_back(B); - } else { - Statements.push_back(B); - } - } - } - if (IsTopLevelCompoundStmt && Finder.LocalNoArrayVarAddressIsFatTaken) - Statements.push_back( - BuildLockVersionAssignmentForLocalNoArrayVars(CS->getEndLoc())); - auto NewCS = CompoundStmt::Create( - SemaRef.Context, Statements, FPOptionsOverride(), CS->getLBracLoc(), - CS->getRBracLoc(), CS->getSafeSpecifier(), CS->getSafeSpecifierLoc(), - CS->getCompSafeZoneSpecifier()); - return NewCS; -} - -// Build allocation unit header for all local vars whose -// address is taken by `&fat`: -// @code -// uint32_t _local_size = 0; -// uint32_t _local_lock_version = _new_version_number(); -// @endcode -std::pair -TransformFatPtr::BuildAllocationUnitHeaderForLocalNoArrayVars( - SourceLocation SLoc) { - QualType UInt32Ty = SemaRef.Context.getIntTypeForBitwidth(32, false); - // Build VarDecl: uint32_t _local_lock_version = _new_version_number(); - VarDecl *LockVersionVD = - VarDecl::Create(SemaRef.Context, FD, SLoc, SLoc, - &SemaRef.Context.Idents.get("_local_lock_version"), - UInt32Ty, nullptr, SC_None); - llvm::SmallVector Args; - LockVersionVD->setInit(BuildFatPtrCall(SLoc, "_new_version_number", Args)); - DeclStmt *LockVersionDS = - new (SemaRef.Context) DeclStmt(DeclGroupRef(LockVersionVD), SLoc, SLoc); - LocalLockVersionVD = LockVersionVD; - - // Build VarDecl: uint32_t _local_size = 0; - VarDecl *SizeVD = VarDecl::Create(SemaRef.Context, FD, SLoc, SLoc, - &SemaRef.Context.Idents.get("_local_size"), - UInt32Ty, nullptr, SC_None); - SizeVD->setInit(IntegerLiteral::Create(SemaRef.Context, llvm::APInt(32, 0), - UInt32Ty, SLoc)); - DeclStmt *SizeDS = - new (SemaRef.Context) DeclStmt(DeclGroupRef(SizeVD), SLoc, SLoc); - - return std::make_pair(LockVersionDS, SizeDS); -} - -// Build allocation unit RecordDecl for every local array var whose element -// address is taken by &fat, for example, if we have an array variable whose -// VarDecl is `int arr[3] = {1, 2, 3};`, we desugar this decl to: -// @code -// struct _arr_AllocationUnit { -// uint32_t lock_version; -// uint32_t size; -// int arr[3]; -// }; -// struct _arr_AllocationUnit _arr = -// { _new_version_number(), 12, {1, 2, 3} }; -// @endcode -std::pair -TransformFatPtr::BuildAllocationUnitForLocalArrayVar(VarDecl *VD) { - SourceLocation SLoc = VD->getLocation(); - // Build RecordDecl: struct _arr_AllocationUnit; - RecordDecl *RD = RecordDecl::Create( - SemaRef.Context, TagDecl::TagKind::TTK_Struct, FD, SLoc, SLoc, - &SemaRef.Context.Idents.get("_" + VD->getNameAsString() + - "_AllocationUnit")); - // Add three Field - RD->startDefinition(); - QualType UInt32Ty = SemaRef.Context.getIntTypeForBitwidth(32, false); - FieldDecl *LockVersionField = FieldDecl::Create( - SemaRef.Context, RD, SLoc, SLoc, &SemaRef.Context.Idents.get("lock_version"), - UInt32Ty, SemaRef.Context.getTrivialTypeSourceInfo(UInt32Ty), - nullptr, false, ICIS_NoInit); - FieldDecl *SizeField = FieldDecl::Create( - SemaRef.Context, RD, SLoc, SLoc, &SemaRef.Context.Idents.get("size"), - UInt32Ty, SemaRef.Context.getTrivialTypeSourceInfo(UInt32Ty), - nullptr, false, ICIS_NoInit); - FieldDecl *VDField = FieldDecl::Create( - SemaRef.Context, RD, SLoc, SLoc, - &SemaRef.Context.Idents.get(VD->getNameAsString()), VD->getType(), - SemaRef.Context.getTrivialTypeSourceInfo(VD->getType()), - nullptr, false, ICIS_NoInit); - LockVersionField->setAccess(AS_public); - SizeField->setAccess(AS_public); - VDField->setAccess(AS_public); - RD->addDecl(LockVersionField); - RD->addDecl(SizeField); - RD->addDecl(VDField); - RD->completeDefinition(); - - // Build VarDecl: struct _arr_AllocationUnit _arr; - VarDecl *NewVD = - VarDecl::Create(SemaRef.Context, FD, SLoc, SLoc, - &SemaRef.Context.Idents.get("_" + VD->getNameAsString()), - QualType(RD->getTypeForDecl(), 0), nullptr, SC_None); - // Build InitExpr - // Build first init expr: _ner_version_number() - llvm::SmallVector Args; - Expr *LockVersionInit = BuildFatPtrCall(SLoc, "_new_version_number", Args); - // Build second init expr by array size - Expr *SizeInit = - IntegerLiteral::Create(SemaRef.Context, - llvm::APInt(32, SemaRef.Context.getTypeSize(VD->getType()) / 8), - UInt32Ty, SLoc); - // Build InitListExpr - SmallVector Inits{LockVersionInit, SizeInit}; - if (VD->getInit()) - Inits.push_back(VD->getInit()); - Expr *ILE = SemaRef.BuildInitList(SLoc, Inits, SLoc).get(); - ILE->setType(QualType(RD->getTypeForDecl(), 0)); - NewVD->setInit(ILE); - AllocationUnitForLocalArrayVarMap[VD] = std::make_pair(RD, NewVD); - DeclStmt *RDDS = new (SemaRef.Context) DeclStmt(DeclGroupRef(RD), SLoc, SLoc); - DeclStmt *NewVDDS = new (SemaRef.Context) DeclStmt(DeclGroupRef(NewVD), SLoc, SLoc); - return std::make_pair(RDDS, NewVDDS); -} - -// Build assignment expr: _local_lock_version = 0; -Expr *TransformFatPtr::BuildLockVersionAssignmentForLocalNoArrayVars( - SourceLocation SLoc) { - if (LocalLockVersionVD) { - QualType VQT = LocalLockVersionVD->getType(); - Expr *LHS = - SemaRef.BuildDeclRefExpr(LocalLockVersionVD, VQT, VK_LValue, SLoc); - Expr *RHS = - IntegerLiteral::Create(SemaRef.Context, llvm::APInt(32, 0), VQT, SLoc); - return SemaRef.CreateBuiltinBinOp(SLoc, BO_Assign, LHS, RHS).get(); - } - return nullptr; -} - -StmtResult TransformFatPtr::TransformCompoundStmt(CompoundStmt *CS) { - return this->TransformCompoundStmt(CS, false); -} - -StmtResult TransformFatPtr::TransformCompoundStmt(CompoundStmt *CS, - bool IsStmtExpr) { - if (CS == nullptr) - return CS; - llvm::SmallVector Statements; - for (auto *B : CS->body()) { - StmtResult Result = BaseTransform::TransformStmt(B); - while (!CheckCallExpr.empty()) { - Statements.push_back(CheckCallExpr.top()); - CheckCallExpr.pop(); - } - Statements.push_back(Result.getAs()); - } - auto NewCS = CompoundStmt::Create( - SemaRef.Context, Statements, FPOptionsOverride(), CS->getLBracLoc(), - CS->getRBracLoc(), CS->getSafeSpecifier(), CS->getSafeSpecifierLoc(), - CS->getCompSafeZoneSpecifier()); - return NewCS; -} - -ExprResult TransformFatPtr::TransformDeclRefExpr(DeclRefExpr *DRE) { - SourceLocation SLoc = DRE->getBeginLoc(); - QualType QT = DesugarFatPtrType(SLoc, DRE->getType()); - DRE->setType(QT); - if (QT->isFatPtrRecordType()) { - if (InsertCheckOffsetCall.first) { - // Build CallExpr to check offset. - llvm::SmallVector Args{DRE, InsertCheckOffsetCall.second}; - Expr *CE = BuildFatPtrCall(SLoc, "_check_offset", Args); - CheckCallExpr.push(CE); - InsertCheckOffsetCall.first = false; - InsertCheckOffsetCall.second = nullptr; - } - if (InsertCheckVersionCall) { - // Build CallExpr to check version. - llvm::SmallVector Args{DRE}; - Expr *CE = BuildFatPtrCall(SLoc, "_check_version", Args); - CheckCallExpr.push(CE); - InsertCheckVersionCall = false; - } - if (DesugarToMemberPtr) { - DesugarToMemberPtr = false; - // Desugar p to p.raw_ptr - return BuildMemberForFatPtr(DRE, "raw_ptr"); - } - } else if (auto VD = dyn_cast(DRE->getDecl())) { - if (AllocationUnitForLocalArrayVarMap.count(VD) || - SemaRef.Context.BSCDesugaredMap.count(VD)) { - // Desugar arr to _arr.arr where arr has its owned AllocationUnit. - RecordDecl *AllocationUnitRD; - VarDecl *AllocationUnitVD; - if (AllocationUnitForLocalArrayVarMap.count(VD)) { - AllocationUnitRD = AllocationUnitForLocalArrayVarMap[VD].first; - AllocationUnitVD = AllocationUnitForLocalArrayVarMap[VD].second; - } else { - AllocationUnitRD = - dyn_cast(SemaRef.Context.BSCDesugaredMap[VD][0]); - AllocationUnitVD = - dyn_cast(SemaRef.Context.BSCDesugaredMap[VD][1]); - } - Expr *AllocationUnitVDRef = SemaRef.BuildDeclRefExpr( - AllocationUnitVD, AllocationUnitVD->getType(), VK_LValue, SLoc); - RecordDecl::field_iterator it = AllocationUnitRD->field_begin(); - it++; - it++; - FieldDecl *ArrayFD = *it; - return SemaRef.BuildMemberExpr( - AllocationUnitVDRef, false, SLoc, NestedNameSpecifierLoc(), SLoc, - ArrayFD, DeclAccessPair::make(AllocationUnitRD, ArrayFD->getAccess()), - false, DeclarationNameInfo(), ArrayFD->getType(), VK_LValue, - OK_Ordinary); - } - } - return DRE; -} - -ExprResult TransformFatPtr::TransformImplicitCastExpr(ImplicitCastExpr *ICE) { - QualType QT = ICE->getType(); - if (DesugarToMemberPtr && QT.isFatPtrType()) { - QT = QT->getFatPtrPointeeType(); - ICE->setType(SemaRef.Context.getPointerType(QT)); - ICE->setCastKind(CK_LValueToRValue); - } else { - QT = DesugarFatPtrType(ICE->getBeginLoc(), QT); - ICE->setType(QT); - } - Expr *SubE = ICE->getSubExpr(); - ICE->setSubExpr(BaseTransform::TransformExpr(SubE).get()); - if (ICE->getCastKind() == CK_NoOp && QT->isFatPtrRecordType() && - SubE->getType()->isFatPtrRecordType()) - ICE->setType(SubE->getType()); - return ICE; -} - -ExprResult TransformFatPtr::TransformParenExpr(ParenExpr *PE) { - QualType QT = PE->getType(); - if (DesugarToMemberPtr && QT.isFatPtrType()) { - QT = QT->getFatPtrPointeeType(); - PE->setType(SemaRef.Context.getPointerType(QT)); - } else { - QT = DesugarFatPtrType(PE->getBeginLoc(), QT); - PE->setType(QT); - } - PE->setSubExpr(BaseTransform::TransformExpr(PE->getSubExpr()).get()); - return PE; -} - -StmtResult TransformFatPtr::TransformDeclStmt(DeclStmt *DS) { - for (auto D : DS->decls()) { - if (VarDecl *VD = dyn_cast(D)) - TransformVarDecl(VD); - else if (RecordDecl *RD = dyn_cast(D)) - TransformRecordDecl(RD); - else - BaseTransform::TransformDefinition(D->getLocation(), D); - } - return DS; -} - -ExprResult -TransformFatPtr::TransformCompoundLiteralExpr(CompoundLiteralExpr *CLE) { - CLE->setType(DesugarFatPtrType(CLE->getBeginLoc(), CLE->getType())); - CLE->setInitializer( - BaseTransform::TransformExpr(CLE->getInitializer()).get()); - return CLE; -} - -ExprResult TransformFatPtr::TransformInitListExpr(InitListExpr *ILE) { - ILE->setType(DesugarFatPtrType(ILE->getBeginLoc(), ILE->getType())); - for (unsigned i = 0; i < ILE->getNumInits(); i++) - ILE->setInit(i, BaseTransform::TransformExpr(ILE->getInit(i)).get()); - return ILE; -} - -StmtResult TransformFatPtr::TransformReturnStmt(ReturnStmt *RS) { - Expr *RV = RS->getRetValue(); - QualType ReturnQT = FD->getReturnType(); - if (ReturnQT->isFatPtrRecordType() && isa(RV)) { - // if a function has fat pointer return type, such as `int *fat foo();` - // `return nullptr;` should be desugared to - // `return (_FatPtr){ nullptr, 0, 0 };` - RS->setRetValue(BuildFatPtrCompoundLiteralExprForRawPtr(RV, ReturnQT)); - } else { - RS->setRetValue(BaseTransform::TransformExpr(RV).get()); - } - return RS; -} - -ExprResult TransformFatPtr::TransformCallExpr(CallExpr *CE) { - CE->setType(DesugarFatPtrType(CE->getBeginLoc(), CE->getType())); - FunctionDecl *CalleeFD = CE->getDirectCallee(); - for (unsigned i = 0; i < CalleeFD->getNumParams(); i++) { - QualType ParamQT = CalleeFD->getParamDecl(i)->getType(); - Expr *ArgE = CE->getArg(i); - if (ParamQT->isFatPtrRecordType() && isa(ArgE)) { - // if a function has fat pointer parameter, such as - // `void foo(int *fat p);` - // `foo(nullptr)` should be desugared to - // `foo((_FatPtr){ nullptr, 0, 0 })` - CE->setArg(i, BuildFatPtrCompoundLiteralExprForRawPtr(ArgE, ParamQT)); - } else - CE->setArg(i, BaseTransform::TransformExpr(ArgE).get()); - } - return CE; -} - -ExprResult -TransformFatPtr::TransformArraySubscriptExpr(ArraySubscriptExpr *ASE) { - SourceLocation SLoc = ASE->getBeginLoc(); - QualType QT = DesugarFatPtrType(SLoc, ASE->getType()); - ASE->setType(QT); - - Expr *LHS = ASE->getLHS(); - Expr *RHS = ASE->getRHS(); - QualType LHSQT = LHS->getType(); - if (LHSQT.isFatPtrType()) { - // p[i] should be desugared to `p.raw_ptr[i]` and insert check call expr. - ASE->setRHS(BaseTransform::TransformExpr(RHS).get()); - DesugarToMemberPtr = true; - InsertCheckVersionCall = true; - InsertCheckOffsetCall.first = true; - // Build offset arg expr, for example: - // for p[i] where p[i] is int type, - // offset should be `i * sizeof(int) + sizeof(int)` - QualType Int32Ty = SemaRef.Context.getIntTypeForBitwidth(32, true); - Expr *OffsetExprLHS = - IntegerLiteral::Create(SemaRef.Context, - llvm::APInt(32, SemaRef.Context.getTypeSize(QT) / 8), Int32Ty, SLoc); - OffsetExprLHS = - SemaRef.CreateBuiltinBinOp(SLoc, BO_Mul, ASE->getRHS(), OffsetExprLHS).get(); - Expr *OffsetExprRHS = - IntegerLiteral::Create(SemaRef.Context, - llvm::APInt(32, SemaRef.Context.getTypeSize(QT) / 8), Int32Ty, SLoc); - Expr *OffsetExpr = - SemaRef.CreateBuiltinBinOp(SLoc, BO_Add, OffsetExprLHS, OffsetExprRHS).get(); - InsertCheckOffsetCall.second = OffsetExpr; - ASE->setLHS(BaseTransform::TransformExpr(LHS).get()); - return ASE; - } else if (IsAccessingArrayMemberThroughFatPtr(ASE)) { - // If current ASE is accessing array member through fat ptr, - // such as `p->arr[i]` or `p->b.arr[i]`, in order to calculate - // offset when transform inner MemberEXpr, we record current ASE. - CurrentASEToAccessMemberThroughFatPtr = ASE; - } - ASE->setLHS(BaseTransform::TransformExpr(LHS).get()); - ASE->setRHS(BaseTransform::TransformExpr(RHS).get()); - return ASE; -} - -ExprResult TransformFatPtr::TransformCStyleCastExpr(CStyleCastExpr *CSCE) { - SourceLocation SLoc = CSCE->getBeginLoc(); - QualType QT = CSCE->getType(); - Expr *SubE = CSCE->getSubExpr(); - QualType SubQT = SubE->getType(); - if (QT->isPointerType() && !QT.isFatPtrType() && SubQT.isFatPtrType()) { - // desuagr `(int*)fat_p` to `(int*)fat_p.raw_ptr` - DesugarToMemberPtr = true; - InsertCheckVersionCall = true; - InsertCheckOffsetCall.first = true; - // Build offset arg expr - Expr *OffsetExpr = - IntegerLiteral::Create(SemaRef.Context, llvm::APInt(32, 1), - SemaRef.Context.getIntTypeForBitwidth(32, true), SLoc); - InsertCheckOffsetCall.second = OffsetExpr; - } else if (QT.isFatPtrType() && SubQT->isPointerType() && !SubQT.isFatPtrType()) { - QT = DesugarFatPtrType(SLoc, QT); - // desugar `(int *fat)raw_p` to `(_FatPtr) { raw_p, 0, 0 }` - Expr *SubRawPtrE = BaseTransform::TransformExpr(SubE).get(); - return BuildFatPtrCompoundLiteralExprForRawPtr(SubRawPtrE, QT); - } else if (QT.isFatPtrType() && SubQT.isFatPtrType()) { - return BaseTransform::TransformExpr(SubE).get(); - } - CSCE->setSubExpr(BaseTransform::TransformExpr(SubE).get()); - return CSCE; -} - -ExprResult TransformFatPtr::TransformUnaryExprOrTypeTraitExpr( - UnaryExprOrTypeTraitExpr *UETT) { - SourceLocation SLoc = UETT->getBeginLoc(); - QualType QT = DesugarFatPtrType(SLoc, UETT->getType()); - UETT->setType(QT); - // Recalculate size of fat ptr which is larger than raw ptr: - // `sizeof(int *)` equals to 8, but `sizeof(int *fat)` equals to 16 - if (UETT->isArgumentType()) { - QualType OldQT = UETT->getArgumentType(); - if (OldQT.isFatPtrType()) { - QualType NewQT = DesugarFatPtrType(SLoc, OldQT); - TypeSourceInfo *NewTInfo = - SemaRef.Context.getTrivialTypeSourceInfo(NewQT, SLoc); - return BaseTransform::RebuildUnaryExprOrTypeTrait( - NewTInfo, UETT->getOperatorLoc(), UETT->getKind(), - UETT->getSourceRange()); - } - } - return UETT; -} - -// BinaryOperator includes: -// reassign fat ptr : p1 = p2, p1 = nullptr -// calculation between fat ptr : p + 1, p - 1, p1 - p2 -ExprResult TransformFatPtr::TransformBinaryOperator(BinaryOperator *BO) { - SourceLocation SLoc = BO->getBeginLoc(); - BO->setType(DesugarFatPtrType(SLoc, BO->getType())); - Expr *LHS = BO->getLHS(); - Expr *RHS = BO->getRHS(); - QualType LHSQT = LHS->getType(); - QualType RHSQT = RHS->getType(); - BinaryOperator::Opcode Op = BO->getOpcode(); - if (Op == BO_Sub && LHSQT.isFatPtrType() && RHSQT.isFatPtrType()) { - // Substraction between two fat ptr - // `p1 - p2` should be desugared to `p1.raw_ptr - p2.raw_ptr` - return HandleBOFatPtrSubFatPtr(BO); - } else if (BO->isAdditiveOp() && LHSQT.isFatPtrType() && RHSQT->isIntegerType()) { - // addition or substraction between fat ptr and integer - // `p + 1` should be desugared to - // `(_FatPtr) { p.raw_ptr + 1, p.key_version, p.offset + 1 * sizeof(int) }` - return HandleBOFatPtrAddOrSubInteger(BO); - } else if (BO->isAssignmentOp() && LHSQT.isFatPtrType() && - isa(RHS)) { - // `p = nullptr;` should be desugared to - // `p = (_FatPtr){ nullptr, 0, 0 };` - LHSQT = DesugarFatPtrType(SLoc, LHSQT); - BO->setLHS(BaseTransform::TransformExpr(LHS).get()); - BO->setRHS(BuildFatPtrCompoundLiteralExprForRawPtr(RHS, LHSQT)); - return BO; - } - BO->setLHS(BaseTransform::TransformExpr(LHS).get()); - BO->setRHS(BaseTransform::TransformExpr(RHS).get()); - return BO; -} - -ExprResult TransformFatPtr::HandleBOFatPtrSubFatPtr(BinaryOperator *BO) { - DesugarToMemberPtr = true; - BO->setLHS(BaseTransform::TransformExpr(BO->getLHS()).get()); - DesugarToMemberPtr = true; - BO->setRHS(BaseTransform::TransformExpr(BO->getRHS()).get()); - return BO; -} - -// addition or substraction between fat ptr and integer -// `p + 1` should be desugared to -// `(_FatPtr) { p.ptr + 1, p.key_version, p.offset + 1 * sizeof(int) }` -ExprResult TransformFatPtr::HandleBOFatPtrAddOrSubInteger(BinaryOperator *BO) { - SourceLocation SLoc = BO->getBeginLoc(); - QualType QT = BO->getType(); - BinaryOperator::Opcode Op = BO->getOpcode(); - Expr *FatPtrLHS = BO->getLHS()->IgnoreImpCasts(); - DesugarToMemberPtr = true; - Expr *LHS = BaseTransform::TransformExpr(BO->getLHS()).get(); - Expr *RHS = BaseTransform::TransformExpr(BO->getRHS()).get(); - Expr *FirstInitE = - SemaRef.CreateBuiltinBinOp(SLoc, Op, LHS, RHS).get(); - Expr *SecondInitE = BuildMemberForFatPtr(FatPtrLHS, "key_version"); - - Expr *ThirdInitLHS = BuildMemberForFatPtr(FatPtrLHS, "offset"); - QualType PointeeQT = FatPtrLHS->getType()->getFatPtrPointeeType(); - Expr *SizeOfExpr = - SemaRef - .CreateUnaryExprOrTypeTraitExpr( - SemaRef.Context.getTrivialTypeSourceInfo(PointeeQT), - SLoc, UETT_SizeOf, SourceRange()) - .get(); - Expr *ThirdInitRHS = - SemaRef.CreateBuiltinBinOp(SLoc, BO_Mul, RHS, SizeOfExpr).get(); - Expr *ThirdInitE = - SemaRef.CreateBuiltinBinOp(SLoc, Op, ThirdInitLHS, ThirdInitRHS).get(); - return BuildFatPtrCompoundLiteralExpr(FirstInitE, SecondInitE, ThirdInitE, QT); -} - -ExprResult TransformFatPtr::TransformUnaryOperator(UnaryOperator *UO) { - QualType QT = DesugarFatPtrType(UO->getBeginLoc(), UO->getType()); - UO->setType(QT); - - UnaryOperator::Opcode Op = UO->getOpcode(); - Expr *SubE = UO->getSubExpr(); - QualType SubQT = SubE->getType(); - if (Op == UO_Deref && SubQT.isFatPtrType()) { - // handle *p if p is fat ptr. - return HandleUODerefFatPtr(UO); - } else if (Op == UO_AddrFat) { - // handle &fat expr. - AddrFatExprBaseKind BaseKind = Finder.AddrFatExprAndBaseMap[UO].first; - switch (BaseKind) { - case AddrFatExprBaseKind::FatPtr: - return HandleUOAddrFatOnExprWithFatPtrBase(UO, Finder.AddrFatExprAndBaseMap[UO].second); - case AddrFatExprBaseKind::LocalNoArrayVar: - return HandleUOAddrFatOnLocalNoArrayVar(UO); - case AddrFatExprBaseKind::LocalArrayVar: - case AddrFatExprBaseKind::GlobalArrayVar: - return HandleUOAddrFatOnArrayVar(UO, Finder.AddrFatExprAndBaseMap[UO].second); - default: - break; - } - } - UO->setSubExpr(BaseTransform::TransformExpr(SubE).get()); - return UO; -} - -ExprResult TransformFatPtr::HandleUODerefFatPtr(UnaryOperator *UO) { - Expr *SubE = UO->getSubExpr(); - DesugarToMemberPtr = true; - InsertCheckVersionCall = true; - InsertCheckOffsetCall.first = true; - // Build offset arg expr, for example: - // for *p, offset should be `sizeof(int)` - Expr *OffsetExpr = - SemaRef - .CreateUnaryExprOrTypeTraitExpr( - SemaRef.Context.getTrivialTypeSourceInfo(UO->getType()), - UO->getBeginLoc(), UETT_SizeOf, SourceRange()) - .get(); - InsertCheckOffsetCall.second = OffsetExpr; - UO->setSubExpr(BaseTransform::TransformExpr(SubE).get()); - return UO; -} - -// if p is fat ptr of struct G, -// 1. `int *fat p1 = &fat p->a;` should be desugared to -// @code -// _FatPtr p1 = (_FatPtr) { &p.ptr->a, p.key_version, -// (int32_t)((uint8_t *)&p.ptr->a - (uint8_t *)p.ptr + p.offset }; -// @endcode -// 2. `struct G *fat p1 = &fat p[i];` should be desugared to -// @code -// _FatPtr p1 = (_FatPtr) { &p.ptr[i], p.key_version, -// (int32_t)((uint8_t *)&p.ptr[i] - (uint8_t *)p.ptr) + p.offset }; -// @endcode -ExprResult TransformFatPtr::HandleUOAddrFatOnExprWithFatPtrBase( - UnaryOperator *UO, Expr *FatPtrBaseE) { - SourceLocation SLoc = UO->getBeginLoc(); - QualType QT = UO->getType(); - Expr *SubE = BaseTransform::TransformExpr(UO->getSubExpr()).get(); - - // Build first init expr: &p.raw_ptr->a - Expr *FirstInitE = SemaRef.CreateBuiltinUnaryOp(SLoc, UO_AddrOf, SubE).get(); - // Build second init expr: p.key_version - Expr *SecondInitE = BuildMemberForFatPtr(FatPtrBaseE, "key_version"); - // Build third init expr: - // `(int32_t)((uint8_t *)&p.ptr->a - (uint8_t *)p.ptr + p.offset` - QualType UInt8PointerTy = SemaRef.Context.getPointerType( - SemaRef.Context.getIntTypeForBitwidth(8, false)); - Expr *ThirdInitLHSLHS = - SemaRef - .BuildCStyleCastExpr( - SLoc, SemaRef.Context.getTrivialTypeSourceInfo(UInt8PointerTy), - SLoc, FirstInitE) - .get(); - Expr *ThirdInitLHSRHS = - SemaRef - .BuildCStyleCastExpr( - SLoc, SemaRef.Context.getTrivialTypeSourceInfo(UInt8PointerTy), - SLoc, BuildMemberForFatPtr(FatPtrBaseE, "raw_ptr")) - .get(); - Expr *ThirdInitLHS = - SemaRef - .CreateBuiltinBinOp(SLoc, BO_Sub, ThirdInitLHSLHS, ThirdInitLHSRHS) - .get(); - QualType Int32Ty = SemaRef.Context.getIntTypeForBitwidth(32, true); - ThirdInitLHS = - SemaRef - .BuildCStyleCastExpr( - SLoc, SemaRef.Context.getTrivialTypeSourceInfo(Int32Ty), SLoc, - ThirdInitLHS) - .get(); - Expr *ThirdInitRHS = BuildMemberForFatPtr(FatPtrBaseE, "offset"); - Expr *ThirdInitE = - SemaRef.CreateBuiltinBinOp(SLoc, BO_Add, ThirdInitLHS, ThirdInitRHS).get(); - return BuildFatPtrCompoundLiteralExpr(FirstInitE, SecondInitE, ThirdInitE, QT); -} - -// if s is local variable whose type has no array, -// 1.`struct S *fat p = &fat s;` should be desugared to -// @code -// _FatPtr p = (_FatPtr) { &s, _local_lock_version, -// (int32_t)((uint8_t *)&a - (uint8_t *)&_local_lock_version) - 8 };` -// @endcode -// 2. `int *fat p = &fat s.a;` should be desugared to -// @code -// _FatPtr p = (_FatPtr) { &s.a, _local_lock_version, -// (int32_t)((uint8_t *)&s.a - (uint8_t *)&_local_lock_version) - 8 };` -// @endcode -ExprResult -TransformFatPtr::HandleUOAddrFatOnLocalNoArrayVar(UnaryOperator *UO) { - SourceLocation SLoc = UO->getBeginLoc(); - QualType QT = UO->getType(); - Expr *SubE = BaseTransform::TransformExpr(UO->getSubExpr()).get(); - // Build first init expr: &a - Expr *FirstInitE = SemaRef.CreateBuiltinUnaryOp(SLoc, UO_AddrOf, SubE).get(); - // Build second init expr: _local_version_lock - Expr *SecondInitE = SemaRef.BuildDeclRefExpr( - LocalLockVersionVD, LocalLockVersionVD->getType(), VK_LValue, SLoc); - // Build third init expr: - // `(int32_t)((uint8_t *)&a - (uint8_t *)&_local_lock_version) - 8` - QualType UInt8PointerTy = SemaRef.Context.getPointerType( - SemaRef.Context.getIntTypeForBitwidth(8, false)); - Expr *ThirdInitLHSLHS = - SemaRef - .BuildCStyleCastExpr( - SLoc, SemaRef.Context.getTrivialTypeSourceInfo(UInt8PointerTy), - SLoc, FirstInitE) - .get(); - Expr *ThirdInitLHSRHS = - SemaRef.CreateBuiltinUnaryOp(SLoc, UO_AddrOf, SecondInitE).get(); - ThirdInitLHSRHS = - SemaRef - .BuildCStyleCastExpr( - SLoc, SemaRef.Context.getTrivialTypeSourceInfo(UInt8PointerTy), - SLoc, ThirdInitLHSRHS) - .get(); - Expr *ThirdInitLHS = - SemaRef - .CreateBuiltinBinOp(SLoc, BO_Sub, ThirdInitLHSLHS,ThirdInitLHSRHS) - .get(); - QualType Int32Ty = SemaRef.Context.getIntTypeForBitwidth(32, true); - ThirdInitLHS = - SemaRef - .BuildCStyleCastExpr( - SLoc, SemaRef.Context.getTrivialTypeSourceInfo(Int32Ty), SLoc, - ThirdInitLHS) - .get(); - Expr *ThirdInitRHS = IntegerLiteral::Create( - SemaRef.Context, llvm::APInt(32, 8), Int32Ty, SLoc); - Expr *ThirdInitE = - SemaRef - .CreateBuiltinBinOp(SLoc, BO_Sub, ThirdInitLHS, ThirdInitRHS) - .get(); - return BuildFatPtrCompoundLiteralExpr(FirstInitE, SecondInitE, ThirdInitE, QT); -} - -// 1. if arr is array variable, -// `int *fat p = &fat arr[5]` should be desugared to -// @code -// _FatPtr p = (_FatPtr) { &_arr.arr[5], _arr.lock_version, -// (int32_t)((uint8_t *)&_arr.arr[5] - (uint8_t *)&_arr.lock_version) - 8 }; -// @endcode -// 2. if s is variable whose type is `struct G { int a; int arr[3]; }` -// `int *fat p = &fat s.a` should be desugared to -// @code -// _FatPtr p = (_FatPtr) { &_s.s.a, _s.lock_version, -// (int32_t)((uint8_t *)&_s.s.a - (uint8_t *)&_s.lock_version) - 8 }; -// @endcode -ExprResult TransformFatPtr::HandleUOAddrFatOnArrayVar( - UnaryOperator *UO, Expr *VarBaseE) { - SourceLocation SLoc = UO->getBeginLoc(); - QualType QT = UO->getType(); - Expr *SubE = BaseTransform::TransformExpr(UO->getSubExpr()).get(); - // Build first init expr: &_arr.arr[5] - Expr *FirstInitE = - SemaRef.CreateBuiltinUnaryOp(SLoc, UO_AddrOf, SubE).get(); - // Build second init expr: _arr.lock_version - RecordDecl *AllocationUnitRD = nullptr; - VarDecl *AllocationUnitVD = nullptr; - auto DRE = dyn_cast(VarBaseE); - auto VD = dyn_cast(DRE->getDecl()); - if (AllocationUnitForLocalArrayVarMap.count(VD)) { - // VD is local var - AllocationUnitRD = AllocationUnitForLocalArrayVarMap[VD].first; - AllocationUnitVD = AllocationUnitForLocalArrayVarMap[VD].second; - } else if (SemaRef.Context.BSCDesugaredMap.count(VD)) { - // VD is global var - AllocationUnitRD = - dyn_cast(SemaRef.Context.BSCDesugaredMap[VD][0]); - AllocationUnitVD = - dyn_cast(SemaRef.Context.BSCDesugaredMap[VD][1]); - } - FieldDecl *LockVersionFD = *(AllocationUnitRD->field_begin()); - Expr *AllocationUnitVDRef = SemaRef.BuildDeclRefExpr( - AllocationUnitVD, AllocationUnitVD->getType(), VK_LValue, SLoc); - Expr *SecondInitE = SemaRef.BuildMemberExpr( - AllocationUnitVDRef, false, SLoc, NestedNameSpecifierLoc(), SLoc, LockVersionFD, - DeclAccessPair::make(AllocationUnitRD, LockVersionFD->getAccess()), false, - DeclarationNameInfo(), LockVersionFD->getType(), VK_LValue, OK_Ordinary); - // Build third init expr: 5 * size(int); - QualType UInt8PointerTy = SemaRef.Context.getPointerType( - SemaRef.Context.getIntTypeForBitwidth(8, false)); - Expr *ThirdInitLHSLHS = - SemaRef - .BuildCStyleCastExpr( - SLoc, SemaRef.Context.getTrivialTypeSourceInfo(UInt8PointerTy), - SLoc, FirstInitE) - .get(); - Expr *ThirdInitLHSRHS = - SemaRef.CreateBuiltinUnaryOp(SLoc, UO_AddrOf, SecondInitE).get(); - ThirdInitLHSRHS = - SemaRef - .BuildCStyleCastExpr( - SLoc, SemaRef.Context.getTrivialTypeSourceInfo(UInt8PointerTy), - SLoc, ThirdInitLHSRHS) - .get(); - Expr *ThirdInitLHS = - SemaRef.CreateBuiltinBinOp(SLoc, BO_Sub, ThirdInitLHSLHS, ThirdInitLHSRHS).get(); - QualType Int32Ty = SemaRef.Context.getIntTypeForBitwidth(32, true); - ThirdInitLHS = - SemaRef - .BuildCStyleCastExpr( - SLoc, SemaRef.Context.getTrivialTypeSourceInfo(Int32Ty), SLoc, - ThirdInitLHS) - .get(); - Expr *ThirdInitRHS = IntegerLiteral::Create( - SemaRef.Context, llvm::APInt(32, 8), Int32Ty, SLoc); - Expr *ThirdInitE = - SemaRef.CreateBuiltinBinOp(SLoc, BO_Sub, ThirdInitLHS, ThirdInitRHS).get(); - return BuildFatPtrCompoundLiteralExpr(FirstInitE, SecondInitE, ThirdInitE, QT); -} - -ExprResult TransformFatPtr::TransformMemberExpr(MemberExpr *ME) { - SourceLocation SLoc = ME->getBeginLoc(); - QualType QT = DesugarFatPtrType(SLoc, ME->getType()); - ME->setType(QT); - - MemberExpr *MemberPtr = nullptr; - if (QT->isFatPtrRecordType()) { - if (InsertCheckOffsetCall.first) { - llvm::SmallVector Args{ME, InsertCheckOffsetCall.second}; - Expr *CE = BuildFatPtrCall(SLoc, "_check_offset", Args); - CheckCallExpr.push(CE); - InsertCheckOffsetCall.first = false; - InsertCheckOffsetCall.second = nullptr; - } - if (InsertCheckVersionCall) { - llvm::SmallVector Args{ME}; - Expr *CE = BuildFatPtrCall(SLoc, "_check_version", Args); - CheckCallExpr.push(CE); - InsertCheckVersionCall = false; - } - if (DesugarToMemberPtr) { - // Desugar s.p to s.p.raw_ptr - MemberPtr = BuildMemberForFatPtr(ME, "raw_ptr"); - DesugarToMemberPtr = false; - } - } - QualType BaseType = ME->getBase()->getType(); - if (ME->isArrow() && BaseType.isFatPtrType()) { - DesugarToMemberPtr = true; - InsertCheckVersionCall = true; - InsertCheckOffsetCall.first = true; - // Build offset arg expr, for example: - // 1. for p->arr[3] where p is fat ptr and p->arr[3] is int type, - // offset is `__builtin_offsetof(struct S, arr[3]) + sizeof(int)` - // 2. for p->a, where p is fat ptr and p->a is int type, - // offset is `__builtin_offsetof(struct S, a) + sizeof(int)` - QualType PointeeQT = BaseType->getFatPtrPointeeType(); - TypeSourceInfo *PointeeTInfo = - SemaRef.Context.getTrivialTypeSourceInfo(PointeeQT, SLoc); - QualType ElementType; - SmallVector Comps; - if (CurrentASEToAccessMemberThroughFatPtr) { - VisitExprToBuildOffsetOfComponent( - CurrentASEToAccessMemberThroughFatPtr, SLoc, Comps); - ElementType = CurrentASEToAccessMemberThroughFatPtr->getType(); - CurrentASEToAccessMemberThroughFatPtr = nullptr; - } else { - VisitExprToBuildOffsetOfComponent(ME, SLoc, Comps); - ElementType = QT; - } - Expr *OffsetExprLHS = - SemaRef.BuildBuiltinOffsetOf(SLoc, PointeeTInfo, Comps, SLoc).get(); - QualType Int32Ty = SemaRef.Context.getIntTypeForBitwidth(32, true); - Expr *OffsetExprRHS = - IntegerLiteral::Create(SemaRef.Context, - llvm::APInt(32, SemaRef.Context.getTypeSize(ElementType) / 8), Int32Ty, SLoc); - InsertCheckOffsetCall.second = - SemaRef.CreateBuiltinBinOp(SLoc, BO_Add, OffsetExprLHS, OffsetExprRHS).get(); - } - ME->setBase(BaseTransform::TransformExpr(ME->getBase()).get()); - return MemberPtr ? MemberPtr : ME; -} - -// Desugar fat qualified QualType ,for example: -// 1. `int *fat` will be desugared to `_FatPtr`; -// 2. `struct S *fat` will be desugared to `_FatPtr`; -// 3. `int *fat *` will be desugared to `_FatPtr *`; -// 4. `int *fat *fat` will be desugared to `_FatPtr<_FatPtr>`; -QualType -TransformFatPtr::DesugarFatPtrType(SourceLocation SLoc, QualType T) { - T = T.getCanonicalType(); - if (!T.isFatQualified() && !T->hasFatFields()) - return T; - if (!FatPtrTD) { - DeclContext::lookup_result Decls = - SemaRef.Context.getTranslationUnitDecl()->lookup( - DeclarationName(&SemaRef.Context.Idents.get("_FatPtr"))); - if (Decls.isSingleResult()) - if (ClassTemplateDecl *CTD = dyn_cast(Decls.front())) - FatPtrTD = CTD; - } - if (FatPtrTD) { - if (auto PT = dyn_cast(T)) { - QualType PointeeT = DesugarFatPtrType(SLoc, PT->getPointeeType()); - if (T.isFatQualified()) { - TemplateArgumentListInfo Args(SLoc, SLoc); - Args.addArgument(TemplateArgumentLoc( - TemplateArgument(PointeeT), - SemaRef.Context.getTrivialTypeSourceInfo(PointeeT, SLoc))); - QualType FatPtrRecordType = - SemaRef.CheckTemplateIdType(TemplateName(FatPtrTD), SLoc, Args); - if (!FatPtrRecordType.isNull() && - !SemaRef.RequireCompleteType(SLoc, FatPtrRecordType, - diag::err_fat_ptr_missing_specialization)) - return FatPtrRecordType; - } else - return SemaRef.Context.getPointerType(PointeeT); - } - return T; - } - SemaRef.Diag(SLoc, diag::err_fat_ptr_type_not_found) << ""; - return T; -} - -Expr *TransformFatPtr::BuildFatPtrCall(SourceLocation SLoc, std::string FDName, - llvm::SmallVector &Args) { - if (!FatPtrFD[FDName]) { - DeclContext::lookup_result Decls = - SemaRef.Context.getTranslationUnitDecl()->lookup( - DeclarationName(&SemaRef.Context.Idents.get(FDName))); - if (Decls.isSingleResult()) - FatPtrFD[FDName] = dyn_cast(Decls.front()); - } - if (FunctionDecl *FD = FatPtrFD[FDName]) { - DeclRefExpr *FDRef = - SemaRef.BuildDeclRefExpr(FD, FD->getType(), VK_LValue, SLoc); - CallExpr *CE = - dyn_cast(SemaRef.BuildCallExpr(nullptr, FDRef, SLoc, Args, SLoc).get()); - // Handle first arg of _check_version and _check_version. - if (CE && CE->getNumArgs() > 0 && - CE->getArg(0)->getType()->isFatPtrRecordType()) - if (auto ICE = dyn_cast(CE->getArg(0))) - ICE->setType(ICE->getSubExpr()->getType()); - return CE; - } - SemaRef.Diag(SLoc, diag::err_fat_ptr_func_not_found) << ""; - return nullptr; -} - -MemberExpr *TransformFatPtr::BuildMemberForFatPtr(Expr *FatPtrBaseE, - std::string FieldName) { - QualType FatPtrQT = - DesugarFatPtrType(FatPtrBaseE->getBeginLoc(), FatPtrBaseE->getType()); - if (FatPtrQT->isRecordType()) - if (RecordDecl *FatPtrRD = FatPtrQT->getAs()->getDecl()) - if (FatPtrRD->getNameAsString() == "_FatPtr") { - RecordDecl::field_iterator it = FatPtrRD->field_begin(); - if (FieldName == "key_version") { - it++; - } else if (FieldName == "offset") { - it++; - it++; - } - FieldDecl *FD = *it; - return SemaRef.BuildMemberExpr( - FatPtrBaseE, false, FatPtrBaseE->getBeginLoc(), - NestedNameSpecifierLoc(), FatPtrBaseE->getBeginLoc(), FD, - DeclAccessPair::make(FatPtrRD, FD->getAccess()), false, - DeclarationNameInfo(), FD->getType(), VK_LValue, OK_Ordinary); - } - return nullptr; -} - -Expr *TransformFatPtr::BuildFatPtrCompoundLiteralExprForRawPtr( - Expr *RawPtrE, QualType QT) { - SourceLocation SLoc = RawPtrE->getBeginLoc(); - llvm::APInt Zero(32, 0); - QualType UInt32Ty = SemaRef.Context.getIntTypeForBitwidth(32, false); - QualType Int32Ty = SemaRef.Context.getIntTypeForBitwidth(32, true); - Expr *SecondInitE = - IntegerLiteral::Create(SemaRef.Context, Zero, UInt32Ty, SLoc); - Expr *ThirdInitE = - IntegerLiteral::Create(SemaRef.Context, Zero, Int32Ty, SLoc); - return BuildFatPtrCompoundLiteralExpr(RawPtrE, SecondInitE, ThirdInitE, QT); -} - -Expr *TransformFatPtr::BuildFatPtrCompoundLiteralExpr( - Expr *FirstInitE, Expr *SecondInitE, Expr *ThirdInitE, QualType QT) { - SourceLocation SLoc = FirstInitE->getBeginLoc(); - SmallVector Inits{FirstInitE, SecondInitE, ThirdInitE}; - Expr *ILE = SemaRef.BuildInitList(SLoc, Inits, SLoc).get(); - ILE->setType(QT); - return SemaRef - .BuildCompoundLiteralExpr( - SLoc, SemaRef.Context.getTrivialTypeSourceInfo(QT), SLoc, ILE) - .get(); -} - -// return true when accessing array member through FatPtr, -// such as `p->arr[3]`, `p->a.b.arr[3]` -bool -TransformFatPtr::IsAccessingArrayMemberThroughFatPtr(Expr *E) { - if (!CurrentASEToAccessMemberThroughFatPtr) { - if (auto ME = dyn_cast(E)) { - if (ME->getBase()->getType().isFatPtrType()) { - return true; - } else { - return IsAccessingArrayMemberThroughFatPtr(ME->getBase()); - } - } else if (auto ASE = dyn_cast(E)) { - return IsAccessingArrayMemberThroughFatPtr(ASE->getBase()); - } else if (auto ICE = dyn_cast(E)) { - return IsAccessingArrayMemberThroughFatPtr(ICE->getSubExpr()); - } else if (auto PE = dyn_cast(E)) { - return IsAccessingArrayMemberThroughFatPtr(PE->getSubExpr()); - } - } - return false; -} - -void TransformFatPtr::VisitExprToBuildOffsetOfComponent( - Expr *E, SourceLocation SLoc, - SmallVector &Comps) { - if (E->getType().isFatPtrType()) { - return; - } else if (auto ASE = dyn_cast(E)) { - VisitExprToBuildOffsetOfComponent(ASE->getBase(), SLoc, Comps); - Comps.push_back(Sema::OffsetOfComponent()); - Comps.back().isBrackets = true; - Comps.back().U.E = ASE->getIdx(); - Comps.back().LocStart = Comps.back().LocEnd = SLoc; - } else if (auto ME = dyn_cast(E)) { - VisitExprToBuildOffsetOfComponent(ME->getBase(), SLoc, Comps); - Comps.push_back(Sema::OffsetOfComponent()); - Comps.back().isBrackets = false; - Comps.back().U.IdentInfo = ME->getMemberDecl()->getIdentifier(); - Comps.front().LocStart = Comps.back().LocEnd = SLoc; - } else if (auto ICE = dyn_cast(E)) { - VisitExprToBuildOffsetOfComponent(ICE->getSubExpr(), SLoc, Comps); - } else if (auto PE = dyn_cast(E)) { - VisitExprToBuildOffsetOfComponent(PE->getSubExpr(), SLoc, Comps); - } -} - -void Sema::DesugarFunctionDeclWithFatPtr(FunctionDecl *FD) { - if (Diags.hasErrorOccurred() || - FD->getTemplatedKind() == FunctionDecl::TK_FunctionTemplate) - return; - // Skip function template and class template. - if (auto RD = dyn_cast(FD->getParent())) { - if (RD->getDescribedClassTemplate()) - return; - } - - // Desugar fat ptr in function. - TransformFatPtr TFP(*this, FD); - TFP.TransformFunctionDecl(); -} - -void Sema::DesugarGlobalVarDeclWithFatPtr(VarDecl *VD) { - if (Diags.hasErrorOccurred()) - return; - - TransformFatPtr TFP(*this); - TFP.TransformVarDecl(VD); -} - -void Sema::DesugarRecordDeclWithFatPtr(RecordDecl *RD) { - if (Diags.hasErrorOccurred() || RD->getDescribedClassTemplate()) - return; - - TransformFatPtr TFP(*this); - TFP.TransformRecordDecl(RD); -} - -bool Sema::CheckFatQualTypeCStyleCast(QualType LHSType, QualType RHSType, - SourceLocation RLoc) { - if (!CheckFatQualTypeCStyleCast(LHSType, RHSType)) { - Diag(RLoc, diag::err_fat_qualcheck_incompatible) - << CompleteTraitType(RHSType) << CompleteTraitType(LHSType); - return false; - } - return true; -} - -bool Sema::CheckFatQualTypeCStyleCast(QualType LHSType, QualType RHSType) { - QualType RHSCanType = RHSType.getCanonicalType(); - QualType LHSCanType = LHSType.getCanonicalType(); - bool IsSameType = (LHSCanType.getTypePtr() == RHSCanType.getTypePtr()); - const auto *LHSPtrType = LHSType->getAs(); - const auto *RHSPtrType = RHSType->getAs(); - bool IsPointer = LHSPtrType && RHSPtrType; - QualType RHSRawType = RHSCanType.getUnqualifiedType(); - QualType LHSRawType = LHSCanType.getUnqualifiedType(); - - if (IsSameType) { - return true; - } - if ((LHSCanType.isFatQualified() || LHSCanType->isFatPtrRecordType()) && - (RHSCanType.isFatQualified() || RHSCanType->isFatPtrRecordType())) { - return true; - } - if (IsPointer) { - if (Context.hasSameType(LHSRawType, RHSRawType)) { - return true; - } else if (LHSCanType.isFatQualified() && RHSCanType.isFatQualified()) { - // T1 *fat is allowed to cast to T2 *fat - return true; - } else if (TryDesugarTrait(RHSType)) { - return true; - } else { - return CheckFatQualTypeCStyleCast(LHSPtrType->getPointeeType(), - RHSPtrType->getPointeeType()); - } - } - return false; -} - -bool Sema::CheckFatQualTypeAssignment(QualType LHSType, QualType RHSType, - SourceLocation RLoc) { - QualType RHSCanType = RHSType.getCanonicalType(); - QualType LHSCanType = LHSType.getCanonicalType(); - const auto *LHSPtrType = LHSType->getAs(); - const auto *RHSPtrType = RHSType->getAs(); - bool IsPointer = LHSPtrType && RHSPtrType; - - if (LHSCanType.isFatQualified() == RHSCanType.isFatQualified()) { - if (TraitDecl *TD = TryDesugarTrait(LHSCanType)) { - if (TD->getTypeImpledVarDecl(RHSCanType->getPointeeType())) - return true; - } - if (Context.hasSameType(LHSCanType, RHSCanType)) { - return true; - } - if (LHSCanType->isVoidPointerType()) { - if (LHSCanType->getPointeeType().isConstQualified() == - RHSCanType->getPointeeType().isConstQualified()) - return true; - } - if (!IsPointer) { - return false; - } else { - return CheckFatQualTypeAssignment(LHSPtrType->getPointeeType(), - RHSPtrType->getPointeeType(), RLoc); - } - } - return false; -} - -bool Sema::CheckFatQualTypeAssignment(QualType LHSType, Expr *RHSExpr) { - QualType LHSCanType = LHSType.getCanonicalType(); - QualType RHSCanType = RHSExpr->getType().getCanonicalType(); - SourceLocation ExprLoc = RHSExpr->getBeginLoc(); - bool Res = true; - - if (LHSCanType.isFatQualified() || RHSCanType.isFatQualified()) { - if (TraitDecl *TD = TryDesugarTrait(LHSCanType)) { - if (RHSCanType->isPointerType()) { - QualType ImplType = RHSCanType->getPointeeType() - .getUnqualifiedType() - .getCanonicalType(); - ImplType.removeLocalOwned(); - if (TD->getTypeImpledVarDecl(ImplType)) - return true; - } - // trait T* fat <-> trait T* fat // legal - if (TD) { - QualType QT = - DesugarTraitToStructTrait(TD, LHSCanType, RHSExpr->getExprLoc()); - if (QT.getCanonicalType() == RHSCanType) { - return true; - } - } - } - - // Fat pointer can be inited by nullptr. - if (LHSCanType.isFatQualified() && isa(RHSExpr)) - return true; - - if (LHSCanType->isVoidPointerType() && RHSCanType->isPointerType()) { - if (LHSCanType->getPointeeType().isConstQualified() == - RHSCanType->getPointeeType().isConstQualified()) - return true; - } - - if (!Context.hasSameType(LHSCanType, RHSCanType)) - Res = false; - } else { - Res = CheckFatQualTypeAssignment(LHSType, RHSCanType, ExprLoc); - } - if ((LHSCanType.isFatQualified() || LHSCanType->isFatPtrRecordType()) && - (RHSCanType.isFatQualified() || RHSCanType->isFatPtrRecordType())) - return true; - if (!Res) { - Diag(ExprLoc, diag::err_fat_qualcheck_incompatible) - << CompleteTraitType(RHSExpr->getType()) << CompleteTraitType(LHSType); - } - return Res; -} - -bool Sema::CheckFatFunctionPointerType(QualType LHSType, Expr *RHSExpr) { - const FunctionProtoType *LHSFuncType = LHSType->getAs() - ->getPointeeType() - ->getAs(); - const FunctionProtoType *RHSFuncType = - RHSExpr->getType()->isFunctionPointerType() - ? RHSExpr->getType() - ->getAs() - ->getPointeeType() - ->getAs() - : RHSExpr->getType()->getAs(); - SourceLocation ExprLoc = RHSExpr->getBeginLoc(); - - // return if no 'fat' in both side - if (!LHSFuncType->hasFatRetOrParams() && !RHSFuncType->hasFatRetOrParams()) { - return true; - } - - if (!Context.hasSameType(LHSFuncType, RHSFuncType)) { - Diag(ExprLoc, diag::err_fat_funcPtr_incompatible) - << LHSType << RHSExpr->getType(); - return false; - } - for (unsigned i = 0; i < LHSFuncType->getNumParams(); i++) { - QualType LHSParamType = LHSFuncType->getParamType(i); - QualType RHSParamType = RHSFuncType->getParamType(i); - if (!Context.hasSameType(LHSParamType, RHSParamType)) { - Diag(ExprLoc, diag::err_fat_funcPtr_incompatible) - << LHSType << RHSExpr->getType(); - return false; - } - } - return true; -} - -// Build allocation unit RecordDecl for every global array var -// which has attribute fat_ptr_checked, for example: -// @code -// __attribute((fat_ptr_checked)) int arr[3] = {1, 2, 3}; -// @endcode -// We desugar this decl to: -// @code -// struct _arr_AllocationUnit { -// uint32_t lock_version; -// uint32_t size; -// int arr[3]; -// }; -// struct _arr_AllocationUnit _arr = { 0, 12, {1, 2, 3} }; -// @endcode -std::pair -Sema::BuildAllocationUnitForGlobalArrayVar(VarDecl *VD) { - SourceLocation SLoc = VD->getLocation(); - // Build RecordDecl - RecordDecl *RD = RecordDecl::Create( - Context, TagDecl::TagKind::TTK_Struct, Context.getTranslationUnitDecl(), - SLoc, SLoc, - &Context.Idents.get("_" + VD->getNameAsString() + "_AllocationUnit")); - // Add three Field - RD->startDefinition(); - QualType UInt32Ty = Context.getIntTypeForBitwidth(32, false); - FieldDecl *LockVersionField = FieldDecl::Create( - Context, RD, SLoc, SLoc, &Context.Idents.get("lock_version"), UInt32Ty, - Context.getTrivialTypeSourceInfo(UInt32Ty), nullptr, false, ICIS_NoInit); - FieldDecl *SizeField = FieldDecl::Create( - Context, RD, SLoc, SLoc, &Context.Idents.get("size"), UInt32Ty, - Context.getTrivialTypeSourceInfo(UInt32Ty), nullptr, false, ICIS_NoInit); - FieldDecl *VDTypeField = FieldDecl::Create( - Context, RD, SLoc, SLoc, &Context.Idents.get(VD->getNameAsString()), - VD->getType(), Context.getTrivialTypeSourceInfo(VD->getType()), nullptr, - false, ICIS_NoInit); - LockVersionField->setAccess(AS_public); - SizeField->setAccess(AS_public); - VDTypeField->setAccess(AS_public); - RD->addDecl(LockVersionField); - RD->addDecl(SizeField); - RD->addDecl(VDTypeField); - RD->completeDefinition(); - PushOnScopeChains(RD, TUScope); - - // Build VarDecl - VarDecl *NewVD = - VarDecl::Create(Context, Context.getTranslationUnitDecl(), SLoc, SLoc, - &Context.Idents.get("_" + VD->getNameAsString()), - QualType(RD->getTypeForDecl(), 0), nullptr, SC_None); - // Build InitExpr - // Build first init expr : 0 - Expr *LockVersionInit = - IntegerLiteral::Create(Context, llvm::APInt(32, 0), UInt32Ty, SLoc); - // Build second init expr : array size - Expr *SizeInit = IntegerLiteral::Create( - Context, llvm::APInt(32, Context.getTypeSize(VD->getType()) / 8), - UInt32Ty, SLoc); - // Build InitListExpr - SmallVector Inits{LockVersionInit, SizeInit}; - if (VD->getInit()) - Inits.push_back(VD->getInit()); - Expr *ILE = BuildInitList(SLoc, Inits, SLoc).get(); - ILE->setType(QualType(RD->getTypeForDecl(), 0)); - NewVD->setInit(ILE); - PushOnScopeChains(NewVD, TUScope); - Context.BSCDesugaredMap[VD].push_back(RD); - Context.BSCDesugaredMap[VD].push_back(NewVD); - return std::make_pair(RD, NewVD); -} -#endif // ENABLE_BSC \ No newline at end of file diff --git a/clang/lib/Sema/BSC/SemaBSCOwnership.cpp b/clang/lib/Sema/BSC/SemaBSCOwnership.cpp index 229f30a2f5ad..c96493b7f583 100644 --- a/clang/lib/Sema/BSC/SemaBSCOwnership.cpp +++ b/clang/lib/Sema/BSC/SemaBSCOwnership.cpp @@ -399,7 +399,7 @@ bool Sema::CheckBorrowFunctionPointerType(QualType LHSType, Expr *RHSExpr) { return true; } if (!Context.hasSameType(LSHFuncType, RSHFuncType)) { - Diag(ExprLoc, diag::err_borrow_funcPtr_incompatible) + Diag(ExprLoc, diag::err_funcPtr_incompatible) << LHSType << RHSExpr->getType(); return false; } diff --git a/clang/lib/Sema/BSC/SemaBSCSafeZone.cpp b/clang/lib/Sema/BSC/SemaBSCSafeZone.cpp index f3813e181333..cad99765f657 100644 --- a/clang/lib/Sema/BSC/SemaBSCSafeZone.cpp +++ b/clang/lib/Sema/BSC/SemaBSCSafeZone.cpp @@ -233,7 +233,7 @@ bool Sema::IsSafeConversion(QualType DestType, Expr *Expr) { // Init a owned or borrow pointer by nullptr is allowed in the safe zone if ((DestType.isOwnedQualified() && DestType->isPointerType()) || - DestType.isBorrowQualified() || DestType.isFatQualified()) { + DestType.isBorrowQualified()) { if (isa(Expr)) return true; } diff --git a/clang/lib/Sema/BSC/SemaDeclBSC.cpp b/clang/lib/Sema/BSC/SemaDeclBSC.cpp index d9dff2e4add7..5ff1856a0c80 100644 --- a/clang/lib/Sema/BSC/SemaDeclBSC.cpp +++ b/clang/lib/Sema/BSC/SemaDeclBSC.cpp @@ -166,7 +166,7 @@ bool Sema::HasSafeZoneInFunction(const FunctionDecl* FnDecl) { /// ==================================================================== void Sema::BSCDataflowAnalysis(const Decl *D, bool EnableOwnershipCheck, bool EnableNullabilityCheck) { - AnalysisDeclContext AC(/* AnalysisDeclContextManager */ nullptr, FD); + AnalysisDeclContext AC(/* AnalysisDeclContextManager */ nullptr, D); // TODO: understand how these parameters affect the CFG. AC.getCFGBuildOptions().PruneTriviallyFalseEdges = true; diff --git a/clang/lib/Sema/CMakeLists.txt b/clang/lib/Sema/CMakeLists.txt index e408444fa565..a665edbb358a 100644 --- a/clang/lib/Sema/CMakeLists.txt +++ b/clang/lib/Sema/CMakeLists.txt @@ -16,7 +16,6 @@ add_clang_library(clangSema BSC/SemaBSCOverload.cpp BSC/SemaBSCOwnedStruct.cpp BSC/SemaBSCCoroutine.cpp - BSC/SemaBSCFatPtr.cpp BSC/SemaBSCOwnership.cpp BSC/SemaBSCSafeZone.cpp BSC/SemaBSCTrait.cpp diff --git a/clang/lib/Sema/DeclSpec.cpp b/clang/lib/Sema/DeclSpec.cpp index a8dee67b7dc6..7226987274eb 100644 --- a/clang/lib/Sema/DeclSpec.cpp +++ b/clang/lib/Sema/DeclSpec.cpp @@ -438,9 +438,7 @@ void DeclSpec::forEachCVRUQualifier( Handle(TQ_owned, "owned", TQ_ownedLoc); if (TypeQualifiers & TQ_borrow) Handle(TQ_borrow, "borrow", TQ_borrowLoc); - if (TypeQualifiers & TQ_fat) - Handle(TQ_fat, "fat", TQ_fatLoc); -#endif + #endif if (TypeQualifiers & TQ_volatile) Handle(TQ_volatile, "volatile", TQ_volatileLoc); if (TypeQualifiers & TQ_restrict) @@ -637,9 +635,7 @@ const char *DeclSpec::getSpecifierName(TQ T) { #if ENABLE_BSC case DeclSpec::TQ_owned: return "owned"; case DeclSpec::TQ_borrow: return "borrow"; - case DeclSpec::TQ_fat: - return "fat"; -#endif + #endif case DeclSpec::TQ_restrict: return "restrict"; case DeclSpec::TQ_volatile: return "volatile"; case DeclSpec::TQ_atomic: return "_Atomic"; @@ -1015,10 +1011,7 @@ bool DeclSpec::SetTypeQual(TQ T, SourceLocation Loc) { case TQ_borrow: TQ_borrowLoc = Loc; return false; - case TQ_fat: - TQ_fatLoc = Loc; - return false; -#endif + #endif case TQ_restrict: TQ_restrictLoc = Loc; return false; case TQ_volatile: TQ_volatileLoc = Loc; return false; case TQ_unaligned: TQ_unalignedLoc = Loc; return false; diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index e0780e8c796b..7f55f3a83696 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -2196,7 +2196,11 @@ static void markEscapingByrefs(const FunctionScopeInfo &FSI, Sema &S) { /// \param BlockType The type of the block expression, if D is a BlockDecl. Sema::PoppedFunctionScopePtr Sema::PopFunctionScopeInfo(const AnalysisBasedWarnings::Policy *WP, - const Decl *D, QualType BlockType) { + const Decl *D, QualType BlockType + #if ENABLE_BSC + , bool isBSCCoroutine + #endif + ) { assert(!FunctionScopes.empty() && "mismatched push/pop!"); markEscapingByrefs(*FunctionScopes.back(), *this); @@ -2207,6 +2211,28 @@ Sema::PopFunctionScopeInfo(const AnalysisBasedWarnings::Policy *WP, if (LangOpts.OpenMP) popOpenMPFunctionRegion(Scope.get()); +#if ENABLE_BSC + // FIXME: Rename EnableOwnershipCheck, maybe EnableBorrowCheck is a better name. + bool EnableOwnershipCheck = LangOpts.BSC ? (!(getLangOpts().DisableOwnershipCheck)) : false; + // If NullabilityCheck is in Mode: {SafeOnly, All}, we should build CFG. + bool EnableNullabilityCheck = LangOpts.BSC ? (getLangOpts().getNullabilityCheck() != LangOptions::NC_NONE) : false; + if (EnableOwnershipCheck || EnableNullabilityCheck) { + if (!isBSCCoroutine && + (getDiagnostics().getNumErrors() == + getDiagnostics().getNumOwnershipErrors() + + getDiagnostics().getNumBorrowCheckErrors() + + getDiagnostics().getNumNullabilityCheckErrors()) && + D) + if (const auto *const CastReturn = dyn_cast_or_null(D)) { + auto md = dyn_cast_or_null(D); + // Don't check ownership rules of destructor parameters + if (!md || !md->isDestructor()) + BSCDataflowAnalysis(D, EnableOwnershipCheck, + EnableNullabilityCheck); + } + } +#endif + // Issue any analysis-based warnings. if (WP && D) AnalysisWarnings.IssueWarnings(*WP, Scope.get(), D, BlockType); diff --git a/clang/lib/Sema/SemaCast.cpp b/clang/lib/Sema/SemaCast.cpp index 741617c32050..b174416b2a54 100644 --- a/clang/lib/Sema/SemaCast.cpp +++ b/clang/lib/Sema/SemaCast.cpp @@ -2879,70 +2879,43 @@ void CastOperation::CheckCStyleCast() { SrcExpr = ExprError(); return; } - QualType SrcType = SrcExpr.get()->getType(); - QualType SrcCanType = SrcType.getCanonicalType(); - QualType DestCanType = DestType.getCanonicalType(); // bsc owned type CStyleCast - if (SrcCanType.isOwnedQualified() || DestCanType.isOwnedQualified()) { - if (!Self.CheckOwnedQualTypeCStyleCast(DestType, SrcType, - SrcExpr.get()->getExprLoc())) { + if (SrcExpr.get()->getType().getCanonicalType().isOwnedQualified() || + DestType.getCanonicalType().isOwnedQualified()) { + if (!Self.CheckOwnedQualTypeCStyleCast(DestType, SrcExpr.get()->getType(), SrcExpr.get()->getExprLoc())) { SrcExpr = ExprError(); return; } } // bsc borrow type CStyleCast - if (SrcCanType.isBorrowQualified() || DestCanType.isBorrowQualified()) { - if (!Self.CheckBorrowQualTypeCStyleCast(DestType, SrcType, - SrcExpr.get()->getExprLoc())) { - SrcExpr = ExprError(); - return; - } - } - // bsc fat type CStyleCast - if (SrcCanType.isFatQualified() || DestCanType.isFatQualified()) { - if (!Self.CheckFatQualTypeCStyleCast(DestType, SrcType, - SrcExpr.get()->getExprLoc())) { + if (SrcExpr.get()->getType().getCanonicalType().isBorrowQualified() || + DestType.getCanonicalType().isBorrowQualified()) { + if (!Self.CheckBorrowQualTypeCStyleCast(DestType, SrcExpr.get()->getType(), SrcExpr.get()->getExprLoc())) { SrcExpr = ExprError(); return; } } if (const auto *LHSPtrType = DestType->getAs()) { - if (const auto *RHSPtrType = SrcType->getAs()) { + if (const auto *RHSPtrType = SrcExpr.get()->getType()->getAs()) { if (LHSPtrType->hasOwnedFields() || RHSPtrType->hasOwnedFields()) { - if (!Self.CheckOwnedQualTypeCStyleCast(DestType, SrcType, - SrcExpr.get()->getExprLoc())) { - SrcExpr = ExprError(); - return; - } - } - if (LHSPtrType->hasBorrowFields() || RHSPtrType->hasBorrowFields()) { - if (!Self.CheckBorrowQualTypeCStyleCast( - DestType, SrcType, SrcExpr.get()->getExprLoc())) { - SrcExpr = ExprError(); - return; - } - } - if (LHSPtrType->hasFatFields() || RHSPtrType->hasFatFields()) { - if (!Self.CheckFatQualTypeCStyleCast(DestType, SrcType, - SrcExpr.get()->getExprLoc())) { + if (!Self.CheckOwnedQualTypeCStyleCast(DestType, SrcExpr.get()->getType(), SrcExpr.get()->getExprLoc())) { SrcExpr = ExprError(); return; } } } } - if (Self.IsTraitExpr(SrcExpr.get()) && DestType->isPointerType()) { - if (!DestType->hasTraitType()) - SrcExpr = Self.ActOnTraitPointerCast(SrcExpr.get()); - if (DestType->isVoidPointerType()) { - Kind = CK_NoOp; - } else { - Kind = CK_BitCast; - } - return; - } else if (SrcCanType->isFatPtrRecordType() && DestType.isFatQualified()) { - return; + } + if (Self.getLangOpts().BSC && Self.IsTraitExpr(SrcExpr.get()) && + DestType->isPointerType()) { + if (!DestType->hasTraitType()) + SrcExpr = Self.ActOnTraitPointerCast(SrcExpr.get()); + if (DestType->isVoidPointerType()) { + Kind = CK_NoOp; + } else { + Kind = CK_BitCast; } + return; } #endif diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 035c35f4c709..27347cea4684 100755 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -15986,43 +15986,12 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body, // the declaration context below. Otherwise, we're unable to transform // 'this' expressions when transforming immediate context functions. -#if ENABLE_BSC - if (LangOpts.BSC) { - if (auto FD = dyn_cast_or_null(dcl)) { - // FIXME: Rename EnableOwnershipCheck, maybe EnableBorrowCheck is a better name. - bool EnableOwnershipCheck = !getLangOpts().DisableOwnershipCheck; - // If NullabilityCheck is in Mode: {SafeOnly, All}, we should build CFG. - bool EnableNullabilityCheck = getLangOpts().getNullabilityCheck() != LangOptions::NC_NONE; - if (EnableOwnershipCheck || EnableNullabilityCheck) { - if (getDiagnostics().getNumErrors() == - getDiagnostics().getNumOwnershipErrors() + - getDiagnostics().getNumBorrowCheckErrors() + - getDiagnostics().getNumNullabilityCheckErrors()) { - bool DoAnalysis = true; - // Skip function template and class template - if (const auto *RD = dyn_cast(FD->getParent())) { - if (RD->getDescribedClassTemplate() != nullptr) - DoAnalysis = false; - } - auto md = dyn_cast_or_null(FD); - // Don't check ownership rules of destructor parameters - if (DoAnalysis && (!md || !md->isDestructor())) - BSCDataflowAnalysis(FD, EnableOwnershipCheck, - EnableNullabilityCheck); - } - } - // Desugar BSC Function. - DesugarDestructorCall(FD); - if (getLangOpts().EnableFatPtr) - DesugarFunctionDeclWithFatPtr(FD); - } - } -#endif - if (!IsInstantiation) PopDeclContext(); PopFunctionScopeInfo(ActivePolicy, dcl); + if (auto *FD = dyn_cast(dcl)) + DesugarDestructorCall(FD); // If any errors have occurred, clear out any temporaries that may have // been leftover. This ensures that these temporaries won't be picked up for // deletion in some later function. @@ -19017,11 +18986,6 @@ void Sema::ActOnFields(Scope *S, SourceLocation RecLoc, Decl *EnclosingDecl, if (!Completed) Record->completeDefinition(); -#if ENABLE_BSC - if (getLangOpts().BSC && getLangOpts().EnableFatPtr) - DesugarRecordDeclWithFatPtr(Record); -#endif - // Handle attributes before checking the layout. ProcessDeclAttributeList(S, Record, Attrs); diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index dd35eac8f173..d3cd9eb36026 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -8688,18 +8688,6 @@ static void handleOperatorAttr(Sema &S, Decl *D, const ParsedAttr &Attrs) { D->addAttr(::new (S.Context) OperatorAttr( S.Context, Attrs, Attrs.getOperatorTypeBuffer().Kind)); } - -static void handleFatPtrCheckedAttr(Sema &S, Decl *D, const ParsedAttr &Attrs) { - if (auto VD = dyn_cast(D)) { - if (VD->hasGlobalStorage() && VD->getType()->isConstantArrayType()) { - D->addAttr(::new (S.Context) FatPtrCheckedAttr(S.Context, Attrs)); - return; - } - } - S.Diag(Attrs.getLoc(), diag::err_fat_ptr_checked_attr_global_array_var); - Attrs.setInvalid(); - return; -} #endif //===----------------------------------------------------------------------===// @@ -9574,10 +9562,6 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL, case ParsedAttr::AT_Operator: handleOperatorAttr(S, D, AL); break; - - case ParsedAttr::AT_FatPtrChecked: - handleFatPtrCheckedAttr(S, D, AL); - break; #endif } } diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 1f10537f65c5..246582275fef 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -5895,13 +5895,6 @@ Sema::CreateBuiltinArraySubscriptExpr(Expr *Base, SourceLocation LLoc, BaseExpr = LHSExp; IndexExpr = RHSExp; ResultType = PTy->getPointeeType(); -#if ENABLE_BSC - } else if (getLangOpts().BSC && getLangOpts().EnableFatPtr && - LHSTy->isFatPtrRecordType()) { - BaseExpr = LHSExp; - IndexExpr = RHSExp; - ResultType = LHSTy->getFatPtrPointeeType(); -#endif } else if (const ObjCObjectPointerType *PTy = LHSTy->getAs()) { BaseExpr = LHSExp; @@ -10447,37 +10440,6 @@ static bool IsTraitEqualExpr(Sema &S, QualType DstType, QualType SrcType, } return false; } - -static bool IsFatPtrEqualExpr(Sema &S, QualType DstType, QualType SrcType) { - if (DstType.getCanonicalType().isFatQualified()) - if (SrcType.getCanonicalType()->isRecordType()) - if (RecordDecl *RD = - SrcType.getCanonicalType()->getAs()->getDecl()) - if (RD->getNameAsString() == "_FatPtr") - return true; - if (SrcType.getCanonicalType().isFatQualified()) - if (DstType.getCanonicalType()->isRecordType()) - if (RecordDecl *RD = - DstType.getCanonicalType()->getAs()->getDecl()) - if (RD->getNameAsString() == "_FatPtr") - return true; - if (DstType.getCanonicalType().isFatQualified()) - if (SrcType.getCanonicalType()->isRecordType()) - if (RecordDecl *RD = - SrcType.getCanonicalType()->getAs()->getDecl()) - if (RD->getNameAsString() == "_FatPtr") - return true; - if (SrcType.getCanonicalType()->isRecordType() && - DstType.getCanonicalType()->isRecordType()) - if (RecordDecl *DstRD = - DstType.getCanonicalType()->getAs()->getDecl()) - if (DstRD->getNameAsString() == "_FatPtr") - if (RecordDecl *SrcRD = - SrcType.getCanonicalType()->getAs()->getDecl()) - if (SrcRD->getNameAsString() == "_FatPtr") - return true; - return false; -} #endif Sema::AssignConvertType @@ -10511,21 +10473,10 @@ Sema::CheckSingleAssignmentConstraints(QualType LHSType, ExprResult &CallerRHS, if (!IsSafeConversion(LHSType, RHS.get())) { return IncompatibleBSCSafeZone; } - QualType LHSCanType = LHSType.getCanonicalType(); - QualType RHSCanType = RHS.get()->getType().getCanonicalType(); - if (RHSCanType.isOwnedQualified() || LHSCanType.isOwnedQualified()) { + if (RHS.get()->getType().getCanonicalType().isOwnedQualified() || LHSType.getCanonicalType().isOwnedQualified()) { if (!CheckOwnedQualTypeAssignment(LHSType, RHS.get())) return IncompatibleOwnedPointer; } - if (RHSCanType.isBorrowQualified() || LHSCanType.isBorrowQualified()) { - if (!CheckBorrowQualTypeAssignment(LHSType, RHS.get())) - return IncompatibleBorrowPointer; - } - if ((RHSCanType.isFatQualified() || RHSCanType->isFatPtrRecordType()) && - (LHSCanType.isFatQualified() || LHSCanType->isFatPtrRecordType())) { - if (!CheckFatQualTypeAssignment(LHSType, RHS.get())) - return IncompatibleFatPointer; - } if (const auto *LHSPtrType = LHSType->getAs()) { if (const auto *RHSPtrType = RHS.get()->getType()->getAs()) { if (LHSPtrType->hasOwnedFields() || RHSPtrType->hasOwnedFields()) { @@ -10533,14 +10484,18 @@ Sema::CheckSingleAssignmentConstraints(QualType LHSType, ExprResult &CallerRHS, return IncompatibleOwnedPointer; } } + } + } + + if (RHS.get()->getType().getCanonicalType().isBorrowQualified() || LHSType.getCanonicalType().isBorrowQualified()) { + if (!CheckBorrowQualTypeAssignment(LHSType, RHS.get())) + return IncompatibleOwnedPointer; + } + if (const auto *LHSPtrType = LHSType->getAs()) { + if (const auto *RHSPtrType = RHS.get()->getType()->getAs()) { if (LHSPtrType->hasBorrowFields() || RHSPtrType->hasBorrowFields()) { if (!CheckBorrowQualTypeAssignment(LHSType, RHS.get())) { - return IncompatibleBorrowPointer; - } - } - if (LHSPtrType->hasFatFields() || RHSPtrType->hasFatFields()) { - if (!CheckFatQualTypeAssignment(LHSType, RHS.get())) { - return IncompatibleFatPointer; + return IncompatibleOwnedPointer; } } } @@ -10552,8 +10507,6 @@ Sema::CheckSingleAssignmentConstraints(QualType LHSType, ExprResult &CallerRHS, return IncompatibleOwnedPointer; if (!CheckBorrowFunctionPointerType(LHSType, RHS.get())) return IncompatibleBorrowPointer; - if (!CheckFatFunctionPointerType(LHSType, RHS.get())) - return IncompatibleFatPointer; } } #endif @@ -14956,12 +14909,11 @@ bool Sema::IsAddrBorrowDerefOp(ExprResult &OrigOp) { return false; } -// Handle BSC AddressOperand, Include &mut, &const, &mut*, &const*, &fat. -QualType Sema::GetBSCAddressOperandQualType(QualType resultType, - ExprResult &Input, - const Expr *InputExpr, - UnaryOperatorKind &Opc, - SourceLocation OpLoc) { +QualType Sema::GetBorrowAddressOperandQualType(QualType resultType, + ExprResult &Input, + const Expr *InputExpr, + UnaryOperatorKind &Opc, + SourceLocation OpLoc) { if (Opc == UO_AddrMut || Opc == UO_AddrMutDeref) { if (Opc == UO_AddrMut && IsAddrBorrowDerefOp(Input)) { Opc = UO_AddrMutDeref; @@ -15015,28 +14967,6 @@ QualType Sema::GetBSCAddressOperandQualType(QualType resultType, resultType = resultType.addConstBorrow(Context); } } - } else if (Opc == UO_AddrFat && !resultType.isNull()) { - if (auto ME = dyn_cast(InputExpr)) { - // if p is no-fat pointer, `&fat p->a` is not allowed. - if (ME->isArrow() && !ME->getBase()->getType().isFatQualified()) - Diag(OpLoc, diag::err_addr_fat_on_no_fat_pointer) - << InputExpr->getSourceRange(); - InputExpr = ME->getBase()->IgnoreImpCasts(); - } else if (auto ASE = dyn_cast(InputExpr)) { - // if p is no-fat pointer, `&fat p[5]` is not allowed. - QualType BaseQT = ASE->getBase()->IgnoreImpCasts()->getType(); - if (BaseQT->isPointerType() && !BaseQT.isFatQualified()) - Diag(OpLoc, diag::err_addr_fat_on_no_fat_pointer) - << InputExpr->getSourceRange(); - InputExpr = ASE->getBase()->IgnoreImpCasts(); - } - if (auto DRE = dyn_cast(InputExpr)) { - if (auto VD = dyn_cast(DRE->getDecl())) - if (VD->hasGlobalStorage() && !VD->hasAttr()) - Diag(OpLoc, diag::err_addr_fat_on_global_var_without_checked) - << InputExpr->getSourceRange(); - } - resultType.addFat(); } return resultType; } @@ -15311,10 +15241,6 @@ static QualType CheckIndirectionOperand(Sema &S, Expr *Op, ExprValueKind &VK, else if (const ObjCObjectPointerType *OPT = OpTy->getAs()) Result = OPT->getPointeeType(); -#if ENABLE_BSC - else if (S.getLangOpts().BSC && S.getLangOpts().EnableFatPtr && OpTy->isFatPtrRecordType()) - Result = OpTy->getFatPtrPointeeType(); -#endif else { ExprResult PR = S.CheckPlaceholderExpr(Op); if (PR.isInvalid()) return QualType(); @@ -15416,9 +15342,6 @@ static inline UnaryOperatorKind ConvertTokenKindToUnaryOpcode( #if ENABLE_BSC case tok::ampmut: Opc = UO_AddrMut; break; case tok::ampconst: Opc = UO_AddrConst; break; - case tok::ampfat: - Opc = UO_AddrFat; - break; #endif } return Opc; @@ -16456,20 +16379,19 @@ ExprResult Sema::CreateBuiltinUnaryOp(SourceLocation OpLoc, #if ENABLE_BSC case UO_AddrMutDeref: case UO_AddrConstDeref: - resultType = GetBSCAddressOperandQualType(Input.get()->getType(), Input, - InputExpr, Opc, OpLoc); + resultType = GetBorrowAddressOperandQualType(Input.get()->getType(), Input, + InputExpr, Opc, OpLoc); break; case UO_AddrMut: case UO_AddrConst: - case UO_AddrFat: #endif case UO_AddrOf: resultType = CheckAddressOfOperand(Input, OpLoc); CheckAddressOfNoDeref(InputExpr); RecordModifiableNonNullParam(*this, InputExpr); #if ENABLE_BSC - resultType = - GetBSCAddressOperandQualType(resultType, Input, InputExpr, Opc, OpLoc); + resultType = GetBorrowAddressOperandQualType(resultType, Input, InputExpr, + Opc, OpLoc); #endif break; case UO_Deref: { @@ -17985,8 +17907,6 @@ bool Sema::DiagnoseAssignmentResult(AssignConvertType ConvTy, isInvalid = true; break; case Incompatible: - if (getLangOpts().BSC && IsFatPtrEqualExpr(*this, DstType, SrcType)) - return false; if (maybeDiagnoseAssignmentToFunction(*this, DstType, SrcExpr)) { if (Complained) *Complained = true; @@ -18002,7 +17922,6 @@ bool Sema::DiagnoseAssignmentResult(AssignConvertType ConvTy, #if ENABLE_BSC case IncompatibleOwnedPointer: case IncompatibleBorrowPointer: - case IncompatibleFatPointer: return false; case IncompatibleBSCSafeZone: return true; diff --git a/clang/lib/Sema/SemaExprMember.cpp b/clang/lib/Sema/SemaExprMember.cpp index 4d6becec6f43..112c767a1b04 100644 --- a/clang/lib/Sema/SemaExprMember.cpp +++ b/clang/lib/Sema/SemaExprMember.cpp @@ -1075,17 +1075,8 @@ Sema::BuildMemberReferenceExpr(Expr *BaseExpr, QualType BaseExprType, ActOnMemberAccessExtraArgs *ExtraArgs) { QualType BaseType = BaseExprType; if (IsArrow) { -#if ENABLE_BSC - if (getLangOpts().BSC && getLangOpts().EnableFatPtr && - BaseType->isFatPtrRecordType()) - BaseType = BaseType->getFatPtrPointeeType(); - else { -#endif assert(BaseType->isPointerType()); BaseType = BaseType->castAs()->getPointeeType(); -#if ENABLE_BSC - } -#endif } R.setBaseObjectType(BaseType); @@ -1505,11 +1496,6 @@ static ExprResult LookupMemberExpr(Sema &S, LookupResult &R, else if (const ObjCObjectPointerType *Ptr = BaseType->getAs()) BaseType = Ptr->getPointeeType(); -# if ENABLE_BSC - else if (S.getLangOpts().BSC && S.getLangOpts().EnableFatPtr && - BaseType->isFatPtrRecordType()) - BaseType = BaseType->getFatPtrPointeeType(); -# endif else if (BaseType->isRecordType()) { // Recover from arrow accesses to records, e.g.: // struct MyRecord foo; @@ -2120,18 +2106,8 @@ Sema::BuildFieldReferenceExpr(Expr *BaseExpr, bool IsArrow, VK = VK_LValue; } else { QualType BaseType = BaseExpr->getType(); - if (IsArrow) -# if ENABLE_BSC - { - if (getLangOpts().BSC && getLangOpts().EnableFatPtr && - BaseType->isFatPtrRecordType()) - BaseType = BaseType->getFatPtrPointeeType(); - else -# endif - BaseType = BaseType->castAs()->getPointeeType(); -# if ENABLE_BSC - } -# endif + if (IsArrow) BaseType = BaseType->castAs()->getPointeeType(); + Qualifiers BaseQuals = BaseType.getQualifiers(); // GC attributes are never picked up by members. diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index 5599d29754fe..8244126a2fac 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -8041,8 +8041,7 @@ BuiltinCandidateTypeSet::AddPointerWithMoreQualifiedTypeVariants(QualType Ty, #if ENABLE_BSC bool hasOwned = VisibleQuals.hasOwned(); bool hasBorrow = VisibleQuals.hasBorrow(); - bool hasFat = VisibleQuals.hasFat(); -#endif + #endif // Iterate through all strict supersets of BaseCVR. for (unsigned CVR = BaseCVR+1; CVR <= Qualifiers::CVRMask; ++CVR) { @@ -8056,11 +8055,7 @@ BuiltinCandidateTypeSet::AddPointerWithMoreQualifiedTypeVariants(QualType Ty, // Skip over borrow if no borrow found anywhere in the types. if ((CVR & Qualifiers::Borrow) && !hasBorrow) continue; - - // Skip over fat if no fat found anywhere in the types. - if ((CVR & Qualifiers::Fat) && !hasFat) - continue; -#endif + #endif // Skip over restrict if no restrict found anywhere in the types, or if // the type cannot be restrict-qualified. diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index 5882e115de86..f04325d2063b 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -1999,13 +1999,10 @@ QualType Sema::BuildQualifiedType(QualType T, SourceLocation Loc, Qs.removeVolatile(); } #if ENABLE_BSC - if (getLangOpts().BSC && !T->isPointerType()) { - if (Qs.hasBorrow()) + if (getLangOpts().BSC && Qs.hasBorrow()) { + if (!T->isPointerType()) Diag(DS ? DS->getBorrowSpecLoc() : Loc, diag::err_typecheck_invalid_borrow_not_pointer) << T; - if (Qs.hasFat()) - Diag(DS ? DS->getFatSpecLoc() : Loc, - diag::err_typecheck_invalid_fat_not_pointer); } #endif diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp index 3dac2d46a7cf..cb19ab6202fa 100644 --- a/clang/lib/Serialization/ASTWriterStmt.cpp +++ b/clang/lib/Serialization/ASTWriterStmt.cpp @@ -758,8 +758,7 @@ void ASTStmtWriter::VisitUnaryOperator(UnaryOperator *E) { Record.push_back(HasFPFeatures); Record.AddStmt(E->getSubExpr()); #if ENABLE_BSC - if (E->getOpcode() == UO_AddrMut || E->getOpcode() == UO_AddrConst || - E->getOpcode() == UO_AddrFat) + if (E->getOpcode() == UO_AddrMut || E->getOpcode() == UO_AddrConst) Record.push_back(UO_AddrOf); else #endif diff --git a/clang/test/BSC/Negative/FatPtr/addr_fat.cbs b/clang/test/BSC/Negative/FatPtr/addr_fat.cbs deleted file mode 100644 index 09c1b718876a..000000000000 --- a/clang/test/BSC/Negative/FatPtr/addr_fat.cbs +++ /dev/null @@ -1,58 +0,0 @@ -// RUN: %clang_cc1 -verify %s - -struct S { int a; }; -void test1() { - struct S *fat p1; - struct S *p2; - int *fat p3 = &fat p1->a; - int *fat p4 = &fat p2->a;// expected-error {{'&fat' on member of no-fat pointer is not allowed}} - float *fat p5 = &fat p1->a;// expected-error {{incompatible fat types, cannot cast 'int *fat' to 'float *fat'}} - float *fat p6 = (float *fat)&fat p1->a; - - int *fat p7; - int *p8; - int *fat p9 = &fat p7[1]; - int *fat p10 = &fat p8[1]; // expected-error {{'&fat' on member of no-fat pointer is not allowed}} - float *fat p11 = &fat p7[1];// expected-error {{incompatible fat types, cannot cast 'int *fat' to 'float *fat'}} - float *fat p12 = (float *fat)&fat p7[1]; - - struct S s; - int arr[10]; - int *fat p13 = &fat s.a; - float *fat p14 = &fat s.a; // expected-error {{incompatible fat types, cannot cast 'int *fat' to 'float *fat'}} - float *fat p15 = (float *fat)&fat s.a; - int *fat p16 = &fat arr[5]; - float *fat p17 = &fat arr[5];// expected-error {{incompatible fat types, cannot cast 'int *fat' to 'float *fat'}} - float *fat p18 = (float *fat)&fat arr[5]; -} - -__attribute__((fat_ptr_checked)) int arr1[5]; -int arr2[5]; -__attribute__((fat_ptr_checked)) static int arr3[5]; -static int arr4[5]; -void test2() { - __attribute__((fat_ptr_checked)) static int arr5[5]; - static int arr6[5]; - int *fat p1 = &fat arr1[0]; - int *fat p2 = &fat arr2[0]; // expected-error {{'&fat' on global variables without fat_ptr_checked attribute is not allowed}} - int *fat p3 = &fat arr3[0]; - int *fat p4 = &fat arr4[0]; // expected-error {{'&fat' on global variables without fat_ptr_checked attribute is not allowed}} - int *fat p5 = &fat arr5[0]; - int *fat p6 = &fat arr6[0]; // expected-error {{'&fat' on global variables without fat_ptr_checked attribute is not allowed}} -} - -__attribute__((fat_ptr_checked)) int g1 = 10; // expected-error {{fat_ptr_checked attribute can only used for global or static array variables}} -__attribute__((fat_ptr_checked)) static int g2 = 10; // expected-error {{fat_ptr_checked attribute can only used for global or static array variables}} -extern __attribute__((fat_ptr_checked)) int g3; // expected-error {{fat_ptr_checked attribute can only used for global or static array variables}} -int g4 = 10; -static int g5 = 10; -extern int g6; -void test3() { - int *fat p1 = &fat g4; // expected-error {{'&fat' on global variables without fat_ptr_checked attribute is not allowed}} - int *fat p2 = &fat g5; // expected-error {{'&fat' on global variables without fat_ptr_checked attribute is not allowed}} - int *fat p3 = &fat g6; // expected-error {{'&fat' on global variables without fat_ptr_checked attribute is not allowed}} - - __attribute__((fat_ptr_checked)) static int x1 = 10; // expected-error {{fat_ptr_checked attribute can only used for global or static array variables}} - static int x2 = 10; - int *fat p4 = &fat x2; // expected-error {{'&fat' on global variables without fat_ptr_checked attribute is not allowed}} -} \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/fat_cast.cbs b/clang/test/BSC/Negative/FatPtr/fat_cast.cbs deleted file mode 100644 index 50008ac52448..000000000000 --- a/clang/test/BSC/Negative/FatPtr/fat_cast.cbs +++ /dev/null @@ -1,72 +0,0 @@ -// RUN: %clang_cc1 -verify %s -void foo1(int *fat p); -void foo2(int *p); -int *fat foo3(); -int * foo4(); - -typedef void (*PF1)(int *fat p); -typedef void (*PF2)(int *p); -typedef int *fat (*PF3)(); -typedef int * (*PF4)(); - -void test1() { //cast between function pointer and function - PF1 p1 = foo1; - PF1 p2 = foo2;// expected-error {{incompatible fat function pointer types, cannot cast 'PF1' (aka 'void (*)(int *fat)') to 'void (int *)'}} - PF2 p3 = foo1;// expected-error {{incompatible fat function pointer types, cannot cast 'PF2' (aka 'void (*)(int *)') to 'void (int *fat)'}} - PF2 p4 = foo2; - PF3 p5 = foo3; - PF3 p6 = foo4;// expected-error {{incompatible fat function pointer types, cannot cast 'PF3' (aka 'int *fat (*)(void)') to 'int *(void)'}} - PF4 p7 = foo3;// expected-error {{incompatible fat function pointer types, cannot cast 'PF4' (aka 'int *(*)(void)') to 'int *fat (void)'}} - PF4 p8 = foo4; -} - -void test2() { //cast between zero and fat pointer - int * fat b1 = 0; // expected-error {{incompatible fat types, cannot cast 'int' to 'int *fat'}} - int * fat b2 = {0};// expected-error {{incompatible fat types, cannot cast 'int' to 'int *fat'}} - foo1(0); // expected-error {{incompatible fat types, cannot cast 'int' to 'int *fat'}} -} - -void test3() { //cast between raw pointer and fat ptr - int a = 1; - // explicit cast between raw pointer and fat ptr is allowed. - int *fat p1 = nullptr; - const int *fat p2 = nullptr; - int *p3 = &a; - const int *p4 = &a; - // T * --> T *fat - p1 = (int *fat)&a; - p2 = (const int *fat)&a; - p1 = (int *fat)p3; - p2 = (const int *fat)p4; - p1 = (int *fat)foo4(); - foo1((int *fat)p3); - // T *fat --> T * - p3 = (int *)p1; - p4 = (const int *)p2; - p3 = (int *)foo3(); - foo2((int *)p1); - // implicit cast between raw pointer and fat ptr is not allowed. - // T * --> T *fat - p1 = p3; // expected-error {{incompatible fat types, cannot cast 'int *' to 'int *fat'}} - p2 = p4; // expected-error {{incompatible fat types, cannot cast 'const int *' to 'const int *fat'}} - p1 = foo4();// expected-error {{incompatible fat types, cannot cast 'int *' to 'int *fat'}} - foo1(p3);// expected-error {{incompatible fat types, cannot cast 'int *' to 'int *fat'}} - // T *fat --> T * - p3 = p1; // expected-error {{incompatible fat types, cannot cast 'int *fat' to 'int *'}} - p4 = p2; // expected-error {{incompatible fat types, cannot cast 'const int *fat' to 'const int *'}} - p3 = foo3();// expected-error {{incompatible fat types, cannot cast 'int *fat' to 'int *'}} - foo2(p1);// expected-error {{incompatible fat types, cannot cast 'int *fat' to 'int *'}} - - safe { - int *fat p1 = nullptr; - const int *fat p2 = nullptr; - int *p3 = (int *)p1; // expected-error {{conversion from type 'int *fat' to 'int *' is forbidden in the safe zone}} - const int *p4 = (const int *)p2; // expected-error {{conversion from type 'const int *fat' to 'const int *' is forbidden in the safe zone}} - // T * --> T *fat - p1 = (int *fat)p3; // expected-error {{conversion from type 'int *' to 'int *fat' is forbidden in the safe zone}} - p2 = (const int *fat)p4; // expected-error {{conversion from type 'const int *' to 'const int *fat' is forbidden in the safe zone}} - // T *fat --> T * - p3 = (int *)p1; // expected-error {{conversion from type 'int *fat' to 'int *' is forbidden in the safe zone}} - p4 = (const int *)p2; // expected-error {{conversion from type 'const int *fat' to 'const int *' is forbidden in the safe zone}} - } -} \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/fat_qulifier_only_for_pointer.cbs b/clang/test/BSC/Negative/FatPtr/fat_qulifier_only_for_pointer.cbs deleted file mode 100644 index 3006aa21235d..000000000000 --- a/clang/test/BSC/Negative/FatPtr/fat_qulifier_only_for_pointer.cbs +++ /dev/null @@ -1,24 +0,0 @@ -// RUN: %clang_cc1 -verify %s -struct R { - int fat p1; // expected-error {{only pointer type can be qualified by fat}} - const int fat p2; // expected-error {{only pointer type can be qualified by fat}} -}; -struct V { - T fat p1; // expected-error {{only pointer type can be qualified by fat}} - const T fat p2; // expected-error {{only pointer type can be qualified by fat}} -}; - -void f1(int fat a); // expected-error {{only pointer type can be qualified by fat}} -void f2(int fat a) {} // expected-error {{only pointer type can be qualified by fat}} -void f3(T fat a) {} // expected-error {{only pointer type can be qualified by fat}} - -void f4(int *fat a) {} -void f5(T *fat a) {} - - -void test() { - int fat b1; // expected-error {{only pointer type can be qualified by fat}} - const int fat b2; // expected-error {{only pointer type can be qualified by fat}} - int fat * b3; // expected-error {{only pointer type can be qualified by fat}} - const int fat * b4; // expected-error {{only pointer type can be qualified by fat}} -} \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/global_array/array_out_of_bounds1.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/global_array/array_out_of_bounds1.cbs deleted file mode 100644 index 5594d6b6d211..000000000000 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/global_array/array_out_of_bounds1.cbs +++ /dev/null @@ -1,12 +0,0 @@ -// RUN: %clang -enable-fat-ptr %s -o %t.output -// RUN: not %t.output 2>&1 | FileCheck %s - -#include - -__attribute__((fat_ptr_checked)) int arr[3]; -int main() { - int *fat p = &fat arr[3]; - *p = 10; - return 0; -} -// CHECK: pointer offset exceed the allocation size! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/global_array/array_out_of_bounds2.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/global_array/array_out_of_bounds2.cbs deleted file mode 100644 index 2d8b7aae4bb6..000000000000 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/global_array/array_out_of_bounds2.cbs +++ /dev/null @@ -1,14 +0,0 @@ -// RUN: %clang -enable-fat-ptr %s -o %t.output -// RUN: not %t.output 2>&1 | FileCheck %s - -#include - -__attribute__((fat_ptr_checked)) int arr[3]; - -int main() { - int *fat p = &fat arr[2]; - int *fat p1 = p + 1; - *p1 = 10; - return 0; -} -// CHECK: pointer offset exceed the allocation size! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds1.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds1.cbs deleted file mode 100644 index 43d004ddc475..000000000000 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds1.cbs +++ /dev/null @@ -1,11 +0,0 @@ -// RUN: %clang -enable-fat-ptr %s -o %t.output -// RUN: not %t.output 2>&1 | FileCheck %s - -#include - -int main() { - int *fat p = checked_malloc(3 * sizeof(int)); - int a = p[3]; - return 0; -} -// CHECK: pointer offset exceed the allocation size! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds2.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds2.cbs deleted file mode 100644 index c3b97993ee12..000000000000 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds2.cbs +++ /dev/null @@ -1,11 +0,0 @@ -// RUN: %clang -enable-fat-ptr %s -o %t.output -// RUN: not %t.output 2>&1 | FileCheck %s - -#include -struct S { int *fat p; }; -int main() { - struct S s = { checked_malloc(3 * sizeof(int)) }; - int a = s.p[3]; - return 0; -} -// CHECK: pointer offset exceed the allocation size! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds3.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds3.cbs deleted file mode 100644 index 910359d28473..000000000000 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds3.cbs +++ /dev/null @@ -1,12 +0,0 @@ -// RUN: %clang -enable-fat-ptr %s -o %t.output -// RUN: not %t.output 2>&1 | FileCheck %s - -#include - -int main() { - int *fat p = checked_malloc(3 * sizeof(int)); - int *fat p1 = p + 3; - int a = *p1; - return 0; -} -// CHECK: pointer offset exceed the allocation size! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds4.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds4.cbs deleted file mode 100644 index 9a6cbe7ba543..000000000000 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds4.cbs +++ /dev/null @@ -1,13 +0,0 @@ -// RUN: %clang -enable-fat-ptr %s -o %t.output -// RUN: not %t.output 2>&1 | FileCheck %s - -#include - -struct S { int a; }; -int main() { - struct S *fat p = checked_malloc(sizeof(struct S)); - struct S *fat p1 = p + 1; - p1->a = 10; - return 0; -} -// CHECK: pointer offset exceed the allocation size! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds5.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds5.cbs deleted file mode 100644 index 8267ee6635cd..000000000000 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds5.cbs +++ /dev/null @@ -1,13 +0,0 @@ -// RUN: %clang -enable-fat-ptr %s -o %t.output -// RUN: not %t.output 2>&1 | FileCheck %s - -#include - -struct S { int a; int arr[3]; }; -int main() { - struct S *fat p1 = checked_malloc(sizeof(struct S) * 2); - struct S *fat p2 = p1 + 1; - int a = p2->arr[3]; - return 0; -} -// CHECK: pointer offset exceed the allocation size! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds6.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds6.cbs deleted file mode 100644 index dedb1e9f8d70..000000000000 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds6.cbs +++ /dev/null @@ -1,11 +0,0 @@ -// RUN: %clang -enable-fat-ptr %s -o %t.output -// RUN: not %t.output 2>&1 | FileCheck %s - -#include - -struct S { int arr[3]; }; -int main() { - struct S *fat p = checked_malloc(sizeof(struct S)); - int a = p->arr[3]; -} -// CHECK: pointer offset exceed the allocation size! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/buffer_overflow.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/buffer_overflow.cbs deleted file mode 100644 index 474e6c77a4ab..000000000000 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/buffer_overflow.cbs +++ /dev/null @@ -1,21 +0,0 @@ -// RUN: %clang -enable-fat-ptr %s -o %t.output -// RUN: not %t.output longstring 2>&1 | FileCheck %s - -#include -#define BUFSIZE 8 - -char *fat fat_strcpy(char *fat dest, const char *src) { - while (*src != '\0') { - *dest = *src; - dest = dest + 1; - src = src + 1; - } - return dest; -} - -int main(int argc, char **argv) { - char *fat buf = checked_malloc(sizeof(char)*BUFSIZE); - fat_strcpy(buf, argv[1]); - return 0; -} -// CHECK: pointer offset exceed the allocation size! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free1.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free1.cbs deleted file mode 100644 index b7cf5d4ce5e7..000000000000 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free1.cbs +++ /dev/null @@ -1,13 +0,0 @@ -// RUN: %clang -enable-fat-ptr %s -o %t.output -// RUN: not %t.output 2>&1 | FileCheck %s - -#include - -int main() { - int *fat p = checked_malloc(sizeof(int)); - checked_free(p); - checked_free(p); - return 0; -} - -// CHECK: version number error when free! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free2.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free2.cbs deleted file mode 100644 index adb31c07c81e..000000000000 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free2.cbs +++ /dev/null @@ -1,14 +0,0 @@ -// RUN: %clang -enable-fat-ptr %s -o %t.output -// RUN: not %t.output 2>&1 | FileCheck %s - -#include - -int main() { - int *fat p1 = checked_malloc(sizeof(int)); - int *fat p2 = p1; - checked_free(p1); - checked_free(p2); - return 0; -} - -// CHECK: version number error when free! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free3.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free3.cbs deleted file mode 100644 index a5a4b818647f..000000000000 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free3.cbs +++ /dev/null @@ -1,16 +0,0 @@ -// RUN: %clang -enable-fat-ptr %s -o %t.output -// RUN: not %t.output 2>&1 | FileCheck %s - -#include - -struct G { int *fat a; }; -int main() { - struct G g = { .a = checked_malloc(sizeof(int)) }; - struct G *fat p = checked_malloc(sizeof(struct G)); - p->a = g.a; - checked_free(p->a); - checked_free(g.a); - return 0; -} - -// CHECK: version number error when free! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free4.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free4.cbs deleted file mode 100644 index f9ddbb207a9c..000000000000 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/double_free4.cbs +++ /dev/null @@ -1,18 +0,0 @@ -// RUN: %clang -enable-fat-ptr %s -o %t.output -// RUN: not %t.output 2>&1 | FileCheck %s - -#include - -int *fat test() { - int *fat p = checked_malloc(sizeof(int)); - checked_free(p); - return p; -} - -int main() { - int *fat p = test(); - checked_free(p); - return 0; -} - -// CHECK: version number error when free! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free1.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free1.cbs deleted file mode 100644 index 42fc9676d354..000000000000 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free1.cbs +++ /dev/null @@ -1,14 +0,0 @@ -// RUN: %clang -enable-fat-ptr %s -o %t.output -// RUN: not %t.output 2>&1 | FileCheck %s - -#include - -int main() { - int *fat p = checked_malloc(sizeof(int)); - *p = 10; - checked_free(p); - *p = 10; - return 0; -} - -// CHECK: version number error! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free2.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free2.cbs deleted file mode 100644 index 31ff1217ea98..000000000000 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free2.cbs +++ /dev/null @@ -1,15 +0,0 @@ -// RUN: %clang -enable-fat-ptr %s -o %t.output -// RUN: not %t.output 2>&1 | FileCheck %s - -#include - -struct S { int a; }; -int main() { - struct S *fat p = checked_malloc(sizeof(struct S)); - p->a = 10; - checked_free(p); - p->a = 10; - return 0; -} - -// CHECK: version number error! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free3.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free3.cbs deleted file mode 100644 index 3c9ef85f86d0..000000000000 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free3.cbs +++ /dev/null @@ -1,17 +0,0 @@ -// RUN: %clang -enable-fat-ptr %s -o %t.output -// RUN: not %t.output 2>&1 | FileCheck %s - -#include - -int *fat test() __attribute__((fat_ptr_checked)) { - int *fat p = checked_malloc(sizeof(int)); - checked_free(p); - return p; -} - -int main() { - int *fat p = test(); - *p = 10; - return 0; -} -// CHECK: version number error! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free4.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free4.cbs deleted file mode 100644 index e0227f63c4db..000000000000 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free4.cbs +++ /dev/null @@ -1,15 +0,0 @@ -// RUN: %clang -enable-fat-ptr %s -o %t.output -// RUN: not %t.output 2>&1 | FileCheck %s - -#include - -struct G { int *fat a; }; -int main() { - struct G g = { .a = checked_malloc(sizeof(int)) }; - struct G *fat p = checked_malloc(sizeof(struct G)); - p->a = g.a; - checked_free(p->a); - *g.a = 10; - return 0; -} -// CHECK: version number error! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free5.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free5.cbs deleted file mode 100644 index 0c420fce218d..000000000000 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free5.cbs +++ /dev/null @@ -1,29 +0,0 @@ -// RUN: %clang -enable-fat-ptr %s -o %t.output -// RUN: not %t.output 2>&1 | FileCheck %s - -#include -#define BUFSIZE 32 - -char *fat fat_strncpy(char *fat dest, const char *src, size_t n) { - size_t i; - for (i = 0; i < n && src[i] != '\0'; i++) { - dest[i] = src[i]; - } - for (; i < n; i++) { - dest[i] = '\0'; - } - return dest; -} - -int main() { - char *fat buf1 = checked_malloc(BUFSIZE); - checked_free(buf1); - - char *fat buf2 = checked_malloc(BUFSIZE); - memset((char *)buf2, 0, BUFSIZE); - - fat_strncpy(buf1, "hack", 5); - checked_free(buf2); - return 0; -} -// CHECK: version number error! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free6.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free6.cbs deleted file mode 100644 index 4b00fec29811..000000000000 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free6.cbs +++ /dev/null @@ -1,14 +0,0 @@ -// RUN: %clang -enable-fat-ptr %s -o %t.output -// RUN: not %t.output 2>&1 | FileCheck %s - -#include - -void pass_fat(int *p) {} - -int main() { - int *fat p = checked_malloc(sizeof(int)); - checked_free(p); - pass_fat((int *)p); - return 0; -} -// CHECK: version number error! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds1.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds1.cbs deleted file mode 100644 index 07ea7ca2204c..000000000000 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds1.cbs +++ /dev/null @@ -1,12 +0,0 @@ -// RUN: %clang -enable-fat-ptr %s -o %t.output -// RUN: not %t.output 2>&1 | FileCheck %s - -#include - -int main() { - int arr[3] = { 1, 2, 3 }; - int *fat p = &fat arr[3]; - *p = 10; - return 0; -} -// CHECK: pointer offset exceed the allocation size! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds2.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds2.cbs deleted file mode 100644 index 80280bf71a33..000000000000 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds2.cbs +++ /dev/null @@ -1,13 +0,0 @@ -// RUN: %clang -enable-fat-ptr %s -o %t.output -// RUN: not %t.output 2>&1 | FileCheck %s - -#include - -int main() { - int arr[3] = { 1, 2, 3 }; - int *fat p = &fat arr[2]; - int *fat p1 = p + 1; - *p1 = 10; - return 0; -} -// CHECK: pointer offset exceed the allocation size! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/buffer_overflow.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/buffer_overflow.cbs deleted file mode 100644 index d4230082860d..000000000000 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/buffer_overflow.cbs +++ /dev/null @@ -1,22 +0,0 @@ -// RUN: %clang -enable-fat-ptr %s -o %t.output -// RUN: not %t.output longstring 2>&1 | FileCheck %s - -#include -#define BUFSIZE 8 - -char *fat fat_strcpy(char *fat dest, const char *src) { - while (*src != '\0') { - *dest = *src; - dest = dest + 1; - src = src + 1; - } - return dest; -} - -int main(int argc, char **argv) { - char buf[BUFSIZE]; - char *fat p = &fat buf[0]; - fat_strcpy(p, argv[1]); - return 0; -} -// CHECK: pointer offset exceed the allocation size! \ No newline at end of file diff --git a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/return_local_address.cbs b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/return_local_address.cbs deleted file mode 100644 index c39bf785d13b..000000000000 --- a/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/return_local_address.cbs +++ /dev/null @@ -1,17 +0,0 @@ -// RUN: %clang -enable-fat-ptr %s -o %t.output -// RUN: not %t.output 2>&1 | FileCheck %s - -#include - -int *fat test() { - int local; - int *fat p = &fat local; - return p; -} - -int main() { - int *fat p = test(); - *p = 10; - return 0; -} -// CHECK: version number error! \ No newline at end of file diff --git a/clang/test/BSC/Positive/FatPtr/global_array.cbs b/clang/test/BSC/Positive/FatPtr/global_array.cbs deleted file mode 100644 index 7f4893eab5f0..000000000000 --- a/clang/test/BSC/Positive/FatPtr/global_array.cbs +++ /dev/null @@ -1,23 +0,0 @@ -// RUN: %clang %s -enable-fat-ptr -o %t.output -// RUN: %t.output -// expected-no-diagnostics - -#include - -__attribute__((fat_ptr_checked)) int arr[3]; -void test1() { - int *fat p = &fat arr[2]; - *p = 10; -} - -void test2() { - int *fat p = &fat arr[1]; - int *fat p1 = p + 1; - *p1 = 10; -} - -int main() { - test1(); - test2(); - return 0; -} \ No newline at end of file diff --git a/clang/test/BSC/Positive/FatPtr/heap.cbs b/clang/test/BSC/Positive/FatPtr/heap.cbs deleted file mode 100644 index 5c8a83374c7e..000000000000 --- a/clang/test/BSC/Positive/FatPtr/heap.cbs +++ /dev/null @@ -1,94 +0,0 @@ -// RUN: %clang %s -enable-fat-ptr -o %t.output -// RUN: %t.output -// expected-no-diagnostics - -#include -struct S { int a; }; -void test1() { - int *fat p1 = checked_malloc(sizeof(int)); - *p1 = 10; - checked_free(p1); - - struct S *fat p2 = checked_malloc(sizeof(struct S)); - p2->a = 10; - checked_free(p2); -} - -struct G { int *fat a; }; -void test2() { - struct G g = { .a = checked_malloc(sizeof(int)) }; - struct G *fat p = checked_malloc(sizeof(struct G)); - *p = g; - *p->a = 10; - checked_free(p->a); - checked_free(p); -} - -void test3() { - int *fat p1 = checked_malloc(sizeof(int)); - int *fat p2 = checked_malloc(sizeof(int)); - *p1 = *p2; - checked_free(p1); - checked_free(p2); -} - -void test4() { - int *fat p1 = checked_malloc(sizeof(int)); - int *fat p2 = checked_malloc(sizeof(int)); - p1 = p2; - checked_free(p1); //only free once -} - -void test5() { - int *fat p = checked_malloc(sizeof(int) * 3); - int a = p[2]; - checked_free(p); -} - -void test6() { - int *fat p1 = checked_malloc(sizeof(int)); - int *fat p2 = p1; - int *p3 = (int *)p1; - float *fat p4 = (float *fat)p1; - checked_free(p1); -} - -void test7() { - int *fat p = checked_malloc(3 * sizeof(int)); - int *fat p1 = p + 2; - int a = *p1; -} - -void test8(int *fat p1, int *fat p2) { - p1 = checked_malloc(3 * sizeof(int)); - p2 = p1 + 2; - int a = *p1; - int b = *p2; -} - -struct K { int arr[3]; }; -void test9() { - struct K *fat p1 = checked_malloc(sizeof(struct K) * 2); - int a = p1->arr[3]; - int *fat p2 = &fat p1->arr[3]; - *p2 = 10; - - struct K *fat p3 = p1 + 1; - int b = p3->arr[2]; - int *fat p4 = &fat p3->arr[2]; - *p4 = 10; -} - -int main() { - test1(); - test2(); - test3(); - test4(); - test5(); - test6(); - test7(); - int *fat p1; - int *fat p2; - test8(p1, p2); - return 0; -} \ No newline at end of file diff --git a/clang/test/BSC/Positive/FatPtr/stack.cbs b/clang/test/BSC/Positive/FatPtr/stack.cbs deleted file mode 100644 index c44dab32adca..000000000000 --- a/clang/test/BSC/Positive/FatPtr/stack.cbs +++ /dev/null @@ -1,24 +0,0 @@ -// RUN: %clang %s -enable-fat-ptr -o %t.output -// RUN: %t.output -// expected-no-diagnostics - -#include - -void test1() { - int arr[3] = { 1, 2, 3 }; - int *fat p = &fat arr[2]; - *p = 10; -} - -void test2() { - int arr[3] = { 1, 2, 3 }; - int *fat p = &fat arr[1]; - int *fat p1 = p + 1; - *p1 = 10; -} - -int main() { - test1(); - test2(); - return 0; -} \ No newline at end of file -- Gitee From 20e435152f42def14ee2b94bdb4f242ecb039734 Mon Sep 17 00:00:00 2001 From: zhaoxuhui Date: Sat, 22 Mar 2025 15:52:13 +0800 Subject: [PATCH 29/29] [BSC] fix failed testcases after rollback --- .../clang/Basic/BSC/DiagnosticBSCSemaKinds.td | 3 +- clang/lib/AST/BSC/TypeBSC.cpp | 82 +-- clang/lib/Analysis/BSC/BSCFatPtrCheck.cpp | 598 ------------------ clang/lib/Sema/Sema.cpp | 42 +- clang/lib/Sema/SemaDecl.cpp | 34 +- .../redefinition_a_owned_struct_1.cbs | 2 +- 6 files changed, 65 insertions(+), 696 deletions(-) delete mode 100644 clang/lib/Analysis/BSC/BSCFatPtrCheck.cpp diff --git a/clang/include/clang/Basic/BSC/DiagnosticBSCSemaKinds.td b/clang/include/clang/Basic/BSC/DiagnosticBSCSemaKinds.td index 656bbf15bf35..a73a262c2ce9 100644 --- a/clang/include/clang/Basic/BSC/DiagnosticBSCSemaKinds.td +++ b/clang/include/clang/Basic/BSC/DiagnosticBSCSemaKinds.td @@ -270,4 +270,5 @@ def err_nullable_pointer_access_member : Error< "cannot access member through nullable pointer">, InGroup; def err_nonnull_assigned_by_nullable : Error< - "nonnull pointer cannot be assigned by nullable pointer">; + "nonnull pointer cannot be assigned by nullable pointer">, + InGroup; diff --git a/clang/lib/AST/BSC/TypeBSC.cpp b/clang/lib/AST/BSC/TypeBSC.cpp index 47c8010a5115..a4c393f37b09 100644 --- a/clang/lib/AST/BSC/TypeBSC.cpp +++ b/clang/lib/AST/BSC/TypeBSC.cpp @@ -66,52 +66,13 @@ bool Type::hasBorrowFields() const { return false; } -bool PointerType::hasFatFields() const { - QualType R = getPointeeType(); - if (R.isFatQualified()) { - return true; - } - if (R.getTypePtr()->hasFatFields()) { - return true; +bool Type::withBorrowFields() const { + if (const auto *RT = dyn_cast(CanonicalType)) { + return RT->withBorrowFields(); } return false; } -bool Type::hasFatFields() const { - if (const auto *RecTy = dyn_cast(CanonicalType)) { - return RecTy->hasFatFields(); - } else if (const auto *PointerTy = dyn_cast(CanonicalType)) { - return PointerTy->hasFatFields(); - } - return false; -} - -bool Type::isFatPtrRecordType() const { - if (this->isRecordType()) - if (RecordDecl *RD = this->getAs()->getDecl()) - if (RD->getNameAsString() == "_FatPtr") - return true; - return false; -} - -QualType Type::getFatPtrPointeeType() const { - if (this->isFatPtrRecordType()) - if (auto TST = dyn_cast(this)) - if (TST->getNumArgs() == 1) - return TST->begin()->getAsType(); - if (auto PT = dyn_cast(this)) - return PT->getPointeeType(); - return QualType(); -} - -bool QualType::isFatPtrType() const { - if (isFatQualified()) - return true; - if (const Type *ty = getTypePtr()) - return ty->isFatPtrRecordType(); - return false; -} - bool FunctionProtoType::hasOwnedRetOrParams() const { if (getReturnType().isOwnedQualified()) { return true; @@ -300,10 +261,8 @@ bool RecordType::hasBorrowFields() const { return false; } -void RecordType::initFatStatus() const { - if (hasFat != fatStatus::unInitFat) - return; - std::vector RecordTypeList; +bool RecordType::withBorrowFields() const { + std::vector RecordTypeList; RecordTypeList.push_back(this); unsigned NextToCheckIndex = 0; @@ -311,39 +270,16 @@ void RecordType::initFatStatus() const { for (FieldDecl *FD : RecordTypeList[NextToCheckIndex]->getDecl()->fields()) { QualType FieldTy = FD->getType(); - if (FieldTy.isFatQualified()) { - hasFat = fatStatus::withFat; - return; - } - QualType tempQT = FieldTy; - const Type *tempT = tempQT.getTypePtr(); - while (tempT->isPointerType()) { - tempQT = tempT->getPointeeType(); - if (tempQT.isFatQualified()) { - hasFat = fatStatus::withFat; - return; - } else { - tempQT = tempQT.getCanonicalType(); - tempT = tempQT.getTypePtr(); - } - } - FieldTy = tempQT.getCanonicalType(); + if (FieldTy.isBorrowQualified()) + return true; + FieldTy = FieldTy.getCanonicalType(); if (const auto *FieldRecTy = FieldTy->getAs()) { - if (llvm::find(RecordTypeList, FieldRecTy) == RecordTypeList.end()) + if (!llvm::is_contained(RecordTypeList, FieldRecTy)) RecordTypeList.push_back(FieldRecTy); } } ++NextToCheckIndex; } - hasFat = fatStatus::withoutFat; - return; -} - -bool RecordType::hasFatFields() const { - if (hasFat == fatStatus::unInitFat) - initFatStatus(); - if (hasFat == fatStatus::withFat) - return true; return false; } diff --git a/clang/lib/Analysis/BSC/BSCFatPtrCheck.cpp b/clang/lib/Analysis/BSC/BSCFatPtrCheck.cpp deleted file mode 100644 index 92e119b81d1e..000000000000 --- a/clang/lib/Analysis/BSC/BSCFatPtrCheck.cpp +++ /dev/null @@ -1,598 +0,0 @@ -#if ENABLE_BSC - -#include "clang/Analysis/Analyses/BSC/BSCFatPtrCheck.h" -#include "clang/AST/ParentMap.h" -#include "clang/AST/StmtVisitor.h" -#include "clang/Analysis/AnalysisDeclContext.h" -#include "clang/Analysis/CFG.h" -#include "clang/Analysis/FlowSensitive/DataflowWorklist.h" -#include "llvm/ADT/DenseMap.h" - -using namespace clang; - -/// Represents the check status of a fat pointer's associated VarDecl after the last operation that may modify it, -/// such as free(p) or p++. -enum class FatPtrCheckStatus : uint8_t { - Unchecked = 0, // No checks have been performed - - KeyCheckedOnly = 1, // Only the key was checked - - OffsetCheckedOnly = 2, // Only the offset was checked - - BothChecked = 3, // Both the key and the offset were checked -}; - -using FatPtrVar = std::pair; -using FatPtrVarCheckStatus = std::map; - -class FatPtrCheckImpl { -public: - // record each BB's Last and Out pointer var check status - llvm::DenseMap BBLastStatus; - llvm::DenseMap BBOutStatus; - - // For branch statement with condition, such as IfStmt, WhileStmt, - // true branch and else branch may have different status. - // we merge the check status of all the paths to the current BB - // For example: - // @code - // int *fat p = checked_malloc(sizeof(int)); - // *p = 0; - // if (p != nullptr) { - // use(p); // p is perhaps freed - // } else { - // *p = 10; // p is checked before, so the check of p can be deleted here - // } - // *p = 5; // p may be freed after the ifstmt, so the check of p is kept here - // @endcode - // CFG is: - // B4(has condition as terminitor) - // true / \ false - // B3 B2 - // \ / - // B1 - // BBOutStatus records the check status of each BB: - // Key is current BB, value is the status after all the statements in current - // for this example, BBOutStatus will be: - // { B4 : p BothChecked }, { B3 : p Unchecked }, { B2 : p BothChecked }, { B1 : p BothChecked } - // Before caculating BBOutStatus of each BB, we merge the check status of all the pred BBs, - // And then, use the merged status as the input of the current BB. - // for B1, the input check status of p is: BBOutStatus[B3][p] & BBOutStatus[B2][p] - - // BBLastStatus records the check status of each BB at last analysis: - // After analysis of current BB, we compare the BBOutStatus and BBLastStatus, - // if they are different, we add the subsequent BB of the current BB to the analysis worklist. - - FatPtrVarCheckStatus runOnBlock(const CFGBlock *block, - FatPtrVarCheckStatus &status, ASTContext &ctx, - const FunctionDecl &fd, ParentMap &PM); - - void initStatus(const CFG &cfg, ASTContext &ctx); - FatPtrVarCheckStatus mergePredStatus(FatPtrVarCheckStatus currStatus, - FatPtrVarCheckStatus predStatus); - - FatPtrCheckImpl() : BBLastStatus(0), BBOutStatus(0) {} -}; - -namespace { -class TransferFunctions : public StmtVisitor { - FatPtrCheckImpl &FCI; - const CFGBlock *Block; - FatPtrVarCheckStatus &CurrStatus; - ASTContext &Ctx; - const FunctionDecl &Fd; - ParentMap &PM; - CallGraph CG; - -public: - TransferFunctions(FatPtrCheckImpl &fci, const CFGBlock *block, - FatPtrVarCheckStatus &status, ASTContext &ctx, - const FunctionDecl &fd, ParentMap &pm) - : FCI(fci), Block(block), CurrStatus(status), Ctx(ctx), Fd(fd), PM(pm){} - - bool mayFreeCallExpr(CallExpr *CE); - void VisitBinaryOperator(BinaryOperator *BO); - void VisitUnaryOperator(UnaryOperator *UO); - void VisitMemberExpr(MemberExpr *ME); - void VisitCallExpr(CallExpr *CE); - void VisitCStyleCastExpr(CStyleCastExpr *CSCE); - void VisitArraySubscriptExpr(ArraySubscriptExpr *ASE); - -private: - // The whitelist of functions that will not free heap memory. - // FIXME: Add more common libc functions that do not free. - std::set MayFreeFnWL = { - "malloc", "checked_malloc", "_check_version", "_check_offset", "_check_version_and_offset", "_new_version_number", - // libc C functions. Need add more. - "printf", "sprintf", "abort", "exit", "srand", - "atoi", "atol", - "strlen", "strcmp", "strncmp", "strcpy", "strncpy", "strcat", "strchar", - "strtod", "strrchr", "strcasecmp", "strdup", "strstr", "strchr", "strpbrk", - "strspn", "atoll", - "memcpy", "memmove", "memcmp", "memset", - "fread", "fputs", "fopen", "syslog", "opendir", - "getpwnam", "getnameinfo", - // Syscalls - "stat", "readlink", "execve", "read", "write", - }; - -}; -} // namespace - -static void VisitMEForFieldPath(Expr *E, FatPtrVar &FP) { - if (auto ME = dyn_cast(E)) { - if (auto FD = dyn_cast(ME->getMemberDecl())) { - FP.second = "." + FD->getNameAsString() + FP.second; - VisitMEForFieldPath(ME->getBase(), FP); - } - } else if (auto DRE = dyn_cast(E)) { - if (VarDecl *VD = dyn_cast(DRE->getDecl())) - FP.first = VD; - } else if (auto ICE = dyn_cast(E)) { - VisitMEForFieldPath(ICE->getSubExpr(), FP); - } else if (auto PE = dyn_cast(E)) { - VisitMEForFieldPath(PE->getSubExpr(), FP); - } -} - -static DeclRefExpr *getDREFromExpr(Expr *E) { - if (auto DRE = dyn_cast(E)) { - return DRE; - } else if (auto ICE = dyn_cast(E)) { - return getDREFromExpr(ICE->getSubExpr()); - } else if (auto PE = dyn_cast(E)) { - return getDREFromExpr(PE->getSubExpr()); - } - return nullptr; -} - -static MemberExpr *getMemberExprFromExpr(Expr *E) { - if (auto ME = dyn_cast(E)) { - return ME; - } else if (auto ICE = dyn_cast(E)) { - return getMemberExprFromExpr(ICE->getSubExpr()); - } else if (auto PE = dyn_cast(E)) { - return getMemberExprFromExpr(PE->getSubExpr()); - } - return nullptr; -} - -static bool IsFatPtrSelfOffSetExpr(BinaryOperator *BO) { - if (!BO->isAssignmentOp()) - return false; - - Expr *LHS = BO->getLHS(); - Expr *RHS = BO->getRHS(); - if (!LHS->getType().isFatPtrType()) - return false; - - // handle `p += 1`, `p -= 1`, if p is fat ptr. - BinaryOperator::Opcode Op = BO->getOpcode(); - if ((Op == BO_AddAssign || Op == BO_SubAssign) && - RHS->getType()->isIntegerType()) - return true; - - // handle `p = p + 1`, if p is fat ptr. - // eg. `p = p + 1`: true - // eg. `p = p2 +1`: false - if (auto *RHSBO = dyn_cast(RHS)) { - if (!RHSBO->isAdditiveOp()) - return false; - - Expr *RBLHS = RHSBO->getLHS(); - Expr *RBRHS = RHSBO->getRHS(); - if (!RBLHS->getType().isFatPtrType() || !RBRHS->getType()->isIntegerType()) - return false; - - if (auto *LHSDRF = getDREFromExpr(LHS)) { - if (auto *RHSLeftDRE = getDREFromExpr(RBLHS)) { - return LHSDRF->getDecl() == RHSLeftDRE->getDecl(); - } - } - // handle `p.a = p.a + 1`, if p.a is fat ptr. - if (auto *LHSME = getMemberExprFromExpr(LHS)) { - if (auto *RHSLeftME = getMemberExprFromExpr(RBLHS)) { - return LHSME->getMemberDecl() == RHSLeftME->getMemberDecl(); - } - } - } - return false; -} - -void TransferFunctions::VisitBinaryOperator(BinaryOperator *BO) { - Expr *LHS = BO->getLHS(); - if (!BO->isAssignmentOp() || !LHS->getType().isFatPtrType()) { - return; - } - - if (DeclRefExpr *DRE = getDREFromExpr(LHS)) { - if (VarDecl *VD = dyn_cast(DRE->getDecl())) { - FatPtrVar FP = {VD, ""}; - if (!VD->getType().isFatPtrType() || !CurrStatus.count(FP)) { - return; - } - if (IsFatPtrSelfOffSetExpr(BO)) { - // handle `p = p + 1`, `p += 1`, if p is fat ptr. - // reset the offset check status when p is self-offset - CurrStatus[FP] = static_cast( - static_cast(CurrStatus[FP]) & - static_cast(FatPtrCheckStatus::KeyCheckedOnly)); - } else { - // reset the both check status when p is reassign - // eg. `p = p2`, `p = p2 + 1`, `p = func()` - CurrStatus[FP] = FatPtrCheckStatus::Unchecked; - } - } - } else if (MemberExpr *ME = getMemberExprFromExpr(LHS)) { - if (auto FD = dyn_cast(ME->getMemberDecl())) { - if (!FD->getType().isFatPtrType()) - return; - FatPtrVar FP; - VisitMEForFieldPath(ME, FP); - if (!CurrStatus.count(FP)) - return; - if (IsFatPtrSelfOffSetExpr(BO)) { - // handle `p.a = p.a + 1`, `p.a += 1`, if p.a is fat ptr. - // reset the offset check status when p is self-offset - CurrStatus[FP] = static_cast( - static_cast(CurrStatus[FP]) & - static_cast(FatPtrCheckStatus::KeyCheckedOnly)); - } else { - // reset the both check status when p.a is reassign - // eg. `p.a = p2.a`, `p.a = p2.a + 1`, `p.a = func()` - CurrStatus[FP] = FatPtrCheckStatus::Unchecked; - } - } - } -} - -// *p will change the check status to BothChecked. -void TransferFunctions::VisitUnaryOperator(UnaryOperator *UO) { - UnaryOperator::Opcode Op = UO->getOpcode(); - FatPtrVar FP; - if (Op == UO_Deref || Op == UO_AddrMutDeref || Op == UO_AddrConstDeref) { - // handle *p if p is fat ptr. - if (DeclRefExpr *DRE = getDREFromExpr(UO->getSubExpr())) { - if (VarDecl *VD = dyn_cast(DRE->getDecl())) { - FP = {VD, ""}; - if (VD->getType().isFatPtrType() && CurrStatus.count(FP)) { - // Label the check kind in the sema phase, for unary expression: *p; - DRE->FatPtrCheckedStatus = static_cast(CurrStatus[FP]); - // update the redundant check status - CurrStatus[FP] = FatPtrCheckStatus::BothChecked; - } - } - } else if (MemberExpr *ME = getMemberExprFromExpr(UO->getSubExpr())) { - // handle *(p->a), if p->a is fat ptr. - if (auto FD = dyn_cast(ME->getMemberDecl())) { - if (FD->getType().isFatPtrType()) { - VisitMEForFieldPath(ME, FP); - if (CurrStatus.count(FP)) { - ME->FatPtrCheckedStatus = static_cast(CurrStatus[FP]); - CurrStatus[FP] = FatPtrCheckStatus::BothChecked; - } - } - } - } - } else if (UO->isIncrementDecrementOp()) { - // handle p++/p--/++p/--p if p is fat ptr. - if (DeclRefExpr *DRE = getDREFromExpr(UO->getSubExpr())) { - if (VarDecl *VD = dyn_cast(DRE->getDecl())) { - FP = {VD, ""}; - if (VD->getType().isFatPtrType() && CurrStatus.count(FP)) { - // reset the offset check status when p is self-offset - CurrStatus[FP] = static_cast( - static_cast(CurrStatus[FP]) & - static_cast(FatPtrCheckStatus::KeyCheckedOnly)); - } - } - } else if (MemberExpr *ME = getMemberExprFromExpr(UO->getSubExpr())) { - // handle (p->a)++, if p->a is fat ptr. - if (auto FD = dyn_cast(ME->getMemberDecl())) { - if (FD->getType().isFatPtrType()) { - // reset the offset check status when p is self-offset - VisitMEForFieldPath(ME, FP); - if (CurrStatus.count(FP)) { - CurrStatus[FP] = static_cast( - static_cast(CurrStatus[FP]) & - static_cast(FatPtrCheckStatus::KeyCheckedOnly)); - } - } - } - } - } -} - -void TransferFunctions::VisitMemberExpr(MemberExpr *ME) { - if (ME->isArrow()) { - FatPtrVar FP; - if (DeclRefExpr *DRE = getDREFromExpr(ME->getBase())) { - if (VarDecl *VD = dyn_cast(DRE->getDecl())) { - FP = {VD, ""}; - if (VD->getType().isFatPtrType() && CurrStatus.count(FP)) { - // Label the check kind in the sema phase, for member expression: p -> a; - DRE->FatPtrCheckedStatus = static_cast(CurrStatus[FP]); - // update the redundant check status - CurrStatus[FP] = FatPtrCheckStatus::BothChecked; - } - } - } else if (MemberExpr *SubME = getMemberExprFromExpr(ME->getBase())) { - // handle p->a->b, if p->a is fat ptr. - if (auto FD = dyn_cast(SubME->getMemberDecl())) { - if (FD->getType().isFatPtrType()) { - VisitMEForFieldPath(SubME, FP); - if (CurrStatus.count(FP)) { - SubME->FatPtrCheckedStatus = static_cast(CurrStatus[FP]); - CurrStatus[FP] = FatPtrCheckStatus::BothChecked; - } - } - } - } - } -} - -// This function judge a user-defined functions in the -// current module may directly or indirectly frees heap memory. -// It conservertively assuems that a function call may free heap objects if it -// -// 1. is an indirect call that is not resolved by compiler or -// 2. calls to potentially unsafe functions -// -// For the second condition, we have a whitelist that contains all the functions -// that we are sure will not free memory, such as malloc. - -bool TransferFunctions::mayFreeCallExpr(CallExpr *CE) { - // Check if it is an direct call, for indirect call, we assuems it may free - if (FunctionDecl *CalleeDecl = CE->getDirectCallee()) { - // Add the CalleeDecl to the call graph - CG.addToCallGraph(CalleeDecl); - CallGraphNode *Node = CG.getNode(CalleeDecl); - if (!Node || Node->empty()) { - // Callee Function definition is in the current module, and has no other function call - if (CalleeDecl->hasBody()) { - return false; - } - // Check if the function is in the whitelist - return MayFreeFnWL.find(CalleeDecl->getNameAsString()) == MayFreeFnWL.end(); - } else { - // Check if the CalleeDecl calls any function that may free heap objects - bool mayFree = false; - for (CallGraphNode::iterator It = Node->begin(); It != Node->end(); - ++It) { - CallGraphNode *CalleeNode = It->Callee; - if (!CalleeNode) - continue; - const FunctionDecl *CalleeFD = - dyn_cast_or_null(CalleeNode->getDecl()); - if (!CalleeFD || MayFreeFnWL.find(CalleeFD->getNameAsString()) == MayFreeFnWL.end()) { - if (CalleeDecl->getNameAsString() != CalleeFD->getNameAsString()){ - mayFree = true; - break; - } - } - } - return mayFree; - } - } - return true; -} - -// For mayfree function call, reset the check status to Unchecked for all the fat pointers -// More optimizations can be done here to reduce resets, including: -// 1. more accurate mayfree analysis -// 2. reset only some pointers through alias analysis. -void TransferFunctions::VisitCallExpr(CallExpr *CE) { - // If the function call may free pointers, reset all fat pointers check status - if (mayFreeCallExpr(CE)) { - for (auto &Entry : CurrStatus) { - Entry.second = FatPtrCheckStatus::Unchecked; - } - return; - } - - // If the function call does not free pointers, check if the arguments are fat pointers - for (unsigned i = 0; i < CE->getNumArgs(); i++) { - Expr *Arg = CE->getArg(i)->IgnoreImpCasts(); - FatPtrVar FP; - if (auto *DRE = dyn_cast(Arg)) { - // If the argument is a fat pointer variable, change its check status to key only - if (VarDecl *VD = dyn_cast(DRE->getDecl())) { - FP = {VD, ""}; - if (VD->getType().isFatPtrType() && CurrStatus.count(FP)) { - CurrStatus[FP] = static_cast( - static_cast(CurrStatus[FP]) & - static_cast(FatPtrCheckStatus::KeyCheckedOnly)); - } - } - } else if(auto *ME = dyn_cast(Arg)) { - // handle func(p->a), if p->a is fat ptr. - if (auto FD = dyn_cast(ME->getMemberDecl())) { - if (FD->getType().isFatPtrType()) { - VisitMEForFieldPath(ME, FP); - if (CurrStatus.count(FP)) { - CurrStatus[FP] = static_cast( - static_cast(CurrStatus[FP]) & - static_cast(FatPtrCheckStatus::KeyCheckedOnly)); - } - } - } - } - } -} - -// Reset the redundant check status to BothChecked for the fat pointers -// which are used after CStyleCast. -void TransferFunctions::VisitCStyleCastExpr(CStyleCastExpr *CSCE) { - FatPtrVar FP; - if (DeclRefExpr *DRE = getDREFromExpr(CSCE->getSubExpr())) { - if (VarDecl *VD = dyn_cast(DRE->getDecl())) { - FP = {VD, ""}; - if (VD->getType().isFatPtrType() && CurrStatus.count(FP)) { - // Label the check kind in the sema phase, for CStyleCast expression: (int *)p; - DRE->FatPtrCheckedStatus = static_cast(CurrStatus[FP]); - // update the redundant check status - CurrStatus[FP] = FatPtrCheckStatus::BothChecked; - } - } - } else if (auto *ME = getMemberExprFromExpr(CSCE->getSubExpr())) { - // handle (int *)(p->a), if p->a is fat ptr. - if (auto FD = dyn_cast(ME->getMemberDecl())) { - if (FD->getType().isFatPtrType()) { - VisitMEForFieldPath(ME, FP); - if (CurrStatus.count(FP)) { - ME->FatPtrCheckedStatus = static_cast(CurrStatus[FP]); - CurrStatus[FP] = FatPtrCheckStatus::BothChecked; - } - } - } - } -} - -void TransferFunctions::VisitArraySubscriptExpr(ArraySubscriptExpr *ASE) { - FatPtrVar FP; - if (DeclRefExpr *DRE = getDREFromExpr(ASE->getLHS())) { - if (VarDecl *VD = dyn_cast(DRE->getDecl())) { - FP = {VD, ""}; - if (VD->getType().isFatPtrType() && CurrStatus.count(FP)) { - // the offset check is not redundant, must be remained for array subscript expression: p[i]; - DRE->FatPtrCheckedStatus = - static_cast(CurrStatus[FP]) & - static_cast(FatPtrCheckStatus::KeyCheckedOnly); - // update the redundant check status to KeyCheckedOnly - CurrStatus[FP] = FatPtrCheckStatus::BothChecked; - } - } - } else if (auto *ME = getMemberExprFromExpr(ASE->getLHS())) { - // handle p->a[i], if p->a is fat ptr. - if (auto FD = dyn_cast(ME->getMemberDecl())) { - if (FD->getType().isFatPtrType()) { - VisitMEForFieldPath(ME, FP); - if (CurrStatus.count(FP)) { - ME->FatPtrCheckedStatus = - static_cast(CurrStatus[FP]) & - static_cast(FatPtrCheckStatus::KeyCheckedOnly); - CurrStatus[FP] = FatPtrCheckStatus::BothChecked; - } - } - } - } -} - -// Traverse all blocks of cfg to collect all fat pointers used, -// including local and global variable and parameters. -// Init check status of these pointers. -void FatPtrCheckImpl::initStatus(const CFG &cfg, ASTContext &ctx) { - const CFGBlock *entry = &cfg.getEntry(); - for (const CFGBlock *B : cfg.const_nodes()) { - if (B != entry && B != &cfg.getExit() && !B->succ_empty() && - !B->pred_empty()) { - for (CFGBlock::const_iterator it = B->begin(), ei = B->end(); it != ei; - ++it) { - const CFGElement &elem = *it; - if (elem.getAs()) { - Stmt *S = const_cast(elem.castAs().getStmt()); - FatPtrVar FP; - if (auto DRE = dyn_cast(S)) { - if (VarDecl *VD = dyn_cast(DRE->getDecl())) - if (VD->getType().isFatPtrType()) { - FP = {VD, ""}; - BBOutStatus[entry][FP] = FatPtrCheckStatus::Unchecked; - } - } else if (auto ME = dyn_cast(S)) { - if (FieldDecl *FD = dyn_cast(ME->getMemberDecl())) { - if (FD->getType().isFatPtrType()) { - VisitMEForFieldPath(ME, FP); - BBOutStatus[entry][FP] = FatPtrCheckStatus::Unchecked; - } - } - } - } - } - } - } -} - -FatPtrVarCheckStatus -FatPtrCheckImpl::mergePredStatus(FatPtrVarCheckStatus currStatus, - FatPtrVarCheckStatus predStatus) { - if (currStatus.empty()) - return predStatus; - for (auto predStatusOfFP : predStatus) { - FatPtrVar FV = predStatusOfFP.first; - FatPtrCheckStatus predKind = predStatusOfFP.second; - if (currStatus.count(FV)) { - FatPtrCheckStatus currKind = currStatus[FV]; - currStatus[FV] = static_cast( - static_cast(currKind) & static_cast(predKind)); - } else { - currStatus[FV] = predKind; - } - } - return currStatus; -} - -FatPtrVarCheckStatus FatPtrCheckImpl::runOnBlock(const CFGBlock *block, - FatPtrVarCheckStatus &status, - ASTContext &ctx, - const FunctionDecl &fd, - ParentMap &PM) { - TransferFunctions TF(*this, block, status, ctx, fd, PM); - - for (CFGBlock::const_iterator it = block->begin(), ei = block->end(); - it != ei; ++it) { - const CFGElement &elem = *it; - if (elem.getAs()) { - const Stmt *S = elem.castAs().getStmt(); - TF.Visit(const_cast(S)); - } - } - - return status; -} - -void clang::runFatPtrReduntantCheck(const FunctionDecl &fd, const CFG &cfg, - AnalysisDeclContext &ac, ASTContext &ctx) { - // The analysis currently has scalability issues for very large CFGs. - // Bail out if it looks too large. - if (cfg.getNumBlockIDs() > 300000) - return; - - FatPtrCheckImpl FCI; - FCI.initStatus(cfg, ctx); - - // Proceed with the worklist. - ForwardDataflowWorklist worklist(cfg, ac); - const CFGBlock *entry = &cfg.getEntry(); - for (const CFGBlock *B : cfg.const_reverse_nodes()) - if (B != entry && !B->pred_empty()) - worklist.enqueueBlock(B); - - while (const CFGBlock *block = worklist.dequeue()) { - // record the last check status of the current block - FatPtrVarCheckStatus lastCurrStatus = FCI.BBLastStatus[block]; - // get the check status of the pred block - FatPtrVarCheckStatus currValStatus, predValStatus; - for (CFGBlock::const_pred_iterator it = block->pred_begin(), - ei = block->pred_end(); - it != ei; ++it) { - if (const CFGBlock *pred = *it) { - predValStatus = FCI.BBOutStatus[pred]; - currValStatus = FCI.mergePredStatus(currValStatus, predValStatus); - } - } - - FatPtrVarCheckStatus BBOutVal = - FCI.runOnBlock(block, currValStatus, ctx, fd, ac.getParentMap()); - FCI.BBOutStatus[block] = BBOutVal; - - // If the value has changed, add the successors in the worklist. - // For While-Stmt, the stmst in the block may change the redundant check status of some fat pointers. - // So, it will be added into the worklist more than once until the analysis result remains unchanged. - if (lastCurrStatus != BBOutVal) { - FCI.BBLastStatus[block] = BBOutVal; - worklist.enqueueSuccessors(block); - } - } -} - -#endif diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index 7f55f3a83696..1c6a912bfebc 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -2211,27 +2211,27 @@ Sema::PopFunctionScopeInfo(const AnalysisBasedWarnings::Policy *WP, if (LangOpts.OpenMP) popOpenMPFunctionRegion(Scope.get()); -#if ENABLE_BSC - // FIXME: Rename EnableOwnershipCheck, maybe EnableBorrowCheck is a better name. - bool EnableOwnershipCheck = LangOpts.BSC ? (!(getLangOpts().DisableOwnershipCheck)) : false; - // If NullabilityCheck is in Mode: {SafeOnly, All}, we should build CFG. - bool EnableNullabilityCheck = LangOpts.BSC ? (getLangOpts().getNullabilityCheck() != LangOptions::NC_NONE) : false; - if (EnableOwnershipCheck || EnableNullabilityCheck) { - if (!isBSCCoroutine && - (getDiagnostics().getNumErrors() == - getDiagnostics().getNumOwnershipErrors() + - getDiagnostics().getNumBorrowCheckErrors() + - getDiagnostics().getNumNullabilityCheckErrors()) && - D) - if (const auto *const CastReturn = dyn_cast_or_null(D)) { - auto md = dyn_cast_or_null(D); - // Don't check ownership rules of destructor parameters - if (!md || !md->isDestructor()) - BSCDataflowAnalysis(D, EnableOwnershipCheck, - EnableNullabilityCheck); - } - } -#endif +// #if ENABLE_BSC +// // FIXME: Rename EnableOwnershipCheck, maybe EnableBorrowCheck is a better name. +// bool EnableOwnershipCheck = LangOpts.BSC ? (!(getLangOpts().DisableOwnershipCheck)) : false; +// // If NullabilityCheck is in Mode: {SafeOnly, All}, we should build CFG. +// bool EnableNullabilityCheck = LangOpts.BSC ? (getLangOpts().getNullabilityCheck() != LangOptions::NC_NONE) : false; +// if (EnableOwnershipCheck || EnableNullabilityCheck) { +// if (!isBSCCoroutine && +// (getDiagnostics().getNumErrors() == +// getDiagnostics().getNumOwnershipErrors() + +// getDiagnostics().getNumBorrowCheckErrors() + +// getDiagnostics().getNumNullabilityCheckErrors()) && +// D) +// if (const auto *const CastReturn = dyn_cast_or_null(D)) { +// auto md = dyn_cast_or_null(D); +// // Don't check ownership rules of destructor parameters +// if (!md || !md->isDestructor()) +// BSCDataflowAnalysis(D, EnableOwnershipCheck, +// EnableNullabilityCheck); +// } +// } +// #endif // Issue any analysis-based warnings. if (WP && D) diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 27347cea4684..02aaf1fdf5ed 100755 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -15990,8 +15990,38 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body, PopDeclContext(); PopFunctionScopeInfo(ActivePolicy, dcl); - if (auto *FD = dyn_cast(dcl)) - DesugarDestructorCall(FD); + +#if ENABLE_BSC + if (LangOpts.BSC) { + if (auto FD = dyn_cast_or_null(dcl)) { + // FIXME: Rename EnableOwnershipCheck, maybe EnableBorrowCheck is a better name. + bool EnableOwnershipCheck = !getLangOpts().DisableOwnershipCheck; + // If NullabilityCheck is in Mode: {SafeOnly, All}, we should build CFG. + bool EnableNullabilityCheck = getLangOpts().getNullabilityCheck() != LangOptions::NC_NONE; + if (EnableOwnershipCheck || EnableNullabilityCheck) { + if (getDiagnostics().getNumErrors() == + getDiagnostics().getNumOwnershipErrors() + + getDiagnostics().getNumBorrowCheckErrors() + + getDiagnostics().getNumNullabilityCheckErrors()) { + bool DoAnalysis = true; + // Skip function template and class template + if (const auto *RD = dyn_cast(FD->getParent())) { + if (RD->getDescribedClassTemplate() != nullptr) + DoAnalysis = false; + } + auto md = dyn_cast_or_null(FD); + // Don't check ownership rules of destructor parameters + if (DoAnalysis && (!md || !md->isDestructor())) + BSCDataflowAnalysis(FD, EnableOwnershipCheck, + EnableNullabilityCheck); + } + } + // Desugar BSC Function. + DesugarDestructorCall(FD); + } + } +#endif + // If any errors have occurred, clear out any temporaries that may have // been leftover. This ensures that these temporaries won't be picked up for // deletion in some later function. diff --git a/clang/test/BSC/Negative/Generic/StructAndFunction/redefinition_a_owned_struct/redefinition_a_owned_struct_1.cbs b/clang/test/BSC/Negative/Generic/StructAndFunction/redefinition_a_owned_struct/redefinition_a_owned_struct_1.cbs index f2db73bc627a..6d318d82673d 100644 --- a/clang/test/BSC/Negative/Generic/StructAndFunction/redefinition_a_owned_struct/redefinition_a_owned_struct_1.cbs +++ b/clang/test/BSC/Negative/Generic/StructAndFunction/redefinition_a_owned_struct/redefinition_a_owned_struct_1.cbs @@ -5,7 +5,7 @@ // RUN: -I %S/../../../../../../../build/lib/clang/15.0.4/include/bsc_include/ \ // RUN: -internal-isystem %S/../../../../../../../lib/Headers/bsc_include \ // RUN: -internal-externc-isystem /usr/include/x86_64-linux-gnu \ -// RUN: -internal-externc-isystem /include -internal-externc-isystem /usr/include -verify +// RUN: -internal-externc-isystem /include -internal-externc-isystem /usr/include -verify #include"vec.hbs" -- Gitee