diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index 328205520b01453e3c0eff9eb77d9f9d331a55a9..64d6e5ec52a91de21475c267cc8e5fb8808c4b2d 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 bc18f1206cb15085b16f3808f71f6ea38682fe0c..fe18feb24b2b4d540592a9b35642624a2a0b7a7f 100644 --- a/clang/include/clang/AST/BSC/WalkerBSC.h +++ b/clang/include/clang/AST/BSC/WalkerBSC.h @@ -89,7 +89,7 @@ public: bool VisitQualType(QualType QT) { if (QT.isOwnedQualified() || QT.isBorrowQualified() || - QT->hasBorrowFields() || QT->hasOwnedFields()) { + QT->hasBorrowFields() || QT->hasOwnedFields() || QT.hasFat()) { return true; } if (IsDesugaredFromTraitType(QT)) { diff --git a/clang/include/clang/AST/CanonicalType.h b/clang/include/clang/AST/CanonicalType.h index 9b7a1d4efe969e2269c5f66d011329bb00dd4fda..89258bedde76dddf432eacb11421f32ae2a36b1a 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/Expr.h b/clang/include/clang/AST/Expr.h index 867dede1da1baa87b726fe8936fa6f680a2ab5d6..98615e310b266a720e9df796c2a0f626fd309fc7 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/AST/OperationKinds.def b/clang/include/clang/AST/OperationKinds.def index 0c48c0a51353f4cdefb3009817ecdc77f84bdee7..b744276b1b46484cd762b7cfef2c2f6186070b7d 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 da654e5a616bc2067c770e923ba4a9e1eac2d70a..b013581c178b6d9b0317c8e74d971ec749034db8 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 cdaca83b0f61cfce23f20183ff8f4f356ea5ed1f..c189a56d6331a2e7447720b0c9d763c2d38a3958 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,21 @@ 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; + bool isFatPtrRecordType() const; + QualType getFatPtrPointeeType() const; + bool hasFat() const; +#endif /// Determine whether this particular QualType instance has the /// "restrict" qualifier set, without looking through typedefs that may have @@ -1034,7 +1064,11 @@ 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); } +#endif /// Add the `volatile` type qualifier to this QualType. void addVolatile() { @@ -1066,7 +1100,8 @@ public: #if ENABLE_BSC void removeLocalOwned(); void removeLocalBorrow(); - #endif + void removeLocalFat(); +#endif void removeLocalVolatile(); void removeLocalRestrict(); void removeLocalCVRQualifiers(unsigned Mask); @@ -1799,9 +1834,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 +2074,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 +2210,8 @@ public: bool hasOwnedFields() const; bool hasBorrowFields() const; - #endif + +#endif /// Test for a particular builtin type. bool isSpecificBuiltinType(unsigned K) const; @@ -2905,11 +2941,11 @@ 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; - #endif +#endif }; /// Represents a type which was implicitly adjusted by the semantic @@ -4069,18 +4105,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 +4448,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,6 +4971,7 @@ protected: withBorrow, withoutBorrow }; + mutable ownedStatus hasOwn = ownedStatus::unInitOwned; mutable borrowStatus hasBorrow = borrowStatus::unInitBorrow; #endif @@ -4952,7 +4993,9 @@ public: bool hasBorrowFields() const; void initBorrowStatus() const; - #endif + + bool hasFatFields() const; +#endif bool isSugared() const { return false; } QualType desugar() const { return QualType(this, 0); } @@ -6986,6 +7029,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 +7057,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 +7090,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/Analysis/Analyses/BSCFatPtrCheck.h b/clang/include/clang/Analysis/Analyses/BSCFatPtrCheck.h new file mode 100644 index 0000000000000000000000000000000000000000..e9df8841a98993dc2338f4d11714f8150fcd28bc --- /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/include/clang/Basic/BSC/BSCAttr.td b/clang/include/clang/Basic/BSC/BSCAttr.td index ee4128dd97b5a2a4ff9bc4ca91cf82330064e315..5de2437c70fdc04de265a939a67963b388cd1735 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 61485f99f7c2c8c83e63e1039a7eb51979d2560d..fad65b1fe4939bba4da4d18465ba3bcd5483fcf9 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,28 @@ 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_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">; +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">; +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/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def index 6530a91d7f785238abe87acc4710b389e99104a9..01b65d0b44a6ae204953b501ab1205460b42804a 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 0def6903efe1341709f6d73ebc912c2d1a8af691..e2f7b89cff7c98f57848f8b0ea4ac4d833ff922d 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 1b7d0328d08c4f108fc4d1f91b02264f32eb13aa..6c782e581767e5cce5d789df841a79f5c25bab31 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 2f84558ae9fd2283ec8949cd71098920b56ee770..bf2f3c3e1bf450731484f493a7546027948d4b67 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 f492feaabcf852abe1676e94079a129b0f98c403..4af40cb08a0cd92ec1275930625c42b1fa88f117 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,7 +3220,22 @@ public: bool IsBSCCompatibleFutureType(QualType Ty); + // BSC Fat Ptr related. + bool CheckFatQualTypeCStyleCast(QualType LHSType, Expr *RHSExpr); + bool CheckFatQualTypeCStyleCast(QualType LHSType, QualType RHSType); + bool CheckFatQualTypeAssignment(QualType LHSType, Expr *RHSExpr); + 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. + bool IsCallDestructorExpr(Expr *E); BSCMethodDecl *getOrInsertBSCDestructor(RecordDecl *RD); void HandleBSCDestructorBody(RecordDecl *RD, BSCMethodDecl *Destructor, std::stack InstanceFields); @@ -12316,20 +12327,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 +12423,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 +12458,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 11d3138573fd14aa6bede638da4ddac0794a0c7f..d730b1638a11a37636066b98719531734449358c 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) { @@ -10150,7 +10189,10 @@ bool ASTContext::typesAreCompatible(QualType LHS, QualType RHS, bool CompareUnqualified) { if (getLangOpts().CPlusPlus) return hasSameType(LHS, RHS); - +#if ENABLE_BSC + 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/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index 196d193d5b249886f6a23f4408c39fa40d920f34..38e841e5c02d662027af609976bbe2a9466a47b5 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 bba9b782c748f8eec03dac9fdf0fce091154196a..5f51c1388ca9baa4a13999185e4269d5206067e8 100644 --- a/clang/lib/AST/BSC/TypeBSC.cpp +++ b/clang/lib/AST/BSC/TypeBSC.cpp @@ -66,6 +66,94 @@ 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(); + } + 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; +} + +// return true T *fat, _FatPtr, ... +bool QualType::isFatPtrType() const { + if (getTypePtr()->isArrayType()) + return false; + QualType CanQT = getCanonicalType(); + return CanQT.isLocalFatQualified() || CanQT.isFatPtrRecordType(); +} + +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 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 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; +} + bool FunctionProtoType::hasOwnedRetOrParams() const { if (getReturnType().isOwnedQualified()) { return true; @@ -90,6 +178,19 @@ bool FunctionProtoType::hasBorrowRetOrParams() const { return false; } +bool FunctionProtoType::hasFatRetOrParams() const { + QualType ReturnType = getReturnType(); + if (ReturnType.hasFat()) { + return true; + } + for (auto ParamType : getParamTypes()) { + if (ParamType.hasFat()) { + return true; + } + } + return false; +} + bool Type::checkFunctionProtoType(SafeZoneSpecifier SZS) const { const FunctionProtoType *FPT = nullptr; if (isFunctionType()) { diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index c55477431d52f1296d758eb2ef8ecc74a949edd5..025a70a754fa08f99660a9426e6ca21d5bbf3806 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 0d7aa2caeab15f0540f409300a5fd9aa32210084..c46223656371469eb70785680d62ca082857cc23 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -8037,8 +8037,19 @@ 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); @@ -8471,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()) @@ -8484,6 +8500,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); } @@ -8802,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); } @@ -8810,7 +8839,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()) @@ -14818,8 +14851,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) @@ -15533,6 +15570,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 8b829627c235bf128416107b8186f1da64fa2d46..444b2e161204df29c20e13be047cb219a4071868 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 0cc42b5cf96f6d9f8f527c5e7a5df2a55e477256..8279e434ad67bd3673cd9a78fa56267cf9c95ccc 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 bc9243999a9c8dceb779aaa1aa5a2568c14eb098..7b4d8d3d47f8651a4f69b18beb9f3cbfc79e3b3d 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/BSCFatPtrCheck.cpp b/clang/lib/Analysis/BSCFatPtrCheck.cpp new file mode 100644 index 0000000000000000000000000000000000000000..86900a4c8a0449a0423ad5f8a1ddb5f425b63058 --- /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 2695d570073142165d8c35c61e1240ea83a46884..97bcac8898d0a425129d7e708ceeadec2cfa1f56 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/Analysis/ThreadSafetyCommon.cpp b/clang/lib/Analysis/ThreadSafetyCommon.cpp index e6ab441058d0c5f56bb0b7cc9fa37d60d9aede8c..89b71e7a37e4fa3bc96ff9f9f3a472131830f644 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 dfa78bf59c65808acaadf4c0d1371c090afe2b11..4ff156690c8210a2be50d21282060cad284a629e 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 73b05690537d94927aab3e7cd10e96b5c99639dd..c9d21233d4ea530710ce940e267bfbe811c57632 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 c689fa310858f069fa18497a1de4dd465472b0a3..9a0fba739fe036bb68149e589a11e4acdbb9ac6e 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 f32da6d90e6514095f24b1246818e9a4a2d4431c..543964111f2c93064b566b926edfa8da3199b154 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 158dfb759f75964c6455f34a819f9c10cfa6d518..81faf5ebd67c13378bfb83ef3e789adfd73f6a36 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 0000000000000000000000000000000000000000..5b1c32a1cb8728da6da8602bfdd015704b6f8302 --- /dev/null +++ b/clang/lib/Headers/bsc_include/bsc_fat_ptr.hbs @@ -0,0 +1,161 @@ +/*===---------- 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 + +// 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; + int32_t offset; +}; + +struct _AllocationUnit { + uint32_t lock_version; + uint32_t size; + 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 *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); +} + +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) { + if (payload_bytes == 0) + _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 = (T*)(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; + } + 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.offset != 0) + _report_error(FreeNotHeadPointer, 0, 0, 0); + memset(phead, 0, phead->size + 8); + free(phead); +} + +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, + // 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); + } + } +} + +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, + // 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); + } + } +} + +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/Lex/Lexer.cpp b/clang/lib/Lex/Lexer.cpp index 27cdfc68c13e63c7a949642134aa84473533606f..c38f31d4324ac60c44b7aa5727e14ed29c0ad352 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 3faa4ceba9ca2408482efd1a7d14c24dbdb63e30..a577b8ff8cc137ceb83ae28e84830e5e537c782e 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -2233,12 +2233,28 @@ 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 (getLangOpts().EnableFatPtr) + Actions.DesugarFunctionDeclWithFatPtr(FD); + 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.hasFat()) { + Actions.DesugarGlobalVarDeclWithFatPtr(VD); + } } } } @@ -4519,6 +4535,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 +5632,7 @@ bool Parser::isTypeSpecifierQualifier() { #if ENABLE_BSC case tok::kw_owned: case tok::kw_borrow: + case tok::kw_fat: #endif // Debugger support. @@ -5785,6 +5808,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 +6132,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 0a6fd4e9e2411eca82b38d686c0eb10bf4500606..14dbb8e519a5294483778d46afa9552b503a0bac 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 a67c1c8dfea5da0973605bc68831a3551aff9036..18aa747e27fa93c6ae2c873b3fa1efed87933929 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 edc2674d198021d30649f46810ca04498e45b938..7ab37e21fc0df218f9661b12a5c54c6610e0bb08 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" @@ -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 new file mode 100644 index 0000000000000000000000000000000000000000..56354522040579591a67a1bdd371482901da596f --- /dev/null +++ b/clang/lib/Sema/BSC/SemaBSCFatPtr.cpp @@ -0,0 +1,2410 @@ +//===--- 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; + } + } +}; + +// 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; + + // 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); + + 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]` + // 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); + // 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); + 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 TransformConditionalOperator(ConditionalOperator *CO); + ExprResult TransformCompoundAssignOperator(CompoundAssignOperator *CAO); + + ExprResult TransformBinaryOperator(BinaryOperator *BO); + ExprResult HandleBOFatPtrAddOrSubInteger(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); + 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); + 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())); + + 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()); + + // Third traversal + CompoundStmt *NewCS = + HandleLocalAllocationUnit(dyn_cast(FD->getBody()), true); + + // 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()) { + 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()); + } + } +} + +void TransformFatPtr::TransformRecordDecl(RecordDecl *RD) { + // Desugar fat ptr field. + for (FieldDecl *FD : RD->fields()) + 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 +// `_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 || (!Finder.LocalNoArrayVarAddressIsFatTaken && + Finder.LocalArrayVarsSet.size() == 0)) + 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); + } + 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 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; + } + } + if (!NeedToReplace) + 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 + 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()) + 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; + + // 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()) { + DesugaredStatements[CS].push_back(CheckCallExpr.top().second); + CheckCallExpr.pop(); + } + DesugaredStatements[CS].push_back(Result.getAs()); + } + 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; +} + +// 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 (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) { + SourceLocation SLoc = ICE->getBeginLoc(); + QualType QT = ICE->getType(); + if (DesugarToMemberPtr && QT.isFatPtrType()) { + QT = DesugarFatPtrType(SLoc, QT.getFatPtrPointeeType()); + ICE->setType(SemaRef.Context.getPointerType(QT)); + ICE->setCastKind(CK_LValueToRValue); + } else { + 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()) + 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()); + PE->setType(SemaRef.Context.getPointerType(QT)); + } else { + QT = DesugarFatPtrType(SLoc, QT); + PE->setType(QT); + } + PE->setSubExpr(BaseTransform::TransformExpr(PE->getSubExpr()).get()); + return 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); + 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() && 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 };` + RS->setRetValue(BuildFatPtrCompoundLiteralExprForRawPtr(RV, ReturnQT)); + } else { + RS->setRetValue(BaseTransform::TransformExpr(RV).get()); + } + 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()); + } + } + } + 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::TransformArraySubscriptExpr(ArraySubscriptExpr *ASE) { + SourceLocation SLoc = ASE->getBeginLoc(); + 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(); + 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 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. + CurrentASEToAccessMemberThroughFatPtr = ASE; + } + ASE->setLHS(BaseTransform::TransformExpr(LHS).get()); + ASE->setRHS(BaseTransform::TransformExpr(RHS).get()); + return MemberRawPtr ? MemberRawPtr : 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` + CSCE->setCastKind(CK_NoOp); + 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(); + Expr *FatPtrE = BuildFatPtrCompoundLiteralExprForRawPtr(SubRawPtrE, QT); + 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()); + 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; +} + +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); + + Expr *LHS = BO->getLHS(); + Expr *RHS = BO->getRHS(); + 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 + // `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()) { + // 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; + } else if (BO->isAssignmentOp() && LHSQT.isFatPtrType() && + 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() && + RHS->isNullExpr(SemaRef.Context)) { + // `p == nullptr` should be desugared to `p.raw_ptr == nullptr` + DesugarToMemberPtr = true; + BO->setLHS(BaseTransform::TransformExpr(LHS).get()); + return BO; + } + BO->setLHS(BaseTransform::TransformExpr(LHS).get()); + BO->setRHS(BaseTransform::TransformExpr(RHS).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(); + 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->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) { + 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); + 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; +} + +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)` + 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)) { + if (ICE->getCastKind() == CK_LValueToRValue) { + return UO; + } + } + UO->setSubExpr(ImplicitCastExpr::Create(SemaRef.Context, SubE->getType(), + CK_LValueToRValue, SubE, nullptr, + VK_PRValue, FPOptionsOverride())); + 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); + + 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; + 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 MemberRawPtr ? MemberRawPtr : 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) { + 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 = + SemaRef.Context.getTranslationUnitDecl()->lookup( + DeclarationName(&SemaRef.Context.Idents.get("_FatPtr"))); + if (Decls.isSingleResult()) + if (ClassTemplateDecl *CTD = dyn_cast(Decls.front())) + 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)) { + QualType PointeeT = DesugarFatPtrType(SLoc, PT->getPointeeType()); + return SemaRef.Context.getPointerType(PointeeT); + } + return T; + } + SemaRef.Diag(SLoc, diag::err_fat_ptr_type_not_found) << ""; + 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) { + 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 *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; +} + +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(), 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 ((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, + 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)) { + if (isa(ICE->getSubExpr())) { + RawPtrE = ICE->getSubExpr(); + } + } + 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); +} + +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(RHSExpr->getBeginLoc(), diag::err_fat_qualcheck_incompatible) + << 1 << RHSType << 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; + } + + if ((LHSCanType.isFatPtrType() && RHSCanType->isIntegerType()) || + (RHSCanType.isFatPtrType() && LHSCanType->isIntegerType())) + return false; + + // 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; + // T1 *fat *fat <-> T2 **fat is ERROR + // T1 *fat * <-> T2 ** is ERROR + // T1 *fat *fat <-> T2 ** is ERROR + return false; + } + + // 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; +} + +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(); + + // 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 + } + return false; + } + + // 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 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) { + 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(); + if (HasDiffFatTypeAtBothFunction(QualType(LHSFuncType, 0), + QualType(RHSFuncType, 0))) { + 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); + 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 +// __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 = { 1, 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 : 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 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 c96493b7f583a7b2f924ae5c3e054c50a098b131..229f30a2f5adb8541bbe65661b629653e551bfe8 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 cad99765f657745e13adcd00b8d51d23a940798e..f3813e181333b47dea93b254ecf85852403cc626 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 28cb56b03c89f69c31db8e8652b0e30d590332f1..d0f2a292fce2f4293699acdb2b6f9cb77e19f597 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" @@ -151,9 +152,9 @@ bool Sema::HasSafeZoneInFunction(const FunctionDecl* FnDecl) { return HasSafeZoneInCompoundStmt(FuncBody); } -void Sema::BSCDataflowAnalysis(const Decl *D, bool EnableOwnershipCheck, +void Sema::BSCDataflowAnalysis(const FunctionDecl *FD, bool EnableOwnershipCheck, bool EnableNullabilityCheck) { - AnalysisDeclContext AC(/* AnalysisDeclContextManager */ nullptr, D); + AnalysisDeclContext AC(/* AnalysisDeclContextManager */ nullptr, FD); AC.getCFGBuildOptions().PruneTriviallyFalseEdges = true; AC.getCFGBuildOptions().AddEHEdges = false; @@ -164,8 +165,7 @@ void Sema::BSCDataflowAnalysis(const Decl *D, bool EnableOwnershipCheck, AC.getCFGBuildOptions().AddAllScopes = true; AC.getCFGBuildOptions().setAllAlwaysAdd(); - if (AC.getCFG()) { - const FunctionDecl *FD = cast(D); + if (CFG *cfg = AC.getCFG()) { unsigned NumNullabilityCheckErrorsInCurrFD = 0; LangOptions::NullCheckZone CheckZone = getLangOpts().getNullabilityCheck(); if (EnableNullabilityCheck && (CheckZone == LangOptions::NC_ALL || HasSafeZoneInFunction(FD))) { @@ -189,6 +189,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/CMakeLists.txt b/clang/lib/Sema/CMakeLists.txt index a665edbb358a22451336d54958a4655682d26714..e408444fa5652504570423a03aab390ee926dc7a 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 7226987274ebf4c650741af3ba84186dbf9ac529..a8dee67b7dc6119b64ff9df0d54d1a3787061ed7 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 a5a00c76f6497f4c872275027db963178e49546e..e0780e8c796b92e0e261fa1dd29b7e2da7ee2a74 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,29 +2207,6 @@ Sema::PopFunctionScopeInfo(const AnalysisBasedWarnings::Policy *WP, if (LangOpts.OpenMP) popOpenMPFunctionRegion(Scope.get()); -#if ENABLE_BSC - // If D does not use memory safety features like "owned, borrow, &mut, &const", - // we do not do borrow checking. - bool useSafeFeature = LangOpts.BSC ? FindSafeFeatures(dyn_cast_or_null(D)) : false; - bool EnableOwnershipCheck = useSafeFeature && !(getLangOpts().DisableOwnershipCheck); - 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 b174416b2a54141d7e60aecb9b218d61a37aebe5..1df1d1a28d4b98790c851bc9792964085d92c021 100644 --- a/clang/lib/Sema/SemaCast.cpp +++ b/clang/lib/Sema/SemaCast.cpp @@ -2879,43 +2879,63 @@ 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 (SrcType.hasFat() || DestType.hasFat()) { + if (!Self.CheckFatQualTypeCStyleCast(DestType, SrcExpr.get())) { 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 (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() || + DestCanType.isFatPtrRecordType()) { + return; } - return; } #endif diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 27347cea4684378753184da53418728f39aa403b..b4b68ff1788b3cf1ebd5a4e656c2ea6bd6e32954 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; } @@ -15986,12 +16009,38 @@ 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)) { + // If FD does not use memory safety features like "owned, + // borrow, &mut, &const", we do not do borrow checking. + bool EnableOwnershipCheck = + !LangOpts.DisableOwnershipCheck && FindSafeFeatures(FD); + bool EnableNullabilityCheck = + LangOpts.getNullabilityCheck() != LangOptions::NC_NONE; + if ((EnableOwnershipCheck || EnableNullabilityCheck) && + getDiagnostics().getNumErrors() == + getDiagnostics().getNumOwnershipErrors() + + getDiagnostics().getNumBorrowCheckErrors() + + getDiagnostics().getNumNullabilityCheckErrors()) { + auto md = dyn_cast_or_null(FD); + // Don't check ownership rules of destructor parameters + if (!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. @@ -18986,6 +19035,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 d3cd9eb36026eebf4b2154fa801576a0c879f665..dd35eac8f173a49987a77e636c9a4f1e13b36d45 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 8fc00e78e8319545b8b9b9a821e5fcd15b6dfecf..5c2da6baaf1a6b321e25a566e7c172afc7432199 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 @@ -5895,6 +5896,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; @@ -7121,7 +7129,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) @@ -8655,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; @@ -9234,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); @@ -9697,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()); @@ -10440,6 +10473,17 @@ static bool IsTraitEqualExpr(Sema &S, QualType DstType, QualType SrcType, } return false; } + +static bool IsFatPtrEqualExpr(Sema &S, QualType DstType, QualType SrcType) { + if (DstType.isFatPtrType() && + (SrcType.isFatPtrType() || SrcType->isNullPtrType())) { + return true; + } + if (DstType->isVoidPointerType() && SrcType.isFatPtrType()) { + return true; + } + return false; +} #endif Sema::AssignConvertType @@ -10473,10 +10517,16 @@ 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 (const auto *LHSPtrType = LHSType->getAs()) { if (const auto *RHSPtrType = RHS.get()->getType()->getAs()) { if (LHSPtrType->hasOwnedFields() || RHSPtrType->hasOwnedFields()) { @@ -10484,22 +10534,20 @@ 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 (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())) { @@ -13437,7 +13485,8 @@ QualType Sema::CheckCompareOperands(ExprResult &LHS, ExprResult &RHS, } #if ENABLE_BSC - if (getLangOpts().BSC && LHSType->isPointerType() && RHSType->isNullPtrType()) + if (getLangOpts().BSC && (RHSType.isFatPtrType() || RHSType->isNullPtrType()) && + (LHSType->isPointerType() || LHSType.isFatPtrType())) return computeResultTy(); #endif @@ -13985,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); @@ -14908,11 +14966,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; @@ -14966,6 +15025,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; } @@ -15193,7 +15274,6 @@ QualType Sema::CheckAddressOfOperand(ExprResult &OrigOp, SourceLocation OpLoc) { return Context.getObjCObjectPointerType(op->getType()); CheckAddressOfPackedMember(op); - return Context.getPointerType(op->getType()); } @@ -15240,6 +15320,11 @@ 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(); @@ -15341,6 +15426,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; @@ -15624,157 +15712,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(); @@ -16269,7 +16366,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 && @@ -16378,19 +16478,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: { @@ -16476,6 +16577,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) { @@ -17906,6 +18012,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; @@ -17921,6 +18029,7 @@ bool Sema::DiagnoseAssignmentResult(AssignConvertType ConvTy, #if ENABLE_BSC case IncompatibleOwnedPointer: case IncompatibleBorrowPointer: + case IncompatibleFatPointer: return false; case IncompatibleBSCSafeZone: return true; @@ -20875,6 +20984,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(); @@ -20908,6 +21022,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/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index 4a98035b77d9e0a12e317db23cf778fca9ccb106..a97fd9eaefde5392ac8f9d10d338b48ea53c0aeb 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(); diff --git a/clang/lib/Sema/SemaExprMember.cpp b/clang/lib/Sema/SemaExprMember.cpp index 112c767a1b045705ce2c956ab0bf306f108a69eb..1615ed62b836f93bec798fd8fa74eff8e4696667 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 8244126a2fac4197d68f28636ae1da534aac8e72..2cfb27547633576b841076fab4d502aad445be84 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 @@ -8041,7 +8048,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 +8063,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/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp index 64a83ad410605e4a28341c7e8b2748d8dec777c1..13f9d49d1c17bed29506ca217e5afebce7c3822f 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 f04325d2063bc014d28ca5f2bdfa000bd05cf982..e4c4c48200dd1b4d90d86f8023c3c4ee09bb01dd 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -1999,10 +1999,22 @@ QualType Sema::BuildQualifiedType(QualType T, SourceLocation Loc, Qs.removeVolatile(); } #if ENABLE_BSC - if (getLangOpts().BSC && Qs.hasBorrow()) { - if (!T->isPointerType()) + if (getLangOpts().BSC) { + if (Qs.hasBorrow() && !T->isPointerType()) { Diag(DS ? DS->getBorrowSpecLoc() : Loc, - diag::err_typecheck_invalid_borrow_not_pointer) << T; + 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/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp index cb19ab6202fa7992edf8e2f2968c4382785aaefb..3dac2d46a7cf0afea77fda68fee5e65c6ce6f296 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 0000000000000000000000000000000000000000..c9c3f31b38d850ad845a2d73015156ee170e0445 --- /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 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 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 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 implicitly 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_explicit_cast.cbs b/clang/test/BSC/Negative/FatPtr/fat_explicit_cast.cbs new file mode 100644 index 0000000000000000000000000000000000000000..970a18d4398cdb1562706ce402c6fb5482f69c0e --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/fat_explicit_cast.cbs @@ -0,0 +1,69 @@ +// RUN: %clang_cc1 -verify %s +void foo1(int *fat p); +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'}} +} + +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; + 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); + + 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}} + } +} + +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 0000000000000000000000000000000000000000..1c705bf1af812e12a71b37473b1deec0bec8e94c --- /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 0000000000000000000000000000000000000000..56d1eec8a21754980ba1844f0d939ea1533cc3af --- /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 new file mode 100644 index 0000000000000000000000000000000000000000..2ef4bd8a7e41cb1437e393892133001ec9483ac4 --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/fat_qulifier_only_for_pointer.cbs @@ -0,0 +1,23 @@ +// 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/func_ptr_has_fat.cbs b/clang/test/BSC/Negative/FatPtr/func_ptr_has_fat.cbs new file mode 100644 index 0000000000000000000000000000000000000000..2e2e1f07225e5bf21970e0b3f81d27fbf5423080 --- /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 0000000000000000000000000000000000000000..ff136be638805314a58f78e640350b9cffd293b1 --- /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 new file mode 100644 index 0000000000000000000000000000000000000000..bcd766bdb6e44c4a693e48a1de265d98b2fd5fbf --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/global_array/array_out_of_bounds1.cbs @@ -0,0 +1,13 @@ +// RUN: %clang -enable-fat-ptr %s -o %t.output +// RUN: not %t.output 2>&1 | FileCheck %s + +#include + +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 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 0000000000000000000000000000000000000000..5b260692ffc0cdffa02d1fd323f90e63c59879dc --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/global_array/array_out_of_bounds2.cbs @@ -0,0 +1,15 @@ +// 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: 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 new file mode 100644 index 0000000000000000000000000000000000000000..975c7550754fc045efe62bea42cca3027698232e --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/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 *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 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 0000000000000000000000000000000000000000..61354828836e6e8db76b8c474ba6a8b4d6f332fe --- /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 0000000000000000000000000000000000000000..2d354b2f52cf2948638c22fdee999cf5b84f8f28 --- /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 0000000000000000000000000000000000000000..f905d5644648476a65385cb1bda12f547c53945a --- /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 0000000000000000000000000000000000000000..a6cdfadb2e08e8c7edb7813338203082d7b10ebd --- /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 0000000000000000000000000000000000000000..0a550df0117d2e5fd71a2f7f84b62f9441b4bdd8 --- /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 0000000000000000000000000000000000000000..281f9d9c6dd08d6a23e7fabbcfe9c8a5664e4962 --- /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 0000000000000000000000000000000000000000..4a4060a9a2b8969703832e1b34a40ee437ee3ea5 --- /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 0000000000000000000000000000000000000000..7259b679359748b54de5ec1f97af1593cce5de4f --- /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 0000000000000000000000000000000000000000..9e5dcc4c251909d8afdccb4fa4949b8b5dded692 --- /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 0000000000000000000000000000000000000000..64f0f702afa64e357ed99c48704e40780c337ff3 --- /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 new file mode 100644 index 0000000000000000000000000000000000000000..51835b6ee0e2756bae995f39aa9d8c0151ca11dc --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds2.cbs @@ -0,0 +1,12 @@ +// 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: 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 new file mode 100644 index 0000000000000000000000000000000000000000..26e2f6ce73756ef4539c494e65f8d08aac17a9bc --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds3.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(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 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 0000000000000000000000000000000000000000..7b7305318d45ba5051addbaf42b4955f1cffb8fb --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/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 + +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: 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 new file mode 100644 index 0000000000000000000000000000000000000000..b80177c9e6aefec6f5aba7136e2ad116c42adafd --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds5.cbs @@ -0,0 +1,14 @@ +// 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: 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 new file mode 100644 index 0000000000000000000000000000000000000000..cd4c9e50872271f1b029f6878ffced4d904a2d92 --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/array_out_of_bounds6.cbs @@ -0,0 +1,12 @@ +// 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: 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 0000000000000000000000000000000000000000..42b814927dccef5904aa0dfcb8d52e3674453b02 --- /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 0000000000000000000000000000000000000000..f29da60932a462bd4eac4dd1612e8948c60b16e5 --- /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 0000000000000000000000000000000000000000..904126abeadb7baf815bac5076682dbe2df3b448 --- /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 new file mode 100644 index 0000000000000000000000000000000000000000..a40d1bc46c3b5cdbd48bfe49aca672af090ad669 --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/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 *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 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 0000000000000000000000000000000000000000..a9c67262df808e6095fc6757973943d45ab06631 --- /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: 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 new file mode 100644 index 0000000000000000000000000000000000000000..64a65afc5220f4a6f3bf50951adb86502ab06188 --- /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: 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 new file mode 100644 index 0000000000000000000000000000000000000000..26db032c3e72130746fe402d535a4703beb42174 --- /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: 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 new file mode 100644 index 0000000000000000000000000000000000000000..8d294688d9f82df655f1f3c9873a585a46f0a258 --- /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: 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 new file mode 100644 index 0000000000000000000000000000000000000000..fbf926b906afb16673d3ec3f439b6d7d8aecbe48 --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free1.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)); + *p = 10; + 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 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 0000000000000000000000000000000000000000..9faa09e3f4c32e7f83ebf78c417e50d5cd68b7b2 --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free2.cbs @@ -0,0 +1,16 @@ +// 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: 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 new file mode 100644 index 0000000000000000000000000000000000000000..fe79ee91d3f897a9df6441cc72ba9e369e159792 --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free3.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(); + *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 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 0000000000000000000000000000000000000000..08ce74e36d4dc8bada228f705ebdc3b4267c567c --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free4.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); + *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 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 0000000000000000000000000000000000000000..1c37a3ec2e69371976f7c7e1e91d43f384625909 --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free5.cbs @@ -0,0 +1,30 @@ +// 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: 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 new file mode 100644 index 0000000000000000000000000000000000000000..1159a594974cea53cc6a1735976f740b2bba6b7f --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/heap/use_after_free6.cbs @@ -0,0 +1,15 @@ +// 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: 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 0000000000000000000000000000000000000000..0e96d5e77f6fc08912c9f300dac794915946d7c8 --- /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 0000000000000000000000000000000000000000..04b70ea5aa83ca98021a7bd9f5b387b363f37578 --- /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 new file mode 100644 index 0000000000000000000000000000000000000000..e99f7725e1b54d92b394c5b51ec574b4f96929ee --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/array_out_of_bounds1.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[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/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 new file mode 100644 index 0000000000000000000000000000000000000000..2b1425733499137e11125342d20d5fc6b3161349 --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/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 + +int main() { + int arr[3] = { 1, 2, 3 }; + int *fat p = &fat arr[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_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 0000000000000000000000000000000000000000..52a72be91eebfdf5d88aac1de5b801ec8985b1e4 --- /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 0000000000000000000000000000000000000000..2c6abd37814b43b21d5455c5ac2bfeedd9c3df78 --- /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 new file mode 100644 index 0000000000000000000000000000000000000000..610d45c170c31afd45f8c37bd49ddb0942bb9230 --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/buffer_overflow.cbs @@ -0,0 +1,23 @@ +// 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: 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 new file mode 100644 index 0000000000000000000000000000000000000000..c2d89d83e759490b2901622de9acf520be894f92 --- /dev/null +++ b/clang/test/BSC/Negative/FatPtr/version_and_offset_check/stack/return_local_address.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 local; + int *fat p = &fat local; + return p; +} + +int main() { + int *fat p = test(); + *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 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 0000000000000000000000000000000000000000..7f4893eab5f06f21e5c61a9ad7ac22b642c13deb --- /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 0000000000000000000000000000000000000000..f30b12beda57f8696cb87a52f25aa661d3b1369e --- /dev/null +++ b/clang/test/BSC/Positive/FatPtr/heap.cbs @@ -0,0 +1,116 @@ +// 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; +} + +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(); + test3(); + test4(); + test5(); + test6(); + test7(); + 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 0000000000000000000000000000000000000000..2989912aba2b1ee593e72f6193dd59246c544f3d --- /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/nullptr_cast/nullptr_cast.cbs b/clang/test/BSC/Positive/FatPtr/nullptr_cast/nullptr_cast.cbs new file mode 100644 index 0000000000000000000000000000000000000000..9a47ca5dac2bed5ad720afb022d4e55ed27a3d18 --- /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 0000000000000000000000000000000000000000..43ba617761990f3ac962bc6545a8f20601ff545c --- /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 0000000000000000000000000000000000000000..cc3f2cb278aa852830b987b70c32ddadbb2848a6 --- /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 0000000000000000000000000000000000000000..8065f96203510bac1eec2892e516ffa7b6e2eb5d --- /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 0000000000000000000000000000000000000000..c8af4371bf4b4cacb0e8202094a075bffc2d948b --- /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 0000000000000000000000000000000000000000..b8d075e41cb07c727547425dc09e2e8fff55c833 --- /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 0000000000000000000000000000000000000000..ad2fac0b66957ccb8e82f4a7af8e67af969ac2dd --- /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 0000000000000000000000000000000000000000..0b8014a09f3d501f9418e066dd18bb785d7678c6 --- /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 new file mode 100644 index 0000000000000000000000000000000000000000..c44dab32adcaab2dbf472130ea2d838c343a253c --- /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 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 0000000000000000000000000000000000000000..7460656d2b6d08dcdc4bbf1e1be3a53772e7b22e --- /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 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 0000000000000000000000000000000000000000..9c5c2136db1102528026fa7e728e2360f0bef03c --- /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; +}